/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: NPL 1.1/GPL 2.0/LGPL 2.1 * * 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 Communicator client code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Pierre Phaneuf * * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the NPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the NPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "nsXMLDocument.h" #include "nsParserCIID.h" #include "nsIParser.h" #include "nsIXMLContent.h" #include "nsIXMLContentSink.h" #include "nsIPresShell.h" #include "nsIPresContext.h" #include "nsIContent.h" #include "nsIContentViewerContainer.h" #include "nsIContentViewer.h" #include "nsIWebShell.h" #include "nsIDocShell.h" #include "nsIMarkupDocumentViewer.h" #include "nsIDocumentLoader.h" #include "nsIHTMLContent.h" #include "nsHTMLParts.h" #include "nsIHTMLStyleSheet.h" #include "nsIHTMLCSSStyleSheet.h" #include "nsIStyleSet.h" #include "nsIComponentManager.h" #include "nsIDOMComment.h" #include "nsIDOMElement.h" #include "nsIDOMText.h" #include "nsIBaseWindow.h" #include "nsIDOMCDATASection.h" #include "nsIDOMProcessingInstruction.h" #include "nsIDOMDocumentType.h" #include "nsINameSpaceManager.h" #include "nsICSSLoader.h" #include "nsCOMPtr.h" #include "nsXPIDLString.h" #include "nsIHttpChannel.h" #include "nsIURI.h" #include "nsIServiceManager.h" #include "nsICharsetAlias.h" #include "nsICharsetAlias.h" #include "nsNetUtil.h" #include "nsDOMError.h" #include "nsScriptSecurityManager.h" #include "nsIPrincipal.h" #include "nsIAggregatePrincipal.h" #include "nsLayoutCID.h" #include "nsDOMAttribute.h" #include "nsGUIEvent.h" #include "nsFIXptr.h" #include "nsCExternalHandlerService.h" #include "nsIMIMEService.h" #include "nsNetUtil.h" #include "nsMimeTypes.h" #include "nsIEventListenerManager.h" #include "nsContentUtils.h" #include "nsIElementFactory.h" #include "nsCRT.h" #include "nsIWindowWatcher.h" #include "nsIAuthPrompt.h" #include "nsIScriptGlobalObjectOwner.h" // XXX The XML world depends on the html atoms #include "nsHTMLAtoms.h" static const char kLoadAsData[] = "loadAsData"; static NS_DEFINE_CID(kCharsetAliasCID, NS_CHARSETALIAS_CID); // ================================================================== // = // ================================================================== nsresult NS_NewDOMDocument(nsIDOMDocument** aInstancePtrResult, const nsAString& aNamespaceURI, const nsAString& aQualifiedName, nsIDOMDocumentType* aDoctype, nsIURI* aBaseURI) { nsresult rv; *aInstancePtrResult = nsnull; nsXMLDocument* doc = new nsXMLDocument(); if (!doc) return NS_ERROR_OUT_OF_MEMORY; rv = doc->Init(); if (NS_FAILED(rv)) { delete doc; return rv; } nsCOMPtr kungFuDeathGrip(doc); doc->SetDocumentURL(aBaseURI); doc->SetBaseURL(aBaseURI); if (aDoctype) { nsCOMPtr tmpNode; rv = doc->AppendChild(aDoctype, getter_AddRefs(tmpNode)); NS_ENSURE_SUCCESS(rv, rv); } if (!aQualifiedName.IsEmpty()) { nsCOMPtr root; rv = doc->CreateElementNS(aNamespaceURI, aQualifiedName, getter_AddRefs(root)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr tmpNode; rv = doc->AppendChild(root, getter_AddRefs(tmpNode)); NS_ENSURE_SUCCESS(rv, rv); } *aInstancePtrResult = doc; NS_ADDREF(*aInstancePtrResult); return NS_OK; } nsresult NS_NewXMLDocument(nsIDocument** aInstancePtrResult) { nsXMLDocument* doc = new nsXMLDocument(); NS_ENSURE_TRUE(doc, NS_ERROR_OUT_OF_MEMORY); nsresult rv = doc->Init(); if (NS_FAILED(rv)) { delete doc; return rv; } *aInstancePtrResult = doc; NS_ADDREF(*aInstancePtrResult); return NS_OK; } nsXMLDocument::nsXMLDocument() : mCountCatalogSheets(0), mCrossSiteAccessEnabled(PR_FALSE), mLoadedAsData(PR_FALSE), mXMLDeclarationBits(0) { } nsXMLDocument::~nsXMLDocument() { if (mAttrStyleSheet) { mAttrStyleSheet->SetOwningDocument(nsnull); } if (mInlineStyleSheet) { mInlineStyleSheet->SetOwningDocument(nsnull); } if (mCSSLoader) { mCSSLoader->DropDocumentReference(); } } // QueryInterface implementation for nsHTMLAnchorElement NS_INTERFACE_MAP_BEGIN(nsXMLDocument) NS_INTERFACE_MAP_ENTRY(nsIXMLDocument) NS_INTERFACE_MAP_ENTRY(nsIHTMLContentContainer) NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor) NS_INTERFACE_MAP_ENTRY(nsIHttpEventSink) NS_INTERFACE_MAP_ENTRY(nsIDOMXMLDocument) NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(XMLDocument) NS_INTERFACE_MAP_END_INHERITING(nsDocument) NS_IMPL_ADDREF_INHERITED(nsXMLDocument, nsDocument) NS_IMPL_RELEASE_INHERITED(nsXMLDocument, nsDocument) NS_IMETHODIMP nsXMLDocument::Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup) { nsresult result = nsDocument::Reset(aChannel, aLoadGroup); if (NS_FAILED(result)) return result; nsCOMPtr url; if (aChannel) { result = aChannel->GetURI(getter_AddRefs(url)); if (NS_FAILED(result)) return result; } if (mAttrStyleSheet) { mAttrStyleSheet->SetOwningDocument(nsnull); } if (mInlineStyleSheet) { mInlineStyleSheet->SetOwningDocument(nsnull); } result = SetDefaultStylesheets(url); mBaseTarget.Truncate(); return result; } ///////////////////////////////////////////////////// // nsIInterfaceRequestor methods: // NS_IMETHODIMP nsXMLDocument::GetInterface(const nsIID& aIID, void** aSink) { if (aIID.Equals(NS_GET_IID(nsIAuthPrompt))) { NS_ENSURE_ARG_POINTER(aSink); *aSink = nsnull; nsresult rv; nsCOMPtr ww(do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv)); if (NS_FAILED(rv)) return rv; nsCOMPtr prompt; rv = ww->GetNewAuthPrompter(nsnull, getter_AddRefs(prompt)); if (NS_FAILED(rv)) return rv; nsIAuthPrompt *p = prompt.get(); NS_ADDREF(p); *aSink = p; return NS_OK; } return QueryInterface(aIID, aSink); } // nsIHttpEventSink NS_IMETHODIMP nsXMLDocument::OnRedirect(nsIHttpChannel *aHttpChannel, nsIChannel *aNewChannel) { NS_ENSURE_ARG_POINTER(aNewChannel); nsresult rv; nsCOMPtr secMan = do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv); if (NS_FAILED(rv)) return rv; nsCOMPtr newLocation; rv = aNewChannel->GetURI(getter_AddRefs(newLocation)); // The redirected URI if (NS_FAILED(rv)) return rv; if (mScriptContext && !mCrossSiteAccessEnabled) { nsCOMPtr stack(do_GetService("@mozilla.org/js/xpc/ContextStack;1", & rv)); if (NS_FAILED(rv)) return rv; JSContext *cx = (JSContext *)mScriptContext->GetNativeContext(); if (!cx) return NS_ERROR_UNEXPECTED; stack->Push(cx); rv = secMan->CheckSameOrigin(nsnull, newLocation); stack->Pop(&cx); if (NS_FAILED(rv)) return rv; } nsCOMPtr newCodebase; rv = secMan->GetCodebasePrincipal(newLocation, getter_AddRefs(newCodebase)); if (NS_FAILED(rv)) return NS_ERROR_FAILURE; nsCOMPtr agg = do_QueryInterface(mPrincipal, &rv); NS_ASSERTION(NS_SUCCEEDED(rv), "Principal not an aggregate."); if (NS_FAILED(rv)) return NS_ERROR_FAILURE; rv = agg->SetCodebase(newCodebase); return rv; } NS_IMETHODIMP nsXMLDocument::EvaluateFIXptr(const nsAString& aExpression, nsIDOMRange **aRange) { return nsFIXptr::Evaluate(this, aExpression, aRange); } NS_IMETHODIMP nsXMLDocument::Load(const nsAString& aUrl) { nsCOMPtr channel; nsCOMPtr uri; nsresult rv; // Create a new URI rv = NS_NewURI(getter_AddRefs(uri), aUrl, nsnull, mDocumentURL); if (NS_FAILED(rv)) return rv; // Get security manager, check to see if we're allowed to load this URI nsCOMPtr secMan = do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv); if (NS_FAILED(rv)) return rv; rv = secMan->CheckConnect(nsnull, uri, "XMLDocument", "load"); if (NS_FAILED(rv)) { // We need to return success here so that JS will get a proper exception // thrown later. Native calls should always result in CheckConnect() succeeding, // but in case JS calls C++ which calls this code the exception might be lost. return NS_OK; } // Partial Reset, need to restore principal for security reasons and // event listener manager so that load listeners etc. will remain. nsCOMPtr principal = mPrincipal; nsCOMPtr elm = mListenerManager; Reset(nsnull, nsnull); mPrincipal = principal; mListenerManager = elm; NS_IF_ADDREF(mListenerManager); SetDocumentURL(uri); SetBaseURL(uri); // Store script context, if any, in case we encounter redirect (because we need it there) nsCOMPtr stack = do_GetService("@mozilla.org/js/xpc/ContextStack;1"); if (stack) { JSContext *cx; if (NS_SUCCEEDED(stack->Peek(&cx)) && cx) { nsISupports *priv = (nsISupports *)::JS_GetContextPrivate(cx); if (priv) { mScriptContext = do_QueryInterface(priv); } } } // Find out if UniversalBrowserRead privileges are enabled - we will need this // in case of a redirect PRBool crossSiteAccessEnabled; rv = secMan->IsCapabilityEnabled("UniversalBrowserRead", &crossSiteAccessEnabled); if (NS_FAILED(rv)) return rv; mCrossSiteAccessEnabled = crossSiteAccessEnabled; // Create a channel rv = NS_NewChannel(getter_AddRefs(channel), uri, nsnull, nsnull, this); if (NS_FAILED(rv)) return rv; // Set a principal for this document mPrincipal = nsnull; nsCOMPtr channelOwner; rv = channel->GetOwner(getter_AddRefs(channelOwner)); if (NS_SUCCEEDED(rv) && channelOwner) { mPrincipal = do_QueryInterface(channelOwner, &rv); } if (NS_FAILED(rv) || !channelOwner) { rv = secMan->GetCodebasePrincipal(uri, getter_AddRefs(mPrincipal)); NS_ENSURE_TRUE(mPrincipal, rv); } // Prepare for loading the XML document "into oneself" nsCOMPtr listener; if (NS_FAILED(rv = StartDocumentLoad(kLoadAsData, channel, nsnull, nsnull, getter_AddRefs(listener), PR_FALSE))) { NS_ERROR("nsXMLDocument::Load: Failed to start the document load."); return rv; } // Start an asynchronous read of the XML document rv = channel->AsyncOpen(listener, nsnull); return rv; } NS_IMETHODIMP nsXMLDocument::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel, nsILoadGroup* aLoadGroup, nsISupports* aContainer, nsIStreamListener **aDocListener, PRBool aReset, nsIContentSink* aSink) { if (nsCRT::strcmp(kLoadAsData, aCommand) == 0) { // We need to disable script & style loading in this case. // We leave them disabled even in EndLoad(), and let anyone // who puts the document on display to worry about enabling. // scripts nsCOMPtr loader; nsresult rv = GetScriptLoader(getter_AddRefs(loader)); if (NS_FAILED(rv)) return rv; if (loader) { loader->SetEnabled(PR_FALSE); // Do not load/process scripts when loading as data } // styles nsCOMPtr cssLoader; rv = GetCSSLoader(*getter_AddRefs(cssLoader)); if (NS_FAILED(rv)) return rv; if (cssLoader) { cssLoader->SetEnabled(PR_FALSE); // Do not load/process styles when loading as data } } else if (nsCRT::strcmp("loadAsInteractiveData", aCommand) == 0) { aCommand = kLoadAsData; // XBL, for example, needs scripts and styles } if (nsCRT::strcmp(aCommand, kLoadAsData) == 0) { mLoadedAsData = PR_TRUE; } nsresult rv = nsDocument::StartDocumentLoad(aCommand, aChannel, aLoadGroup, aContainer, aDocListener, aReset, aSink); if (NS_FAILED(rv)) return rv; nsAutoString charset(NS_LITERAL_STRING("UTF-8")); PRInt32 charsetSource = kCharsetFromDocTypeDefault; nsCOMPtr aUrl; rv = aChannel->GetURI(getter_AddRefs(aUrl)); if (NS_FAILED(rv)) return rv; { // check channel's charset... nsCAutoString charsetVal; rv = aChannel->GetContentCharset(charsetVal); if (NS_SUCCEEDED(rv)) { nsCOMPtr calias(do_GetService(kCharsetAliasCID,&rv)); if(NS_SUCCEEDED(rv) && (nsnull != calias) ) { nsAutoString preferred; rv = calias->GetPreferred(NS_ConvertASCIItoUCS2(charsetVal), preferred); if(NS_SUCCEEDED(rv)){ charset = preferred; charsetSource = kCharsetFromChannel; } } } } //end of checking channel's charset static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID); nsCOMPtr parser = do_CreateInstance(kCParserCID, &rv); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr sink; if (aSink) { sink = do_QueryInterface(aSink); } else { // This is silly, aContainer should be an nsIWebShell nsCOMPtr webShell; if (aContainer) { webShell = do_QueryInterface(aContainer); NS_ENSURE_TRUE(webShell, NS_ERROR_FAILURE); } rv = NS_NewXMLContentSink(getter_AddRefs(sink), this, aUrl, webShell, aChannel); NS_ENSURE_SUCCESS(rv, rv); } // Set the parser as the stream listener for the document loader... rv = CallQueryInterface(parser, aDocListener); NS_ENSURE_SUCCESS(rv, rv); SetDocumentCharacterSet(charset); parser->SetDocumentCharset(charset, charsetSource); parser->SetCommand(aCommand); parser->SetContentSink(sink); parser->Parse(aUrl, nsnull, PR_FALSE, (void *)this); return NS_OK; } NS_IMETHODIMP nsXMLDocument::EndLoad() { if (mLoadedAsData) { // Generate a document load event for the case when an XML document was loaded // as pure data without any presentation attached to it. nsEventStatus status = nsEventStatus_eIgnore; nsEvent event; event.eventStructType = NS_EVENT; event.message = NS_PAGE_LOAD; nsCOMPtr sgo; nsCOMPtr container = do_QueryReferent(mDocumentContainer); if (container) { container->GetScriptGlobalObject(getter_AddRefs(sgo)); } nsCxPusher pusher(sgo); HandleDOMEvent(nsnull, &event, nsnull, NS_EVENT_FLAG_INIT, &status); } return nsDocument::EndLoad(); } NS_IMETHODIMP nsXMLDocument::GetAttributeStyleSheet(nsIHTMLStyleSheet** aResult) { NS_ENSURE_ARG_POINTER(aResult); *aResult = mAttrStyleSheet; NS_ENSURE_TRUE(mAttrStyleSheet, NS_ERROR_NOT_AVAILABLE); // probably not the right error... NS_ADDREF(*aResult); return NS_OK; } NS_IMETHODIMP nsXMLDocument::GetInlineStyleSheet(nsIHTMLCSSStyleSheet** aResult) { NS_ENSURE_ARG_POINTER(aResult); *aResult = mInlineStyleSheet; NS_ENSURE_TRUE(mInlineStyleSheet, NS_ERROR_NOT_AVAILABLE); // probably not the right error... NS_ADDREF(*aResult); return NS_OK; } // subclass hook for sheet ordering void nsXMLDocument::InternalAddStyleSheet(nsIStyleSheet* aSheet, PRUint32 aFlags) { // XXXbz this catalog stuff should be in the UA level in the cascade! if (aFlags & NS_STYLESHEET_FROM_CATALOG) { // always after other catalog sheets mStyleSheets.InsertObjectAt(aSheet, mCountCatalogSheets); ++mCountCatalogSheets; } else if (aSheet == mAttrStyleSheet) { // always after catalog sheets NS_ASSERTION(mStyleSheets.Count() == 0 || mAttrStyleSheet != mStyleSheets[0], "Adding attr sheet twice!"); mStyleSheets.InsertObjectAt(aSheet, mCountCatalogSheets); } else if (aSheet == mInlineStyleSheet) { // always last NS_ASSERTION(mStyleSheets.Count() == 0 || mStyleSheets[mStyleSheets.Count() - 1] != mInlineStyleSheet, "Adding style attr sheet twice!"); mStyleSheets.AppendObject(aSheet); } else { PRInt32 count = mStyleSheets.Count(); if (count != 0 && mInlineStyleSheet == mStyleSheets[count - 1]) { // keep attr sheet last mStyleSheets.InsertObjectAt(aSheet, count - 1); } else { mStyleSheets.AppendObject(aSheet); } } } void nsXMLDocument::InternalInsertStyleSheetAt(nsIStyleSheet* aSheet, PRInt32 aIndex) { NS_ASSERTION(0 <= aIndex && aIndex <= ( mStyleSheets.Count() /* Don't count Attribute stylesheet */ - 1 /* Don't count catalog sheets */ - mCountCatalogSheets /* No insertion allowed after StyleAttr stylesheet */ - (mInlineStyleSheet ? 1: 0) ), "index out of bounds"); // offset w.r.t. catalog style sheets and the attr style sheet mStyleSheets.InsertObjectAt(aSheet, aIndex + mCountCatalogSheets + 1); } already_AddRefed nsXMLDocument::InternalGetStyleSheetAt(PRInt32 aIndex) { PRInt32 count = InternalGetNumberOfStyleSheets(); if (aIndex >= 0 && aIndex < count) { nsIStyleSheet* sheet = mStyleSheets[aIndex + mCountCatalogSheets + 1]; NS_ADDREF(sheet); return sheet; } else { NS_ERROR("Index out of range"); return nsnull; } } PRInt32 nsXMLDocument::InternalGetNumberOfStyleSheets() { PRInt32 count = mStyleSheets.Count(); if (count != 0 && mInlineStyleSheet == mStyleSheets[count - 1]) --count; count -= (mCountCatalogSheets + 1); // +1 for the attr sheet NS_ASSERTION(count >= 0, "Why did we end up with a negative count?"); return count; } // nsIDOMDocument interface NS_IMETHODIMP nsXMLDocument::GetDoctype(nsIDOMDocumentType** aDocumentType) { return nsDocument::GetDoctype(aDocumentType); } NS_IMETHODIMP nsXMLDocument::CreateCDATASection(const nsAString& aData, nsIDOMCDATASection** aReturn) { NS_ENSURE_ARG_POINTER(aReturn); *aReturn = nsnull; nsReadingIterator begin; nsReadingIterator end; aData.BeginReading(begin); aData.EndReading(end); if (FindInReadable(NS_LITERAL_STRING("]]>"),begin,end)) return NS_ERROR_DOM_INVALID_CHARACTER_ERR; nsCOMPtr content; nsresult rv = NS_NewXMLCDATASection(getter_AddRefs(content)); if (NS_SUCCEEDED(rv)) { rv = CallQueryInterface(content, aReturn); (*aReturn)->AppendData(aData); } return rv; } NS_IMETHODIMP nsXMLDocument::CreateEntityReference(const nsAString& aName, nsIDOMEntityReference** aReturn) { NS_ENSURE_ARG_POINTER(aReturn); *aReturn = nsnull; return NS_OK; } NS_IMETHODIMP nsXMLDocument::CreateProcessingInstruction(const nsAString& aTarget, const nsAString& aData, nsIDOMProcessingInstruction** aReturn) { NS_ENSURE_ARG_POINTER(aReturn); *aReturn = nsnull; nsCOMPtr content; nsresult rv = NS_NewXMLProcessingInstruction(getter_AddRefs(content), aTarget, aData); if (NS_FAILED(rv)) { return rv; } return CallQueryInterface(content, aReturn); } NS_IMETHODIMP nsXMLDocument::CreateElement(const nsAString& aTagName, nsIDOMElement** aReturn) { NS_ENSURE_ARG_POINTER(aReturn); *aReturn = nsnull; NS_ENSURE_TRUE(!aTagName.IsEmpty(), NS_ERROR_DOM_INVALID_CHARACTER_ERR); nsCOMPtr nodeInfo; nsresult rv; rv = mNodeInfoManager->GetNodeInfo(aTagName, nsnull, kNameSpaceID_None, *getter_AddRefs(nodeInfo)); NS_ENSURE_SUCCESS(rv, rv); return CreateElement(nodeInfo, aReturn); } NS_IMETHODIMP nsXMLDocument::CloneNode(PRBool aDeep, nsIDOMNode** aReturn) { NS_ENSURE_ARG_POINTER(aReturn); *aReturn = nsnull; nsresult rv; nsCOMPtr docType, newDocType; nsCOMPtr newDoc; // Get the doctype prior to new document construction. There's no big // advantage now to dealing with the doctype separately, but maybe one // day we'll do something significant with the doctype on document creation. GetDoctype(getter_AddRefs(docType)); if (docType) { nsCOMPtr newDocTypeNode; rv = docType->CloneNode(PR_TRUE, getter_AddRefs(newDocTypeNode)); if (NS_FAILED(rv)) return rv; newDocType = do_QueryInterface(newDocTypeNode); } // Create an empty document nsAutoString emptyStr; emptyStr.Truncate(); rv = NS_NewDOMDocument(getter_AddRefs(newDoc), emptyStr, emptyStr, newDocType, mDocumentURL); if (NS_FAILED(rv)) return rv; if (aDeep) { // If there was a doctype, a new one has already been inserted into the // new document. We might have to add nodes before it. PRBool beforeDocType = (docType.get() != nsnull); nsCOMPtr childNodes; GetChildNodes(getter_AddRefs(childNodes)); if (childNodes) { PRUint32 index, count; childNodes->GetLength(&count); for (index=0; index < count; index++) { nsCOMPtr child; childNodes->Item(index, getter_AddRefs(child)); if (child && (child != docType)) { nsCOMPtr newChild; rv = child->CloneNode(aDeep, getter_AddRefs(newChild)); if (NS_FAILED(rv)) return rv; nsCOMPtr dummyNode; if (beforeDocType) { rv = newDoc->InsertBefore(newChild, docType, getter_AddRefs(dummyNode)); if (NS_FAILED(rv)) return NS_ERROR_FAILURE; } else { rv = newDoc->AppendChild(newChild, getter_AddRefs(dummyNode)); if (NS_FAILED(rv)) return NS_ERROR_FAILURE; } } else { beforeDocType = PR_FALSE; } } } } return CallQueryInterface(newDoc, aReturn); } NS_IMETHODIMP nsXMLDocument::ImportNode(nsIDOMNode* aImportedNode, PRBool aDeep, nsIDOMNode** aReturn) { return nsDocument::ImportNode(aImportedNode, aDeep, aReturn); } NS_IMETHODIMP nsXMLDocument::CreateAttributeNS(const nsAString& aNamespaceURI, const nsAString& aQualifiedName, nsIDOMAttr** aReturn) { NS_ENSURE_ARG_POINTER(aReturn); *aReturn = nsnull; nsCOMPtr nodeInfo; nsresult rv = mNodeInfoManager->GetNodeInfo(aQualifiedName, aNamespaceURI, *getter_AddRefs(nodeInfo)); NS_ENSURE_SUCCESS(rv, rv); nsAutoString value; nsDOMAttribute* attribute = new nsDOMAttribute(nsnull, nodeInfo, value); NS_ENSURE_TRUE(attribute, NS_ERROR_OUT_OF_MEMORY); return CallQueryInterface(attribute, aReturn); } NS_IMETHODIMP nsXMLDocument::CreateElementNS(const nsAString& aNamespaceURI, const nsAString& aQualifiedName, nsIDOMElement** aReturn) { NS_ENSURE_ARG_POINTER(aReturn); *aReturn = nsnull; nsresult rv = NS_OK; nsCOMPtr nodeInfo; rv = mNodeInfoManager->GetNodeInfo(aQualifiedName, aNamespaceURI, *getter_AddRefs(nodeInfo)); NS_ENSURE_SUCCESS(rv, rv); return CreateElement(nodeInfo, aReturn); } static nsIContent * MatchId(nsIContent *aContent, const nsAString& aName) { nsAutoString value; nsIContent *result = nsnull; PRInt32 ns; aContent->GetNameSpaceID(ns); if (kNameSpaceID_XHTML == ns) { if ((NS_CONTENT_ATTR_HAS_VALUE == aContent->GetAttr(kNameSpaceID_None, nsHTMLAtoms::id, value)) && aName.Equals(value)) { return aContent; } } else { nsCOMPtr xmlContent = do_QueryInterface(aContent); nsCOMPtr IDValue; if (xmlContent && NS_SUCCEEDED(xmlContent->GetID(*getter_AddRefs(IDValue))) && IDValue) { const PRUnichar* IDValStr = nsnull; IDValue->GetUnicode(&IDValStr); if (aName.Equals(IDValStr)) { return aContent; } } } PRInt32 i, count; aContent->ChildCount(count); for (i = 0; i < count && result == nsnull; i++) { nsIContent *child; aContent->ChildAt(i, child); result = MatchId(child, aName); NS_RELEASE(child); } return result; } NS_IMETHODIMP nsXMLDocument::GetElementById(const nsAString& aElementId, nsIDOMElement** aReturn) { NS_ENSURE_ARG_POINTER(aReturn); *aReturn = nsnull; NS_WARN_IF_FALSE(!aElementId.IsEmpty(),"getElementById(\"\"), fix caller?"); if (aElementId.IsEmpty()) return NS_OK; // If we tried to load a document and something went wrong, we might not have // root content. This can happen when you do document.load() and the document // to load is not XML, for example. if (!mRootContent) return NS_OK; // XXX For now, we do a brute force search of the content tree. // We should come up with a more efficient solution. nsCOMPtr content(do_QueryInterface(MatchId(mRootContent,aElementId))); nsresult rv = NS_OK; if (content) { rv = CallQueryInterface(content, aReturn); } return rv; } // nsIXMLDocument NS_IMETHODIMP nsXMLDocument::SetDefaultStylesheets(nsIURI* aUrl) { nsresult result = NS_OK; if (aUrl) { result = NS_NewHTMLStyleSheet(getter_AddRefs(mAttrStyleSheet), aUrl, this); if (NS_SUCCEEDED(result)) { result = NS_NewHTMLCSSStyleSheet(getter_AddRefs(mInlineStyleSheet), aUrl, this); AddStyleSheet(mAttrStyleSheet, 0); // tell the world about our new style sheet if (NS_SUCCEEDED(result)) { AddStyleSheet(mInlineStyleSheet, 0); // tell the world about our new style sheet } } } return result; } NS_IMETHODIMP nsXMLDocument::SetXMLDeclaration(const nsAString& aVersion, const nsAString& aEncoding, const nsAString& aStandalone) { if (aVersion.IsEmpty()) { mXMLDeclarationBits = 0; return NS_OK; } mXMLDeclarationBits = XML_DECLARATION_BITS_DECLARATION_EXISTS; if (!aEncoding.IsEmpty()) { mXMLDeclarationBits |= XML_DECLARATION_BITS_ENCODING_EXISTS; } if (aStandalone.Equals(NS_LITERAL_STRING("yes"))) { mXMLDeclarationBits |= XML_DECLARATION_BITS_STANDALONE_EXISTS | XML_DECLARATION_BITS_STANDALONE_YES; } else if (aStandalone.Equals(NS_LITERAL_STRING("no"))) { mXMLDeclarationBits |= XML_DECLARATION_BITS_STANDALONE_EXISTS; } return NS_OK; } NS_IMETHODIMP nsXMLDocument::GetXMLDeclaration(nsAString& aVersion, nsAString& aEncoding, nsAString& aStandalone) { aVersion.Truncate(); aEncoding.Truncate(); aStandalone.Truncate(); if (!(mXMLDeclarationBits & XML_DECLARATION_BITS_DECLARATION_EXISTS)) { return NS_OK; } aVersion.Assign(NS_LITERAL_STRING("1.0")); // always until we start supporting 1.1 etc. if (mXMLDeclarationBits & XML_DECLARATION_BITS_ENCODING_EXISTS) { GetDocumentCharacterSet(aEncoding); // This is what we have stored, not necessarily what was written in the original } if (mXMLDeclarationBits & XML_DECLARATION_BITS_STANDALONE_EXISTS) { if (mXMLDeclarationBits & XML_DECLARATION_BITS_STANDALONE_YES) { aStandalone.Assign(NS_LITERAL_STRING("yes")); } else { aStandalone.Assign(NS_LITERAL_STRING("no")); } } return NS_OK; } NS_IMETHODIMP nsXMLDocument::SetBaseTarget(const nsAString &aBaseTarget) { mBaseTarget.Assign(aBaseTarget); return NS_OK; } NS_IMETHODIMP nsXMLDocument::GetBaseTarget(nsAString &aBaseTarget) { aBaseTarget.Assign(mBaseTarget); return NS_OK; } NS_IMETHODIMP nsXMLDocument::GetCSSLoader(nsICSSLoader*& aLoader) { nsresult result = NS_OK; if (! mCSSLoader) { result = NS_NewCSSLoader(this, getter_AddRefs(mCSSLoader)); if (mCSSLoader) { mCSSLoader->SetCaseSensitive(PR_TRUE); // No quirks in XML mCSSLoader->SetCompatibilityMode(eCompatibility_FullStandards); } } aLoader = mCSSLoader; NS_IF_ADDREF(aLoader); return result; } nsresult nsXMLDocument::CreateElement(nsINodeInfo *aNodeInfo, nsIDOMElement** aResult) { *aResult = nsnull; nsresult rv; nsCOMPtr content; PRInt32 namespaceID; aNodeInfo->GetNamespaceID(namespaceID); nsCOMPtr elementFactory; nsContentUtils::GetNSManagerWeakRef()->GetElementFactory(namespaceID, getter_AddRefs(elementFactory)); if (elementFactory) { rv = elementFactory->CreateInstanceByTag(aNodeInfo, getter_AddRefs(content)); } else { rv = NS_NewXMLElement(getter_AddRefs(content), aNodeInfo); } NS_ENSURE_SUCCESS(rv, rv); content->SetContentID(mNextContentID++); return CallQueryInterface(content, aResult); }