Back to index

lightning-sunbird  0.9+nobinonly
nsJSUtils.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* vim: set ts=2 sw=2 et tw=78: */
00003 /* ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is mozilla.org code.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 1998
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Vidur Apparao <vidur@netscape.com>
00025  *   L. David Baron <dbaron@mozillafoundation.org>
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either of the GNU General Public License Version 2 or later (the "GPL"),
00029  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00048 #include "nsJSUtils.h"
00049 #include "jsapi.h"
00050 #include "jsdbgapi.h"
00051 #include "prprf.h"
00052 #include "nsIScriptContext.h"
00053 #include "nsIScriptObjectOwner.h"
00054 #include "nsIScriptGlobalObject.h"
00055 #include "nsIServiceManager.h"
00056 #include "nsIXPConnect.h"
00057 #include "nsCOMPtr.h"
00058 #include "nsContentUtils.h"
00059 #include "nsDOMClassInfo.h"
00060 #include "nsIDOMGCParticipant.h"
00061 #include "nsIWeakReference.h"
00062 #include "nsIScriptSecurityManager.h"
00063 
00064 
00065 JSBool
00066 nsJSUtils::GetCallingLocation(JSContext* aContext, const char* *aFilename,
00067                               PRUint32* aLineno, nsIPrincipal* aPrincipal)
00068 {
00069   // Get the current filename and line number
00070   JSStackFrame* frame = nsnull;
00071   JSScript* script = nsnull;
00072   do {
00073     frame = ::JS_FrameIterator(aContext, &frame);
00074 
00075     if (frame) {
00076       script = ::JS_GetFrameScript(aContext, frame);
00077     }
00078   } while (frame && !script);
00079 
00080   if (script) {
00081     // If aPrincipals is non-null then our caller is asking us to ensure
00082     // that the filename we return does not have elevated privileges.
00083     if (aPrincipal) {
00084       uint32 flags = JS_GetScriptFilenameFlags(script);
00085 
00086       // Use the principal for the filename if it shouldn't be receiving
00087       // implicit XPCNativeWrappers.
00088       if (flags & JSFILENAME_SYSTEM) {
00089         nsIScriptSecurityManager *ssm = nsContentUtils::GetSecurityManager();
00090 
00091         nsCOMPtr<nsIPrincipal> sysPrin;
00092         ssm->GetSystemPrincipal(getter_AddRefs(sysPrin));
00093 
00094         if (aPrincipal != sysPrin) {
00095           JSPrincipals* jsprins;
00096           aPrincipal->GetJSPrincipals(aContext, &jsprins);
00097 
00098           *aFilename = jsprins->codebase;
00099           *aLineno = 0;
00100           JSPRINCIPALS_DROP(aContext, jsprins);
00101           return JS_TRUE;
00102         }
00103       }
00104     }
00105 
00106     const char* filename = ::JS_GetScriptFilename(aContext, script);
00107 
00108     if (filename) {
00109       PRUint32 lineno = 0;
00110       jsbytecode* bytecode = ::JS_GetFramePC(aContext, frame);
00111 
00112       if (bytecode) {
00113         lineno = ::JS_PCToLineNumber(aContext, script, bytecode);
00114       }
00115 
00116       *aFilename = filename;
00117       *aLineno = lineno;
00118       return JS_TRUE;
00119     }
00120   }
00121 
00122   return JS_FALSE;
00123 }
00124 
00125 jsval
00126 nsJSUtils::ConvertStringToJSVal(const nsString& aProp, JSContext* aContext)
00127 {
00128   JSString *jsstring =
00129     ::JS_NewUCStringCopyN(aContext, NS_REINTERPRET_CAST(const jschar*,
00130                                                         aProp.get()),
00131                           aProp.Length());
00132 
00133   // set the return value
00134   return STRING_TO_JSVAL(jsstring);
00135 }
00136 
00137 PRBool
00138 nsJSUtils::ConvertJSValToXPCObject(nsISupports** aSupports, REFNSIID aIID,
00139                                    JSContext* aContext, jsval aValue)
00140 {
00141   *aSupports = nsnull;
00142   if (JSVAL_IS_NULL(aValue)) {
00143     return JS_TRUE;
00144   }
00145 
00146   if (JSVAL_IS_OBJECT(aValue)) {
00147     // WrapJS does all the work to recycle an existing wrapper and/or do a QI
00148     nsresult rv = nsContentUtils::XPConnect()->
00149       WrapJS(aContext, JSVAL_TO_OBJECT(aValue), aIID, (void**)aSupports);
00150 
00151     return NS_SUCCEEDED(rv);
00152   }
00153 
00154   return JS_FALSE;
00155 }
00156 
00157 void
00158 nsJSUtils::ConvertJSValToString(nsAString& aString, JSContext* aContext,
00159                                 jsval aValue)
00160 {
00161   JSString *jsstring;
00162   if ((jsstring = ::JS_ValueToString(aContext, aValue)) != nsnull) {
00163     aString.Assign(NS_REINTERPRET_CAST(const PRUnichar*,
00164                                        ::JS_GetStringChars(jsstring)),
00165                    ::JS_GetStringLength(jsstring));
00166   }
00167   else {
00168     aString.Truncate();
00169   }
00170 }
00171 
00172 PRBool
00173 nsJSUtils::ConvertJSValToUint32(PRUint32* aProp, JSContext* aContext,
00174                                 jsval aValue)
00175 {
00176   uint32 temp;
00177   if (::JS_ValueToECMAUint32(aContext, aValue, &temp)) {
00178     *aProp = (PRUint32)temp;
00179   }
00180   else {
00181     ::JS_ReportError(aContext, "Parameter must be an integer");
00182     return JS_FALSE;
00183   }
00184 
00185   return JS_TRUE;
00186 }
00187 
00188 nsIScriptGlobalObject *
00189 nsJSUtils::GetStaticScriptGlobal(JSContext* aContext, JSObject* aObj)
00190 {
00191   nsISupports* supports;
00192   JSClass* clazz;
00193   JSObject* parent;
00194   JSObject* glob = aObj; // starting point for search
00195 
00196   if (!glob)
00197     return nsnull;
00198 
00199   while ((parent = ::JS_GetParent(aContext, glob)))
00200     glob = parent;
00201 
00202   clazz = JS_GET_CLASS(aContext, glob);
00203 
00204   if (!clazz ||
00205       !(clazz->flags & JSCLASS_HAS_PRIVATE) ||
00206       !(clazz->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS) ||
00207       !(supports = (nsISupports*)::JS_GetPrivate(aContext, glob))) {
00208     return nsnull;
00209   }
00210 
00211   nsCOMPtr<nsIXPConnectWrappedNative> wrapper(do_QueryInterface(supports));
00212   NS_ENSURE_TRUE(wrapper, nsnull);
00213 
00214   nsCOMPtr<nsIScriptGlobalObject> sgo(do_QueryWrappedNative(wrapper));
00215 
00216   // We're returning a pointer to something that's about to be
00217   // released, but that's ok here.
00218   return sgo;
00219 }
00220 
00221 nsIScriptContext *
00222 nsJSUtils::GetStaticScriptContext(JSContext* aContext, JSObject* aObj)
00223 {
00224   nsIScriptGlobalObject *nativeGlobal = GetStaticScriptGlobal(aContext, aObj);
00225   if (!nativeGlobal)
00226     return nsnull;
00227 
00228   return nativeGlobal->GetContext();
00229 }
00230 
00231 nsIScriptGlobalObject *
00232 nsJSUtils::GetDynamicScriptGlobal(JSContext* aContext)
00233 {
00234   nsIScriptContext *scriptCX = GetDynamicScriptContext(aContext);
00235   if (!scriptCX)
00236     return nsnull;
00237   return scriptCX->GetGlobalObject();
00238 }
00239 
00240 nsIScriptContext *
00241 nsJSUtils::GetDynamicScriptContext(JSContext *aContext)
00242 {
00243   return GetScriptContextFromJSContext(aContext);
00244 }
00245 
00246 #define MARKED_OBJECT_BIT (PRWord(1<<0))
00247 
00248 void
00249 nsMarkedJSFunctionHolder_base::Set(nsISupports *aPotentialFunction,
00250                                    nsIDOMGCParticipant *aParticipant)
00251 {
00252   if (PRWord(mObject) & MARKED_OBJECT_BIT) {
00253     nsDOMClassInfo::ReleaseWrapper(this);
00254   }
00255   nsISupports *oldVal = (nsISupports*)(PRWord(mObject) & ~MARKED_OBJECT_BIT);
00256   if (!TryMarkedSet(aPotentialFunction, aParticipant)) {
00257     NS_ASSERTION((PRWord(aPotentialFunction) & MARKED_OBJECT_BIT) == 0,
00258                  "low bit set");
00259     NS_IF_ADDREF(aPotentialFunction);
00260     mObject = aPotentialFunction;
00261   }
00262   NS_IF_RELEASE(oldVal);
00263 }
00264 
00265 static nsIXPConnectJSObjectHolder* HolderToWrappedJS(void *aKey)
00266 {
00267   nsMarkedJSFunctionHolder_base *holder = NS_STATIC_CAST(
00268     nsMarkedJSFunctionHolder_base*, aKey);
00269 
00270   NS_ASSERTION(PRWord(holder->mObject) & MARKED_OBJECT_BIT,
00271                "yikes, not a marked object");
00272 
00273   nsIWeakReference* weakRef =
00274     (nsIWeakReference*)(PRWord(holder->mObject) & ~MARKED_OBJECT_BIT);
00275 
00276   // This entire interface is a hack to avoid reference counting, so
00277   // this actually doesn't do any reference counting, and we don't leak
00278   // anything.  This is needed so we don't add and remove GC roots in
00279   // the middle of GC.
00280   nsWeakRefToIXPConnectWrappedJS *result;
00281   if (NS_FAILED(CallQueryReferent(weakRef, &result)))
00282     result = nsnull;
00283   return result;
00284 }
00285 
00286 PRBool
00287 nsMarkedJSFunctionHolder_base::TryMarkedSet(nsISupports *aPotentialFunction,
00288                                             nsIDOMGCParticipant *aParticipant)
00289 {
00290   if (!aParticipant)
00291     return PR_FALSE;
00292 
00293   nsCOMPtr<nsIXPConnectWrappedJS_MOZILLA_1_8_BRANCH> wrappedJS =
00294     do_QueryInterface(aPotentialFunction);
00295   if (!wrappedJS) // a non-JS implementation
00296     return PR_FALSE;
00297 
00298   // XXX We really only need to pass PR_TRUE for
00299   // root-if-externally-referenced if this is an onload, onerror,
00300   // onreadystatechange, etc., so we could pass the responsibility for
00301   // choosing that to the caller.
00302   nsresult rv =
00303     nsDOMClassInfo::PreserveWrapper(this, HolderToWrappedJS, aParticipant,
00304                                     PR_TRUE);
00305   NS_ENSURE_SUCCESS(rv, PR_FALSE);
00306 
00307   nsIWeakReference* weakRef; // [STRONG]
00308   wrappedJS->GetWeakReference(&weakRef);
00309   NS_ENSURE_TRUE(weakRef, PR_FALSE);
00310 
00311   NS_ASSERTION((PRWord(weakRef) & MARKED_OBJECT_BIT) == 0, "low bit set");
00312   mObject = (nsISupports*)(PRWord(weakRef) | MARKED_OBJECT_BIT);
00313   return PR_TRUE;
00314 }
00315 
00316 already_AddRefed<nsISupports>
00317 nsMarkedJSFunctionHolder_base::Get(REFNSIID aIID)
00318 {
00319   nsISupports *result;
00320   if (PRWord(mObject) & MARKED_OBJECT_BIT) {
00321     nsIWeakReference* weakRef =
00322       (nsIWeakReference*)(PRWord(mObject) & ~MARKED_OBJECT_BIT);
00323     nsresult rv =
00324       weakRef->QueryReferent(aIID, NS_REINTERPRET_CAST(void**, &result));
00325     if (NS_FAILED(rv)) {
00326       NS_NOTREACHED("GC preservation didn't work");
00327       result = nsnull;
00328     }
00329   } else {
00330     NS_IF_ADDREF(result = mObject);
00331   }
00332   return result;
00333 }
00334 
00335 nsMarkedJSFunctionHolder_base::~nsMarkedJSFunctionHolder_base()
00336 {
00337   if (PRWord(mObject) & MARKED_OBJECT_BIT) {
00338     nsDOMClassInfo::ReleaseWrapper(this);
00339   }
00340   nsISupports *obj = (nsISupports*)(PRWord(mObject) & ~MARKED_OBJECT_BIT);
00341   NS_IF_RELEASE(obj);
00342 }