diff --git a/mozilla/content/base/public/nsContentUtils.h b/mozilla/content/base/public/nsContentUtils.h new file mode 100644 index 00000000000..62b61aec0f7 --- /dev/null +++ b/mozilla/content/base/public/nsContentUtils.h @@ -0,0 +1,455 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * ***** 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): + * + * 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 content utilities. */ + +#ifndef nsContentUtils_h___ +#define nsContentUtils_h___ + +#include "jspubtd.h" +#include "nsAString.h" +#include "nsIDOMNode.h" +#include "nsIDOMScriptObjectFactory.h" +#include "nsIJSContextStack.h" +#include "nsIScriptContext.h" +#include "nsCOMArray.h" +#include "nsIStatefulFrame.h" +#include "nsIPref.h" +#include "nsINodeInfo.h" +#include "nsNodeInfoManager.h" + +class nsIXPConnect; +class nsIContent; +class nsIDocument; +class nsIDocShell; +class nsINameSpaceManager; +class nsIScriptSecurityManager; +class nsIThreadJSContextStack; +class nsIParserService; +class nsIIOService; +class nsIURI; +class imgIDecoderObserver; +class imgIRequest; +class imgILoader; +class nsIPrefBranch; +class nsIPref; + +class nsContentUtils +{ +public: + static nsresult Init(); + + static nsresult ReparentContentWrapper(nsIContent *aContent, + nsIContent *aNewParent, + nsIDocument *aNewDocument, + nsIDocument *aOldDocument); + + static PRBool IsCallerChrome(); + + /* + * Returns true if the nodes are both in the same document or + * if neither is in a document. + * Returns false if the nodes are not in the same document. + */ + static PRBool InSameDoc(nsIDOMNode *aNode, + nsIDOMNode *aOther); + + /** + * Do not ever pass null pointers to this method. If one of your + * nsIContents is null, you have to decide for yourself what + * "IsDescendantOf" really means. + * + * @param aPossibleDescendant node to test for being a descendant of + * aPossibleAncestor + * @param aPossibleAncestor node to test for being an ancestor of + * aPossibleDescendant + * @return PR_TRUE if aPossibleDescendant is a descendant of + * aPossibleAncestor (or is aPossibleAncestor). PR_FALSE + * otherwise. + */ + static PRBool ContentIsDescendantOf(nsIContent* aPossibleDescendant, + nsIContent* aPossibleAncestor); + + /* + * This method fills the |aArray| with all ancestor nodes of |aNode| + * including |aNode| at the zero index. + * + * These elements were |nsIDOMNode*|s before casting to |void*| and must + * be cast back to |nsIDOMNode*| on usage, or bad things will happen. + */ + static nsresult GetAncestors(nsIDOMNode* aNode, + nsVoidArray* aArray); + + /* + * This method fills |aAncestorNodes| with all ancestor nodes of |aNode| + * including |aNode| (QI'd to nsIContent) at the zero index. + * For each ancestor, there is a corresponding element in |aAncestorOffsets| + * which is the IndexOf the child in relation to its parent. + * + * The elements of |aAncestorNodes| were |nsIContent*|s before casting to + * |void*| and must be cast back to |nsIContent*| on usage, or bad things + * will happen. + * + * This method just sucks. + */ + static nsresult GetAncestorsAndOffsets(nsIDOMNode* aNode, + PRInt32 aOffset, + nsVoidArray* aAncestorNodes, + nsVoidArray* aAncestorOffsets); + + /* + * The out parameter, |aCommonAncestor| will be the closest node, if any, + * to both |aNode| and |aOther| which is also an ancestor of each. + */ + static nsresult GetCommonAncestor(nsIDOMNode *aNode, + nsIDOMNode *aOther, + nsIDOMNode** aCommonAncestor); + + /* + * |aDifferentNodes| will contain up to 3 elements. + * The first, if present, is the common ancestor of |aNode| and |aOther|. + * The second, if present, is the ancestor node of |aNode| which is + * closest to the common ancestor, but not an ancestor of |aOther|. + * The third, if present, is the ancestor node of |aOther| which is + * closest to the common ancestor, but not an ancestor of |aNode|. + * + * @throws NS_ERROR_FAILURE if aNode and aOther are disconnected. + */ + static nsresult GetFirstDifferentAncestors(nsIDOMNode *aNode, + nsIDOMNode *aOther, + nsCOMArray& aDifferentNodes); + + /** + * Compares the document position of nodes which may have parents. + * DO NOT pass in nodes that cannot have a parentNode. In other words: + * DO NOT pass in Attr, Document, DocumentFragment, Entity, or Notation! + * The results will be completely wrong! + * + * @param aNode The node to which you are comparing. + * @param aOther The reference node to which aNode is compared. + * + * @return The document position flags of the nodes. + * + * @see nsIDOMNode + * @see nsIDOM3Node + */ + static PRUint16 ComparePositionWithAncestors(nsIDOMNode *aNode, + nsIDOMNode *aOther); + + /** + * Reverses the document position flags passed in. + * + * @param aDocumentPosition The document position flags to be reversed. + * + * @return The reversed document position flags. + * + * @see nsIDOMNode + * @see nsIDOM3Node + */ + static PRUint16 ReverseDocumentPosition(PRUint16 aDocumentPosition); + + static PRUint32 CopyNewlineNormalizedUnicodeTo(const nsAString& aSource, + PRUint32 aSrcOffset, + PRUnichar* aDest, + PRUint32 aLength, + PRBool& aLastCharCR); + + static PRUint32 CopyNewlineNormalizedUnicodeTo(nsReadingIterator& aSrcStart, const nsReadingIterator& aSrcEnd, nsAString& aDest); + + static nsISupports * + GetClassInfoInstance(nsDOMClassInfoID aID); + + static const nsDependentSubstring TrimCharsInSet(const char* aSet, + const nsAString& aValue); + + static const nsDependentSubstring TrimWhitespace(const nsAString& aStr, + PRBool aTrimTrailing = PR_TRUE); + + static void Shutdown(); + + /** + * 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 CheckSameOrigin(nsIDOMNode* aTrustedNode, + nsIDOMNode* aUnTrustedNode); + + // Check if the (JS) caller can access aNode. + static PRBool CanCallerAccess(nsIDOMNode *aNode); + + /** + * Get the docshell through the JS context that's currently on the stack. + * If there's no JS context currently on the stack aDocShell will be null. + * + * @param aDocShell The docshell or null if no JS context + */ + static nsIDocShell *GetDocShellFromCaller(); + + /** + * Get the document through the JS context that's currently on the stack. + * If there's no JS context currently on the stack aDocument will be null. + * + * @param aDocument The document or null if no JS context + */ + static nsIDOMDocument *GetDocumentFromCaller(); + + // Check if a node is in the document prolog, i.e. before the document + // element. + static PRBool InProlog(nsIDOMNode *aNode); + + static nsIParserService* GetParserServiceWeakRef(); + + static nsINameSpaceManager* GetNSManagerWeakRef() + { + return sNameSpaceManager; + }; + + static nsIIOService* GetIOServiceWeakRef() + { + return sIOService; + }; + + static nsIScriptSecurityManager* GetSecurityManager() + { + return sSecurityManager; + } + + static nsresult GenerateStateKey(nsIContent* aContent, + nsIStatefulFrame::SpecialStateID aID, + nsACString& aKey); + + /** + * Create a new nsIURI from aSpec, using aBaseURI as the base. The + * origin charset of the new nsIURI will be the document charset of + * aDocument. + */ + static nsresult NewURIWithDocumentCharset(nsIURI** aResult, + const nsAString& aSpec, + nsIDocument* aDocument, + nsIURI* aBaseURI); + + /** + * Determine whether aContent is in some way associated with aForm. If the + * form is a container the only elements that are considered to be associated + * with a form are the elements that are contained within the form. If the + * form is a leaf element then all elements will be accepted into this list, + * since this can happen due to content fixup when a form spans table rows or + * table cells. + */ + static PRBool BelongsInForm(nsIDOMHTMLFormElement *aForm, + nsIContent *aContent); + + static nsresult CheckQName(const nsAString& aQualifiedName, + PRBool aNamespaceAware = PR_TRUE); + + static nsresult GetNodeInfoFromQName(const nsAString& aNamespaceURI, + const nsAString& aQualifiedName, + nsNodeInfoManager* aNodeInfoManager, + nsINodeInfo** aNodeInfo); + + static nsAdoptingCString GetCharPref(const char *aPref); + static PRPackedBool GetBoolPref(const char *aPref, + PRBool aDefault = PR_FALSE); + static PRInt32 GetIntPref(const char *aPref, PRInt32 aDefault = 0); + static nsAdoptingString GetLocalizedStringPref(const char *aPref); + static nsAdoptingString GetStringPref(const char *aPref); + static void RegisterPrefCallback(const char *aPref, + PrefChangedFunc aCallback, + void * aClosure); + static void UnregisterPrefCallback(const char *aPref, + PrefChangedFunc aCallback, + void * aClosure); + static nsIPrefBranch *GetPrefBranch() + { + return sPrefBranch; + } + + static nsresult GetDocumentAndPrincipal(nsIDOMNode* aNode, + nsIDocument** aDocument, + nsIPrincipal** aPrincipal); + + /** + * Method to do security and content policy checks on the image URI + * + * @param aURI uri of the image to be loaded + * @param aContext the context the image is loaded in (eg an element) + * @param aLoadingDocument the document we belong to + * @return PR_TRUE if the load can proceed, or PR_FALSE if it is blocked + */ + static PRBool CanLoadImage(nsIURI* aURI, + nsISupports* aContext, + nsIDocument* aLoadingDocument); + /** + * Method to start an image load. This does not do any security checks. + * + * @param aURI uri of the image to be loaded + * @param aLoadingDocument the document we belong to + * @param aObserver the observer for the image load + * @param aLoadFlags the load flags to use. See nsIRequest + * @return the imgIRequest for the image load + */ + static nsresult LoadImage(nsIURI* aURI, + nsIDocument* aLoadingDocument, + nsIURI* aReferrer, + imgIDecoderObserver* aObserver, + PRInt32 aLoadFlags, + imgIRequest** aRequest); + + /** + * Convenience method to create a new nodeinfo that differs only by name + * from aNodeInfo. + */ + static nsresult NameChanged(nsINodeInfo *aNodeInfo, nsIAtom *aName, + nsINodeInfo** aResult) + { + nsNodeInfoManager *niMgr = aNodeInfo->NodeInfoManager(); + + return niMgr->GetNodeInfo(aName, aNodeInfo->GetPrefixAtom(), + aNodeInfo->NamespaceID(), aResult); + } + + /** + * Convenience method to create a new nodeinfo that differs only by prefix + * from aNodeInfo. + */ + static nsresult PrefixChanged(nsINodeInfo *aNodeInfo, nsIAtom *aPrefix, + nsINodeInfo** aResult) + { + nsNodeInfoManager *niMgr = aNodeInfo->NodeInfoManager(); + + return niMgr->GetNodeInfo(aNodeInfo->NameAtom(), aPrefix, + aNodeInfo->NamespaceID(), aResult); + } + + /** + * Retrieve a pointer to the document that owns aNodeInfo. + */ + static nsIDocument *GetDocument(nsINodeInfo *aNodeInfo) + { + return aNodeInfo->NodeInfoManager()->GetDocument(); + } + + /** + * Returns the appropriate event argument name for the specified + * namespace. Added because we need to switch between SVG's "evt" + * and the rest of the world's "event". + */ + static const char *GetEventArgName(PRInt32 aNameSpaceID); + +private: + static nsresult doReparentContentWrapper(nsIContent *aChild, + nsIDocument *aNewDocument, + nsIDocument *aOldDocument, + JSContext *cx, + JSObject *parent_obj); + + + static nsIDOMScriptObjectFactory *sDOMScriptObjectFactory; + + static nsIXPConnect *sXPConnect; + + static nsIScriptSecurityManager *sSecurityManager; + + static nsIThreadJSContextStack *sThreadJSContextStack; + + static nsIParserService *sParserService; + + static nsINameSpaceManager *sNameSpaceManager; + + static nsIIOService *sIOService; + + static nsIPrefBranch *sPrefBranch; + + static nsIPref *sPref; + + static imgILoader* sImgLoader; + + static PRBool sInitialized; +}; + + +class nsCxPusher +{ +public: + nsCxPusher(nsISupports *aCurrentTarget) + : mScriptIsRunning(PR_FALSE) + { + if (aCurrentTarget) { + Push(aCurrentTarget); + } + } + + ~nsCxPusher() + { + Pop(); + } + + void Push(nsISupports *aCurrentTarget); + void Pop(); + +private: + nsCOMPtr mStack; + nsCOMPtr mScx; + PRBool mScriptIsRunning; +}; + + +#define NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(_class) \ + if (aIID.Equals(NS_GET_IID(nsIClassInfo))) { \ + foundInterface = \ + nsContentUtils::GetClassInfoInstance(eDOMClassInfo_##_class##_id); \ + if (!foundInterface) { \ + *aInstancePtr = nsnull; \ + return NS_ERROR_OUT_OF_MEMORY; \ + } \ + } else + +#define NS_INTERFACE_MAP_ENTRY_TEAROFF(_interface, _allocator) \ + if (aIID.Equals(NS_GET_IID(_interface))) { \ + foundInterface = NS_STATIC_CAST(_interface *, _allocator); \ + if (!foundInterface) { \ + *aInstancePtr = nsnull; \ + return NS_ERROR_OUT_OF_MEMORY; \ + } \ + } else + +#endif /* nsContentUtils_h___ */ diff --git a/mozilla/content/base/public/nsINameSpaceManager.h b/mozilla/content/base/public/nsINameSpaceManager.h new file mode 100644 index 00000000000..29d7e32ac9a --- /dev/null +++ b/mozilla/content/base/public/nsINameSpaceManager.h @@ -0,0 +1,107 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** 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. + * + * 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): + * + * 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 ***** */ + +#ifndef nsINameSpaceManager_h___ +#define nsINameSpaceManager_h___ + +#include "nsISupports.h" +#include "nsAString.h" + +class nsIAtom; +class nsString; +class nsINameSpace; + +#define kNameSpaceID_Unknown -1 +#define kNameSpaceID_None 0 +#define kNameSpaceID_XMLNS 1 // not really a namespace, but it needs to play the game +#define kNameSpaceID_XML 2 +#define kNameSpaceID_XHTML 3 +#define kNameSpaceID_XLink 4 +#define kNameSpaceID_XSLT 5 +#define kNameSpaceID_XBL 6 +#define kNameSpaceID_MathML 7 +#define kNameSpaceID_RDF 8 +#define kNameSpaceID_XUL 9 +#define kNameSpaceID_SVG 10 +#define kNameSpaceID_XMLEvents 11 + +#define NS_NAMESPACEMANAGER_CONTRACTID "@mozilla.org/content/namespacemanager;1" + +#define NS_INAMESPACEMANAGER_IID \ + { 0x409cd4de, 0xb3ca, 0x11d8, \ + { 0xb2, 0x67, 0x00, 0x0a, 0x95, 0xdc, 0x23, 0x4c } } + +/** + * The Name Space Manager tracks the associtation between a NameSpace + * URI and the PRInt32 runtime id. Mappings between NameSpaces and + * NameSpace prefixes are managed by nsINameSpaces. + * + * All NameSpace URIs are stored in a global table so that IDs are + * consistent accross the app. NameSpace IDs are only consistent at runtime + * ie: they are not guaranteed to be consistent accross app sessions. + * + * The nsINameSpaceManager needs to have a live reference for as long as + * the NameSpace IDs are needed. + * + * To create a stack of NameSpaces, call CreateRootNameSpace, and then create + * child NameSpaces from the root. + * + */ + +class nsINameSpaceManager : public nsISupports +{ +public: + NS_DEFINE_STATIC_IID_ACCESSOR(NS_INAMESPACEMANAGER_IID) + + NS_IMETHOD CreateRootNameSpace(nsINameSpace** aRootNameSpace) = 0; + + NS_IMETHOD RegisterNameSpace(const nsAString& aURI, + PRInt32& aNameSpaceID) = 0; + + NS_IMETHOD GetNameSpaceURI(PRInt32 aNameSpaceID, + nsAString& aURI) = 0; + NS_IMETHOD GetNameSpaceID(const nsAString& aURI, + PRInt32* aNameSpaceID) = 0; + + virtual PRBool HasElementCreator(PRInt32 aNameSpaceID) = 0; +}; + +nsresult NS_GetNameSpaceManager(nsINameSpaceManager** aInstancePtrResult); + +void NS_NameSpaceManagerShutdown(); + +#endif // nsINameSpaceManager_h___ diff --git a/mozilla/content/base/src/nsContentUtils.cpp b/mozilla/content/base/src/nsContentUtils.cpp new file mode 100644 index 00000000000..f75f812c85d --- /dev/null +++ b/mozilla/content/base/src/nsContentUtils.cpp @@ -0,0 +1,1908 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * ***** 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 "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" +#include "nsISupportsPrimitives.h" +#include "nsLayoutAtoms.h" +#include "imgIDecoderObserver.h" +#include "imgIRequest.h" +#include "imgILoader.h" +#include "nsILoadGroup.h" +#include "nsContentPolicyUtils.h" +#include "nsDOMString.h" +#include "nsGenericElement.h" +#include "nsNodeInfoManager.h" +#include "nsCRT.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; +nsIPrefBranch *nsContentUtils::sPrefBranch = nsnull; +nsIPref *nsContentUtils::sPref = nsnull; +imgILoader *nsContentUtils::sImgLoader = 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); + + // 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); + 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; + } + + // 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; + } + + 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; +} + +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); + NS_IF_RELEASE(sImgLoader); + NS_IF_RELEASE(sPrefBranch); + NS_IF_RELEASE(sPref); +} + +// 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