/* -*- 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 Communicator client code, released * March 31, 1998. * * 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): * Johnny Stenback * Christopher A. Aillon * * Alternatively, the contents of this file may be used under the * terms of the GNU Public License (the "GPL"), in which case the * provisions of the GPL are applicable instead of those above. * If you wish to allow use of your version of this file only * under the terms of the GPL and not to allow others to use your * version of this file under the NPL, indicate your decision by * deleting the provisions above and replace them with the notice * and other provisions required by the GPL. If you do not delete * the provisions above, a recipient may use your version of this * file under either the NPL or the GPL. */ /* A namespace class for static layout utilities. */ #include "jsapi.h" #include "nsCOMPtr.h" #include "nsAString.h" #include "nsPrintfCString.h" #include "nsUnicharUtils.h" #include "nsIServiceManagerUtils.h" #include "nsIScriptGlobalObject.h" #include "nsIScriptContext.h" #include "nsIDOMScriptObjectFactory.h" #include "nsDOMCID.h" #include "nsContentUtils.h" #include "nsIXPConnect.h" #include "nsIContent.h" #include "nsIDocument.h" #include "nsINodeInfo.h" #include "nsReadableUtils.h" #include "nsIDOMDocument.h" #include "nsIDOMNodeList.h" #include "nsIDOM3Node.h" #include "nsIIOService.h" #include "nsIURI.h" #include "nsNetCID.h" #include "nsNetUtil.h" #include "nsIScriptSecurityManager.h" #include "nsDOMError.h" #include "nsIDOMWindowInternal.h" #include "nsIJSContextStack.h" #include "nsIDocShell.h" #include "nsIDocShellTreeItem.h" #include "nsParserCIID.h" #include "nsIParserService.h" #include "nsIServiceManager.h" #include "nsIAttribute.h" #include "nsIContentList.h" #include "nsIHTMLDocument.h" #include "nsIDOMHTMLDocument.h" #include "nsIDOMHTMLCollection.h" #include "nsIDOMHTMLFormElement.h" #include "nsIForm.h" #include "nsIFormControl.h" #include "nsHTMLAtoms.h" static const char kJSStackContractID[] = "@mozilla.org/js/xpc/ContextStack;1"; static NS_DEFINE_IID(kParserServiceCID, NS_PARSERSERVICE_CID); nsIDOMScriptObjectFactory *nsContentUtils::sDOMScriptObjectFactory = nsnull; nsIXPConnect *nsContentUtils::sXPConnect = nsnull; nsIScriptSecurityManager *nsContentUtils::sSecurityManager = nsnull; nsIThreadJSContextStack *nsContentUtils::sThreadJSContextStack = nsnull; nsIParserService *nsContentUtils::sParserService = nsnull; nsINameSpaceManager *nsContentUtils::sNameSpaceManager = nsnull; nsIIOService *nsContentUtils::sIOService = nsnull; PRBool nsContentUtils::sInitialized = PR_FALSE; // static nsresult nsContentUtils::Init() { if (sInitialized) { NS_WARNING("Init() called twice"); return NS_OK; } nsresult rv = CallGetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &sSecurityManager); NS_ENSURE_SUCCESS(rv, rv); rv = NS_GetNameSpaceManager(&sNameSpaceManager); NS_ENSURE_SUCCESS(rv, rv); rv = CallGetService(nsIXPConnect::GetCID(), &sXPConnect); if (NS_FAILED(rv)) { // We could be a standalone DOM engine without JS, so no // nsIXPConnect is actually ok... sXPConnect = nsnull; } rv = CallGetService(kJSStackContractID, &sThreadJSContextStack); if (NS_FAILED(rv) && sXPConnect) { // However, if we can't get a context stack after getting // an nsIXPConnect, things are broken, so let's fail here. return rv; } rv = CallGetService(NS_IOSERVICE_CONTRACTID, &sIOService); if (NS_FAILED(rv)) { // This makes life easier, but we can live without it. sIOService = nsnull; } sInitialized = PR_TRUE; return NS_OK; } /** * Access a cached parser service. Don't addref. We need only one * reference to it and this class has that one. */ /* static */ nsIParserService* nsContentUtils::GetParserServiceWeakRef() { // XXX: This isn't accessed from several threads, is it? if (!sParserService) { // Lock, recheck sCachedParserService and aquire if this should be // safe for multiple threads. nsresult rv = CallGetService(kParserServiceCID, &sParserService); if (NS_FAILED(rv)) { sParserService = nsnull; } } return sParserService; } // static nsresult nsContentUtils::GetStaticScriptGlobal(JSContext* aContext, JSObject* aObj, nsIScriptGlobalObject** aNativeGlobal) { if (!sXPConnect) { *aNativeGlobal = nsnull; return NS_OK; } JSObject* parent; JSObject* glob = aObj; // starting point for search if (!glob) return NS_ERROR_FAILURE; while (nsnull != (parent = JS_GetParent(aContext, glob))) { glob = parent; } nsCOMPtr wrapped_native; nsresult rv = sXPConnect->GetWrappedNativeOfJSObject(aContext, glob, getter_AddRefs(wrapped_native)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr native; rv = wrapped_native->GetNative(getter_AddRefs(native)); NS_ENSURE_SUCCESS(rv, rv); return CallQueryInterface(native, aNativeGlobal); } //static nsresult nsContentUtils::GetStaticScriptContext(JSContext* aContext, JSObject* aObj, nsIScriptContext** aScriptContext) { nsCOMPtr nativeGlobal; GetStaticScriptGlobal(aContext, aObj, getter_AddRefs(nativeGlobal)); if (!nativeGlobal) return NS_ERROR_FAILURE; nsIScriptContext* scriptContext = nsnull; nativeGlobal->GetContext(&scriptContext); *aScriptContext = scriptContext; return scriptContext ? NS_OK : NS_ERROR_FAILURE; } //static nsresult nsContentUtils::GetDynamicScriptGlobal(JSContext* aContext, nsIScriptGlobalObject** aNativeGlobal) { nsCOMPtr scriptCX; GetDynamicScriptContext(aContext, getter_AddRefs(scriptCX)); if (!scriptCX) { *aNativeGlobal = nsnull; return NS_ERROR_FAILURE; } return scriptCX->GetGlobalObject(aNativeGlobal); } //static nsresult nsContentUtils::GetDynamicScriptContext(JSContext *aContext, nsIScriptContext** aScriptContext) { return GetScriptContextFromJSContext(aContext, aScriptContext); } template struct NormalizeNewlinesCharTraits { public: typedef typename OutputIterator::value_type value_type; public: NormalizeNewlinesCharTraits(OutputIterator& aIterator) : mIterator(aIterator) { } void writechar(typename OutputIterator::value_type aChar) { *mIterator++ = aChar; } private: OutputIterator mIterator; }; #ifdef HAVE_CPP_PARTIAL_SPECIALIZATION template struct NormalizeNewlinesCharTraits { public: typedef CharT value_type; public: NormalizeNewlinesCharTraits(CharT* aCharPtr) : mCharPtr(aCharPtr) { } void writechar(CharT aChar) { *mCharPtr++ = aChar; } private: CharT* mCharPtr; }; #else NS_SPECIALIZE_TEMPLATE struct NormalizeNewlinesCharTraits { public: typedef char value_type; public: NormalizeNewlinesCharTraits(char* aCharPtr) : mCharPtr(aCharPtr) { } void writechar(char aChar) { *mCharPtr++ = aChar; } private: char* mCharPtr; }; NS_SPECIALIZE_TEMPLATE struct NormalizeNewlinesCharTraits { public: typedef PRUnichar value_type; public: NormalizeNewlinesCharTraits(PRUnichar* aCharPtr) : mCharPtr(aCharPtr) { } void writechar(PRUnichar aChar) { *mCharPtr++ = aChar; } private: PRUnichar* mCharPtr; }; #endif template class CopyNormalizeNewlines { public: typedef typename OutputIterator::value_type value_type; public: CopyNormalizeNewlines(OutputIterator* aDestination, PRBool aLastCharCR=PR_FALSE) : mLastCharCR(aLastCharCR), mDestination(aDestination), mWritten(0) { } PRUint32 GetCharsWritten() { return mWritten; } PRBool IsLastCharCR() { return mLastCharCR; } PRUint32 write(const typename OutputIterator::value_type* aSource, PRUint32 aSourceLength) { const typename OutputIterator::value_type* done_writing = aSource + aSourceLength; // If the last source buffer ended with a CR... if (mLastCharCR) { // ..and if the next one is a LF, then skip it since // we've already written out a newline if (aSourceLength && (*aSource == value_type('\n'))) { ++aSource; } mLastCharCR = PR_FALSE; } PRUint32 num_written = 0; while ( aSource < done_writing ) { if (*aSource == value_type('\r')) { mDestination->writechar('\n'); ++aSource; // If we've reached the end of the buffer, record // that we wrote out a CR if (aSource == done_writing) { mLastCharCR = PR_TRUE; } // If the next character is a LF, skip it else if (*aSource == value_type('\n')) { ++aSource; } } else { mDestination->writechar(*aSource++); } ++num_written; } mWritten += num_written; return aSourceLength; } private: PRBool mLastCharCR; OutputIterator* mDestination; PRUint32 mWritten; }; // static PRUint32 nsContentUtils::CopyNewlineNormalizedUnicodeTo(const nsAString& aSource, PRUint32 aSrcOffset, PRUnichar* aDest, PRUint32 aLength, PRBool& aLastCharCR) { typedef NormalizeNewlinesCharTraits sink_traits; sink_traits dest_traits(aDest); CopyNormalizeNewlines normalizer(&dest_traits,aLastCharCR); nsReadingIterator fromBegin, fromEnd; copy_string(aSource.BeginReading(fromBegin).advance( PRInt32(aSrcOffset) ), aSource.BeginReading(fromEnd).advance( PRInt32(aSrcOffset+aLength) ), normalizer); aLastCharCR = normalizer.IsLastCharCR(); return normalizer.GetCharsWritten(); } // static PRUint32 nsContentUtils::CopyNewlineNormalizedUnicodeTo(nsReadingIterator& aSrcStart, const nsReadingIterator& aSrcEnd, nsAString& aDest) { typedef nsWritingIterator WritingIterator; typedef NormalizeNewlinesCharTraits sink_traits; WritingIterator iter; aDest.BeginWriting(iter); sink_traits dest_traits(iter); CopyNormalizeNewlines normalizer(&dest_traits); copy_string(aSrcStart, aSrcEnd, normalizer); return normalizer.GetCharsWritten(); } // static void nsContentUtils::Shutdown() { sInitialized = PR_FALSE; NS_IF_RELEASE(sDOMScriptObjectFactory); NS_IF_RELEASE(sXPConnect); NS_IF_RELEASE(sSecurityManager); NS_IF_RELEASE(sThreadJSContextStack); NS_IF_RELEASE(sNameSpaceManager); NS_IF_RELEASE(sParserService); NS_IF_RELEASE(sIOService); } // static nsISupports * nsContentUtils::GetClassInfoInstance(nsDOMClassInfoID aID) { if (!sDOMScriptObjectFactory) { static NS_DEFINE_CID(kDOMScriptObjectFactoryCID, NS_DOM_SCRIPT_OBJECT_FACTORY_CID); CallGetService(kDOMScriptObjectFactoryCID, &sDOMScriptObjectFactory); if (!sDOMScriptObjectFactory) { return nsnull; } } return sDOMScriptObjectFactory->GetClassInfoInstance(aID); } // static nsresult nsContentUtils::GetDocumentAndPrincipal(nsIDOMNode* aNode, nsIDocument** aDocument, nsIPrincipal** aPrincipal) { // For performance reasons it's important to try to QI the node to // nsIContent before trying to QI to nsIDocument since a QI miss on // a node is potentially expensive. nsCOMPtr content = do_QueryInterface(aNode); nsCOMPtr attr; if (!content) { CallQueryInterface(aNode, aDocument); if (!*aDocument) { attr = do_QueryInterface(aNode); if (!attr) { // aNode is not a nsIContent, a nsIAttribute or a nsIDocument, // something weird is going on... NS_ERROR("aNode is not nsIContent, nsIAttribute or nsIDocument!"); return NS_ERROR_UNEXPECTED; } } } if (!*aDocument) { nsCOMPtr domDoc; aNode->GetOwnerDocument(getter_AddRefs(domDoc)); if (!domDoc) { // if we can't get a doc then lets try to get principal through nodeinfo // manager nsINodeInfo *ni; if (content) { ni = content->GetNodeInfo(); } else { ni = attr->NodeInfo(); } if (!ni) { // we can't get to the principal so we'll give up return NS_OK; } ni->GetDocumentPrincipal(aPrincipal); if (!*aPrincipal) { // we can't get to the principal so we'll give up return NS_OK; } } else { CallQueryInterface(domDoc, aDocument); if (!*aDocument) { NS_ERROR("QI to nsIDocument failed"); return NS_ERROR_UNEXPECTED; } } } if (!*aPrincipal) { NS_IF_ADDREF(*aPrincipal = (*aDocument)->GetPrincipal()); } return NS_OK; } /** * Checks whether two nodes come from the same origin. aTrustedNode is * considered 'safe' in that a user can operate on it and that it isn't * a js-object that implements nsIDOMNode. * Never call this function with the first node provided by script, it * must always be known to be a 'real' node! */ // static nsresult nsContentUtils::CheckSameOrigin(nsIDOMNode *aTrustedNode, nsIDOMNode *aUnTrustedNode) { NS_PRECONDITION(aTrustedNode, "There must be a trusted node"); PRBool isSystem = PR_FALSE; sSecurityManager->SubjectPrincipalIsSystem(&isSystem); if (isSystem) { // we're running as system, grant access to the node. return NS_OK; } /* * Get hold of each node's document or principal */ // In most cases this is a document, so lets try that first nsCOMPtr trustedDoc = do_QueryInterface(aTrustedNode); nsCOMPtr trustedPrincipal; if (!trustedDoc) { #ifdef DEBUG nsCOMPtr trustCont = do_QueryInterface(aTrustedNode); NS_ASSERTION(trustCont, "aTrustedNode is neither nsIContent nor nsIDocument!"); #endif nsCOMPtr domDoc; aTrustedNode->GetOwnerDocument(getter_AddRefs(domDoc)); if (!domDoc) { // In theory this should never happen. But since theory and reality are // different for XUL elements we'll try to get the principal from the // nsINodeInfoManager. nsCOMPtr cont = do_QueryInterface(aTrustedNode); NS_ENSURE_TRUE(cont, NS_ERROR_UNEXPECTED); nsINodeInfo *ni = cont->GetNodeInfo(); NS_ENSURE_TRUE(ni, NS_ERROR_UNEXPECTED); ni->GetDocumentPrincipal(getter_AddRefs(trustedPrincipal)); if (!trustedPrincipal) { // Can't get principal of aTrustedNode so we can't check security // against it return NS_ERROR_UNEXPECTED; } } else { trustedDoc = do_QueryInterface(domDoc); NS_ASSERTION(trustedDoc, "QI to nsIDocument failed"); } } nsCOMPtr unTrustedDoc; nsCOMPtr unTrustedPrincipal; nsresult rv = GetDocumentAndPrincipal(aUnTrustedNode, getter_AddRefs(unTrustedDoc), getter_AddRefs(unTrustedPrincipal)); NS_ENSURE_SUCCESS(rv, rv); if (!unTrustedDoc && !unTrustedPrincipal) { // We can't get hold of the principal for this node. This should happen // very rarely, like for textnodes out of the tree and