/* -*- 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): * * 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 "nsIServiceManager.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 "nsIJSContextStack.h" #include "nsIDocShell.h" #include "nsIDocShellTreeItem.h" static const char *sJSStackContractID = "@mozilla.org/js/xpc/ContextStack;1"; nsIDOMScriptObjectFactory *nsContentUtils::sDOMScriptObjectFactory = nsnull; nsIXPConnect *nsContentUtils::sXPConnect = nsnull; // static nsresult nsContentUtils::Init() { NS_ENSURE_TRUE(!sXPConnect, NS_ERROR_ALREADY_INITIALIZED); nsresult rv = nsServiceManager::GetService(nsIXPConnect::GetCID(), nsIXPConnect::GetIID(), (nsISupports **)&sXPConnect); NS_ENSURE_SUCCESS(rv, rv); return rv; } // 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) { *aScriptContext = nsnull; // XXX We rely on the rule that if any JSContext in our JSRuntime has a // private set then that private *must* be a pointer to an nsISupports. nsISupports *supports = (nsIScriptContext*)JS_GetContextPrivate(aContext); if (!supports) { return NS_OK; } return CallQueryInterface(supports, 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() { NS_IF_RELEASE(sDOMScriptObjectFactory); NS_IF_RELEASE(sXPConnect); } // static nsISupports * nsContentUtils::GetClassInfoInstance(nsDOMClassInfoID aID) { if (!sDOMScriptObjectFactory) { static NS_DEFINE_CID(kDOMScriptObjectFactoryCID, NS_DOM_SCRIPT_OBJECT_FACTORY_CID); nsServiceManager::GetService(kDOMScriptObjectFactoryCID, NS_GET_IID(nsIDOMScriptObjectFactory), (nsISupports **)&sDOMScriptObjectFactory); if (!sDOMScriptObjectFactory) { return nsnull; } } return sDOMScriptObjectFactory->GetClassInfoInstance(aID); } // static nsresult nsContentUtils::doReparentContentWrapper(nsIContent *aChild, nsIDocument *aNewDocument, nsIDocument *aOldDocument, JSContext *cx, JSObject *parent_obj) { nsCOMPtr old_wrapper; nsresult rv; rv = sXPConnect->ReparentWrappedNativeIfFound(cx, ::JS_GetGlobalObject(cx), parent_obj, aChild, getter_AddRefs(old_wrapper)); NS_ENSURE_SUCCESS(rv, rv); if (!old_wrapper) { // If aChild isn't wrapped none of it's children are wrapped so // there's no need to walk into aChild's children. return NS_OK; } if (aOldDocument) { nsCOMPtr old_ref; aOldDocument->RemoveReference(aChild, getter_AddRefs(old_ref)); if (old_ref) { // Transfer the reference from aOldDocument to aNewDocument aNewDocument->AddReference(aChild, old_ref); } } JSObject *old; rv = old_wrapper->GetJSObject(&old); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr child; PRInt32 count = 0, i; aChild->ChildCount(count); for (i = 0; i < count; i++) { aChild->ChildAt(i, *getter_AddRefs(child)); NS_ENSURE_TRUE(child, NS_ERROR_UNEXPECTED); rv = doReparentContentWrapper(child, aNewDocument, aOldDocument, cx, old); NS_ENSURE_SUCCESS(rv, rv); } return rv; } static nsresult GetContextFromDocument(nsIDocument *aDocument, JSContext **cx) { *cx = nsnull; nsCOMPtr sgo; aDocument->GetScriptGlobalObject(getter_AddRefs(sgo)); if (!sgo) { // No script global, no context. return NS_OK; } nsCOMPtr scx; sgo->GetContext(getter_AddRefs(scx)); if (!scx) { // No context left in the old scope... return NS_OK; } *cx = (JSContext *)scx->GetNativeContext(); return NS_OK; } // static nsresult nsContentUtils::ReparentContentWrapper(nsIContent *aContent, nsIContent *aNewParent, nsIDocument *aNewDocument, nsIDocument *aOldDocument) { if (!aNewDocument || aNewDocument == aOldDocument) { return NS_OK; } nsCOMPtr old_doc(aOldDocument); if (!old_doc) { nsCOMPtr ni; aContent->GetNodeInfo(*getter_AddRefs(ni)); if (ni) { ni->GetDocument(*getter_AddRefs(old_doc)); } if (!aOldDocument) { // If we can't find our old document we don't know what our old // scope was so there's no way to find the old wrapper return NS_OK; } } NS_ENSURE_TRUE(sXPConnect, NS_ERROR_NOT_INITIALIZED); nsCOMPtr new_parent; if (!aNewParent) { nsCOMPtr root; old_doc->GetRootContent(getter_AddRefs(root)); if (root.get() == aContent) { new_parent = old_doc; } } else { new_parent = aNewParent; } JSContext *cx = nsnull; GetContextFromDocument(old_doc, &cx); if (!cx) { // No JSContext left in the old scope, can't find the old wrapper // w/o the old context. return NS_OK; } nsCOMPtr wrapper; nsresult rv; rv = sXPConnect->GetWrappedNativeOfNativeObject(cx, ::JS_GetGlobalObject(cx), aContent, NS_GET_IID(nsISupports), getter_AddRefs(wrapper)); NS_ENSURE_SUCCESS(rv, rv); if (!wrapper) { // aContent is not wrapped (and thus none of it's children are // wrapped) so there's no need to reparent anything. return NS_OK; } // Wrap the new parent and reparent aContent nsCOMPtr holder; rv = sXPConnect->WrapNative(cx, ::JS_GetGlobalObject(cx), new_parent, NS_GET_IID(nsISupports), getter_AddRefs(holder)); NS_ENSURE_SUCCESS(rv, rv); JSObject *obj; rv = holder->GetJSObject(&obj); NS_ENSURE_SUCCESS(rv, rv); return doReparentContentWrapper(aContent, aNewDocument, aOldDocument, cx, obj); } PRBool nsContentUtils::IsCallerChrome() { nsCOMPtr docShell; nsCOMPtr stack(do_GetService(sJSStackContractID)); if (stack) { JSContext *cx = nsnull; stack->Peek(&cx); if (cx) { nsCOMPtr sgo; nsContentUtils::GetDynamicScriptGlobal(cx, getter_AddRefs(sgo)); if (sgo) { sgo->GetDocShell(getter_AddRefs(docShell)); } } } nsCOMPtr item(do_QueryInterface(docShell)); if (item) { PRInt32 callerType = nsIDocShellTreeItem::typeChrome; item->GetItemType(&callerType); if (callerType != nsIDocShellTreeItem::typeChrome) { return PR_FALSE; } } return PR_TRUE; }