/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/NPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): * L. David Baron * Pierre Phaneuf */ #include "plstr.h" #include "nsCOMPtr.h" #include "nsIInterfaceRequestor.h" #include "nsDocument.h" #include "nsIArena.h" #include "nsIURL.h" #include "nsILoadGroup.h" #include "nsIChannel.h" #include "nsString.h" #include "nsIContent.h" #include "nsIDocumentObserver.h" #include "nsIStyleSet.h" #include "nsIStyleSheet.h" #include "nsIPresShell.h" #include "nsIPresContext.h" #include "nsIDocumentObserver.h" #include "nsIEventListenerManager.h" #include "nsIScriptGlobalObject.h" #include "nsIScriptEventListener.h" #include "nsIDOMEvent.h" #include "nsIPrivateDOMEvent.h" #include "nsIEventStateManager.h" #include "nsContentList.h" #include "nsIObserver.h" #include "nsIDOMEventListener.h" #include "nsIDOMFormListener.h" #include "nsIDOMPaintListener.h" #include "nsIDOMMouseListener.h" #include "nsIDOMMouseMotionListener.h" #include "nsIDOMKeyListener.h" #include "nsIDOMFocusListener.h" #include "nsIDOMLoadListener.h" #include "nsIDOMStyleSheet.h" #include "nsIDOMStyleSheetList.h" #include "nsDOMAttribute.h" #include "nsDOMCID.h" #include "nsIDOMScriptObjectFactory.h" #include "nsIDOMDOMImplementation.h" #include "nsIDOMDocumentView.h" #include "nsIDOMAbstractView.h" #include "nsIDOMDocumentXBL.h" #include "nsIDOMNavigator.h" #include "nsGenericElement.h" #include "nsICSSStyleSheet.h" #include "nsITextContent.h" #include "nsIDocumentEncoder.h" #include "nsIHTMLContentSink.h" #include "nsIParser.h" #include "nsParserCIID.h" #include "nsIFileStreams.h" #include "nsRange.h" #include "nsIDOMText.h" #include "nsIDOMComment.h" #include "nsDOMDocumentType.h" #include "nsINameSpaceManager.h" #include "nsIServiceManager.h" #include "nsLayoutAtoms.h" #include "nsContentCID.h" #include "nsLayoutCID.h" #include "nsIDOMRange.h" #include "nsIEnumerator.h" #include "nsDOMError.h" #include "nsIScrollableView.h" #include "nsIFrame.h" #include "nsContentUtils.h" #include "nsNodeInfoManager.h" #include "nsIXBLService.h" #include "nsNetUtil.h" // for NS_MakeAbsoluteURI #include "nsIScriptSecurityManager.h" #include "nsIAggregatePrincipal.h" #include "nsIPrivateDOMImplementation.h" #include "nsIInterfaceRequestor.h" #include "nsIDOMWindowInternal.h" #include "nsIDOMElement.h" #include "nsIBoxObject.h" #include "nsPIBoxObject.h" #include "nsXULAtoms.h" static NS_DEFINE_CID(kDOMScriptObjectFactoryCID, NS_DOM_SCRIPT_OBJECT_FACTORY_CID); static NS_DEFINE_CID(kPresShellCID, NS_PRESSHELL_CID); static NS_DEFINE_IID(kCParserCID, NS_PARSER_IID); #include "nsILineBreakerFactory.h" #include "nsIWordBreakerFactory.h" #include "nsLWBrkCIID.h" #include "nsIHTMLDocument.h" class nsDOMStyleSheetList : public nsIDOMStyleSheetList, public nsIScriptObjectOwner, public nsIDocumentObserver { public: nsDOMStyleSheetList(nsIDocument *aDocument); virtual ~nsDOMStyleSheetList(); NS_DECL_ISUPPORTS NS_DECL_IDOMSTYLESHEETLIST NS_IMETHOD BeginUpdate(nsIDocument *aDocument) { return NS_OK; } NS_IMETHOD EndUpdate(nsIDocument *aDocument) { return NS_OK; } NS_IMETHOD BeginLoad(nsIDocument *aDocument) { return NS_OK; } NS_IMETHOD EndLoad(nsIDocument *aDocument) { return NS_OK; } NS_IMETHOD BeginReflow(nsIDocument *aDocument, nsIPresShell* aShell) { return NS_OK; } NS_IMETHOD EndReflow(nsIDocument *aDocument, nsIPresShell* aShell) { return NS_OK; } NS_IMETHOD ContentChanged(nsIDocument *aDocument, nsIContent* aContent, nsISupports* aSubContent) { return NS_OK; } NS_IMETHOD ContentStatesChanged(nsIDocument* aDocument, nsIContent* aContent1, nsIContent* aContent2) { return NS_OK; } NS_IMETHOD AttributeChanged(nsIDocument *aDocument, nsIContent* aContent, PRInt32 aNameSpaceID, nsIAtom* aAttribute, PRInt32 aHint) { return NS_OK; } NS_IMETHOD ContentAppended(nsIDocument *aDocument, nsIContent* aContainer, PRInt32 aNewIndexInContainer) { return NS_OK; } NS_IMETHOD ContentInserted(nsIDocument *aDocument, nsIContent* aContainer, nsIContent* aChild, PRInt32 aIndexInContainer) { return NS_OK; } NS_IMETHOD ContentReplaced(nsIDocument *aDocument, nsIContent* aContainer, nsIContent* aOldChild, nsIContent* aNewChild, PRInt32 aIndexInContainer) { return NS_OK; } NS_IMETHOD ContentRemoved(nsIDocument *aDocument, nsIContent* aContainer, nsIContent* aChild, PRInt32 aIndexInContainer) { return NS_OK; } NS_IMETHOD StyleSheetAdded(nsIDocument *aDocument, nsIStyleSheet* aStyleSheet); NS_IMETHOD StyleSheetRemoved(nsIDocument *aDocument, nsIStyleSheet* aStyleSheet); NS_IMETHOD StyleSheetDisabledStateChanged(nsIDocument *aDocument, nsIStyleSheet* aStyleSheet, PRBool aDisabled) { return NS_OK; } NS_IMETHOD StyleRuleChanged(nsIDocument *aDocument, nsIStyleSheet* aStyleSheet, nsIStyleRule* aStyleRule, PRInt32 aHint) { return NS_OK; } NS_IMETHOD StyleRuleAdded(nsIDocument *aDocument, nsIStyleSheet* aStyleSheet, nsIStyleRule* aStyleRule) { return NS_OK; } NS_IMETHOD StyleRuleRemoved(nsIDocument *aDocument, nsIStyleSheet* aStyleSheet, nsIStyleRule* aStyleRule) { return NS_OK; } NS_IMETHOD DocumentWillBeDestroyed(nsIDocument *aDocument); // nsIScriptObjectOwner interface NS_IMETHOD GetScriptObject(nsIScriptContext *aContext, void** aScriptObject); NS_IMETHOD SetScriptObject(void* aScriptObject); protected: PRInt32 mLength; nsIDocument* mDocument; void* mScriptObject; }; nsDOMStyleSheetList::nsDOMStyleSheetList(nsIDocument *aDocument) { NS_INIT_REFCNT(); mLength = -1; // Not reference counted to avoid circular references. // The document will tell us when its going away. mDocument = aDocument; mDocument->AddObserver(this); mScriptObject = nsnull; } nsDOMStyleSheetList::~nsDOMStyleSheetList() { if (nsnull != mDocument) { mDocument->RemoveObserver(this); } mDocument = nsnull; } NS_IMPL_ADDREF(nsDOMStyleSheetList) NS_IMPL_RELEASE(nsDOMStyleSheetList) NS_INTERFACE_MAP_BEGIN(nsDOMStyleSheetList) NS_INTERFACE_MAP_ENTRY(nsIDOMStyleSheetList) NS_INTERFACE_MAP_ENTRY(nsIScriptObjectOwner) NS_INTERFACE_MAP_ENTRY(nsIDocumentObserver) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMStyleSheetList) NS_INTERFACE_MAP_END NS_IMETHODIMP nsDOMStyleSheetList::GetLength(PRUint32* aLength) { if (nsnull != mDocument) { // XXX Find the number and then cache it. We'll use the // observer notification to figure out if new ones have // been added or removed. if (-1 == mLength) { PRUint32 count = 0; PRInt32 i, imax = mDocument->GetNumberOfStyleSheets(); for (i = 0; i < imax; i++) { nsCOMPtr sheet(dont_AddRef(mDocument->GetStyleSheetAt(i))); if (!sheet) continue; nsCOMPtr domss(do_QueryInterface(sheet)); if (domss) { count++; } } mLength = count; } *aLength = mLength; } else { *aLength = 0; } return NS_OK; } NS_IMETHODIMP nsDOMStyleSheetList::Item(PRUint32 aIndex, nsIDOMStyleSheet** aReturn) { *aReturn = nsnull; if (nsnull != mDocument) { PRUint32 count = 0; PRInt32 i, imax = mDocument->GetNumberOfStyleSheets(); // XXX Not particularly efficient, but does anyone care? for (i = 0; (i < imax) && (nsnull == *aReturn); i++) { nsCOMPtr sheet(dont_AddRef(mDocument->GetStyleSheetAt(i))); if (!sheet) continue; nsCOMPtr domss(do_QueryInterface(sheet)); if (domss) { if (count++ == aIndex) { *aReturn = domss; NS_IF_ADDREF(*aReturn); } } } } return NS_OK; } NS_IMETHODIMP nsDOMStyleSheetList::GetScriptObject(nsIScriptContext *aContext, void** aScriptObject) { nsresult res = NS_OK; if (nsnull == mScriptObject) { nsISupports *supports = (nsISupports *)(nsIDOMStyleSheetList *)this; nsISupports *parent = (nsISupports *)mDocument; // XXX Should be done through factory res = NS_NewScriptStyleSheetList(aContext, supports, parent, (void**)&mScriptObject); } *aScriptObject = mScriptObject; return res; } NS_IMETHODIMP nsDOMStyleSheetList::SetScriptObject(void* aScriptObject) { mScriptObject = aScriptObject; return NS_OK; } NS_IMETHODIMP nsDOMStyleSheetList::StyleSheetAdded(nsIDocument *aDocument, nsIStyleSheet* aStyleSheet) { if (-1 != mLength) { nsCOMPtr domss(do_QueryInterface(aStyleSheet)); if (domss) { mLength++; } } return NS_OK; } NS_IMETHODIMP nsDOMStyleSheetList::StyleSheetRemoved(nsIDocument *aDocument, nsIStyleSheet* aStyleSheet) { if (-1 != mLength) { nsCOMPtr domss(do_QueryInterface(aStyleSheet)); if (domss) { mLength--; } } return NS_OK; } NS_IMETHODIMP nsDOMStyleSheetList::DocumentWillBeDestroyed(nsIDocument *aDocument) { if (nsnull != mDocument) { aDocument->RemoveObserver(this); mDocument = nsnull; } return NS_OK; } // ================================================================== // = // ================================================================== class nsDOMImplementation : public nsIDOMDOMImplementation, public nsIScriptObjectOwner, public nsIPrivateDOMImplementation { public: nsDOMImplementation(nsIURI* aBaseURI = nsnull); virtual ~nsDOMImplementation(); NS_DECL_ISUPPORTS // nsIDOMDOMImplementation NS_IMETHOD HasFeature(const nsAReadableString& aFeature, const nsAReadableString& aVersion, PRBool* aReturn); NS_IMETHOD CreateDocumentType(const nsAReadableString& aQualifiedName, const nsAReadableString& aPublicId, const nsAReadableString& aSystemId, nsIDOMDocumentType** aReturn); NS_IMETHOD CreateDocument(const nsAReadableString& aNamespaceURI, const nsAReadableString& aQualifiedName, nsIDOMDocumentType* aDoctype, nsIDOMDocument** aReturn); // nsIScriptObjectOwner NS_IMETHOD GetScriptObject(nsIScriptContext *aContext, void** aScriptObject); NS_IMETHOD SetScriptObject(void *aScriptObject); //nsIPrivateDOMImplementation NS_IMETHOD Init(nsIURI* aBaseURI); protected: void *mScriptObject; nsCOMPtr mBaseURI; }; NS_LAYOUT nsresult NS_NewDOMImplementation(nsIDOMDOMImplementation** aInstancePtrResult) { nsDOMImplementation* domImpl = new nsDOMImplementation(); if (domImpl == nsnull) return NS_ERROR_OUT_OF_MEMORY; return domImpl->QueryInterface(NS_GET_IID(nsIDOMDOMImplementation), (void**) aInstancePtrResult); } nsDOMImplementation::nsDOMImplementation(nsIURI* aBaseURI) { NS_INIT_REFCNT(); mScriptObject = nsnull; mBaseURI = aBaseURI; } nsDOMImplementation::~nsDOMImplementation() { } NS_IMPL_ISUPPORTS4(nsDOMImplementation, nsIDOMDOMImplementation, nsIPrivateDOMImplementation, nsIScriptObjectOwner, nsIDOMDOMImplementation) NS_IMETHODIMP nsDOMImplementation::HasFeature(const nsAReadableString& aFeature, const nsAReadableString& aVersion, PRBool* aReturn) { return nsGenericElement::InternalIsSupported(aFeature, aVersion, aReturn); } NS_IMETHODIMP nsDOMImplementation::CreateDocumentType(const nsAReadableString& aQualifiedName, const nsAReadableString& aPublicId, const nsAReadableString& aSystemId, nsIDOMDocumentType** aReturn) { NS_ENSURE_ARG_POINTER(aReturn); return NS_NewDOMDocumentType(aReturn, aQualifiedName, nsnull, nsnull, aPublicId, aSystemId, nsString()); } NS_IMETHODIMP nsDOMImplementation::CreateDocument(const nsAReadableString& aNamespaceURI, const nsAReadableString& aQualifiedName, nsIDOMDocumentType* aDoctype, nsIDOMDocument** aReturn) { NS_ENSURE_ARG_POINTER(aReturn); *aReturn = nsnull; return NS_NewDOMDocument(aReturn, aNamespaceURI, aQualifiedName, aDoctype, mBaseURI); } NS_IMETHODIMP nsDOMImplementation::GetScriptObject(nsIScriptContext *aContext, void** aScriptObject) { nsresult result = NS_OK; if (!mScriptObject) { nsCOMPtr factory(do_GetService(kDOMScriptObjectFactoryCID, &result)); if (NS_OK == result) { nsCOMPtr global(dont_AddRef(aContext->GetGlobalObject())); result = factory->NewScriptDOMImplementation(aContext, (nsISupports*)(nsIDOMDOMImplementation*)this, global, &mScriptObject); } } *aScriptObject = mScriptObject; return result; } NS_IMETHODIMP nsDOMImplementation::SetScriptObject(void *aScriptObject) { mScriptObject = nsnull; return NS_OK; } NS_IMETHODIMP nsDOMImplementation::Init(nsIURI* aBaseURI) { mBaseURI = aBaseURI; return NS_OK; } // ================================================================== // = // ================================================================== MOZ_DECL_CTOR_COUNTER(nsDocumentChildNodes) nsDocumentChildNodes::nsDocumentChildNodes(nsIDocument* aDocument) { MOZ_COUNT_CTOR(nsDocumentChildNodes); // We don't reference count our document reference (to avoid circular // references). We'll be told when the document goes away. mDocument = aDocument; } nsDocumentChildNodes::~nsDocumentChildNodes() { MOZ_COUNT_DTOR(nsDocumentChildNodes); } NS_IMETHODIMP nsDocumentChildNodes::GetLength(PRUint32* aLength) { if (nsnull != mDocument) { PRInt32 count; mDocument->GetChildCount(count); *aLength = (PRUint32)count; } else { *aLength = 0; } return NS_OK; } NS_IMETHODIMP nsDocumentChildNodes::Item(PRUint32 aIndex, nsIDOMNode** aReturn) { nsresult result = NS_OK; nsCOMPtr content; *aReturn = nsnull; if (nsnull != mDocument) { result = mDocument->ChildAt(aIndex, *getter_AddRefs(content)); if ((NS_OK == result) && (nsnull != content)) { result = content->QueryInterface(NS_GET_IID(nsIDOMNode), (void**)aReturn); } } return result; } void nsDocumentChildNodes::DropReference() { mDocument = nsnull; } // ================================================================== // = // ================================================================== nsDocument::nsDocument() { NS_INIT_REFCNT(); mArena = nsnull; mDocumentTitle = nsnull; mDocumentURL = nsnull; mCharacterSet.AssignWithConversion("ISO-8859-1"); mParentDocument = nsnull; mRootContent = nsnull; mScriptObject = nsnull; mListenerManager = nsnull; mInDestructor = PR_FALSE; mDOMStyleSheets = nsnull; mNameSpaceManager = nsnull; mHeaderData = nsnull; mChildNodes = nsnull; mModCount = 0; mPrincipal = nsnull; mNextContentID = NS_CONTENT_ID_COUNTER_BASE; mDTD = 0; mBoxObjectTable = nsnull; // Force initialization. mBindingManager = do_CreateInstance("@mozilla.org/xbl/binding-manager;1"); nsCOMPtr observer(do_QueryInterface(mBindingManager)); if (observer) // We must always be the first observer of the document. mObservers.InsertElementAt(observer, 0); Init();/* XXX */ } nsDocument::~nsDocument() { // XXX Inform any remaining observers that we are going away. // Note that this currently contradicts the rule that all // observers must hold on to live references to the document. // This notification will occur only after the reference has // been dropped. mInDestructor = PR_TRUE; PRInt32 index; for (index = 0; index < mObservers.Count(); index++) { nsIDocumentObserver* observer = (nsIDocumentObserver*)mObservers.ElementAt(index); observer->DocumentWillBeDestroyed(this); if (observer != (nsIDocumentObserver*)mObservers.ElementAt(index)) { index--; } } if (nsnull != mDocumentTitle) { delete mDocumentTitle; mDocumentTitle = nsnull; } NS_IF_RELEASE(mDocumentURL); NS_IF_RELEASE(mPrincipal); mDocumentLoadGroup = null_nsCOMPtr(); mParentDocument = nsnull; // Delete references to sub-documents index = mSubDocuments.Count(); while (--index >= 0) { nsIDocument* subdoc = (nsIDocument*) mSubDocuments.ElementAt(index); NS_RELEASE(subdoc); } mRootContent = nsnull; mChildren->Clear(); // Delete references to style sheets index = mStyleSheets.Count(); while (--index >= 0) { nsIStyleSheet* sheet = (nsIStyleSheet*) mStyleSheets.ElementAt(index); sheet->SetOwningDocument(nsnull); NS_RELEASE(sheet); } if (nsnull != mChildNodes) { mChildNodes->DropReference(); NS_RELEASE(mChildNodes); } NS_IF_RELEASE(mArena); NS_IF_RELEASE(mListenerManager); NS_IF_RELEASE(mDOMStyleSheets); NS_IF_RELEASE(mNameSpaceManager); if (nsnull != mHeaderData) { delete mHeaderData; mHeaderData = nsnull; } NS_IF_RELEASE(mDTD); delete mBoxObjectTable; } NS_INTERFACE_MAP_BEGIN(nsDocument) NS_INTERFACE_MAP_ENTRY(nsIDocument) NS_INTERFACE_MAP_ENTRY(nsIDOMDocument) NS_INTERFACE_MAP_ENTRY(nsIDOMNSDocument) NS_INTERFACE_MAP_ENTRY(nsIDOMDocumentEvent) NS_INTERFACE_MAP_ENTRY(nsIDOMDocumentStyle) NS_INTERFACE_MAP_ENTRY(nsIDOMDocumentView) NS_INTERFACE_MAP_ENTRY(nsIDOMDocumentXBL) NS_INTERFACE_MAP_ENTRY(nsIScriptObjectOwner) NS_INTERFACE_MAP_ENTRY(nsIJSScriptObject) NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal) NS_INTERFACE_MAP_ENTRY(nsIDOMEventReceiver) NS_INTERFACE_MAP_ENTRY(nsIDOMEventTarget) NS_INTERFACE_MAP_ENTRY(nsIDOMNode) NS_INTERFACE_MAP_ENTRY(nsIDiskDocument) NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDocument) NS_INTERFACE_MAP_END NS_IMPL_ADDREF(nsDocument) NS_IMPL_RELEASE(nsDocument) nsresult nsDocument::Init() { if (mNameSpaceManager) { return NS_ERROR_ALREADY_INITIALIZED; } nsresult rv; rv = NS_NewISupportsArray(getter_AddRefs(mChildren)); NS_ENSURE_SUCCESS(rv, rv); rv = NS_NewHeapArena(&mArena, nsnull); NS_ENSURE_SUCCESS(rv, rv); rv = NS_NewNameSpaceManager(&mNameSpaceManager); NS_ENSURE_SUCCESS(rv, rv); mNodeInfoManager = new nsNodeInfoManager(); NS_ENSURE_TRUE(mNodeInfoManager, NS_ERROR_OUT_OF_MEMORY); mNodeInfoManager->Init(mNameSpaceManager); return rv; } nsIArena* nsDocument::GetArena() { if (nsnull != mArena) { NS_ADDREF(mArena); } return mArena; } nsresult nsDocument::Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup) { nsresult rv = NS_OK; if (nsnull != mDocumentTitle) { delete mDocumentTitle; mDocumentTitle = nsnull; } NS_IF_RELEASE(mDocumentURL); NS_IF_RELEASE(mPrincipal); mDocumentLoadGroup = null_nsCOMPtr(); // Delete references to sub-documents PRInt32 index = mSubDocuments.Count(); while (--index >= 0) { nsIDocument* subdoc = (nsIDocument*) mSubDocuments.ElementAt(index); NS_RELEASE(subdoc); } mRootContent = nsnull; PRUint32 count, i; mChildren->Count(&count); for (i = 0; i < count; i++) { nsCOMPtr content(dont_AddRef(NS_STATIC_CAST(nsIContent*,mChildren->ElementAt(i)))); content->SetDocument(nsnull, PR_TRUE, PR_TRUE); ContentRemoved(nsnull, content, index); } mChildren->Clear(); // Delete references to style sheets index = mStyleSheets.Count(); while (--index >= 0) { nsIStyleSheet* sheet = (nsIStyleSheet*) mStyleSheets.ElementAt(index); sheet->SetOwningDocument(nsnull); PRInt32 pscount = mPresShells.Count(); PRInt32 psindex; for (psindex = 0; psindex < pscount; psindex++) { nsIPresShell* shell = (nsIPresShell*)mPresShells.ElementAt(psindex); nsCOMPtr set; if (NS_SUCCEEDED(shell->GetStyleSet(getter_AddRefs(set)))) { if (set) { set->RemoveDocStyleSheet(sheet); } } } // XXX Tell observers? NS_RELEASE(sheet); } mStyleSheets.Clear(); NS_IF_RELEASE(mListenerManager); NS_IF_RELEASE(mDOMStyleSheets); NS_IF_RELEASE(mNameSpaceManager); if (aChannel) { nsCOMPtr uri; (void) aChannel->GetOriginalURI(getter_AddRefs(uri)); PRBool isChrome = PR_FALSE; PRBool isRes = PR_FALSE; (void)uri->SchemeIs(nsIURI::CHROME, &isChrome); (void)uri->SchemeIs(nsIURI::RESOURCE, &isRes); if (isChrome || isRes) (void)aChannel->GetOriginalURI(&mDocumentURL); else (void)aChannel->GetURI(&mDocumentURL); nsCOMPtr owner; aChannel->GetOwner(getter_AddRefs(owner)); if (owner) owner->QueryInterface(NS_GET_IID(nsIPrincipal), (void**)&mPrincipal); } if (aLoadGroup) { mDocumentLoadGroup = getter_AddRefs(NS_GetWeakReference(aLoadGroup)); // there was an assertion here that aLoadGroup was not null. This is no longer valid // nsWebShell::SetDocument does not create a load group, and it works just fine. } if (NS_OK == rv) { rv = NS_NewNameSpaceManager(&mNameSpaceManager); } return rv; } nsresult nsDocument::SetDocumentURL(nsIURI* aURI) { NS_IF_RELEASE(mDocumentURL); mDocumentURL = aURI; NS_IF_ADDREF(mDocumentURL); return NS_OK; } NS_IMETHODIMP nsDocument::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel, nsILoadGroup* aLoadGroup, nsISupports* aContainer, nsIStreamListener **aDocListener, PRBool aReset) { nsresult rv = NS_OK; if (aReset) rv = Reset(aChannel, aLoadGroup); return rv; } NS_IMETHODIMP nsDocument::StopDocumentLoad() { return NS_OK; } const nsString* nsDocument::GetDocumentTitle() const { return mDocumentTitle; } nsIURI* nsDocument::GetDocumentURL() const { nsIURI* url = mDocumentURL; NS_IF_ADDREF(url); return url; } NS_IMETHODIMP nsDocument::GetPrincipal(nsIPrincipal **aPrincipal) { if (!mPrincipal) { nsresult rv; NS_WITH_SERVICE(nsIScriptSecurityManager, securityManager, NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv); if (NS_FAILED(rv)) return rv; if (NS_FAILED(rv = securityManager->GetCodebasePrincipal(mDocumentURL, &mPrincipal))) return rv; } if(aPrincipal) { *aPrincipal = mPrincipal; NS_ADDREF(*aPrincipal); } return NS_OK; } NS_IMETHODIMP nsDocument::AddPrincipal(nsIPrincipal *aNewPrincipal) { nsresult rv; if (!mPrincipal) GetPrincipal(nsnull); nsCOMPtr agg(do_QueryInterface(mPrincipal, &rv)); if (NS_SUCCEEDED(rv)) { rv = agg->Intersect(aNewPrincipal); if (NS_FAILED(rv)) return rv; } return NS_OK; } NS_IMETHODIMP nsDocument::GetContentType(nsAWritableString& aContentType) const { // Must be implemented by derived class. return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsDocument::GetDocumentLoadGroup(nsILoadGroup **aGroup) const { nsCOMPtr group(do_QueryReferent(mDocumentLoadGroup)); *aGroup = group; NS_IF_ADDREF(*aGroup); return NS_OK; } NS_IMETHODIMP nsDocument::GetBaseURL(nsIURI*& aURL) const { aURL = mDocumentURL; NS_IF_ADDREF(aURL); return NS_OK; } NS_IMETHODIMP nsDocument::GetDocumentCharacterSet(nsAWritableString& oCharSetID) { oCharSetID = mCharacterSet; return NS_OK; } NS_IMETHODIMP nsDocument::SetDocumentCharacterSet(const nsAReadableString& aCharSetID) { if (!mCharacterSet.Equals(aCharSetID)) { mCharacterSet = aCharSetID; PRInt32 n = mCharSetObservers.Count(); for (PRInt32 i = 0; i < n; i++) { nsIObserver* observer = (nsIObserver*) mCharSetObservers.ElementAt(i); observer->Observe((nsIDocument*) this, NS_LITERAL_STRING("charset").get(), nsPromiseFlatString(aCharSetID).get()); } } return NS_OK; } NS_IMETHODIMP nsDocument::AddCharSetObserver(nsIObserver* aObserver) { NS_ENSURE_ARG_POINTER(aObserver); NS_ENSURE_TRUE(mCharSetObservers.AppendElement(aObserver), NS_ERROR_FAILURE); return NS_OK; } NS_IMETHODIMP nsDocument::RemoveCharSetObserver(nsIObserver* aObserver) { NS_ENSURE_ARG_POINTER(aObserver); NS_ENSURE_TRUE(mCharSetObservers.RemoveElement(aObserver), NS_ERROR_FAILURE); return NS_OK; } NS_IMETHODIMP nsDocument::GetLineBreaker(nsILineBreaker** aResult) { if (!mLineBreaker) { // no line breaker, find a default one nsresult result; nsCOMPtr lbf(do_GetService(NS_LWBRK_CONTRACTID, &result)); if (NS_SUCCEEDED(result)) { nsAutoString lbarg; lbf->GetBreaker(lbarg, getter_AddRefs(mLineBreaker)); } } *aResult = mLineBreaker; NS_IF_ADDREF(*aResult); return NS_OK; // XXX we should do error handling here } NS_IMETHODIMP nsDocument::SetLineBreaker(nsILineBreaker* aLineBreaker) { mLineBreaker = aLineBreaker; return NS_OK; } NS_IMETHODIMP nsDocument::GetWordBreaker(nsIWordBreaker** aResult) { if (!mWordBreaker) { // no word breaker, find a default one nsresult result; nsCOMPtr wbf(do_GetService(NS_LWBRK_CONTRACTID, &result)); if (NS_SUCCEEDED(result)) { nsAutoString wbarg; wbf->GetBreaker(wbarg, getter_AddRefs(mWordBreaker)); } } *aResult = mWordBreaker; NS_IF_ADDREF(*aResult); return NS_OK; // XXX we should do error handling here } NS_IMETHODIMP nsDocument::SetWordBreaker(nsIWordBreaker* aWordBreaker) { mWordBreaker = aWordBreaker; return NS_OK; } NS_IMETHODIMP nsDocument::GetHeaderData(nsIAtom* aHeaderField, nsAWritableString& aData) const { aData.Truncate(); const nsDocHeaderData* data = mHeaderData; while (nsnull != data) { if (data->mField == aHeaderField) { aData = data->mData; break; } data = data->mNext; } return NS_OK; } NS_IMETHODIMP nsDocument::SetHeaderData(nsIAtom* aHeaderField, const nsAReadableString& aData) { if (nsnull != aHeaderField) { if (nsnull == mHeaderData) { if (0 < aData.Length()) { // don't bother storing empty string mHeaderData = new nsDocHeaderData(aHeaderField, aData); } } else { nsDocHeaderData* data = mHeaderData; nsDocHeaderData** lastPtr = &mHeaderData; do { // look for existing and replace if (data->mField == aHeaderField) { if (0 < aData.Length()) { data->mData.Assign(aData); } else { // don't store empty string (*lastPtr)->mNext = data->mNext; data->mNext = nsnull; delete data; } return NS_OK; } lastPtr = &(data->mNext); data = data->mNext; } while (nsnull != data); // didn't find, append if (0 < aData.Length()) { *lastPtr = new nsDocHeaderData(aHeaderField, aData); } } return NS_OK; } return NS_ERROR_NULL_POINTER; } #if 0 // XXX Temp hack: moved to nsMarkupDocument NS_IMETHODIMP nsDocument::CreateShell(nsIPresContext* aContext, nsIViewManager* aViewManager, nsIStyleSet* aStyleSet, nsIPresShell** aInstancePtrResult) { NS_PRECONDITION(nsnull != aInstancePtrResult, "null ptr"); if (nsnull == aInstancePtrResult) { return NS_ERROR_NULL_POINTER; } nsresult rv; nsIPresShell* shell = do_CreateInstance(kPresShellCID,&rv); if (NS_FAILED(rv)) { return rv; } if (NS_OK != shell->Init(this, aContext, aViewManager, aStyleSet)) { NS_RELEASE(shell); return rv; } // Note: we don't hold a ref to the shell (it holds a ref to us) mPresShells.AppendElement(shell); *aInstancePtrResult = shell; return NS_OK; } #endif PRBool nsDocument::DeleteShell(nsIPresShell* aShell) { return mPresShells.RemoveElement(aShell); } PRInt32 nsDocument::GetNumberOfShells() { return mPresShells.Count(); } nsIPresShell* nsDocument::GetShellAt(PRInt32 aIndex) { nsIPresShell* shell = (nsIPresShell*) mPresShells.ElementAt(aIndex); if (nsnull != shell) { NS_ADDREF(shell); } return shell; } nsIDocument* nsDocument::GetParentDocument() { if (nsnull != mParentDocument) { NS_ADDREF(mParentDocument); } return mParentDocument; } /** * Note that we do *not* AddRef our parent because that would * create a circular reference. */ void nsDocument::SetParentDocument(nsIDocument* aParent) { mParentDocument = aParent; } void nsDocument::AddSubDocument(nsIDocument* aSubDoc) { NS_ADDREF(aSubDoc); mSubDocuments.AppendElement(aSubDoc); } PRInt32 nsDocument::GetNumberOfSubDocuments() { return mSubDocuments.Count(); } nsIDocument* nsDocument::GetSubDocumentAt(PRInt32 aIndex) { nsIDocument* doc = (nsIDocument*) mSubDocuments.ElementAt(aIndex); if (nsnull != doc) { NS_ADDREF(doc); } return doc; } nsIContent* nsDocument::GetRootContent() { if (nsnull != mRootContent) { NS_ADDREF(mRootContent); } return mRootContent; } void nsDocument::SetRootContent(nsIContent* aRoot) { if (mRootContent) { PRInt32 index = mChildren->IndexOf(mRootContent); if (aRoot) { mChildren->ReplaceElementAt(aRoot, index); } else { mChildren->RemoveElementAt(index); } } else if (aRoot) { mChildren->AppendElement(aRoot); } mRootContent = aRoot; } NS_IMETHODIMP nsDocument::ChildAt(PRInt32 aIndex, nsIContent*& aResult) const { nsCOMPtr content( dont_AddRef(NS_STATIC_CAST(nsIContent*, mChildren->ElementAt(aIndex))) ); NS_IF_ADDREF(aResult = content); return NS_OK; } NS_IMETHODIMP nsDocument::IndexOf(nsIContent* aPossibleChild, PRInt32& aIndex) const { aIndex = mChildren->IndexOf(aPossibleChild); return NS_OK; } NS_IMETHODIMP nsDocument::GetChildCount(PRInt32& aCount) { PRUint32 count; mChildren->Count(&count); aCount = NS_STATIC_CAST(PRInt32, count); return NS_OK; } PRInt32 nsDocument::GetNumberOfStyleSheets() { return mStyleSheets.Count(); } nsIStyleSheet* nsDocument::GetStyleSheetAt(PRInt32 aIndex) { nsIStyleSheet* sheet = (nsIStyleSheet*)mStyleSheets.ElementAt(aIndex); NS_IF_ADDREF(sheet); return sheet; } PRInt32 nsDocument::GetIndexOfStyleSheet(nsIStyleSheet* aSheet) { return mStyleSheets.IndexOf(aSheet); } void nsDocument::InternalAddStyleSheet(nsIStyleSheet* aSheet) // subclass hook for sheet ordering { mStyleSheets.AppendElement(aSheet); } void nsDocument::AddStyleSheetToStyleSets(nsIStyleSheet* aSheet) { PRInt32 count = mPresShells.Count(); PRInt32 index; for (index = 0; index < count; index++) { nsIPresShell* shell = (nsIPresShell*)mPresShells.ElementAt(index); nsCOMPtr set; if (NS_SUCCEEDED(shell->GetStyleSet(getter_AddRefs(set)))) { if (set) { set->AddDocStyleSheet(aSheet, this); } } } } void nsDocument::AddStyleSheet(nsIStyleSheet* aSheet) { NS_PRECONDITION(nsnull != aSheet, "null arg"); InternalAddStyleSheet(aSheet); NS_ADDREF(aSheet); aSheet->SetOwningDocument(this); PRBool enabled = PR_TRUE; aSheet->GetEnabled(enabled); if (enabled) { AddStyleSheetToStyleSets(aSheet); // XXX should observers be notified for disabled sheets??? I think not, but I could be wrong for (PRInt32 index = 0; index < mObservers.Count(); index++) { nsIDocumentObserver* observer = (nsIDocumentObserver*)mObservers.ElementAt(index); observer->StyleSheetAdded(this, aSheet); if (observer != (nsIDocumentObserver*)mObservers.ElementAt(index)) { index--; } } } } void nsDocument::RemoveStyleSheetFromStyleSets(nsIStyleSheet* aSheet) { PRInt32 count = mPresShells.Count(); PRInt32 index; for (index = 0; index < count; index++) { nsIPresShell* shell = (nsIPresShell*)mPresShells.ElementAt(index); nsCOMPtr set; if (NS_SUCCEEDED(shell->GetStyleSet(getter_AddRefs(set)))) { if (set) { set->RemoveDocStyleSheet(aSheet); } } } } void nsDocument::RemoveStyleSheet(nsIStyleSheet* aSheet) { NS_PRECONDITION(nsnull != aSheet, "null arg"); mStyleSheets.RemoveElement(aSheet); PRBool enabled = PR_TRUE; aSheet->GetEnabled(enabled); if (enabled) { RemoveStyleSheetFromStyleSets(aSheet); // XXX should observers be notified for disabled sheets??? I think not, but I could be wrong for (PRInt32 index = 0; index < mObservers.Count(); index++) { nsIDocumentObserver* observer = (nsIDocumentObserver*)mObservers.ElementAt(index); observer->StyleSheetRemoved(this, aSheet); if (observer != (nsIDocumentObserver*)mObservers.ElementAt(index)) { index--; } } } aSheet->SetOwningDocument(nsnull); NS_RELEASE(aSheet); } NS_IMETHODIMP nsDocument::UpdateStyleSheets(nsISupportsArray* aOldSheets, nsISupportsArray* aNewSheets) { PRUint32 oldCount; aOldSheets->Count(&oldCount); nsCOMPtr sheet; PRUint32 i; for (i = 0; i < oldCount; i++) { nsCOMPtr supp; aOldSheets->GetElementAt(i, getter_AddRefs(supp)); sheet = do_QueryInterface(supp); if (sheet) { mStyleSheets.RemoveElement(sheet); PRBool enabled = PR_TRUE; sheet->GetEnabled(enabled); if (enabled) { RemoveStyleSheetFromStyleSets(sheet); } sheet->SetOwningDocument(nsnull); nsIStyleSheet* sheetPtr = sheet.get(); NS_RELEASE(sheetPtr); } } PRUint32 newCount; aNewSheets->Count(&newCount); for (i = 0; i < newCount; i++) { nsCOMPtr supp; aNewSheets->GetElementAt(i, getter_AddRefs(supp)); sheet = do_QueryInterface(supp); if (sheet) { InternalAddStyleSheet(sheet); nsIStyleSheet* sheetPtr = sheet; NS_ADDREF(sheetPtr); sheet->SetOwningDocument(this); PRBool enabled = PR_TRUE; sheet->GetEnabled(enabled); if (enabled) { AddStyleSheetToStyleSets(sheet); sheet->SetOwningDocument(nsnull); } } } for (PRInt32 index = 0; index < mObservers.Count(); index++) { nsIDocumentObserver* observer = (nsIDocumentObserver*)mObservers.ElementAt(index); observer->StyleSheetRemoved(this, sheet); if (observer != (nsIDocumentObserver*)mObservers.ElementAt(index)) { index--; } } return NS_OK; } void nsDocument::InternalInsertStyleSheetAt(nsIStyleSheet* aSheet, PRInt32 aIndex) { // subclass hook for sheet ordering mStyleSheets.InsertElementAt(aSheet, aIndex); } NS_IMETHODIMP nsDocument::InsertStyleSheetAt(nsIStyleSheet* aSheet, PRInt32 aIndex, PRBool aNotify) { NS_PRECONDITION(nsnull != aSheet, "null ptr"); InternalInsertStyleSheetAt(aSheet, aIndex); NS_ADDREF(aSheet); aSheet->SetOwningDocument(this); PRBool enabled = PR_TRUE; aSheet->GetEnabled(enabled); PRInt32 count; PRInt32 index; if (enabled) { count = mPresShells.Count(); for (index = 0; index < count; index++) { nsIPresShell* shell = (nsIPresShell*)mPresShells.ElementAt(index); nsCOMPtr set; shell->GetStyleSet(getter_AddRefs(set)); if (set) { set->AddDocStyleSheet(aSheet, this); } } } if (aNotify) { // notify here even if disabled, there may have been others that weren't notified for (index = 0; index < mObservers.Count(); index++) { nsIDocumentObserver* observer = (nsIDocumentObserver*)mObservers.ElementAt(index); observer->StyleSheetAdded(this, aSheet); if (observer != (nsIDocumentObserver*)mObservers.ElementAt(index)) { index--; } } } return NS_OK; } void nsDocument::SetStyleSheetDisabledState(nsIStyleSheet* aSheet, PRBool aDisabled) { NS_PRECONDITION(nsnull != aSheet, "null arg"); PRInt32 index = mStyleSheets.IndexOf((void *)aSheet); PRInt32 count; // If we're actually in the document style sheet list if (-1 != index) { count = mPresShells.Count(); for (index = 0; index < count; index++) { nsIPresShell* shell = (nsIPresShell*)mPresShells.ElementAt(index); nsCOMPtr set; if (NS_SUCCEEDED(shell->GetStyleSet(getter_AddRefs(set)))) { if (set) { if (aDisabled) { set->RemoveDocStyleSheet(aSheet); } else { set->AddDocStyleSheet(aSheet, this); } } } } } for (index = 0; index < mObservers.Count(); index++) { nsIDocumentObserver* observer = (nsIDocumentObserver*)mObservers.ElementAt(index); observer->StyleSheetDisabledStateChanged(this, aSheet, aDisabled); if (observer != (nsIDocumentObserver*)mObservers.ElementAt(index)) { index--; } } } NS_IMETHODIMP nsDocument::GetScriptGlobalObject(nsIScriptGlobalObject** aScriptGlobalObject) { NS_ENSURE_ARG_POINTER(aScriptGlobalObject); *aScriptGlobalObject = mScriptGlobalObject; NS_IF_ADDREF(*aScriptGlobalObject); return NS_OK; } NS_IMETHODIMP nsDocument::SetScriptGlobalObject(nsIScriptGlobalObject *aScriptGlobalObject) { // XXX HACK ALERT! If the script context owner is null, the document // will soon be going away. So tell our content that to lose its // reference to the document. This has to be done before we // actually set the script context owner to null so that the // content elements can remove references to their script objects. if (!aScriptGlobalObject) { PRUint32 ucount, index; mChildren->Count(&ucount); for (index = 0; index < ucount; index++) { nsCOMPtr content(dont_AddRef(NS_STATIC_CAST(nsIContent*,mChildren->ElementAt(index)))); content->SetDocument(nsnull, PR_TRUE, PR_TRUE); } // Propagate the out-of-band notification to each PresShell's // anonymous content as well. This ensures that there aren't any // accidental script references left in anonymous content keeping // the document alive. (While not strictly necessary -- the // PresShell owns us -- it's tidy.) PRInt32 count; for (count = mPresShells.Count() - 1; count >= 0; --count) { nsIPresShell* shell = NS_STATIC_CAST(nsIPresShell*, mPresShells[count]); if (! shell) continue; shell->ReleaseAnonymousContent(); } } mScriptGlobalObject = aScriptGlobalObject; return NS_OK; } NS_IMETHODIMP nsDocument::GetNameSpaceManager(nsINameSpaceManager*& aManager) { aManager = mNameSpaceManager; NS_IF_ADDREF(aManager); return NS_OK; } // Note: We don't hold a reference to the document observer; we assume // that it has a live reference to the document. void nsDocument::AddObserver(nsIDocumentObserver* aObserver) { // XXX Make sure the observer isn't already in the list if (mObservers.IndexOf(aObserver) == -1) { mObservers.AppendElement(aObserver); } } PRBool nsDocument::RemoveObserver(nsIDocumentObserver* aObserver) { // If we're in the process of destroying the document (and we're // informing the observers of the destruction), don't remove the // observers from the list. This is not a big deal, since we // don't hold a live reference to the observers. if (!mInDestructor) return mObservers.RemoveElement(aObserver); else return (mObservers.IndexOf(aObserver) != -1); } NS_IMETHODIMP nsDocument::BeginUpdate() { PRInt32 i; // Get new value of count for every iteration in case // observers remove themselves during the loop. for (i = 0; i < mObservers.Count(); i++) { nsIDocumentObserver* observer = (nsIDocumentObserver*) mObservers[i]; observer->BeginUpdate(this); // Make sure that the observer didn't remove itself during the // notification. If it did, update our index and count. if (observer != (nsIDocumentObserver*)mObservers[i]) { i--; } } return NS_OK; } NS_IMETHODIMP nsDocument::EndUpdate() { PRInt32 i; // Get new value of count for every iteration in case // observers remove themselves during the loop. for (i = 0; i < mObservers.Count(); i++) { nsIDocumentObserver* observer = (nsIDocumentObserver*) mObservers[i]; observer->EndUpdate(this); // Make sure that the observer didn't remove itself during the // notification. If it did, update our index and count. if (observer != (nsIDocumentObserver*)mObservers[i]) { i--; } } return NS_OK; } NS_IMETHODIMP nsDocument::BeginLoad() { PRInt32 i; // Get new value of count for every iteration in case // observers remove themselves during the loop. for (i = 0; i < mObservers.Count(); i++) { nsIDocumentObserver* observer = (nsIDocumentObserver*) mObservers[i]; observer->BeginLoad(this); // Make sure that the observer didn't remove itself during the // notification. If it did, update our index and count. if (observer != (nsIDocumentObserver*)mObservers[i]) { i--; } } return NS_OK; } NS_IMETHODIMP nsDocument::EndLoad() { PRInt32 i; // Get new value of count for every iteration in case // observers remove themselves during the loop. for (i = 0; i < mObservers.Count(); i++) { nsIDocumentObserver* observer = (nsIDocumentObserver*) mObservers[i]; observer->EndLoad(this); // Make sure that the observer didn't remove itself during the // notification. If it did, update our index and count. if (observer != (nsIDocumentObserver*)mObservers[i]) { i--; } } return NS_OK; } NS_IMETHODIMP nsDocument::ContentChanged(nsIContent* aContent, nsISupports* aSubContent) { PRInt32 i; // Get new value of count for every iteration in case // observers remove themselves during the loop. for (i = 0; i < mObservers.Count(); i++) { nsIDocumentObserver* observer = (nsIDocumentObserver*)mObservers[i]; observer->ContentChanged(this, aContent, aSubContent); // Make sure that the observer didn't remove itself during the // notification. If it did, update our index and count. if (observer != (nsIDocumentObserver*)mObservers[i]) { i--; } } return NS_OK; } NS_IMETHODIMP nsDocument::ContentStatesChanged(nsIContent* aContent1, nsIContent* aContent2) { PRInt32 i; // Get new value of count for every iteration in case // observers remove themselves during the loop. for (i = 0; i < mObservers.Count(); i++) { nsIDocumentObserver* observer = (nsIDocumentObserver*)mObservers[i]; observer->ContentStatesChanged(this, aContent1, aContent2); // Make sure that the observer didn't remove itself during the // notification. If it did, update our index and count. if (observer != (nsIDocumentObserver*)mObservers[i]) { i--; } } return NS_OK; } NS_IMETHODIMP nsDocument::ContentAppended(nsIContent* aContainer, PRInt32 aNewIndexInContainer) { PRInt32 i; // Get new value of count for every iteration in case // observers remove themselves during the loop. for (i = 0; i < mObservers.Count(); i++) { nsIDocumentObserver* observer = (nsIDocumentObserver*)mObservers[i]; observer->ContentAppended(this, aContainer, aNewIndexInContainer); // Make sure that the observer didn't remove itself during the // notification. If it did, update our index and count. if (observer != (nsIDocumentObserver*)mObservers[i]) { i--; } } return NS_OK; } NS_IMETHODIMP nsDocument::ContentInserted(nsIContent* aContainer, nsIContent* aChild, PRInt32 aIndexInContainer) { PRInt32 i; // Get new value of count for every iteration in case // observers remove themselves during the loop. for (i = 0; i < mObservers.Count(); i++) { nsIDocumentObserver* observer = (nsIDocumentObserver*)mObservers[i]; observer->ContentInserted(this, aContainer, aChild, aIndexInContainer); // Make sure that the observer didn't remove itself during the // notification. If it did, update our index and count. if (observer != (nsIDocumentObserver*)mObservers[i]) { i--; } } return NS_OK; } NS_IMETHODIMP nsDocument::ContentReplaced(nsIContent* aContainer, nsIContent* aOldChild, nsIContent* aNewChild, PRInt32 aIndexInContainer) { PRInt32 i; // Get new value of count for every iteration in case // observers remove themselves during the loop. for (i = 0; i < mObservers.Count(); i++) { nsIDocumentObserver* observer = (nsIDocumentObserver*)mObservers[i]; observer->ContentReplaced(this, aContainer, aOldChild, aNewChild, aIndexInContainer); // Make sure that the observer didn't remove itself during the // notification. If it did, update our index and count. if (observer != (nsIDocumentObserver*)mObservers[i]) { i--; } } return NS_OK; } NS_IMETHODIMP nsDocument::ContentRemoved(nsIContent* aContainer, nsIContent* aChild, PRInt32 aIndexInContainer) { PRInt32 i; // Get new value of count for every iteration in case // observers remove themselves during the loop. for (i = 0; i < mObservers.Count(); i++) { nsIDocumentObserver* observer = (nsIDocumentObserver*)mObservers[i]; observer->ContentRemoved(this, aContainer, aChild, aIndexInContainer); // Make sure that the observer didn't remove itself during the // notification. If it did, update our index and count. if (observer != (nsIDocumentObserver*)mObservers[i]) { i--; } } return NS_OK; } NS_IMETHODIMP nsDocument::AttributeChanged(nsIContent* aChild, PRInt32 aNameSpaceID, nsIAtom* aAttribute, PRInt32 aHint) { PRInt32 i; nsresult result = NS_OK; // Get new value of count for every iteration in case // observers remove themselves during the loop. for (i = 0; i < mObservers.Count(); i++) { nsIDocumentObserver* observer = (nsIDocumentObserver*)mObservers[i]; nsresult rv = observer->AttributeChanged(this, aChild, aNameSpaceID, aAttribute, aHint); if (NS_FAILED(rv) && NS_SUCCEEDED(result)) result = rv; // Make sure that the observer didn't remove itself during the // notification. If it did, update our index and count. if (observer != (nsIDocumentObserver*)mObservers[i]) { i--; } } return result; } NS_IMETHODIMP nsDocument::StyleRuleChanged(nsIStyleSheet* aStyleSheet, nsIStyleRule* aStyleRule, PRInt32 aHint) { PRInt32 i; // Get new value of count for every iteration in case // observers remove themselves during the loop. for (i = 0; i < mObservers.Count(); i++) { nsIDocumentObserver* observer = (nsIDocumentObserver*)mObservers[i]; observer->BeginUpdate(this); observer->StyleRuleChanged(this, aStyleSheet, aStyleRule, aHint); // Make sure that the observer didn't remove itself during the // notification. If it did, update our index and count. if (observer != (nsIDocumentObserver*)mObservers[i]) { i--; } else { observer->EndUpdate(this); } } return NS_OK; } NS_IMETHODIMP nsDocument::StyleRuleAdded(nsIStyleSheet* aStyleSheet, nsIStyleRule* aStyleRule) { PRInt32 i; // Get new value of count for every iteration in case // observers remove themselves during the loop. for (i = 0; i < mObservers.Count(); i++) { nsIDocumentObserver* observer = (nsIDocumentObserver*)mObservers[i]; observer->BeginUpdate(this); observer->StyleRuleAdded(this, aStyleSheet, aStyleRule); // Make sure that the observer didn't remove itself during the // notification. If it did, update our index and count. if (observer != (nsIDocumentObserver*)mObservers[i]) { i--; } else { observer->EndUpdate(this); } } return NS_OK; } NS_IMETHODIMP nsDocument::StyleRuleRemoved(nsIStyleSheet* aStyleSheet, nsIStyleRule* aStyleRule) { PRInt32 i; // Get new value of count for every iteration in case // observers remove themselves during the loop. for (i = 0; i < mObservers.Count(); i++) { nsIDocumentObserver* observer = (nsIDocumentObserver*)mObservers[i]; observer->BeginUpdate(this); observer->StyleRuleRemoved(this, aStyleSheet, aStyleRule); // Make sure that the observer didn't remove itself during the // notification. If it did, update our index and count. if (observer != (nsIDocumentObserver*)mObservers[i]) { i--; } else { observer->EndUpdate(this); } } return NS_OK; } nsresult nsDocument::GetScriptObject(nsIScriptContext *aContext, void** aScriptObject) { nsresult res = NS_OK; nsCOMPtr global; if (nsnull == mScriptObject) { // XXX We make the (possibly erroneous) assumption that the first // presentation shell represents the "primary view" of the document // and that the JS parent chain should incorporate just that view. // This is done for lack of a better model when we have multiple // views. nsIPresShell* shell = (nsIPresShell*) mPresShells.ElementAt(0); if (shell) { nsCOMPtr cx; shell->GetPresContext(getter_AddRefs(cx)); nsCOMPtr container; res = cx->GetContainer(getter_AddRefs(container)); if (NS_SUCCEEDED(res) && container) { global = do_GetInterface(container); } } // XXX If we can't find a view, parent to the calling context's // global object. This may not be right either, but we need // something. else { global = getter_AddRefs(aContext->GetGlobalObject()); } if (NS_SUCCEEDED(res)) { res = NS_NewScriptDocument(aContext, (nsISupports *)(nsIDOMDocument *)this, (nsISupports *)global, (void**)&mScriptObject); } } *aScriptObject = mScriptObject; return res; } nsresult nsDocument::SetScriptObject(void *aScriptObject) { mScriptObject = aScriptObject; return NS_OK; } // // nsIDOMDocument interface // NS_IMETHODIMP nsDocument::GetDoctype(nsIDOMDocumentType** aDoctype) { NS_ENSURE_ARG_POINTER(aDoctype); *aDoctype = nsnull; PRUint32 i, count; mChildren->Count(&count); nsCOMPtr rootContentNode( do_QueryInterface(mRootContent) ); nsCOMPtr node; for (i = 0; i < count; i++) { mChildren->QueryElementAt(i, NS_GET_IID(nsIDOMNode), getter_AddRefs(node)); 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 node->QueryInterface(NS_GET_IID(nsIDOMDocumentType), (void **)aDoctype); } } } return NS_OK; } NS_IMETHODIMP nsDocument::GetImplementation(nsIDOMDOMImplementation** aImplementation) { // For now, create a new implementation every time. This shouldn't // be a high bandwidth operation nsDOMImplementation* impl = new nsDOMImplementation(mDocumentURL); if (nsnull == impl) { return NS_ERROR_OUT_OF_MEMORY; } return impl->QueryInterface(NS_GET_IID(nsIDOMDOMImplementation), (void**)aImplementation); } NS_IMETHODIMP nsDocument::GetDocumentElement(nsIDOMElement** aDocumentElement) { if (nsnull == aDocumentElement) { return NS_ERROR_NULL_POINTER; } nsresult res = NS_OK; if (nsnull != mRootContent) { res = mRootContent->QueryInterface(NS_GET_IID(nsIDOMElement), (void**)aDocumentElement); NS_ASSERTION(NS_OK == res, "Must be a DOM Element"); } else { *aDocumentElement = nsnull; } return res; } NS_IMETHODIMP nsDocument::CreateElement(const nsAReadableString& aTagName, nsIDOMElement** aReturn) { // Should be implemented by subclass return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsDocument::CreateTextNode(const nsAReadableString& aData, nsIDOMText** aReturn) { nsCOMPtr text; nsresult rv = NS_NewTextNode(getter_AddRefs(text)); if (NS_OK == rv) { rv = text->QueryInterface(NS_GET_IID(nsIDOMText), (void**)aReturn); (*aReturn)->AppendData(aData); } return rv; } NS_IMETHODIMP nsDocument::CreateDocumentFragment(nsIDOMDocumentFragment** aReturn) { return NS_NewDocumentFragment(aReturn, this); } NS_IMETHODIMP nsDocument::CreateComment(const nsAReadableString& aData, nsIDOMComment** aReturn) { nsCOMPtr comment; nsresult rv = NS_NewCommentNode(getter_AddRefs(comment)); if (NS_OK == rv) { rv = comment->QueryInterface(NS_GET_IID(nsIDOMComment), (void**)aReturn); (*aReturn)->AppendData(aData); } return rv; } NS_IMETHODIMP nsDocument::CreateCDATASection(const nsAReadableString& aData, nsIDOMCDATASection** aReturn) { // Should be implemented by subclass return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsDocument::CreateProcessingInstruction(const nsAReadableString& aTarget, const nsAReadableString& aData, nsIDOMProcessingInstruction** aReturn) { // Should be implemented by subclass return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsDocument::CreateAttribute(const nsAReadableString& aName, nsIDOMAttr** aReturn) { NS_ENSURE_ARG_POINTER(aReturn); NS_ENSURE_TRUE(mNodeInfoManager, NS_ERROR_NOT_INITIALIZED); nsAutoString value; nsDOMAttribute* attribute; nsCOMPtr nodeInfo; nsresult rv = mNodeInfoManager->GetNodeInfo(aName, nsnull, kNameSpaceID_None, *getter_AddRefs(nodeInfo)); NS_ENSURE_SUCCESS(rv, rv); attribute = new nsDOMAttribute(nsnull, nodeInfo, value); NS_ENSURE_TRUE(attribute, NS_ERROR_OUT_OF_MEMORY); return attribute->QueryInterface(NS_GET_IID(nsIDOMAttr), (void**)aReturn); } NS_IMETHODIMP nsDocument::CreateEntityReference(const nsAReadableString& aName, nsIDOMEntityReference** aReturn) { // Should be implemented by subclass return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsDocument::GetElementsByTagName(const nsAReadableString& aTagname, nsIDOMNodeList** aReturn) { nsCOMPtr nameAtom(dont_AddRef(NS_NewAtom(aTagname))); nsContentList* list = new nsContentList(this, nameAtom, kNameSpaceID_Unknown); NS_ENSURE_TRUE(list, NS_ERROR_OUT_OF_MEMORY); return list->QueryInterface(NS_GET_IID(nsIDOMNodeList), (void **)aReturn); } NS_IMETHODIMP nsDocument::GetElementsByTagNameNS(const nsAReadableString& aNamespaceURI, const nsAReadableString& aLocalName, nsIDOMNodeList** aReturn) { PRInt32 nameSpaceId = kNameSpaceID_Unknown; nsContentList* list = nsnull; if (!aNamespaceURI.Equals(NS_LITERAL_STRING("*"))) { mNameSpaceManager->GetNameSpaceID(aNamespaceURI, nameSpaceId); if (nameSpaceId == kNameSpaceID_Unknown) { // Unkonwn namespace means no matches, we create an empty list... list = new nsContentList(this, nsnull, kNameSpaceID_None); NS_ENSURE_TRUE(list, NS_ERROR_OUT_OF_MEMORY); } } if (!list) { nsCOMPtr nameAtom(dont_AddRef(NS_NewAtom(aLocalName))); list = new nsContentList(this, nameAtom, nameSpaceId); NS_ENSURE_TRUE(list, NS_ERROR_OUT_OF_MEMORY); } return list->QueryInterface(NS_GET_IID(nsIDOMNodeList), (void **)aReturn); } NS_IMETHODIMP nsDocument::GetStyleSheets(nsIDOMStyleSheetList** aStyleSheets) { if (nsnull == mDOMStyleSheets) { mDOMStyleSheets = new nsDOMStyleSheetList(this); if (nsnull == mDOMStyleSheets) { return NS_ERROR_OUT_OF_MEMORY; } NS_ADDREF(mDOMStyleSheets); } *aStyleSheets = mDOMStyleSheets; NS_ADDREF(mDOMStyleSheets); return NS_OK; } NS_IMETHODIMP nsDocument::GetCharacterSet(nsAWritableString& aCharacterSet) { return GetDocumentCharacterSet(aCharacterSet); } NS_IMETHODIMP nsDocument::ImportNode(nsIDOMNode* aImportedNode, PRBool aDeep, nsIDOMNode** aReturn) { NS_ENSURE_ARG(aImportedNode); NS_ENSURE_ARG_POINTER(aReturn); return aImportedNode->CloneNode(aDeep, aReturn); } NS_IMETHODIMP nsDocument::AddBinding(nsIDOMElement* aContent, const nsAReadableString& aURL) { nsCOMPtr bm; GetBindingManager(getter_AddRefs(bm)); nsCOMPtr content(do_QueryInterface(aContent)); return bm->AddLayeredBinding(content, aURL); } NS_IMETHODIMP nsDocument::RemoveBinding(nsIDOMElement* aContent, const nsAReadableString& aURL) { if (mBindingManager) { nsCOMPtr content(do_QueryInterface(aContent)); return mBindingManager->RemoveLayeredBinding(content, aURL); } return NS_ERROR_FAILURE; } NS_IMETHODIMP nsDocument::LoadBindingDocument(const nsAReadableString& aURL, nsIDOMDocument** aResult) { if (mBindingManager) { nsCOMPtr doc; mBindingManager->LoadBindingDocument(this, aURL, getter_AddRefs(doc)); nsCOMPtr domDoc(do_QueryInterface(doc)); *aResult = domDoc; NS_IF_ADDREF(*aResult); return NS_OK; } return NS_ERROR_FAILURE; } NS_IMETHODIMP nsDocument::GetBindingParent(nsIDOMNode* aNode, nsIDOMElement** aResult) { *aResult = nsnull; nsCOMPtr content(do_QueryInterface(aNode)); if (!content) return NS_ERROR_FAILURE; nsCOMPtr result; content->GetBindingParent(getter_AddRefs(result)); nsCOMPtr elt(do_QueryInterface(result)); *aResult = elt; NS_IF_ADDREF(*aResult); return NS_OK; } static nsresult GetElementByAttribute(nsIContent* aContent, nsIAtom* aAttrName, const nsAReadableString& aAttrValue, PRBool aUniversalMatch, nsIDOMElement** aResult) { nsAutoString value; nsresult rv = aContent->GetAttribute(kNameSpaceID_None, aAttrName, value); if (rv == NS_CONTENT_ATTR_HAS_VALUE) { if (aUniversalMatch || value.Equals(aAttrValue)) return aContent->QueryInterface(NS_GET_IID(nsIDOMElement), (void**)aResult); } PRInt32 childCount; aContent->ChildCount(childCount); for (PRInt32 i = 0; i < childCount; ++i) { nsCOMPtr current; current->ChildAt(i, *getter_AddRefs(current)); GetElementByAttribute(current, aAttrName, aAttrValue, aUniversalMatch, aResult); if (*aResult) return NS_OK; } return NS_OK; } NS_IMETHODIMP nsDocument::GetAnonymousElementByAttribute(nsIDOMElement* aElement, const nsAReadableString& aAttrName, const nsAReadableString& aAttrValue, nsIDOMElement** aResult) { *aResult = nsnull; nsCOMPtr nodeList; GetAnonymousNodes(aElement, getter_AddRefs(nodeList)); if (!nodeList) return NS_OK; nsCOMPtr attribute = getter_AddRefs(NS_NewAtom(aAttrName)); PRUint32 length; nodeList->GetLength(&length); PRBool universalMatch = aAttrValue.Equals(NS_LITERAL_STRING("*")); for (PRUint32 i = 0; i < length; ++i) { nsCOMPtr current; nodeList->Item(i, getter_AddRefs(current)); nsCOMPtr content(do_QueryInterface(current)); GetElementByAttribute(content, attribute, aAttrValue, universalMatch, aResult); if (*aResult) return NS_OK; } return NS_OK; } NS_IMETHODIMP nsDocument::GetAnonymousNodes(nsIDOMElement* aElement, nsIDOMNodeList** aResult) { *aResult = nsnull; if (mBindingManager) { nsCOMPtr content(do_QueryInterface(aElement)); return mBindingManager->GetAnonymousNodesFor(content, aResult); } return NS_OK; } NS_IMETHODIMP nsDocument::GetLocation(jsval* aLocation) { if (mScriptGlobalObject) { nsCOMPtr window(do_QueryInterface(mScriptGlobalObject)); if(window) { return window->GetLocation(aLocation); } } return NS_OK; } NS_IMETHODIMP nsDocument::SetLocation(jsval aLocation) { if (mScriptGlobalObject) { nsCOMPtr window(do_QueryInterface(mScriptGlobalObject)); if(window) { return window->SetLocation(aLocation); } } return NS_OK; } NS_IMETHODIMP nsDocument::CreateRange(nsIDOMRange** aReturn) { return NS_NewRange(aReturn); } NS_IMETHODIMP nsDocument::GetDefaultView(nsIDOMAbstractView** aDefaultView) { NS_ENSURE_ARG_POINTER(aDefaultView); *aDefaultView = nsnull; nsIPresShell *shell = NS_STATIC_CAST(nsIPresShell *, mPresShells.ElementAt(0)); NS_ENSURE_TRUE(shell, NS_OK); nsCOMPtr ctx; nsresult rv = shell->GetPresContext(getter_AddRefs(ctx)); NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && ctx, rv); nsCOMPtr container; rv = ctx->GetContainer(getter_AddRefs(container)); NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && container, rv); nsCOMPtr ifrq(do_QueryInterface(container)); NS_ENSURE_TRUE(ifrq, NS_OK); nsCOMPtr window; ifrq->GetInterface(NS_GET_IID(nsIDOMWindowInternal), getter_AddRefs(window)); NS_ENSURE_TRUE(window, NS_OK); window->QueryInterface(NS_GET_IID(nsIDOMAbstractView), (void **)aDefaultView); return NS_OK; } NS_IMETHODIMP nsDocument::Load (const nsAReadableString& aUrl) { // Should be implemented by subclass return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsDocument::GetPlugins(nsIDOMPluginArray** aPlugins) { NS_ENSURE_ARG_POINTER(aPlugins); *aPlugins = nsnull; // XXX Could also get this through mScriptGlobalObject nsIPresShell *shell = NS_STATIC_CAST(nsIPresShell *, mPresShells.ElementAt(0)); NS_ENSURE_TRUE(shell, NS_OK); nsCOMPtr ctx; nsresult rv = shell->GetPresContext(getter_AddRefs(ctx)); NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && ctx, rv); nsCOMPtr container; rv = ctx->GetContainer(getter_AddRefs(container)); NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && container, rv); nsCOMPtr ifrq(do_QueryInterface(container)); NS_ENSURE_TRUE(ifrq, NS_OK); nsCOMPtr window; ifrq->GetInterface(NS_GET_IID(nsIDOMWindowInternal), getter_AddRefs(window)); NS_ENSURE_TRUE(window, NS_OK); nsCOMPtr navigator; window->GetNavigator(getter_AddRefs(navigator)); NS_ENSURE_TRUE(navigator, NS_OK); return navigator->GetPlugins(aPlugins); } NS_IMETHODIMP nsDocument::GetBoxObjectFor(nsIDOMElement* aElement, nsIBoxObject** aResult) { nsresult rv; *aResult = nsnull; if (!mBoxObjectTable) mBoxObjectTable = new nsSupportsHashtable; else { nsISupportsKey key(aElement); nsCOMPtr supports(dont_AddRef(NS_STATIC_CAST(nsISupports*, mBoxObjectTable->Get(&key)))); nsCOMPtr boxObject(do_QueryInterface(supports)); if (boxObject) { *aResult = boxObject; NS_ADDREF(*aResult); return NS_OK; } } nsCOMPtr shell(getter_AddRefs(GetShellAt(0))); if (!shell) return NS_ERROR_FAILURE; PRInt32 namespaceID; nsCOMPtr tag; NS_WITH_SERVICE(nsIXBLService, xblService, "@mozilla.org/xbl;1", &rv); nsCOMPtr content(do_QueryInterface(aElement)); xblService->ResolveTag(content, &namespaceID, getter_AddRefs(tag)); nsCAutoString contractID("@mozilla.org/layout/xul-boxobject"); if (namespaceID == nsXULAtoms::nameSpaceID) { if (tag.get() == nsXULAtoms::browser) contractID += "-browser"; else if (tag.get() == nsXULAtoms::editor) contractID += "-editor"; else if (tag.get() == nsXULAtoms::iframe) contractID += "-iframe"; else if (tag.get() == nsXULAtoms::menu) contractID += "-menu"; else if (tag.get() == nsXULAtoms::popupset) contractID += "-popupset"; else if (tag.get() == nsXULAtoms::tree) contractID += "-tree"; else if (tag.get() == nsXULAtoms::scrollbox) contractID += "-scrollbox"; else if (tag.get() == nsXULAtoms::outliner) contractID += "-outliner"; } contractID += ";1"; nsCOMPtr boxObject(do_CreateInstance(contractID.get())); if (!boxObject) return NS_ERROR_FAILURE; nsCOMPtr privateBox(do_QueryInterface(boxObject)); if (NS_FAILED(rv = privateBox->Init(content, shell))) return rv; SetBoxObjectFor(aElement, boxObject); *aResult = boxObject; NS_ADDREF(*aResult); return NS_OK; } NS_IMETHODIMP nsDocument::SetBoxObjectFor(nsIDOMElement* aElement, nsIBoxObject* aBoxObject) { if (!mBoxObjectTable) { if (!aBoxObject) return NS_OK; mBoxObjectTable = new nsSupportsHashtable(12); } nsISupportsKey key(aElement); if (aBoxObject) mBoxObjectTable->Put(&key, aBoxObject); else { nsCOMPtr supp; mBoxObjectTable->Remove(&key, getter_AddRefs(supp)); nsCOMPtr boxObject(do_QueryInterface(supp)); if (boxObject) boxObject->SetDocument(nsnull); } return NS_OK; } // // nsIDOMNode methods // NS_IMETHODIMP nsDocument::GetNodeName(nsAWritableString& aNodeName) { aNodeName.Assign(NS_LITERAL_STRING("#document")); return NS_OK; } NS_IMETHODIMP nsDocument::GetNodeValue(nsAWritableString& aNodeValue) { aNodeValue.Truncate(); return NS_OK; } NS_IMETHODIMP nsDocument::SetNodeValue(const nsAReadableString& aNodeValue) { return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR; } NS_IMETHODIMP nsDocument::GetNodeType(PRUint16* aNodeType) { *aNodeType = nsIDOMNode::DOCUMENT_NODE; return NS_OK; } NS_IMETHODIMP nsDocument::GetParentNode(nsIDOMNode** aParentNode) { *aParentNode = nsnull; return NS_OK; } NS_IMETHODIMP nsDocument::GetChildNodes(nsIDOMNodeList** aChildNodes) { if (nsnull == mChildNodes) { mChildNodes = new nsDocumentChildNodes(this); if (nsnull == mChildNodes) { return NS_ERROR_OUT_OF_MEMORY; } NS_ADDREF(mChildNodes); } return mChildNodes->QueryInterface(NS_GET_IID(nsIDOMNodeList), (void**)aChildNodes); } NS_IMETHODIMP nsDocument::HasChildNodes(PRBool* aHasChildNodes) { NS_ENSURE_ARG(aHasChildNodes); PRUint32 count; mChildren->Count(&count); *aHasChildNodes = (count != 0); return NS_OK; } NS_IMETHODIMP nsDocument::HasAttributes(PRBool* aHasAttributes) { NS_ENSURE_ARG(aHasAttributes); *aHasAttributes = PR_FALSE; return NS_OK; } NS_IMETHODIMP nsDocument::GetFirstChild(nsIDOMNode** aFirstChild) { nsresult result = NS_OK; PRUint32 count; mChildren->Count(&count); if (count) { result = mChildren->QueryElementAt(0, NS_GET_IID(nsIDOMNode), NS_REINTERPRET_CAST(void**, aFirstChild)); } else { *aFirstChild = nsnull; } return result; } NS_IMETHODIMP nsDocument::GetLastChild(nsIDOMNode** aLastChild) { nsresult result = NS_OK; PRUint32 count; mChildren->Count(&count); if (count) { result = mChildren->QueryElementAt(count-1, NS_GET_IID(nsIDOMNode), NS_REINTERPRET_CAST(void**, aLastChild)); } else { *aLastChild = nsnull; } return result; } NS_IMETHODIMP nsDocument::GetPreviousSibling(nsIDOMNode** aPreviousSibling) { *aPreviousSibling = nsnull; return NS_OK; } NS_IMETHODIMP nsDocument::GetNextSibling(nsIDOMNode** aNextSibling) { *aNextSibling = nsnull; return NS_OK; } NS_IMETHODIMP nsDocument::GetAttributes(nsIDOMNamedNodeMap** aAttributes) { *aAttributes = nsnull; return NS_OK; } NS_IMETHODIMP nsDocument::GetNamespaceURI(nsAWritableString& aNamespaceURI) { aNamespaceURI.Truncate(); return NS_OK; } NS_IMETHODIMP nsDocument::GetPrefix(nsAWritableString& aPrefix) { aPrefix.Truncate(); return NS_OK; } NS_IMETHODIMP nsDocument::SetPrefix(const nsAReadableString& aPrefix) { return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR; } NS_IMETHODIMP nsDocument::GetLocalName(nsAWritableString& aLocalName) { aLocalName.Truncate(); return NS_OK; } NS_IMETHODIMP nsDocument::InsertBefore(nsIDOMNode* aNewChild, nsIDOMNode* aRefChild, nsIDOMNode** aReturn) { NS_ASSERTION(nsnull != aNewChild, "null ptr"); PRInt32 index; PRUint16 nodeType; *aReturn = nsnull; // Do we need to do this? if (nsnull == aNewChild) { return NS_ERROR_NULL_POINTER; } // If it's a child type we can't handle (per DOM spec), or if it's an // element and we already have a root (our addition to DOM spec), throw // HIERARCHY_REQUEST_ERR. aNewChild->GetNodeType(&nodeType); if (((COMMENT_NODE != nodeType) && (TEXT_NODE != nodeType) && (PROCESSING_INSTRUCTION_NODE != nodeType) && (DOCUMENT_TYPE_NODE != nodeType) && (ELEMENT_NODE != nodeType)) || ((ELEMENT_NODE == nodeType) && mRootContent)){ return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR; } nsCOMPtr content( do_QueryInterface(aNewChild) ); if (!content) { return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR; } if (!aRefChild) { PRUint32 count; mChildren->Count(&count); index = count; mChildren->AppendElement(content); } else { nsCOMPtr refContent( do_QueryInterface(aRefChild) ); if (!refContent) { return NS_ERROR_DOM_NOT_FOUND_ERR; } index = mChildren->IndexOf(refContent); if (index != -1) { mChildren->InsertElementAt(content, index); } else { // couldn't find refChild return NS_ERROR_DOM_NOT_FOUND_ERR; } } // If we get here, we've succesfully inserted content into the // index-th spot in mChildren. if (ELEMENT_NODE == nodeType) mRootContent = content; ContentInserted(nsnull, content, index); content->SetDocument(this, PR_TRUE, PR_TRUE); *aReturn = aNewChild; NS_ADDREF(aNewChild); return NS_OK; } NS_IMETHODIMP nsDocument::ReplaceChild(nsIDOMNode* aNewChild, nsIDOMNode* aOldChild, nsIDOMNode** aReturn) { NS_ASSERTION(((nsnull != aNewChild) && (nsnull != aOldChild)), "null ptr"); nsresult result = NS_OK; PRInt32 index; PRUint16 nodeType; *aReturn = nsnull; // is this necessary? if ((nsnull == aNewChild) || (nsnull == aOldChild)) { return NS_ERROR_NULL_POINTER; } aNewChild->GetNodeType(&nodeType); if ((COMMENT_NODE != nodeType) && (TEXT_NODE != nodeType) && (PROCESSING_INSTRUCTION_NODE != nodeType) && (DOCUMENT_TYPE_NODE != nodeType) && (ELEMENT_NODE != nodeType)) { return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR; } nsCOMPtr content( do_QueryInterface(aNewChild) ); nsCOMPtr refContent( do_QueryInterface(aOldChild) ); if (!content || !refContent) { return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR; } if ((ELEMENT_NODE == nodeType) && mRootContent && (mRootContent != refContent.get())) { // Caller attempted to add a second element as a child. return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR; } index = mChildren->IndexOf(refContent); if (-1 == index) { // The reference child is not a child of the document. return NS_ERROR_DOM_NOT_FOUND_ERR; } refContent->SetDocument(nsnull, PR_TRUE, PR_TRUE); ContentRemoved(nsnull, refContent, index); mChildren->ReplaceElementAt(content, index); // This is OK because we checked above. if (ELEMENT_NODE == nodeType) mRootContent = content; ContentInserted(nsnull, content, index); content->SetDocument(this, PR_TRUE, PR_TRUE); *aReturn = aOldChild; NS_ADDREF(aOldChild); return result; } NS_IMETHODIMP nsDocument::RemoveChild(nsIDOMNode* aOldChild, nsIDOMNode** aReturn) { NS_ASSERTION(nsnull != aOldChild, "null ptr"); *aReturn = nsnull; // do we need to do this? if (nsnull == aOldChild) { return NS_ERROR_NULL_POINTER; } nsCOMPtr content( do_QueryInterface(aOldChild) ); if (!content) { return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR; } PRInt32 index = mChildren->IndexOf(content); if (-1 == index) { return NS_ERROR_DOM_NOT_FOUND_ERR; } ContentRemoved(nsnull, content, index); mChildren->RemoveElementAt(index); if (content.get() == mRootContent) mRootContent = nsnull; content->SetDocument(nsnull, PR_TRUE, PR_TRUE); *aReturn = aOldChild; NS_ADDREF(aOldChild); return NS_OK; } NS_IMETHODIMP nsDocument::AppendChild(nsIDOMNode* aNewChild, nsIDOMNode** aReturn) { return InsertBefore(aNewChild, nsnull, aReturn); } NS_IMETHODIMP nsDocument::CloneNode(PRBool aDeep, nsIDOMNode** aReturn) { // XXX should be implemented by subclass *aReturn = nsnull; return NS_OK; } NS_IMETHODIMP nsDocument::Normalize() { // XXX Not completely correct, since you can still have unnormalized // text nodes as immediate children of the document. if (mRootContent) { nsCOMPtr node(do_QueryInterface(mRootContent)); if (node) { return node->Normalize(); } } return NS_OK; } NS_IMETHODIMP nsDocument::IsSupported(const nsAReadableString& aFeature, const nsAReadableString& aVersion, PRBool* aReturn) { return nsGenericElement::InternalIsSupported(aFeature, aVersion, aReturn); } NS_IMETHODIMP nsDocument::GetOwnerDocument(nsIDOMDocument** aOwnerDocument) { *aOwnerDocument = nsnull; return NS_OK; } nsresult nsDocument::GetListenerManager(nsIEventListenerManager **aInstancePtrResult) { if (nsnull != mListenerManager) { return mListenerManager->QueryInterface(NS_GET_IID(nsIEventListenerManager), (void**) aInstancePtrResult); } if (NS_OK == GetNewListenerManager(aInstancePtrResult)) { mListenerManager = *aInstancePtrResult; NS_ADDREF(mListenerManager); return NS_OK; } return NS_ERROR_FAILURE; } nsresult nsDocument::GetNewListenerManager(nsIEventListenerManager **aInstancePtrResult) { return NS_NewEventListenerManager(aInstancePtrResult); } nsresult nsDocument::HandleEvent(nsIDOMEvent *aEvent) { return DispatchEvent(aEvent); } nsresult nsDocument::HandleDOMEvent(nsIPresContext* aPresContext, nsEvent* aEvent, nsIDOMEvent** aDOMEvent, PRUint32 aFlags, nsEventStatus* aEventStatus) { nsresult mRet = NS_OK; nsIDOMEvent* domEvent = nsnull; if (NS_EVENT_FLAG_INIT & aFlags) { if (!aDOMEvent) { aDOMEvent = &domEvent; } aEvent->flags = aFlags; aFlags &= ~(NS_EVENT_FLAG_CANT_BUBBLE | NS_EVENT_FLAG_CANT_CANCEL); } //Capturing stage if (NS_EVENT_FLAG_BUBBLE != aFlags && nsnull != mScriptGlobalObject) { mScriptGlobalObject->HandleDOMEvent(aPresContext, aEvent, aDOMEvent, NS_EVENT_FLAG_CAPTURE, aEventStatus); } //Local handling stage if (mListenerManager && !(aEvent->flags & NS_EVENT_FLAG_STOP_DISPATCH) && !(NS_EVENT_FLAG_BUBBLE & aFlags && NS_EVENT_FLAG_CANT_BUBBLE & aEvent->flags)) { aEvent->flags |= aFlags; mListenerManager->HandleEvent(aPresContext, aEvent, aDOMEvent, this, aFlags, aEventStatus); aEvent->flags &= ~aFlags; } //Bubbling stage if (NS_EVENT_FLAG_CAPTURE != aFlags && nsnull != mScriptGlobalObject) { mScriptGlobalObject->HandleDOMEvent(aPresContext, aEvent, aDOMEvent, NS_EVENT_FLAG_BUBBLE, aEventStatus); } if (NS_EVENT_FLAG_INIT & aFlags) { // We're leaving the DOM event loop so if we created a DOM event, release here. if (nsnull != *aDOMEvent) { nsrefcnt rc; NS_RELEASE2(*aDOMEvent, rc); if (0 != rc) { //Okay, so someone in the DOM loop (a listener, JS object) still has a ref to the DOM Event but //the internal data hasn't been malloc'd. Force a copy of the data here so the DOM Event is still valid. nsIPrivateDOMEvent *mPrivateEvent; if (NS_OK == (*aDOMEvent)->QueryInterface(NS_GET_IID(nsIPrivateDOMEvent), (void**)&mPrivateEvent)) { mPrivateEvent->DuplicatePrivateData(); NS_RELEASE(mPrivateEvent); } } } aDOMEvent = nsnull; } return mRet; } nsresult nsDocument::AddEventListenerByIID(nsIDOMEventListener *aListener, const nsIID& aIID) { nsCOMPtr manager; GetListenerManager(getter_AddRefs(manager)); if (manager) { manager->AddEventListenerByIID(aListener, aIID, NS_EVENT_FLAG_BUBBLE); return NS_OK; } return NS_ERROR_FAILURE; } nsresult nsDocument::RemoveEventListenerByIID(nsIDOMEventListener *aListener, const nsIID& aIID) { if (nsnull != mListenerManager) { mListenerManager->RemoveEventListenerByIID(aListener, aIID, NS_EVENT_FLAG_BUBBLE); return NS_OK; } return NS_ERROR_FAILURE; } nsresult nsDocument::AddEventListener(const nsAReadableString& aType, nsIDOMEventListener* aListener, PRBool aUseCapture) { nsCOMPtr manager; GetListenerManager(getter_AddRefs(manager)); if (manager) { PRInt32 flags = aUseCapture ? NS_EVENT_FLAG_CAPTURE : NS_EVENT_FLAG_BUBBLE; manager->AddEventListenerByType(aListener, aType, flags); return NS_OK; } return NS_ERROR_FAILURE; } nsresult nsDocument::RemoveEventListener(const nsAReadableString& aType, nsIDOMEventListener* aListener, PRBool aUseCapture) { if (nsnull != mListenerManager) { PRInt32 flags = aUseCapture ? NS_EVENT_FLAG_CAPTURE : NS_EVENT_FLAG_BUBBLE; mListenerManager->RemoveEventListenerByType(aListener, aType, flags); return NS_OK; } return NS_ERROR_FAILURE; } NS_IMETHODIMP nsDocument::DispatchEvent(nsIDOMEvent* aEvent) { // Obtain a presentation context PRInt32 count = GetNumberOfShells(); if (count == 0) return NS_OK; nsCOMPtr shell(getter_AddRefs(GetShellAt(0))); // Retrieve the context nsCOMPtr presContext; shell->GetPresContext(getter_AddRefs(presContext)); nsCOMPtr esm; if (NS_SUCCEEDED(presContext->GetEventStateManager(getter_AddRefs(esm)))) { return esm->DispatchNewEvent((nsISupports *)(nsIDOMDocument *)this, aEvent); } return NS_ERROR_FAILURE; } NS_IMETHODIMP nsDocument::CreateEvent(const nsAReadableString& aEventType, nsIDOMEvent** aReturn) { // Obtain a presentation context PRInt32 count = GetNumberOfShells(); if (count == 0) return NS_OK; nsCOMPtr shell(getter_AddRefs(GetShellAt(0))); // Retrieve the context nsCOMPtr presContext; shell->GetPresContext(getter_AddRefs(presContext)); if (presContext) { nsCOMPtr manager; GetListenerManager(getter_AddRefs(manager)); if (manager) { return manager->CreateEvent(presContext, nsnull, aEventType, aReturn); } } return NS_ERROR_FAILURE; } PRBool nsDocument::AddProperty(JSContext *aContext, JSObject *aObj, jsval aID, jsval *aVp) { return PR_TRUE; } PRBool nsDocument::DeleteProperty(JSContext *aContext, JSObject *aObj, jsval aID, jsval *aVp) { return PR_TRUE; } PRBool nsDocument::GetProperty(JSContext *aContext, JSObject *aObj, jsval aID, jsval *aVp) { return PR_TRUE; } PRBool nsDocument::SetProperty(JSContext *aContext, JSObject *aObj, jsval aID, jsval *aVp) { PRBool result = PR_TRUE; if (JS_TypeOfValue(aContext, *aVp) == JSTYPE_FUNCTION && JSVAL_IS_STRING(aID)) { const PRUnichar* str = NS_REINTERPRET_CAST(const PRUnichar *, JS_GetStringChars(JS_ValueToString(aContext, aID))); if (str && str[0] == 'o' && str[1] == 'n' && str[2]) { PRBool iidFound = PR_TRUE; nsIID theIID; nsCOMPtr atom(dont_AddRef(NS_NewAtom(str))); if (atom.get() == nsLayoutAtoms::onmousedown || atom.get() == nsLayoutAtoms::onmouseup || atom.get() == nsLayoutAtoms::onclick || atom.get() == nsLayoutAtoms::onmouseover || atom.get() == nsLayoutAtoms::onmouseout) { theIID = NS_GET_IID(nsIDOMMouseListener); } else if (atom.get() == nsLayoutAtoms::onkeydown || atom.get() == nsLayoutAtoms::onkeyup || atom.get() == nsLayoutAtoms::onkeypress) { theIID = NS_GET_IID(nsIDOMKeyListener); } else if (atom.get() == nsLayoutAtoms::onmousemove) { theIID = NS_GET_IID(nsIDOMMouseMotionListener); } else if (atom.get() == nsLayoutAtoms::onfocus || atom.get() == nsLayoutAtoms::onblur) { theIID = NS_GET_IID(nsIDOMFocusListener); } else if (atom.get() == nsLayoutAtoms::onsubmit || atom.get() == nsLayoutAtoms::onreset || atom.get() == nsLayoutAtoms::onchange || atom.get() == nsLayoutAtoms::onselect) { theIID = NS_GET_IID(nsIDOMFormListener); } else if (atom.get() == nsLayoutAtoms::onload || atom.get() == nsLayoutAtoms::onunload || atom.get() == nsLayoutAtoms::onabort || atom.get() == nsLayoutAtoms::onerror) { theIID = NS_GET_IID(nsIDOMLoadListener); } else if (atom.get() == nsLayoutAtoms::onpaint) { theIID = NS_GET_IID(nsIDOMPaintListener); } else { iidFound = PR_FALSE; } if (iidFound) { nsCOMPtr manager; GetListenerManager(getter_AddRefs(manager)); if (manager) { nsCOMPtr scriptContext; nsresult rv = nsContentUtils::GetStaticScriptContext(aContext, NS_REINTERPRET_CAST(JSObject*, mScriptObject), getter_AddRefs(scriptContext)); if (NS_SUCCEEDED(rv) && scriptContext) { rv = manager->RegisterScriptEventListener(scriptContext, this, atom, theIID); } if (NS_FAILED(rv)) result = PR_FALSE; } } } } return result; } PRBool nsDocument::EnumerateProperty(JSContext *aContext, JSObject *aObj) { return PR_TRUE; } PRBool nsDocument::Resolve(JSContext *aContext, JSObject *aObj, jsval aID, PRBool *aDidDefineProperty) { *aDidDefineProperty = PR_FALSE; return PR_TRUE; } PRBool nsDocument::Convert(JSContext *aContext, JSObject *aObj, jsval aID) { return PR_TRUE; } void nsDocument::Finalize(JSContext *aContext, JSObject *aObj) { } /** * Finds text in content */ NS_IMETHODIMP nsDocument::FindNext(const nsAReadableString& aSearchStr, PRBool aMatchCase, PRBool aSearchDown, PRBool &aIsFound) { aIsFound = PR_FALSE; return NS_ERROR_FAILURE; } NS_IMETHODIMP nsDocument::InitDiskDocument(nsIFile *aFile) { // aFile may be nsnull here mFileSpec = nsnull; // delete if we have one if (aFile) { // we clone the nsIFile here, rather than just holding onto a ref, // in case the caller does something to aFile later nsresult rv = aFile->Clone(getter_AddRefs(mFileSpec)); if (NS_FAILED(rv)) return rv; } mModCount = 0; return NS_OK; } NS_IMETHODIMP nsDocument::SaveFile( nsIFile* aFile, PRBool aReplaceExisting, PRBool aSaveCopy, const PRUnichar* aFileType, // MIME type of file to save const PRUnichar* aFileCharset, PRUint32 aSaveFlags, PRUint32 aWrapColumn) { NS_ENSURE_ARG_POINTER(aFile); NS_ENSURE_ARG_POINTER(aFileType); NS_ENSURE_ARG_POINTER(aFileCharset); nsresult rv = NS_OK; // if we're not replacing an existing file but the file // exists, somethine is wrong PRBool fileExists; rv = aFile->Exists(&fileExists); if (NS_FAILED(rv)) return rv; if (!aReplaceExisting && fileExists) return NS_ERROR_FAILURE; // where are the file I/O errors? nsCOMPtr outputStream(do_CreateInstance(NS_LOCALFILEOUTPUTSTREAM_CONTRACTID, &rv)); if (NS_FAILED(rv)) return rv; rv = outputStream->Init(aFile, -1, -1); if (NS_FAILED(rv)) return rv; // Get a document encoder instance nsCAutoString contractID(NS_DOC_ENCODER_CONTRACTID_BASE); contractID.AppendWithConversion(aFileType); nsCOMPtr encoder(do_CreateInstance(contractID.get(), &rv)); if (NS_FAILED(rv)) return rv; nsAutoString fileType(aFileType); // sucky copy rv = encoder->Init(this, fileType, aSaveFlags); if (NS_FAILED(rv)) return rv; if (aSaveFlags & nsIDocumentEncoder::OutputWrap) encoder->SetWrapColumn(aWrapColumn); nsAutoString charsetStr(aFileCharset); if (charsetStr.Length() == 0) { rv = GetDocumentCharacterSet(charsetStr); if(NS_FAILED(rv)) { charsetStr.AssignWithConversion("ISO-8859-1"); } } encoder->SetCharset(charsetStr); rv = encoder->EncodeToStream(outputStream); if (NS_SUCCEEDED(rv)) { // if everything went OK and we're not just saving off a copy, // store the new fileSpec in the doc if (!aSaveCopy) { // we clone the nsIFile here, rather than just holding onto a ref, // in case the caller does something to aFile later rv = aFile->Clone(getter_AddRefs(mFileSpec)); if (NS_SUCCEEDED(rv)) { // and mark the document as clean ResetModificationCount(); } } } return rv; } NS_IMETHODIMP nsDocument::GetFileSpec(nsIFile * *aFileSpec) { NS_ENSURE_ARG_POINTER(aFileSpec); if (!mFileSpec) return NS_ERROR_NOT_INITIALIZED; NS_IF_ADDREF(*aFileSpec = mFileSpec); return NS_OK; } NS_IMETHODIMP nsDocument::FlushPendingNotifications(PRBool aFlushReflows) { if (aFlushReflows) { PRInt32 i, count = mPresShells.Count(); for (i = 0; i < count; i++) { nsIPresShell* shell = NS_STATIC_CAST(nsIPresShell*, mPresShells[i]); if (shell) { shell->FlushPendingNotifications(); } } } return NS_OK; } NS_IMETHODIMP nsDocument::GetAndIncrementContentID(PRInt32* aID) { *aID = mNextContentID++; return NS_OK; } NS_IMETHODIMP nsDocument::GetBindingManager(nsIBindingManager** aResult) { *aResult = mBindingManager; NS_IF_ADDREF(*aResult); return NS_OK; } NS_IMETHODIMP nsDocument::GetModificationCount(PRInt32 *outModCount) { if (!outModCount) return NS_ERROR_NULL_POINTER; *outModCount = mModCount; return NS_OK; } NS_IMETHODIMP nsDocument::ResetModificationCount() { mModCount = 0; return NS_OK; } NS_IMETHODIMP nsDocument::IncrementModificationCount(PRInt32 aNumMods) { mModCount += aNumMods; //NS_ASSERTION(mModCount >= 0, "Modification count went negative"); return NS_OK; } NS_IMETHODIMP nsDocument::GetNodeInfoManager(nsINodeInfoManager*& aNodeInfoManager) { NS_ENSURE_TRUE(mNodeInfoManager, NS_ERROR_NOT_INITIALIZED); aNodeInfoManager = mNodeInfoManager; NS_ADDREF(aNodeInfoManager); return NS_OK; } // // FindContent does a depth-first search from aStartNode // and returns the first of aTest1 or aTest2 which it finds. // I think. // nsIContent* nsDocument::FindContent(const nsIContent* aStartNode, const nsIContent* aTest1, const nsIContent* aTest2) const { PRInt32 count; aStartNode->ChildCount(count); PRInt32 index; for(index = 0; index < count;index++) { nsIContent* child; aStartNode->ChildAt(index, child); nsIContent* content = FindContent(child,aTest1,aTest2); if (content != nsnull) { NS_IF_RELEASE(child); return content; } if (child == aTest1 || child == aTest2) { NS_IF_RELEASE(content); return child; } NS_IF_RELEASE(child); NS_IF_RELEASE(content); } return nsnull; } NS_IMETHODIMP nsDocument::GetDTD(nsIDTD** aDTD) const { if (!aDTD) return NS_ERROR_INVALID_ARG; if (!mDTD) { nsCOMPtr doctype; // Wish for mutable: nsresult rv = NS_CONST_CAST(nsDocument* , this)->GetDoctype(getter_AddRefs(doctype)); if (NS_FAILED(rv)) return rv; if (!doctype) return NS_ERROR_FAILURE; nsAutoString doctypename; rv = doctype->GetName(doctypename); if (NS_FAILED(rv)) return rv; nsCOMPtr parser( do_CreateInstance(kCParserCID, &rv) ); if (NS_FAILED(rv)) return rv; if (!parser) return NS_ERROR_FAILURE; nsIDTD* dtd = 0; rv = parser->CreateCompatibleDTD(&dtd, &doctypename, eViewNormal, 0, eDTDMode_unknown); if (NS_FAILED(rv)) return rv; if (!dtd) return NS_ERROR_FAILURE; // Wish again for mutable: NS_CONST_CAST(nsDocument* , this)->mDTD = dtd; } NS_ADDREF(mDTD); *aDTD = mDTD; return NS_OK; }