/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim: set ts=2 sw=2 et tw=78: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla 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/MPL/ * * 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 the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Johnny Stenback * Christopher A. Aillon * * Alternatively, the contents of this file may be used under the terms of * either of 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 MPL, 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 MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* A namespace class for static layout utilities. */ #include "nsJSUtils.h" #include "nsCOMPtr.h" #include "nsAString.h" #include "nsPrintfCString.h" #include "nsUnicharUtils.h" #include "nsIPrefService.h" #include "nsIPrefBranch.h" #include "nsIPrefLocalizedString.h" #include "nsServiceManagerUtils.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 "nsIDOMNode.h" #include "nsIDOM3Node.h" #include "nsIIOService.h" #include "nsIURI.h" #include "nsNetCID.h" #include "nsNetUtil.h" #include "nsIScriptSecurityManager.h" #include "nsDOMError.h" #include "nsPIDOMWindow.h" #include "nsIJSContextStack.h" #include "nsIDocShell.h" #include "nsIDocShellTreeItem.h" #include "nsParserCIID.h" #include "nsIParserService.h" #include "nsIServiceManager.h" #include "nsIAttribute.h" #include "nsContentList.h" #include "nsIHTMLDocument.h" #include "nsIDOMHTMLDocument.h" #include "nsIDOMHTMLCollection.h" #include "nsIDOMHTMLFormElement.h" #include "nsIForm.h" #include "nsIFormControl.h" #include "nsHTMLAtoms.h" #include "nsHTMLParts.h" #include "nsISupportsPrimitives.h" #include "nsLayoutAtoms.h" #include "imgIDecoderObserver.h" #include "imgIRequest.h" #include "imgIContainer.h" #include "imgILoader.h" #include "nsIImage.h" #include "gfxIImageFrame.h" #include "nsIImageLoadingContent.h" #include "nsIInterfaceRequestor.h" #include "nsIInterfaceRequestorUtils.h" #include "nsILink.h" #include "nsILoadGroup.h" #include "nsContentPolicyUtils.h" #include "nsDOMString.h" #include "nsGenericElement.h" #include "nsNodeInfoManager.h" #include "nsCRT.h" #ifdef MOZ_XTF #include "nsIXTFService.h" static NS_DEFINE_CID(kXTFServiceCID, NS_XTFSERVICE_CID); #endif #include "nsIMIMEService.h" #include "jsdbgapi.h" #include "nsIJSRuntimeService.h" #include "nsIFragmentContentSink.h" #include "nsIScriptObjectPrincipal.h" // for ReportToConsole #include "nsIStringBundle.h" #include "nsIScriptError.h" #include "nsIConsoleService.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; #ifdef MOZ_XTF nsIXTFService *nsContentUtils::sXTFService = nsnull; #endif nsIPrefBranch *nsContentUtils::sPrefBranch = nsnull; nsIPref *nsContentUtils::sPref = nsnull; imgILoader *nsContentUtils::sImgLoader = nsnull; nsIConsoleService *nsContentUtils::sConsoleService; nsIStringBundleService *nsContentUtils::sStringBundleService; nsIStringBundle *nsContentUtils::sStringBundles[PropertiesFile_COUNT]; nsVoidArray *nsContentUtils::sPtrsToPtrsToRelease; nsIJSRuntimeService *nsContentUtils::sJSRuntimeService; JSRuntime *nsContentUtils::sScriptRuntime; PRInt32 nsContentUtils::sScriptRootCount = 0; 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); // It's ok to not have a pref service. CallGetService(NS_PREFSERVICE_CONTRACTID, &sPrefBranch); // It's ok to not have prefs too. CallGetService(NS_PREF_CONTRACTID, &sPref); rv = NS_GetNameSpaceManager(&sNameSpaceManager); NS_ENSURE_SUCCESS(rv, rv); rv = nsGenericElement::InitHashes(); NS_ENSURE_SUCCESS(rv, rv); rv = CallGetService(nsIXPConnect::GetCID(), &sXPConnect); NS_ENSURE_SUCCESS(rv, rv); 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; } // Ignore failure and just don't load images rv = CallGetService("@mozilla.org/image/loader;1", &sImgLoader); if (NS_FAILED(rv)) { // no image loading for us. Oh, well. sImgLoader = nsnull; } sPtrsToPtrsToRelease = new nsVoidArray(); if (!sPtrsToPtrsToRelease) { return NS_ERROR_OUT_OF_MEMORY; } 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; } #ifdef MOZ_XTF nsIXTFService* nsContentUtils::GetXTFServiceWeakRef() { if (!sXTFService) { nsresult rv = CallGetService(kXTFServiceCID, &sXTFService); if (NS_FAILED(rv)) { sXTFService = nsnull; } } return sXTFService; } #endif 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_HTMLParanoidFragmentSinkShutdown(); NS_XHTMLParanoidFragmentSinkShutdown(); PRInt32 i; for (i = 0; i < PRInt32(PropertiesFile_COUNT); ++i) NS_IF_RELEASE(sStringBundles[i]); NS_IF_RELEASE(sStringBundleService); NS_IF_RELEASE(sConsoleService); 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); #ifdef MOZ_XTF NS_IF_RELEASE(sXTFService); #endif NS_IF_RELEASE(sImgLoader); NS_IF_RELEASE(sPrefBranch); NS_IF_RELEASE(sPref); if (sPtrsToPtrsToRelease) { for (i = 0; i < sPtrsToPtrsToRelease->Count(); ++i) { nsISupports** ptrToPtr = NS_STATIC_CAST(nsISupports**, sPtrsToPtrsToRelease->ElementAt(i)); NS_RELEASE(*ptrToPtr); } delete sPtrsToPtrsToRelease; sPtrsToPtrsToRelease = nsnull; } } // 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; } *aPrincipal = ni->NodeInfoManager()->GetDocumentPrincipal(); if (!*aPrincipal) { // we can't get to the principal so we'll give up return NS_OK; } NS_ADDREF(*aPrincipal); } 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); nsIPrincipal* trustedPrincipal = nsnull; 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 // nsNodeInfoManager. nsCOMPtr cont = do_QueryInterface(aTrustedNode); NS_ENSURE_TRUE(cont, NS_ERROR_UNEXPECTED); nsINodeInfo *ni = cont->GetNodeInfo(); NS_ENSURE_TRUE(ni, NS_ERROR_UNEXPECTED); trustedPrincipal = ni->NodeInfoManager()->GetDocumentPrincipal(); 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