/* -*- 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.org 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 ***** */ #include "nsJSEnvironment.h" #include "nsIScriptContextOwner.h" #include "nsIScriptGlobalObject.h" #include "nsIScriptObjectPrincipal.h" #include "nsIDOMChromeWindow.h" #include "nsIDOMWindowInternal.h" #include "nsIDOMNode.h" #include "nsIDOMElement.h" #include "nsIDOMDocument.h" #include "nsIDOMText.h" #include "nsIDOMAttr.h" #include "nsIDOMNamedNodeMap.h" #include "nsIDOMNodeList.h" #include "nsIDOMKeyEvent.h" #include "nsIDOMHTMLImageElement.h" #include "nsIDOMHTMLOptionElement.h" #include "nsIDOMChromeWindow.h" #include "nsIScriptSecurityManager.h" #include "nsDOMCID.h" #include "nsIServiceManager.h" #include "nsIXPConnect.h" #include "nsIJSContextStack.h" #include "nsIJSRuntimeService.h" #include "nsCOMPtr.h" #include "nsReadableUtils.h" #include "nsJSUtils.h" #include "nsIDocShell.h" #include "nsIDocShellTreeItem.h" #include "nsPresContext.h" #include "nsIConsoleService.h" #include "nsIScriptError.h" #include "nsIInterfaceRequestor.h" #include "nsIInterfaceRequestorUtils.h" #include "nsIPrompt.h" #include "nsIObserverService.h" #include "nsGUIEvent.h" #include "nsScriptNameSpaceManager.h" #include "nsIThread.h" #include "nsITimer.h" #include "nsDOMClassInfo.h" #include "nsIAtom.h" #include "nsContentUtils.h" #include "jscntxt.h" #include "jsdbgapi.h" #include "nsIDOMGCParticipant.h" #include "nsIDocument.h" #include "nsIContent.h" // For locale aware string methods #include "plstr.h" #include "nsIPlatformCharset.h" #include "nsICharsetConverterManager.h" #include "nsUnicharUtils.h" #include "nsILocaleService.h" #include "nsICollation.h" #include "nsCollationCID.h" #include "nsDOMClassInfo.h" #ifdef NS_DEBUG #include "jsgc.h" // for WAY_TOO_MUCH_GC, if defined for GC debugging #endif #ifdef MOZ_JSDEBUGGER #include "jsdIDebuggerService.h" #endif static NS_DEFINE_CID(kCollationFactoryCID, NS_COLLATIONFACTORY_CID); #include "nsIStringBundle.h" #ifdef MOZ_LOGGING // Force PR_LOGGING so we can get JS strict warnings even in release builds #define FORCE_PR_LOG 1 #endif #include "prlog.h" #include "prthread.h" #ifdef OJI #include "nsIJVMManager.h" #include "nsILiveConnectManager.h" #endif const size_t gStackSize = 8192; #ifdef PR_LOGGING static PRLogModuleInfo* gJSDiagnostics; #endif // Thank you Microsoft! #ifndef WINCE #ifdef CompareString #undef CompareString #endif #endif // WINCE #define NS_GC_DELAY 2000 // ms #define NS_FIRST_GC_DELAY 10000 // ms // if you add statics here, add them to the list in nsJSEnvironment::Startup static nsITimer *sGCTimer; static PRBool sReadyForGC; nsScriptNameSpaceManager *gNameSpaceManager; static nsIJSRuntimeService *sRuntimeService; JSRuntime *nsJSEnvironment::sRuntime; static const char kJSRuntimeServiceContractID[] = "@mozilla.org/js/xpc/RuntimeService;1"; static const char kDOMStringBundleURL[] = "chrome://global/locale/dom/dom.properties"; static PRThread *gDOMThread; static JSGCCallback gOldJSGCCallback; static PRBool sIsInitialized; static PRBool sDidShutdown; static PRInt32 sContextCount; static PRTime sMaxScriptRunTime; static PRTime sMaxChromeScriptRunTime; static nsIScriptSecurityManager *sSecurityManager; static nsICollation *gCollation; static nsIUnicodeDecoder *gDecoder; void JS_DLL_CALLBACK NS_ScriptErrorReporter(JSContext *cx, const char *message, JSErrorReport *report) { // XXX this means we are not going to get error reports on non DOM contexts nsIScriptContext *context = nsJSUtils::GetDynamicScriptContext(cx); nsEventStatus status = nsEventStatus_eIgnore; if (context) { nsIScriptGlobalObject *globalObject = context->GetGlobalObject(); if (globalObject) { nsAutoString fileName, msg; if (report) { fileName.AssignWithConversion(report->filename); const PRUnichar *m = NS_REINTERPRET_CAST(const PRUnichar*, report->ucmessage); if (m) { msg.Assign(m); } } if (msg.IsEmpty() && message) { msg.AssignWithConversion(message); } // First, notify the DOM that we have a script error. /* We do not try to report Out Of Memory via a dom * event because the dom event handler would encounter * an OOM exception trying to process the event, and * then we'd need to generate a new OOM event for that * new OOM instance -- this isn't pretty. */ nsIDocShell *docShell = globalObject->GetDocShell(); if (docShell && (!report || (report->errorNumber != JSMSG_OUT_OF_MEMORY && !JSREPORT_IS_WARNING(report->flags)))) { static PRInt32 errorDepth; // Recursion prevention ++errorDepth; nsCOMPtr presContext; docShell->GetPresContext(getter_AddRefs(presContext)); if (presContext && errorDepth < 2) { nsScriptErrorEvent errorevent(PR_TRUE, NS_SCRIPT_ERROR); errorevent.fileName = fileName.get(); errorevent.errorMsg = msg.get(); errorevent.lineNr = report ? report->lineno : 0; // HandleDOMEvent() must be synchronous for the recursion block // (errorDepth) to work. globalObject->HandleDOMEvent(presContext, &errorevent, nsnull, NS_EVENT_FLAG_INIT, &status); } --errorDepth; } if (status != nsEventStatus_eConsumeNoDefault) { // Make an nsIScriptError and populate it with information from // this error. nsCOMPtr errorObject = do_CreateInstance("@mozilla.org/scripterror;1"); if (errorObject != nsnull) { nsresult rv; const char *category = nsnull; // Set category to XUL or content, if possible. if (docShell) { nsCOMPtr docShellTI(do_QueryInterface(docShell, &rv)); if (NS_SUCCEEDED(rv) && docShellTI) { PRInt32 docShellType; rv = docShellTI->GetItemType(&docShellType); if (NS_SUCCEEDED(rv)) { category = docShellType == nsIDocShellTreeItem::typeChrome ? "chrome javascript" : "content javascript"; } } } if (report) { PRUint32 column = report->uctokenptr - report->uclinebuf; rv = errorObject->Init(msg.get(), fileName.get(), NS_REINTERPRET_CAST(const PRUnichar*, report->uclinebuf), report->lineno, column, report->flags, category); } else if (message) { rv = errorObject->Init(msg.get(), nsnull, nsnull, 0, 0, 0, category); } if (NS_SUCCEEDED(rv)) { nsCOMPtr consoleService = do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv); if (NS_SUCCEEDED(rv)) { consoleService->LogMessage(errorObject); } } } } } } #ifdef DEBUG // Print it to stderr as well, for the benefit of those invoking // mozilla with -console. nsCAutoString error; error.Assign("JavaScript "); if (!report) { error.Append("[no report]: "); error.Append(message); } else { if (JSREPORT_IS_STRICT(report->flags)) error.Append("strict "); if (JSREPORT_IS_WARNING(report->flags)) error.Append("warning: "); else error.Append("error: "); error.Append(report->filename); error.Append(", line "); error.AppendInt(report->lineno, 10); error.Append(": "); if (report->ucmessage) { AppendUTF16toUTF8(NS_REINTERPRET_CAST(const PRUnichar*, report->ucmessage), error); } else { error.Append(message); } if (status != nsEventStatus_eIgnore && !JSREPORT_IS_WARNING(report->flags)) error.Append(" Error was suppressed by event handler\n"); } fprintf(stderr, "%s\n", error.get()); fflush(stderr); #endif #ifdef PR_LOGGING if (report) { if (!gJSDiagnostics) gJSDiagnostics = PR_NewLogModule("JSDiagnostics"); if (gJSDiagnostics) { PR_LOG(gJSDiagnostics, JSREPORT_IS_WARNING(report->flags) ? PR_LOG_WARNING : PR_LOG_ERROR, ("file %s, line %u: %s\n%s%s", report->filename, report->lineno, message, report->linebuf ? report->linebuf : "", (report->linebuf && report->linebuf[strlen(report->linebuf)-1] != '\n') ? "\n" : "")); } } #endif // XXX do we really want to be doing this? ::JS_ClearPendingException(cx); } JS_STATIC_DLL_CALLBACK(JSBool) LocaleToUnicode(JSContext *cx, char *src, jsval *rval) { nsresult rv; if (!gDecoder) { // use app default locale nsCOMPtr localeService = do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv); if (NS_SUCCEEDED(rv)) { nsCOMPtr appLocale; rv = localeService->GetApplicationLocale(getter_AddRefs(appLocale)); if (NS_SUCCEEDED(rv)) { nsAutoString localeStr; rv = appLocale-> GetCategory(NS_LITERAL_STRING(NSILOCALE_TIME), localeStr); NS_ASSERTION(NS_SUCCEEDED(rv), "failed to get app locale info"); nsCOMPtr platformCharset = do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &rv); if (NS_SUCCEEDED(rv)) { nsCAutoString charset; rv = platformCharset->GetDefaultCharsetForLocale(localeStr, charset); if (NS_SUCCEEDED(rv)) { // get/create unicode decoder for charset nsCOMPtr ccm = do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv); if (NS_SUCCEEDED(rv)) ccm->GetUnicodeDecoder(charset.get(), &gDecoder); } } } } } JSString *str = nsnull; PRInt32 srcLength = PL_strlen(src); if (gDecoder) { PRInt32 unicharLength = srcLength; PRUnichar *unichars = (PRUnichar *)malloc((srcLength + 1) * sizeof(PRUnichar)); if (unichars) { rv = gDecoder->Convert(src, &srcLength, unichars, &unicharLength); if (NS_SUCCEEDED(rv)) { // terminate the returned string unichars[unicharLength] = 0; // nsIUnicodeDecoder::Convert may use fewer than srcLength PRUnichars if (unicharLength + 1 < srcLength + 1) { PRUnichar *shrunkUnichars = (PRUnichar *)realloc(unichars, (unicharLength + 1) * sizeof(PRUnichar)); if (shrunkUnichars) unichars = shrunkUnichars; } str = JS_NewUCString(cx, NS_REINTERPRET_CAST(jschar*, unichars), unicharLength); } if (!str) free(unichars); } } if (!str) { nsDOMClassInfo::ThrowJSException(cx, NS_ERROR_OUT_OF_MEMORY); return JS_FALSE; } *rval = STRING_TO_JSVAL(str); return JS_TRUE; } static JSBool ChangeCase(JSContext *cx, JSString *src, jsval *rval, void(* changeCaseFnc)(const nsAString&, nsAString&)) { nsAutoString result; changeCaseFnc(nsDependentJSString(src), result); JSString *ucstr = JS_NewUCStringCopyN(cx, (jschar*)result.get(), result.Length()); if (!ucstr) { return JS_FALSE; } *rval = STRING_TO_JSVAL(ucstr); return JS_TRUE; } static JSBool JS_DLL_CALLBACK LocaleToUpperCase(JSContext *cx, JSString *src, jsval *rval) { return ChangeCase(cx, src, rval, ToUpperCase); } static JSBool JS_DLL_CALLBACK LocaleToLowerCase(JSContext *cx, JSString *src, jsval *rval) { return ChangeCase(cx, src, rval, ToLowerCase); } static JSBool JS_DLL_CALLBACK LocaleCompare(JSContext *cx, JSString *src1, JSString *src2, jsval *rval) { nsresult rv; if (!gCollation) { nsCOMPtr localeService = do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv); if (NS_SUCCEEDED(rv)) { nsCOMPtr locale; rv = localeService->GetApplicationLocale(getter_AddRefs(locale)); if (NS_SUCCEEDED(rv)) { nsCOMPtr colFactory = do_CreateInstance(kCollationFactoryCID, &rv); if (NS_SUCCEEDED(rv)) { rv = colFactory->CreateCollation(locale, &gCollation); } } } if (NS_FAILED(rv)) { nsDOMClassInfo::ThrowJSException(cx, rv); return JS_FALSE; } } PRInt32 result; rv = gCollation->CompareString(nsICollation::kCollationStrengthDefault, nsDependentJSString(src1), nsDependentJSString(src2), &result); if (NS_FAILED(rv)) { nsDOMClassInfo::ThrowJSException(cx, rv); return JS_FALSE; } *rval = INT_TO_JSVAL(result); return JS_TRUE; } // The number of branch callbacks between calls to JS_MaybeGC #define MAYBE_GC_BRANCH_COUNT_MASK 0x00000fff // 4095 // The number of branch callbacks before we even check if our start // timestamp is initialized. This is a fairly low number as we want to // initialize the timestamp early enough to not waste much time before // we get there, but we don't want to bother doing this too early as // it's not generally necessary. #define INITIALIZE_TIME_BRANCH_COUNT_MASK 0x000000ff // 255 // This function is called after each JS branch execution JSBool JS_DLL_CALLBACK nsJSContext::DOMBranchCallback(JSContext *cx, JSScript *script) { // Get the native context nsJSContext *ctx = NS_STATIC_CAST(nsJSContext *, ::JS_GetContextPrivate(cx)); PRUint32 callbackCount = ++ctx->mBranchCallbackCount; if (callbackCount & INITIALIZE_TIME_BRANCH_COUNT_MASK) { return JS_TRUE; } if (callbackCount == INITIALIZE_TIME_BRANCH_COUNT_MASK + 1 && LL_IS_ZERO(ctx->mBranchCallbackTime)) { // Initialize mBranchCallbackTime to start timing how long the // script has run ctx->mBranchCallbackTime = PR_Now(); ctx->mIsTrackingChromeCodeTime = ::JS_IsSystemObject(cx, ::JS_GetGlobalObject(cx)); return JS_TRUE; } if (callbackCount & MAYBE_GC_BRANCH_COUNT_MASK) { return JS_TRUE; } // XXX Save the branch callback time so we can restore it after the GC, // because GCing can cause JS to run on our context, causing our // ScriptEvaluated to be called, and clearing our branch callback time and // count. See bug 302333. PRTime callbackTime = ctx->mBranchCallbackTime; // Run the GC if we get this far. JS_MaybeGC(cx); // Now restore the callback time and count, in case they got reset. ctx->mBranchCallbackTime = callbackTime; ctx->mBranchCallbackCount = callbackCount; PRTime now = PR_Now(); PRTime duration; LL_SUB(duration, now, callbackTime); // Check the amount of time this script has been running if (duration < (ctx->mIsTrackingChromeCodeTime ? sMaxChromeScriptRunTime : sMaxScriptRunTime)) { return JS_TRUE; } // If we get here we're most likely executing an infinite loop in JS, // we'll tell the user about this and we'll give the user the option // of stopping the execution of the script. nsIScriptGlobalObject *global = ctx->GetGlobalObject(); NS_ENSURE_TRUE(global, JS_TRUE); nsIDocShell *docShell = global->GetDocShell(); NS_ENSURE_TRUE(docShell, JS_TRUE); nsCOMPtr ireq(do_QueryInterface(docShell)); NS_ENSURE_TRUE(ireq, JS_TRUE); // Get the nsIPrompt interface from the docshell nsCOMPtr prompt; ireq->GetInterface(NS_GET_IID(nsIPrompt), getter_AddRefs(prompt)); NS_ENSURE_TRUE(prompt, JS_TRUE); nsresult rv; // Check if we should offer the option to debug PRBool debugPossible = (cx->runtime && cx->runtime->debuggerHandler); #ifdef MOZ_JSDEBUGGER // Get the debugger service if necessary. if (debugPossible) { PRBool jsds_IsOn = PR_FALSE; const char jsdServiceCtrID[] = "@mozilla.org/js/jsd/debugger-service;1"; nsCOMPtr jsdHook; nsCOMPtr jsds = do_GetService(jsdServiceCtrID, &rv); // Check if there's a user for the debugger service that's 'on' for us if (NS_SUCCEEDED(rv)) { jsds->GetDebuggerHook(getter_AddRefs(jsdHook)); jsds->GetIsOn(&jsds_IsOn); if (jsds_IsOn) { // If this is not true, the next call would start jsd... rv = jsds->OnForRuntime(cx->runtime); jsds_IsOn = NS_SUCCEEDED(rv); } } // If there is a debug handler registered for this runtime AND // ((jsd is on AND has a hook) OR (jsd isn't on (something else debugs))) // then something useful will be done with our request to debug. debugPossible = ((jsds_IsOn && (jsdHook != nsnull)) || !jsds_IsOn); } #endif // Get localizable strings nsCOMPtr stringService(do_GetService(NS_STRINGBUNDLE_CONTRACTID)); if (!stringService) return JS_TRUE; nsCOMPtr bundle; stringService->CreateBundle(kDOMStringBundleURL, getter_AddRefs(bundle)); if (!bundle) return JS_TRUE; nsXPIDLString title, msg, stopButton, waitButton, debugButton; rv = bundle->GetStringFromName(NS_LITERAL_STRING("KillScriptTitle").get(), getter_Copies(title)); rv |= bundle->GetStringFromName(NS_LITERAL_STRING("StopScriptButton").get(), getter_Copies(stopButton)); rv |= bundle->GetStringFromName(NS_LITERAL_STRING("WaitForScriptButton").get(), getter_Copies(waitButton)); if (debugPossible) { rv |= bundle->GetStringFromName(NS_LITERAL_STRING("DebugScriptButton").get(), getter_Copies(debugButton)); rv |= bundle->GetStringFromName(NS_LITERAL_STRING("KillScriptWithDebugMessage").get(), getter_Copies(msg)); } else { rv |= bundle->GetStringFromName(NS_LITERAL_STRING("KillScriptMessage").get(), getter_Copies(msg)); } //GetStringFromName can return NS_OK and still give NULL string if (NS_FAILED(rv) || !title || !msg || !stopButton || !waitButton || (debugPossible && !debugButton)) { NS_ERROR("Failed to get localized strings."); return JS_TRUE; } PRInt32 buttonPressed = 1; //In case user exits dialog by clicking X PRUint32 buttonFlags = (nsIPrompt::BUTTON_TITLE_IS_STRING * (nsIPrompt::BUTTON_POS_0 + nsIPrompt::BUTTON_POS_1)); // Add a third button if necessary: if (debugPossible) buttonFlags += nsIPrompt::BUTTON_TITLE_IS_STRING * nsIPrompt::BUTTON_POS_2; // Open the dialog. rv = prompt->ConfirmEx(title, msg, buttonFlags, stopButton, waitButton, debugButton, nsnull, nsnull, &buttonPressed); if (NS_FAILED(rv) || (buttonPressed == 1)) { // Allow the script to run this long again ctx->mBranchCallbackTime = PR_Now(); return JS_TRUE; } else if ((buttonPressed == 2) && debugPossible) { // Debug the script jsval rval; switch(cx->runtime->debuggerHandler(cx, script, cx->fp->pc, &rval, cx->runtime->debuggerHandlerData)) { case JSTRAP_RETURN: cx->fp->rval = rval; return JS_TRUE; case JSTRAP_ERROR: cx->throwing = JS_FALSE; return JS_FALSE; case JSTRAP_THROW: JS_SetPendingException(cx, rval); return JS_FALSE; case JSTRAP_CONTINUE: default: return JS_TRUE; } } return JS_FALSE; } #define JS_OPTIONS_DOT_STR "javascript.options." static const char js_options_dot_str[] = JS_OPTIONS_DOT_STR; static const char js_strict_option_str[] = JS_OPTIONS_DOT_STR "strict"; static const char js_werror_option_str[] = JS_OPTIONS_DOT_STR "werror"; int PR_CALLBACK nsJSContext::JSOptionChangedCallback(const char *pref, void *data) { nsJSContext *context = NS_REINTERPRET_CAST(nsJSContext *, data); PRUint32 oldDefaultJSOptions = context->mDefaultJSOptions; PRUint32 newDefaultJSOptions = oldDefaultJSOptions; PRBool strict = nsContentUtils::GetBoolPref(js_strict_option_str); if (strict) newDefaultJSOptions |= JSOPTION_STRICT; else newDefaultJSOptions &= ~JSOPTION_STRICT; PRBool werror = nsContentUtils::GetBoolPref(js_werror_option_str); if (werror) newDefaultJSOptions |= JSOPTION_WERROR; else newDefaultJSOptions &= ~JSOPTION_WERROR; if (newDefaultJSOptions != oldDefaultJSOptions) { // Set options only if we used the old defaults; otherwise the page has // customized some via the options object and we defer to its wisdom. if (::JS_GetOptions(context->mContext) == oldDefaultJSOptions) ::JS_SetOptions(context->mContext, newDefaultJSOptions); // Save the new defaults for the next page load (InitContext). context->mDefaultJSOptions = newDefaultJSOptions; } return 0; } nsJSContext::nsJSContext(JSRuntime *aRuntime) : mGCOnDestruction(PR_TRUE) { ++sContextCount; mDefaultJSOptions = JSOPTION_PRIVATE_IS_NSISUPPORTS | JSOPTION_NATIVE_BRANCH_CALLBACK #ifdef DEBUG | JSOPTION_STRICT // lint catching for development #endif ; // Let xpconnect resync its JSContext tracker. We do this before creating // a new JSContext just in case the heap manager recycles the JSContext // struct. nsContentUtils::XPConnect()->SyncJSContexts(); mContext = ::JS_NewContext(aRuntime, gStackSize); if (mContext) { ::JS_SetContextPrivate(mContext, NS_STATIC_CAST(nsIScriptContext *, this)); // Make sure the new context gets the default context options ::JS_SetOptions(mContext, mDefaultJSOptions); // Check for the JS strict option, which enables extra error checks nsContentUtils::RegisterPrefCallback(js_options_dot_str, JSOptionChangedCallback, this); JSOptionChangedCallback(js_options_dot_str, this); ::JS_SetBranchCallback(mContext, DOMBranchCallback); static JSLocaleCallbacks localeCallbacks = { LocaleToUpperCase, LocaleToLowerCase, LocaleCompare, LocaleToUnicode }; ::JS_SetLocaleCallbacks(mContext, &localeCallbacks); } mIsInitialized = PR_FALSE; mNumEvaluations = 0; mOwner = nsnull; mTerminations = nsnull; mScriptsEnabled = PR_TRUE; mBranchCallbackCount = 0; mBranchCallbackTime = LL_ZERO; mProcessingScriptTag = PR_FALSE; mIsTrackingChromeCodeTime = PR_FALSE; InvalidateContextAndWrapperCache(); } nsJSContext::~nsJSContext() { NS_PRECONDITION(!mTerminations, "Shouldn't have termination funcs by now"); // Cope with JS_NewContext failure in ctor (XXXbe move NewContext to Init?) if (!mContext) return; // Clear our entry in the JSContext, bugzilla bug 66413 ::JS_SetContextPrivate(mContext, nsnull); // Clear the branch callback, bugzilla bug 238218 ::JS_SetBranchCallback(mContext, nsnull); // Unregister our "javascript.options.*" pref-changed callback. nsContentUtils::UnregisterPrefCallback(js_options_dot_str, JSOptionChangedCallback, this); // Release mGlobalWrapperRef before the context is destroyed mGlobalWrapperRef = nsnull; // Let xpconnect destroy the JSContext when it thinks the time is right. nsIXPConnect *xpc = nsContentUtils::XPConnect(); if (xpc) { PRBool do_gc = mGCOnDestruction && !sGCTimer && sReadyForGC; xpc->ReleaseJSContext(mContext, !do_gc); } else { ::JS_DestroyContext(mContext); } --sContextCount; if (!sContextCount && sDidShutdown) { // The last context is being deleted, and we're already in the // process of shutting down, release the JS runtime service, and // the security manager. NS_IF_RELEASE(sRuntimeService); NS_IF_RELEASE(sSecurityManager); NS_IF_RELEASE(gCollation); NS_IF_RELEASE(gDecoder); } } // QueryInterface implementation for nsJSContext NS_INTERFACE_MAP_BEGIN(nsJSContext) NS_INTERFACE_MAP_ENTRY(nsIScriptContext) NS_INTERFACE_MAP_ENTRY(nsIXPCScriptNotify) NS_INTERFACE_MAP_ENTRY(nsITimerCallback) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIScriptContext) NS_INTERFACE_MAP_END NS_IMPL_ADDREF(nsJSContext) NS_IMPL_RELEASE(nsJSContext) nsresult nsJSContext::EvaluateStringWithValue(const nsAString& aScript, void *aScopeObject, nsIPrincipal *aPrincipal, const char *aURL, PRUint32 aLineNo, const char* aVersion, void* aRetValue, PRBool* aIsUndefined) { NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED); if (!mScriptsEnabled) { if (aIsUndefined) { *aIsUndefined = PR_TRUE; } return NS_OK; } nsresult rv; if (!aScopeObject) aScopeObject = ::JS_GetGlobalObject(mContext); // Safety first: get an object representing the script's principals, i.e., // the entities who signed this script, or the fully-qualified-domain-name // or "codebase" from which it was loaded. JSPrincipals *jsprin; nsIPrincipal *principal = aPrincipal; if (aPrincipal) { aPrincipal->GetJSPrincipals(mContext, &jsprin); } else { nsIScriptGlobalObject *global = GetGlobalObject(); if (!global) return NS_ERROR_FAILURE; nsCOMPtr objPrincipal = do_QueryInterface(global, &rv); if (NS_FAILED(rv)) return NS_ERROR_FAILURE; principal = objPrincipal->GetPrincipal(); if (!principal) return NS_ERROR_FAILURE; principal->GetJSPrincipals(mContext, &jsprin); } // From here on, we must JSPRINCIPALS_DROP(jsprin) before returning... PRBool ok = PR_FALSE; rv = sSecurityManager->CanExecuteScripts(mContext, principal, &ok); if (NS_FAILED(rv)) { JSPRINCIPALS_DROP(mContext, jsprin); return NS_ERROR_FAILURE; } // Push our JSContext on the current thread's context stack so JS called // from native code via XPConnect uses the right context. Do this whether // or not the SecurityManager said "ok", in order to simplify control flow // below where we pop before returning. nsCOMPtr stack = do_GetService("@mozilla.org/js/xpc/ContextStack;1", &rv); if (NS_FAILED(rv) || NS_FAILED(stack->Push(mContext))) { JSPRINCIPALS_DROP(mContext, jsprin); return NS_ERROR_FAILURE; } jsval val; nsJSContext::TerminationFuncHolder holder(this); if (ok) { JSVersion newVersion = JSVERSION_UNKNOWN; // SecurityManager said "ok", but don't execute if aVersion is specified // and unknown. Do execute with the default version (and avoid thrashing // the context's version) if aVersion is not specified. ok = (!aVersion || (newVersion = ::JS_StringToVersion(aVersion)) != JSVERSION_UNKNOWN); if (ok) { JSVersion oldVersion = JSVERSION_UNKNOWN; if (aVersion) oldVersion = ::JS_SetVersion(mContext, newVersion); ok = ::JS_EvaluateUCScriptForPrincipals(mContext, (JSObject *)aScopeObject, jsprin, (jschar*)PromiseFlatString(aScript).get(), aScript.Length(), aURL, aLineNo, &val); if (aVersion) { ::JS_SetVersion(mContext, oldVersion); } if (!ok) { // Tell XPConnect about any pending exceptions. This is needed // to avoid dropping JS exceptions in case we got here through // nested calls through XPConnect. nsContentUtils::NotifyXPCIfExceptionPending(mContext); } } } // Whew! Finally done with these manually ref-counted things. JSPRINCIPALS_DROP(mContext, jsprin); // If all went well, convert val to a string (XXXbe unless undefined?). if (ok) { if (aIsUndefined) { *aIsUndefined = JSVAL_IS_VOID(val); } *NS_STATIC_CAST(jsval*, aRetValue) = val; } else { if (aIsUndefined) { *aIsUndefined = PR_TRUE; } } // Pop here, after JS_ValueToString and any other possible evaluation. if (NS_FAILED(stack->Pop(nsnull))) rv = NS_ERROR_FAILURE; // ScriptEvaluated needs to come after we pop the stack ScriptEvaluated(PR_TRUE); return rv; } // Helper function to convert a jsval to an nsAString, and set // exception flags if the conversion fails. static nsresult JSValueToAString(JSContext *cx, jsval val, nsAString *result, PRBool *isUndefined) { if (isUndefined) { *isUndefined = JSVAL_IS_VOID(val); } if (!result) { return NS_OK; } JSString* jsstring = ::JS_ValueToString(cx, val); if (jsstring) { result->Assign(NS_REINTERPRET_CAST(const PRUnichar*, ::JS_GetStringChars(jsstring)), ::JS_GetStringLength(jsstring)); } else { result->Truncate(); // We failed to convert val to a string. We're either OOM, or the // security manager denied access to .toString(), or somesuch, on // an object. Treat this case as if the result were undefined. if (isUndefined) { *isUndefined = PR_TRUE; } if (!::JS_IsExceptionPending(cx)) { // JS_ValueToString() returned null w/o an exception // pending. That means we're OOM. return NS_ERROR_OUT_OF_MEMORY; } // Tell XPConnect about any pending exceptions. This is needed to // avoid dropping JS exceptions in case we got here through nested // calls through XPConnect. nsContentUtils::NotifyXPCIfExceptionPending(cx); } return NS_OK; } nsresult nsJSContext::EvaluateString(const nsAString& aScript, void *aScopeObject, nsIPrincipal *aPrincipal, const char *aURL, PRUint32 aLineNo, const char* aVersion, nsAString *aRetValue, PRBool* aIsUndefined) { NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED); if (!mScriptsEnabled) { *aIsUndefined = PR_TRUE; if (aRetValue) { aRetValue->Truncate(); } return NS_OK; } nsresult rv; if (!aScopeObject) aScopeObject = ::JS_GetGlobalObject(mContext); // Safety first: get an object representing the script's principals, i.e., // the entities who signed this script, or the fully-qualified-domain-name // or "codebase" from which it was loaded. JSPrincipals *jsprin; nsIPrincipal *principal = aPrincipal; if (aPrincipal) { aPrincipal->GetJSPrincipals(mContext, &jsprin); } else { nsCOMPtr objPrincipal = do_QueryInterface(GetGlobalObject(), &rv); if (NS_FAILED(rv)) return NS_ERROR_FAILURE; principal = objPrincipal->GetPrincipal(); if (!principal) return NS_ERROR_FAILURE; principal->GetJSPrincipals(mContext, &jsprin); } // From here on, we must JSPRINCIPALS_DROP(jsprin) before returning... PRBool ok = PR_FALSE; rv = sSecurityManager->CanExecuteScripts(mContext, principal, &ok); if (NS_FAILED(rv)) { JSPRINCIPALS_DROP(mContext, jsprin); return NS_ERROR_FAILURE; } // Push our JSContext on the current thread's context stack so JS called // from native code via XPConnect uses the right context. Do this whether // or not the SecurityManager said "ok", in order to simplify control flow // below where we pop before returning. nsCOMPtr stack = do_GetService("@mozilla.org/js/xpc/ContextStack;1", &rv); if (NS_FAILED(rv) || NS_FAILED(stack->Push(mContext))) { JSPRINCIPALS_DROP(mContext, jsprin); return NS_ERROR_FAILURE; } // The result of evaluation, used only if there were no errors. This need // not be a GC root currently, provided we run the GC only from the branch // callback or from ScriptEvaluated. TODO: use JS_Begin/EndRequest to keep // the GC from racing with JS execution on any thread. jsval val; nsJSContext::TerminationFuncHolder holder(this); if (ok) { JSVersion newVersion = JSVERSION_UNKNOWN; // SecurityManager said "ok", but don't execute if aVersion is specified // and unknown. Do execute with the default version (and avoid thrashing // the context's version) if aVersion is not specified. ok = (!aVersion || (newVersion = ::JS_StringToVersion(aVersion)) != JSVERSION_UNKNOWN); if (ok) { JSVersion oldVersion = JSVERSION_UNKNOWN; if (aVersion) oldVersion = ::JS_SetVersion(mContext, newVersion); ok = ::JS_EvaluateUCScriptForPrincipals(mContext, (JSObject *)aScopeObject, jsprin, (jschar*)PromiseFlatString(aScript).get(), aScript.Length(), aURL, aLineNo, &val); if (aVersion) { ::JS_SetVersion(mContext, oldVersion); } if (!ok) { // Tell XPConnect about any pending exceptions. This is needed // to avoid dropping JS exceptions in case we got here through // nested calls through XPConnect. nsContentUtils::NotifyXPCIfExceptionPending(mContext); } } } // Whew! Finally done with these manually ref-counted things. JSPRINCIPALS_DROP(mContext, jsprin); // If all went well, convert val to a string (XXXbe unless undefined?). if (ok) { rv = JSValueToAString(mContext, val, aRetValue, aIsUndefined); } else { if (aIsUndefined) { *aIsUndefined = PR_TRUE; } if (aRetValue) { aRetValue->Truncate(); } } // Pop here, after JS_ValueToString and any other possible evaluation. if (NS_FAILED(stack->Pop(nsnull))) rv = NS_ERROR_FAILURE; // ScriptEvaluated needs to come after we pop the stack ScriptEvaluated(PR_TRUE); return rv; } nsresult nsJSContext::CompileScript(const PRUnichar* aText, PRInt32 aTextLength, void *aScopeObject, nsIPrincipal *aPrincipal, const char *aURL, PRUint32 aLineNo, const char* aVersion, void** aScriptObject) { NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED); nsresult rv; NS_ENSURE_ARG_POINTER(aPrincipal); if (!aScopeObject) aScopeObject = ::JS_GetGlobalObject(mContext); JSPrincipals *jsprin; aPrincipal->GetJSPrincipals(mContext, &jsprin); // From here on, we must JSPRINCIPALS_DROP(jsprin) before returning... PRBool ok = PR_FALSE; rv = sSecurityManager->CanExecuteScripts(mContext, aPrincipal, &ok); if (NS_FAILED(rv)) { JSPRINCIPALS_DROP(mContext, jsprin); return NS_ERROR_FAILURE; } *aScriptObject = nsnull; if (ok) { JSVersion newVersion = JSVERSION_UNKNOWN; // SecurityManager said "ok", but don't compile if aVersion is specified // and unknown. Do compile with the default version (and avoid thrashing // the context's version) if aVersion is not specified. if (!aVersion || (newVersion = ::JS_StringToVersion(aVersion)) != JSVERSION_UNKNOWN) { JSVersion oldVersion = JSVERSION_UNKNOWN; if (aVersion) oldVersion = ::JS_SetVersion(mContext, newVersion); JSScript* script = ::JS_CompileUCScriptForPrincipals(mContext, (JSObject*) aScopeObject, jsprin, (jschar*) aText, aTextLength, aURL, aLineNo); if (script) { *aScriptObject = (void*) ::JS_NewScriptObject(mContext, script); if (! *aScriptObject) { ::JS_DestroyScript(mContext, script); script = nsnull; } } if (!script) rv = NS_ERROR_OUT_OF_MEMORY; if (aVersion) ::JS_SetVersion(mContext, oldVersion); } } // Whew! Finally done with these manually ref-counted things. JSPRINCIPALS_DROP(mContext, jsprin); return rv; } nsresult nsJSContext::ExecuteScript(void* aScriptObject, void *aScopeObject, nsAString* aRetValue, PRBool* aIsUndefined) { NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED); if (!mScriptsEnabled) { if (aIsUndefined) { *aIsUndefined = PR_TRUE; } if (aRetValue) { aRetValue->Truncate(); } return NS_OK; } nsresult rv; if (!aScopeObject) aScopeObject = ::JS_GetGlobalObject(mContext); // Push our JSContext on our thread's context stack, in case native code // called from JS calls back into JS via XPConnect. nsCOMPtr stack = do_GetService("@mozilla.org/js/xpc/ContextStack;1", &rv); if (NS_FAILED(rv) || NS_FAILED(stack->Push(mContext))) { return NS_ERROR_FAILURE; } // The result of evaluation, used only if there were no errors. This need // not be a GC root currently, provided we run the GC only from the branch // callback or from ScriptEvaluated. TODO: use JS_Begin/EndRequest to keep // the GC from racing with JS execution on any thread. jsval val; JSBool ok; nsJSContext::TerminationFuncHolder holder(this); ok = ::JS_ExecuteScript(mContext, (JSObject*) aScopeObject, (JSScript*) ::JS_GetPrivate(mContext, (JSObject*)aScriptObject), &val); if (ok) { // If all went well, convert val to a string (XXXbe unless undefined?). rv = JSValueToAString(mContext, val, aRetValue, aIsUndefined); } else { if (aIsUndefined) { *aIsUndefined = PR_TRUE; } if (aRetValue) { aRetValue->Truncate(); } // Tell XPConnect about any pending exceptions. This is needed to // avoid dropping JS exceptions in case we got here through nested // calls through XPConnect. nsContentUtils::NotifyXPCIfExceptionPending(mContext); } // Pop here, after JS_ValueToString and any other possible evaluation. if (NS_FAILED(stack->Pop(nsnull))) rv = NS_ERROR_FAILURE; // ScriptEvaluated needs to come after we pop the stack ScriptEvaluated(PR_TRUE); return rv; } static inline const char * AtomToEventHandlerName(nsIAtom *aName) { const char *name; aName->GetUTF8String(&name); #ifdef DEBUG const char *cp; char c; for (cp = name; *cp != '\0'; ++cp) { c = *cp; NS_ASSERTION (('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'), "non-ASCII non-alphabetic event handler name"); } #endif return name; } nsresult nsJSContext::CompileEventHandler(void *aTarget, nsIAtom *aName, const char *aEventName, const nsAString& aBody, const char *aURL, PRUint32 aLineNo, PRBool aShared, void** aHandler) { NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED); if (!sSecurityManager) { NS_ERROR("Huh, we need a script security manager to compile " "an event handler!"); return NS_ERROR_UNEXPECTED; } JSObject *target = (JSObject*)aTarget; JSPrincipals *jsprin = nsnull; if (target) { // Get the principal of the event target (the object principal), // don't get the principal of the global object in this context // since that opens up security exploits with delayed event // handler compilation on stale DOM objects (objects that live in // a document that has already been unloaded). nsCOMPtr prin; nsresult rv = sSecurityManager->GetObjectPrincipal(mContext, target, getter_AddRefs(prin)); NS_ENSURE_SUCCESS(rv, rv); prin->GetJSPrincipals(mContext, &jsprin); NS_ENSURE_TRUE(jsprin, NS_ERROR_NOT_AVAILABLE); } const char *charName = AtomToEventHandlerName(aName); const char *argList[] = { aEventName }; JSFunction* fun = ::JS_CompileUCFunctionForPrincipals(mContext, aShared ? nsnull : target, jsprin, charName, 1, argList, (jschar*)PromiseFlatString(aBody).get(), aBody.Length(), aURL, aLineNo); if (jsprin) { JSPRINCIPALS_DROP(mContext, jsprin); } if (!fun) { return NS_ERROR_FAILURE; } JSObject *handler = ::JS_GetFunctionObject(fun); if (aHandler) *aHandler = (void*) handler; return NS_OK; } nsresult nsJSContext::CompileFunction(void* aTarget, const nsACString& aName, PRUint32 aArgCount, const char** aArgArray, const nsAString& aBody, const char* aURL, PRUint32 aLineNo, PRBool aShared, void** aFunctionObject) { NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED); JSPrincipals *jsprin = nsnull; nsIScriptGlobalObject *global = GetGlobalObject(); if (global) { // XXXbe why the two-step QI? speed up via a new GetGlobalObjectData func? nsCOMPtr globalData = do_QueryInterface(global); if (globalData) { nsIPrincipal *prin = globalData->GetPrincipal(); if (!prin) return NS_ERROR_FAILURE; prin->GetJSPrincipals(mContext, &jsprin); } } JSObject *target = (JSObject*)aTarget; JSFunction* fun = ::JS_CompileUCFunctionForPrincipals(mContext, aShared ? nsnull : target, jsprin, PromiseFlatCString(aName).get(), aArgCount, aArgArray, (jschar*)PromiseFlatString(aBody).get(), aBody.Length(), aURL, aLineNo); if (jsprin) JSPRINCIPALS_DROP(mContext, jsprin); if (!fun) return NS_ERROR_FAILURE; JSObject *handler = ::JS_GetFunctionObject(fun); if (aFunctionObject) *aFunctionObject = (void*) handler; return NS_OK; } nsresult nsJSContext::CallEventHandler(JSObject *aTarget, JSObject *aHandler, uintN argc, jsval *argv, jsval *rval) { NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED); *rval = JSVAL_VOID; if (!mScriptsEnabled) { return NS_OK; } // This one's a lot easier than EvaluateString because we don't have to // hassle with principals: they're already compiled into the JS function. nsresult rv; nsCOMPtr stack = do_GetService("@mozilla.org/js/xpc/ContextStack;1", &rv); if (NS_FAILED(rv) || NS_FAILED(stack->Push(mContext))) return NS_ERROR_FAILURE; // check if the event handler can be run on the object in question rv = sSecurityManager->CheckFunctionAccess(mContext, aHandler, aTarget); if (NS_SUCCEEDED(rv)) { // We're not done yet! Some event listeners are confused about their // script context, so check whether we might actually be the wrong script // context. To be safe, do CheckFunctionAccess checks for both. nsCOMPtr content; const JSClass *jsClass = JS_GET_CLASS(mContext, aTarget); if (jsClass && !((~jsClass->flags) & (JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS))) { nsISupports *priv = NS_STATIC_CAST(nsISupports *, JS_GetPrivate(mContext, aTarget)); nsCOMPtr xpcWrapper = do_QueryInterface(priv); if (xpcWrapper) { content = do_QueryWrappedNative(xpcWrapper); } } if (content) { // XXXbz XBL2/sXBL issue nsIDocument* ownerDoc = content->GetOwnerDoc(); if (ownerDoc) { nsIScriptGlobalObject* global = ownerDoc->GetScriptGlobalObject(); if (global) { nsIScriptContext* context = global->GetContext(); if (context && context != this) { JSContext* cx = NS_STATIC_CAST(JSContext*, context->GetNativeContext()); rv = stack->Push(cx); if (NS_SUCCEEDED(rv)) { rv = sSecurityManager->CheckFunctionAccess(cx, aHandler, aTarget); // Here we lose no matter what; we don't want to leave the wrong // cx on the stack. I guess default to leaving mContext, to // cover those cases when we really do have a different context // for the handler and the node. That's probably safer. if (NS_FAILED(stack->Pop(nsnull))) { return NS_ERROR_FAILURE; } } } } } } } nsJSContext::TerminationFuncHolder holder(this); if (NS_SUCCEEDED(rv)) { jsval funval = OBJECT_TO_JSVAL(aHandler); PRBool ok = ::JS_CallFunctionValue(mContext, aTarget, funval, argc, argv, rval); if (!ok) { // Tell XPConnect about any pending exceptions. This is needed // to avoid dropping JS exceptions in case we got here through // nested calls through XPConnect. nsContentUtils::NotifyXPCIfExceptionPending(mContext); // Don't pass back results from failed calls. *rval = JSVAL_VOID; // Tell the caller that the handler threw an error. rv = NS_ERROR_FAILURE; } } if (NS_FAILED(stack->Pop(nsnull))) return NS_ERROR_FAILURE; // Need to lock, since ScriptEvaluated can GC. PRBool locked = PR_FALSE; if (NS_SUCCEEDED(rv) && JSVAL_IS_GCTHING(*rval)) { locked = ::JS_LockGCThing(mContext, JSVAL_TO_GCTHING(*rval)); if (!locked) { rv = NS_ERROR_OUT_OF_MEMORY; } } // ScriptEvaluated needs to come after we pop the stack ScriptEvaluated(PR_TRUE); if (locked) { ::JS_UnlockGCThing(mContext, JSVAL_TO_GCTHING(*rval)); } return rv; } nsresult nsJSContext::BindCompiledEventHandler(void *aTarget, nsIAtom *aName, void *aHandler) { NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED); const char *charName = AtomToEventHandlerName(aName); JSObject *funobj = (JSObject*) aHandler; JSObject *target = (JSObject*) aTarget; nsresult rv; // Push our JSContext on our thread's context stack, in case native code // called from JS calls back into JS via XPConnect. nsCOMPtr stack = do_GetService("@mozilla.org/js/xpc/ContextStack;1", &rv); if (NS_FAILED(rv) || NS_FAILED(stack->Push(mContext))) { return NS_ERROR_FAILURE; } // Make sure the handler function is parented by its event target object if (funobj && ::JS_GetParent(mContext, funobj) != target) { funobj = ::JS_CloneFunctionObject(mContext, funobj, target); if (!funobj) rv = NS_ERROR_OUT_OF_MEMORY; } if (NS_SUCCEEDED(rv) && !::JS_DefineProperty(mContext, target, charName, OBJECT_TO_JSVAL(funobj), nsnull, nsnull, JSPROP_ENUMERATE | JSPROP_PERMANENT)) { rv = NS_ERROR_FAILURE; } if (NS_FAILED(stack->Pop(nsnull)) && NS_SUCCEEDED(rv)) { rv = NS_ERROR_FAILURE; } return rv; } void nsJSContext::SetDefaultLanguageVersion(const char* aVersion) { ::JS_SetVersion(mContext, ::JS_StringToVersion(aVersion)); } nsIScriptGlobalObject * nsJSContext::GetGlobalObject() { JSObject *global = ::JS_GetGlobalObject(mContext); if (!global) { NS_WARNING("Context has no global."); return nsnull; } JSClass *c = JS_GET_CLASS(mContext, global); if (!c || ((~c->flags) & (JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS))) { NS_WARNING("Global is not an nsISupports."); return nsnull; } nsCOMPtr sgo; nsISupports *priv = (nsISupports *)::JS_GetPrivate(mContext, global); nsCOMPtr wrapped_native = do_QueryInterface(priv); if (wrapped_native) { // The global object is a XPConnect wrapped native, the native in // the wrapper might be the nsIScriptGlobalObject sgo = do_QueryWrappedNative(wrapped_native); } else { sgo = do_QueryInterface(priv); } // This'll return a pointer to something we're about to release, but // that's ok, the JS object will hold it alive long enough. return sgo; } void * nsJSContext::GetNativeContext() { return mContext; } const JSClass* NS_DOMClassInfo_GetXPCNativeWrapperClass(); void NS_DOMClassInfo_SetXPCNativeWrapperClass(JSClass* aClass); nsresult nsJSContext::InitContext(nsIScriptGlobalObject *aGlobalObject) { // Make sure callers of this use // WillInitializeContext/DidInitializeContext around this call. NS_ENSURE_TRUE(!mIsInitialized, NS_ERROR_ALREADY_INITIALIZED); if (!mContext) return NS_ERROR_OUT_OF_MEMORY; InvalidateContextAndWrapperCache(); nsresult rv; if (!gNameSpaceManager) { gNameSpaceManager = new nsScriptNameSpaceManager; NS_ENSURE_TRUE(gNameSpaceManager, NS_ERROR_OUT_OF_MEMORY); rv = gNameSpaceManager->Init(); NS_ENSURE_SUCCESS(rv, rv); } ::JS_SetErrorReporter(mContext, NS_ScriptErrorReporter); if (!aGlobalObject) { // If we don't get a global object then there's nothing more to do here. return NS_OK; } nsIXPConnect *xpc = nsContentUtils::XPConnect(); JSObject *global = ::JS_GetGlobalObject(mContext); nsCOMPtr holder; // If there's already a global object in mContext we won't tell // XPConnect to wrap aGlobalObject since it's already wrapped. if (!global) { nsCOMPtr chromeWindow(do_QueryInterface(aGlobalObject)); PRUint32 flags = 0; if (chromeWindow) { // Flag this object and scripts compiled against it as "system", for // optional automated XPCNativeWrapper construction when chrome views // a content DOM. flags = nsIXPConnect::FLAG_SYSTEM_GLOBAL_OBJECT; // Always enable E4X for XUL and other chrome content -- there is no // need to preserve the