Fix build error when building with --enable-functiontimer. b=613589 r=mwu a=build...
[mozilla-central.git] / dom / base / nsJSEnvironment.cpp
blob36361be06c8d3128230d8892143ca39f11a31f5f
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 sw=2 et tw=78: */
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
16 * The Original Code is mozilla.org code.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998
21 * the Initial Developer. All Rights Reserved.
23 * Contributor(s):
24 * Mark Hammond <mhammond@skippinet.com.au>
26 * Alternatively, the contents of this file may be used under the terms of
27 * either of the GNU General Public License Version 2 or later (the "GPL"),
28 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
40 #include "jscntxt.h"
41 #include "nsJSEnvironment.h"
42 #include "nsIScriptGlobalObject.h"
43 #include "nsIScriptObjectPrincipal.h"
44 #include "nsIDOMChromeWindow.h"
45 #include "nsPIDOMWindow.h"
46 #include "nsIDOMNode.h"
47 #include "nsIDOMElement.h"
48 #include "nsIDOMDocument.h"
49 #include "nsIDOMText.h"
50 #include "nsIDOMAttr.h"
51 #include "nsIDOMNamedNodeMap.h"
52 #include "nsIDOMNodeList.h"
53 #include "nsIDOMKeyEvent.h"
54 #include "nsIDOMHTMLImageElement.h"
55 #include "nsIDOMHTMLOptionElement.h"
56 #include "nsIScriptSecurityManager.h"
57 #include "nsDOMCID.h"
58 #include "nsIServiceManager.h"
59 #include "nsIXPConnect.h"
60 #include "nsIJSContextStack.h"
61 #include "nsIJSRuntimeService.h"
62 #include "nsCOMPtr.h"
63 #include "nsISupportsPrimitives.h"
64 #include "nsReadableUtils.h"
65 #include "nsJSUtils.h"
66 #include "nsIDocShell.h"
67 #include "nsIDocShellTreeItem.h"
68 #include "nsPresContext.h"
69 #include "nsIConsoleService.h"
70 #include "nsIScriptError.h"
71 #include "nsIInterfaceRequestor.h"
72 #include "nsIInterfaceRequestorUtils.h"
73 #include "nsIPrompt.h"
74 #include "nsIObserverService.h"
75 #include "nsGUIEvent.h"
76 #include "nsThreadUtils.h"
77 #include "nsITimer.h"
78 #include "nsIAtom.h"
79 #include "nsContentUtils.h"
80 #include "nsEventDispatcher.h"
81 #include "nsIContent.h"
82 #include "nsCycleCollector.h"
83 #include "nsNetUtil.h"
84 #include "nsXPCOMCIDInternal.h"
85 #include "nsIXULRuntime.h"
87 // For locale aware string methods
88 #include "plstr.h"
89 #include "nsIPlatformCharset.h"
90 #include "nsICharsetConverterManager.h"
91 #include "nsUnicharUtils.h"
92 #include "nsILocaleService.h"
93 #include "nsICollation.h"
94 #include "nsCollationCID.h"
95 #include "nsDOMClassInfo.h"
97 #include "jsdbgapi.h" // for JS_ClearWatchPointsForObject
98 #include "jsxdrapi.h"
99 #include "nsIArray.h"
100 #include "nsIObjectInputStream.h"
101 #include "nsIObjectOutputStream.h"
102 #include "nsITimelineService.h"
103 #include "nsDOMScriptObjectHolder.h"
104 #include "prmem.h"
105 #include "WrapperFactory.h"
106 #include "nsGlobalWindow.h"
108 #ifdef XP_MACOSX
109 // AssertMacros.h defines 'check' and conflicts with AccessCheck.h
110 #undef check
111 #endif
112 #include "AccessCheck.h"
114 #ifdef MOZ_JSDEBUGGER
115 #include "jsdIDebuggerService.h"
116 #endif
117 #ifdef MOZ_LOGGING
118 // Force PR_LOGGING so we can get JS strict warnings even in release builds
119 #define FORCE_PR_LOG 1
120 #endif
121 #include "prlog.h"
122 #include "prthread.h"
124 #include "mozilla/FunctionTimer.h"
126 const size_t gStackSize = 8192;
128 #ifdef PR_LOGGING
129 static PRLogModuleInfo* gJSDiagnostics;
130 #endif
132 // Thank you Microsoft!
133 #ifndef WINCE
134 #ifdef CompareString
135 #undef CompareString
136 #endif
137 #endif // WINCE
139 // The amount of time we wait between a request to GC (due to leaving
140 // a page) and doing the actual GC.
141 #define NS_GC_DELAY 2000 // ms
143 // The amount of time we wait until we force a GC in case the previous
144 // GC timer happened to fire while we were in the middle of loading a
145 // page (we'll GC once the page is loaded if that happens before this
146 // amount of time has passed).
147 #define NS_LOAD_IN_PROCESS_GC_DELAY 4000 // ms
149 // The amount of time we wait from the first request to GC to actually
150 // doing the first GC.
151 #define NS_FIRST_GC_DELAY 10000 // ms
153 #define JAVASCRIPT nsIProgrammingLanguage::JAVASCRIPT
155 // The max number of delayed cycle collects..
156 #define NS_MAX_DELAYED_CCOLLECT 45
157 // The max number of user interaction notifications in inactive state before
158 // we try to call cycle collector more aggressively.
159 #define NS_CC_SOFT_LIMIT_INACTIVE 6
160 // The max number of user interaction notifications in active state before
161 // we try to call cycle collector more aggressively.
162 #define NS_CC_SOFT_LIMIT_ACTIVE 12
163 // When higher probability MaybeCC is used, the number of sDelayedCCollectCount
164 // is multiplied with this number.
165 #define NS_PROBABILITY_MULTIPLIER 3
166 // Cycle collector is never called more often than every NS_MIN_CC_INTERVAL
167 // milliseconds. Exceptions are low memory situation and memory pressure
168 // notification.
169 #define NS_MIN_CC_INTERVAL 10000 // ms
170 // If previous cycle collection collected more than this number of objects,
171 // the next collection will happen somewhat soon.
172 #define NS_COLLECTED_OBJECTS_LIMIT 5000
173 // CC will be called if GC has been called at least this number of times and
174 // there are at least NS_MIN_SUSPECT_CHANGES new suspected objects.
175 #define NS_MAX_GC_COUNT 5
176 #define NS_MIN_SUSPECT_CHANGES 10
177 // CC will be called if there are at least NS_MAX_SUSPECT_CHANGES new suspected
178 // objects.
179 #define NS_MAX_SUSPECT_CHANGES 100
181 // if you add statics here, add them to the list in nsJSRuntime::Startup
183 static PRUint32 sDelayedCCollectCount;
184 static PRUint32 sCCollectCount;
185 static PRBool sUserIsActive;
186 static PRTime sPreviousCCTime;
187 static PRUint32 sCollectedObjectsCounts;
188 static PRUint32 sSavedGCCount;
189 static PRUint32 sCCSuspectChanges;
190 static PRUint32 sCCSuspectedCount;
191 static nsITimer *sGCTimer;
192 static PRBool sReadyForGC;
194 // The number of currently pending document loads. This count isn't
195 // guaranteed to always reflect reality and can't easily as we don't
196 // have an easy place to know when a load ends or is interrupted in
197 // all cases. This counter also gets reset if we end up GC'ing while
198 // we're waiting for a slow page to load. IOW, this count may be 0
199 // even when there are pending loads.
200 static PRUint32 sPendingLoadCount;
202 // Boolean that tells us whether or not the current GC timer
203 // (sGCTimer) was scheduled due to a GC timer firing while we were in
204 // the middle of loading a page.
205 static PRBool sLoadInProgressGCTimer;
207 nsScriptNameSpaceManager *gNameSpaceManager;
209 static nsIJSRuntimeService *sRuntimeService;
210 JSRuntime *nsJSRuntime::sRuntime;
212 static const char kJSRuntimeServiceContractID[] =
213 "@mozilla.org/js/xpc/RuntimeService;1";
215 static JSGCCallback gOldJSGCCallback;
217 static PRBool sIsInitialized;
218 static PRBool sDidShutdown;
220 static PRInt32 sContextCount;
222 static PRTime sMaxScriptRunTime;
223 static PRTime sMaxChromeScriptRunTime;
225 static nsIScriptSecurityManager *sSecurityManager;
227 static nsICollation *gCollation;
229 static nsIUnicodeDecoder *gDecoder;
231 // nsUserActivityObserver observes user-interaction-active and
232 // user-interaction-inactive notifications. It counts the number of
233 // notifications and if the number is bigger than NS_CC_SOFT_LIMIT_ACTIVE
234 // (in case the current notification is user-interaction-active) or
235 // NS_CC_SOFT_LIMIT_INACTIVE (current notification is user-interaction-inactive)
236 // MaybeCC is called with aHigherParameter set to PR_TRUE, otherwise PR_FALSE.
238 // When moving from active state to inactive, nsJSContext::IntervalCC() is
239 // called unless the timer related to page load is active.
241 class nsUserActivityObserver : public nsIObserver
243 public:
244 nsUserActivityObserver()
245 : mUserActivityCounter(0), mOldCCollectCount(0) {}
246 NS_DECL_ISUPPORTS
247 NS_DECL_NSIOBSERVER
248 private:
249 PRUint32 mUserActivityCounter;
250 PRUint32 mOldCCollectCount;
253 NS_IMPL_ISUPPORTS1(nsUserActivityObserver, nsIObserver)
255 NS_IMETHODIMP
256 nsUserActivityObserver::Observe(nsISupports* aSubject, const char* aTopic,
257 const PRUnichar* aData)
259 if (mOldCCollectCount != sCCollectCount) {
260 mOldCCollectCount = sCCollectCount;
261 // Cycle collector was called between user interaction notifications, so
262 // we can reset the counter.
263 mUserActivityCounter = 0;
265 PRBool higherProbability = PR_FALSE;
266 ++mUserActivityCounter;
267 if (!strcmp(aTopic, "user-interaction-inactive")) {
268 #ifdef DEBUG_smaug
269 printf("user-interaction-inactive\n");
270 #endif
271 if (sUserIsActive) {
272 sUserIsActive = PR_FALSE;
273 if (!sGCTimer) {
274 nsJSContext::IntervalCC();
275 return NS_OK;
278 higherProbability = (mUserActivityCounter > NS_CC_SOFT_LIMIT_INACTIVE);
279 } else if (!strcmp(aTopic, "user-interaction-active")) {
280 #ifdef DEBUG_smaug
281 printf("user-interaction-active\n");
282 #endif
283 sUserIsActive = PR_TRUE;
284 higherProbability = (mUserActivityCounter > NS_CC_SOFT_LIMIT_ACTIVE);
285 } else if (!strcmp(aTopic, "xpcom-shutdown")) {
286 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
287 if (obs) {
288 obs->RemoveObserver(this, "user-interaction-active");
289 obs->RemoveObserver(this, "user-interaction-inactive");
290 obs->RemoveObserver(this, "xpcom-shutdown");
292 return NS_OK;
294 nsJSContext::MaybeCC(higherProbability);
295 return NS_OK;
298 // nsCCMemoryPressureObserver observes the memory-pressure notifications
299 // and forces a cycle collection when it happens.
301 class nsCCMemoryPressureObserver : public nsIObserver
303 public:
304 NS_DECL_ISUPPORTS
305 NS_DECL_NSIOBSERVER
308 NS_IMPL_ISUPPORTS1(nsCCMemoryPressureObserver, nsIObserver)
310 NS_IMETHODIMP
311 nsCCMemoryPressureObserver::Observe(nsISupports* aSubject, const char* aTopic,
312 const PRUnichar* aData)
314 nsJSContext::CC(nsnull);
315 return NS_OK;
318 /****************************************************************
319 ************************** AutoFree ****************************
320 ****************************************************************/
322 class AutoFree {
323 public:
324 AutoFree(void *aPtr) : mPtr(aPtr) {
326 ~AutoFree() {
327 if (mPtr)
328 nsMemory::Free(mPtr);
330 void Invalidate() {
331 mPtr = 0;
333 private:
334 void *mPtr;
337 class nsAutoPoolRelease {
338 public:
339 nsAutoPoolRelease(JSArenaPool *p, void *m) : mPool(p), mMark(m) {}
340 ~nsAutoPoolRelease() { JS_ARENA_RELEASE(mPool, mMark); }
341 private:
342 JSArenaPool *mPool;
343 void *mMark;
346 // A utility function for script languages to call. Although it looks small,
347 // the use of nsIDocShell and nsPresContext triggers a huge number of
348 // dependencies that most languages would not otherwise need.
349 // XXXmarkh - This function is mis-placed!
350 PRBool
351 NS_HandleScriptError(nsIScriptGlobalObject *aScriptGlobal,
352 nsScriptErrorEvent *aErrorEvent,
353 nsEventStatus *aStatus)
355 PRBool called = PR_FALSE;
356 nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(aScriptGlobal));
357 nsIDocShell *docShell = win ? win->GetDocShell() : nsnull;
358 if (docShell) {
359 nsRefPtr<nsPresContext> presContext;
360 docShell->GetPresContext(getter_AddRefs(presContext));
362 static PRInt32 errorDepth; // Recursion prevention
363 ++errorDepth;
365 if (presContext && errorDepth < 2) {
366 // Dispatch() must be synchronous for the recursion block
367 // (errorDepth) to work.
368 nsEventDispatcher::Dispatch(win, presContext, aErrorEvent, nsnull,
369 aStatus);
370 called = PR_TRUE;
372 --errorDepth;
374 return called;
377 class ScriptErrorEvent : public nsRunnable
379 public:
380 ScriptErrorEvent(nsIScriptGlobalObject* aScriptGlobal,
381 PRUint32 aLineNr, PRUint32 aColumn, PRUint32 aFlags,
382 const nsAString& aErrorMsg,
383 const nsAString& aFileName,
384 const nsAString& aSourceLine,
385 PRBool aDispatchEvent,
386 PRUint64 aWindowID)
387 : mScriptGlobal(aScriptGlobal), mLineNr(aLineNr), mColumn(aColumn),
388 mFlags(aFlags), mErrorMsg(aErrorMsg), mFileName(aFileName),
389 mSourceLine(aSourceLine), mDispatchEvent(aDispatchEvent),
390 mWindowID(aWindowID)
393 NS_IMETHOD Run()
395 nsEventStatus status = nsEventStatus_eIgnore;
396 // First, notify the DOM that we have a script error.
397 if (mDispatchEvent) {
398 nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(mScriptGlobal));
399 nsIDocShell* docShell = win ? win->GetDocShell() : nsnull;
400 if (docShell &&
401 !JSREPORT_IS_WARNING(mFlags) &&
402 !sHandlingScriptError) {
403 sHandlingScriptError = PR_TRUE; // Recursion prevention
405 nsRefPtr<nsPresContext> presContext;
406 docShell->GetPresContext(getter_AddRefs(presContext));
408 if (presContext) {
409 nsScriptErrorEvent errorevent(PR_TRUE, NS_LOAD_ERROR);
411 errorevent.fileName = mFileName.get();
413 nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(win));
414 NS_ENSURE_STATE(sop);
415 nsIPrincipal* p = sop->GetPrincipal();
416 NS_ENSURE_STATE(p);
418 PRBool sameOrigin = mFileName.IsVoid();
420 if (p && !sameOrigin) {
421 nsCOMPtr<nsIURI> errorURI;
422 NS_NewURI(getter_AddRefs(errorURI), mFileName);
423 if (errorURI) {
424 // FIXME: Once error reports contain the origin of the
425 // error (principals) we should change this to do the
426 // security check based on the principals and not
427 // URIs. See bug 387476.
428 sameOrigin = NS_SUCCEEDED(p->CheckMayLoad(errorURI, PR_FALSE));
432 NS_NAMED_LITERAL_STRING(xoriginMsg, "Script error.");
433 if (sameOrigin) {
434 errorevent.errorMsg = mErrorMsg.get();
435 errorevent.lineNr = mLineNr;
436 } else {
437 NS_WARNING("Not same origin error!");
438 errorevent.errorMsg = xoriginMsg.get();
439 errorevent.lineNr = 0;
440 // FIXME: once the principal of the script is not tied to
441 // the filename, we can stop using the post-redirect
442 // filename if we want and remove this line. Note that
443 // apparently we can't handle null filenames in the error
444 // event dispatching code.
445 static PRUnichar nullFilename[] = { PRUnichar(0) };
446 errorevent.fileName = nullFilename;
449 nsEventDispatcher::Dispatch(win, presContext, &errorevent, nsnull,
450 &status);
453 sHandlingScriptError = PR_FALSE;
457 if (status != nsEventStatus_eConsumeNoDefault) {
458 // Make an nsIScriptError and populate it with information from
459 // this error.
460 nsCOMPtr<nsIScriptError> errorObject =
461 do_CreateInstance("@mozilla.org/scripterror;1");
463 if (errorObject != nsnull) {
464 nsresult rv = NS_ERROR_NOT_AVAILABLE;
466 // Set category to chrome or content
467 nsCOMPtr<nsIScriptObjectPrincipal> scriptPrincipal =
468 do_QueryInterface(mScriptGlobal);
469 NS_ASSERTION(scriptPrincipal, "Global objects must implement "
470 "nsIScriptObjectPrincipal");
471 nsCOMPtr<nsIPrincipal> systemPrincipal;
472 sSecurityManager->GetSystemPrincipal(getter_AddRefs(systemPrincipal));
473 const char * category =
474 scriptPrincipal->GetPrincipal() == systemPrincipal
475 ? "chrome javascript"
476 : "content javascript";
478 nsCOMPtr<nsIScriptError2> error2(do_QueryInterface(errorObject));
479 if (error2) {
480 rv = error2->InitWithWindowID(mErrorMsg.get(), mFileName.get(),
481 mSourceLine.get(),
482 mLineNr, mColumn, mFlags,
483 category, mWindowID);
484 } else {
485 rv = errorObject->Init(mErrorMsg.get(), mFileName.get(),
486 mSourceLine.get(),
487 mLineNr, mColumn, mFlags,
488 category);
491 if (NS_SUCCEEDED(rv)) {
492 nsCOMPtr<nsIConsoleService> consoleService =
493 do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv);
494 if (NS_SUCCEEDED(rv)) {
495 consoleService->LogMessage(errorObject);
500 return NS_OK;
504 nsCOMPtr<nsIScriptGlobalObject> mScriptGlobal;
505 PRUint32 mLineNr;
506 PRUint32 mColumn;
507 PRUint32 mFlags;
508 nsString mErrorMsg;
509 nsString mFileName;
510 nsString mSourceLine;
511 PRBool mDispatchEvent;
512 PRUint64 mWindowID;
514 static PRBool sHandlingScriptError;
517 PRBool ScriptErrorEvent::sHandlingScriptError = PR_FALSE;
519 // NOTE: This function could be refactored to use the above. The only reason
520 // it has not been done is that the code below only fills the error event
521 // after it has a good nsPresContext - whereas using the above function
522 // would involve always filling it. Is that a concern?
523 void
524 NS_ScriptErrorReporter(JSContext *cx,
525 const char *message,
526 JSErrorReport *report)
528 // We don't want to report exceptions too eagerly, but warnings in the
529 // absence of werror are swallowed whole, so report those now.
530 if (!JSREPORT_IS_WARNING(report->flags)) {
531 JSStackFrame * fp = nsnull;
532 while ((fp = JS_FrameIterator(cx, &fp))) {
533 if (JS_IsScriptFrame(cx, fp)) {
534 return;
538 nsIXPConnect* xpc = nsContentUtils::XPConnect();
539 if (xpc) {
540 nsAXPCNativeCallContext *cc = nsnull;
541 xpc->GetCurrentNativeCallContext(&cc);
542 if (cc) {
543 nsAXPCNativeCallContext *prev = cc;
544 while (NS_SUCCEEDED(prev->GetPreviousCallContext(&prev)) && prev) {
545 PRUint16 lang;
546 if (NS_SUCCEEDED(prev->GetLanguage(&lang)) &&
547 lang == nsAXPCNativeCallContext::LANG_JS) {
548 return;
555 // XXX this means we are not going to get error reports on non DOM contexts
556 nsIScriptContext *context = nsJSUtils::GetDynamicScriptContext(cx);
558 // Note: we must do this before running any more code on cx (if cx is the
559 // dynamic script context).
560 ::JS_ClearPendingException(cx);
562 if (context) {
563 nsIScriptGlobalObject *globalObject = context->GetGlobalObject();
565 if (globalObject) {
566 nsAutoString fileName, msg;
567 if (!report->filename) {
568 fileName.SetIsVoid(PR_TRUE);
569 } else {
570 fileName.AssignWithConversion(report->filename);
573 const PRUnichar *m = reinterpret_cast<const PRUnichar*>
574 (report->ucmessage);
575 if (m) {
576 msg.Assign(m);
579 if (msg.IsEmpty() && message) {
580 msg.AssignWithConversion(message);
584 /* We do not try to report Out Of Memory via a dom
585 * event because the dom event handler would encounter
586 * an OOM exception trying to process the event, and
587 * then we'd need to generate a new OOM event for that
588 * new OOM instance -- this isn't pretty.
590 nsAutoString sourceLine;
591 sourceLine.Assign(reinterpret_cast<const PRUnichar*>(report->uclinebuf));
592 nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(globalObject);
593 PRUint64 windowID = win ? win->WindowID() : 0;
594 nsContentUtils::AddScriptRunner(
595 new ScriptErrorEvent(globalObject, report->lineno,
596 report->uctokenptr - report->uclinebuf,
597 report->flags, msg, fileName, sourceLine,
598 report->errorNumber != JSMSG_OUT_OF_MEMORY,
599 windowID));
603 #ifdef DEBUG
604 // Print it to stderr as well, for the benefit of those invoking
605 // mozilla with -console.
606 nsCAutoString error;
607 error.Assign("JavaScript ");
608 if (JSREPORT_IS_STRICT(report->flags))
609 error.Append("strict ");
610 if (JSREPORT_IS_WARNING(report->flags))
611 error.Append("warning: ");
612 else
613 error.Append("error: ");
614 error.Append(report->filename);
615 error.Append(", line ");
616 error.AppendInt(report->lineno, 10);
617 error.Append(": ");
618 if (report->ucmessage) {
619 AppendUTF16toUTF8(reinterpret_cast<const PRUnichar*>(report->ucmessage),
620 error);
621 } else {
622 error.Append(message);
625 fprintf(stderr, "%s\n", error.get());
626 fflush(stderr);
627 #endif
629 #ifdef PR_LOGGING
630 if (!gJSDiagnostics)
631 gJSDiagnostics = PR_NewLogModule("JSDiagnostics");
633 if (gJSDiagnostics) {
634 PR_LOG(gJSDiagnostics,
635 JSREPORT_IS_WARNING(report->flags) ? PR_LOG_WARNING : PR_LOG_ERROR,
636 ("file %s, line %u: %s\n%s%s",
637 report->filename, report->lineno, message,
638 report->linebuf ? report->linebuf : "",
639 (report->linebuf &&
640 report->linebuf[strlen(report->linebuf)-1] != '\n')
641 ? "\n"
642 : ""));
644 #endif
647 static JSBool
648 LocaleToUnicode(JSContext *cx, char *src, jsval *rval)
650 nsresult rv;
652 if (!gDecoder) {
653 // use app default locale
654 nsCOMPtr<nsILocaleService> localeService =
655 do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv);
656 if (NS_SUCCEEDED(rv)) {
657 nsCOMPtr<nsILocale> appLocale;
658 rv = localeService->GetApplicationLocale(getter_AddRefs(appLocale));
659 if (NS_SUCCEEDED(rv)) {
660 nsAutoString localeStr;
661 rv = appLocale->
662 GetCategory(NS_LITERAL_STRING(NSILOCALE_TIME), localeStr);
663 NS_ASSERTION(NS_SUCCEEDED(rv), "failed to get app locale info");
665 nsCOMPtr<nsIPlatformCharset> platformCharset =
666 do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &rv);
668 if (NS_SUCCEEDED(rv)) {
669 nsCAutoString charset;
670 rv = platformCharset->GetDefaultCharsetForLocale(localeStr, charset);
671 if (NS_SUCCEEDED(rv)) {
672 // get/create unicode decoder for charset
673 nsCOMPtr<nsICharsetConverterManager> ccm =
674 do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
675 if (NS_SUCCEEDED(rv))
676 ccm->GetUnicodeDecoder(charset.get(), &gDecoder);
683 JSString *str = nsnull;
684 PRInt32 srcLength = PL_strlen(src);
686 if (gDecoder) {
687 PRInt32 unicharLength = srcLength;
688 PRUnichar *unichars =
689 (PRUnichar *)JS_malloc(cx, (srcLength + 1) * sizeof(PRUnichar));
690 if (unichars) {
691 rv = gDecoder->Convert(src, &srcLength, unichars, &unicharLength);
692 if (NS_SUCCEEDED(rv)) {
693 // terminate the returned string
694 unichars[unicharLength] = 0;
696 // nsIUnicodeDecoder::Convert may use fewer than srcLength PRUnichars
697 if (unicharLength + 1 < srcLength + 1) {
698 PRUnichar *shrunkUnichars =
699 (PRUnichar *)JS_realloc(cx, unichars,
700 (unicharLength + 1) * sizeof(PRUnichar));
701 if (shrunkUnichars)
702 unichars = shrunkUnichars;
704 str = JS_NewUCString(cx,
705 reinterpret_cast<jschar*>(unichars),
706 unicharLength);
708 if (!str)
709 JS_free(cx, unichars);
713 if (!str) {
714 nsDOMClassInfo::ThrowJSException(cx, NS_ERROR_OUT_OF_MEMORY);
715 return JS_FALSE;
718 *rval = STRING_TO_JSVAL(str);
719 return JS_TRUE;
723 static JSBool
724 ChangeCase(JSContext *cx, JSString *src, jsval *rval,
725 void(* changeCaseFnc)(const nsAString&, nsAString&))
727 nsAutoString result;
728 changeCaseFnc(nsDependentJSString(src), result);
730 JSString *ucstr = JS_NewUCStringCopyN(cx, (jschar*)result.get(), result.Length());
731 if (!ucstr) {
732 return JS_FALSE;
735 *rval = STRING_TO_JSVAL(ucstr);
737 return JS_TRUE;
740 static JSBool
741 LocaleToUpperCase(JSContext *cx, JSString *src, jsval *rval)
743 return ChangeCase(cx, src, rval, ToUpperCase);
746 static JSBool
747 LocaleToLowerCase(JSContext *cx, JSString *src, jsval *rval)
749 return ChangeCase(cx, src, rval, ToLowerCase);
752 static JSBool
753 LocaleCompare(JSContext *cx, JSString *src1, JSString *src2, jsval *rval)
755 nsresult rv;
757 if (!gCollation) {
758 nsCOMPtr<nsILocaleService> localeService =
759 do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv);
761 if (NS_SUCCEEDED(rv)) {
762 nsCOMPtr<nsILocale> locale;
763 rv = localeService->GetApplicationLocale(getter_AddRefs(locale));
765 if (NS_SUCCEEDED(rv)) {
766 nsCOMPtr<nsICollationFactory> colFactory =
767 do_CreateInstance(NS_COLLATIONFACTORY_CONTRACTID, &rv);
769 if (NS_SUCCEEDED(rv)) {
770 rv = colFactory->CreateCollation(locale, &gCollation);
775 if (NS_FAILED(rv)) {
776 nsDOMClassInfo::ThrowJSException(cx, rv);
778 return JS_FALSE;
782 PRInt32 result;
783 rv = gCollation->CompareString(nsICollation::kCollationStrengthDefault,
784 nsDependentJSString(src1),
785 nsDependentJSString(src2),
786 &result);
788 if (NS_FAILED(rv)) {
789 nsDOMClassInfo::ThrowJSException(cx, rv);
791 return JS_FALSE;
794 *rval = INT_TO_JSVAL(result);
796 return JS_TRUE;
799 #ifdef DEBUG
800 // A couple of useful functions to call when you're debugging.
801 nsGlobalWindow *
802 JSObject2Win(JSContext *cx, JSObject *obj)
804 nsIXPConnect *xpc = nsContentUtils::XPConnect();
805 if (!xpc) {
806 return nsnull;
809 nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
810 xpc->GetWrappedNativeOfJSObject(cx, obj, getter_AddRefs(wrapper));
811 if (wrapper) {
812 nsCOMPtr<nsPIDOMWindow> win = do_QueryWrappedNative(wrapper);
813 if (win) {
814 return static_cast<nsGlobalWindow *>
815 (static_cast<nsPIDOMWindow *>(win));
819 return nsnull;
822 void
823 PrintWinURI(nsGlobalWindow *win)
825 if (!win) {
826 printf("No window passed in.\n");
827 return;
830 nsCOMPtr<nsIDocument> doc = do_QueryInterface(win->GetExtantDocument());
831 if (!doc) {
832 printf("No document in the window.\n");
833 return;
836 nsIURI *uri = doc->GetDocumentURI();
837 if (!uri) {
838 printf("Document doesn't have a URI.\n");
839 return;
842 nsCAutoString spec;
843 uri->GetSpec(spec);
844 printf("%s\n", spec.get());
847 void
848 PrintWinCodebase(nsGlobalWindow *win)
850 if (!win) {
851 printf("No window passed in.\n");
852 return;
855 nsIPrincipal *prin = win->GetPrincipal();
856 if (!prin) {
857 printf("Window doesn't have principals.\n");
858 return;
861 nsCOMPtr<nsIURI> uri;
862 prin->GetURI(getter_AddRefs(uri));
863 if (!uri) {
864 printf("No URI, maybe the system principal.\n");
865 return;
868 nsCAutoString spec;
869 uri->GetSpec(spec);
870 printf("%s\n", spec.get());
873 void
874 DumpString(const nsAString &str)
876 printf("%s\n", NS_ConvertUTF16toUTF8(str).get());
878 #endif
880 static void
881 MaybeGC(JSContext *cx)
883 size_t bytes = cx->runtime->gcBytes;
884 size_t lastBytes = cx->runtime->gcLastBytes;
885 if ((bytes > 8192 && bytes > lastBytes * 16)
886 #ifdef DEBUG
887 || cx->runtime->gcZeal > 0
888 #endif
890 JS_GC(cx);
894 static already_AddRefed<nsIPrompt>
895 GetPromptFromContext(nsJSContext* ctx)
897 nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(ctx->GetGlobalObject()));
898 NS_ENSURE_TRUE(win, nsnull);
900 nsIDocShell *docShell = win->GetDocShell();
901 NS_ENSURE_TRUE(docShell, nsnull);
903 nsCOMPtr<nsIInterfaceRequestor> ireq(do_QueryInterface(docShell));
904 NS_ENSURE_TRUE(ireq, nsnull);
906 // Get the nsIPrompt interface from the docshell
907 nsIPrompt* prompt;
908 ireq->GetInterface(NS_GET_IID(nsIPrompt), (void**)&prompt);
909 return prompt;
912 JSBool
913 nsJSContext::DOMOperationCallback(JSContext *cx)
915 nsresult rv;
917 // Get the native context
918 nsJSContext *ctx = static_cast<nsJSContext *>(::JS_GetContextPrivate(cx));
920 if (!ctx) {
921 // Can happen; see bug 355811
922 return JS_TRUE;
925 // XXX Save the operation callback time so we can restore it after the GC,
926 // because GCing can cause JS to run on our context, causing our
927 // ScriptEvaluated to be called, and clearing our operation callback time.
928 // See bug 302333.
929 PRTime callbackTime = ctx->mOperationCallbackTime;
930 PRTime modalStateTime = ctx->mModalStateTime;
932 MaybeGC(cx);
934 // Now restore the callback time and count, in case they got reset.
935 ctx->mOperationCallbackTime = callbackTime;
936 ctx->mModalStateTime = modalStateTime;
938 PRTime now = PR_Now();
940 if (callbackTime == 0) {
941 // Initialize mOperationCallbackTime to start timing how long the
942 // script has run
943 ctx->mOperationCallbackTime = now;
944 return JS_TRUE;
947 if (ctx->mModalStateDepth) {
948 // We're waiting on a modal dialog, nothing more to do here.
949 return JS_TRUE;
952 PRTime duration = now - callbackTime;
954 // Check the amount of time this script has been running, or if the
955 // dialog is disabled.
956 JSObject* global = ::JS_GetGlobalForScopeChain(cx);
957 PRBool isTrackingChromeCodeTime =
958 global && xpc::AccessCheck::isChrome(global->getCompartment());
959 if (duration < (isTrackingChromeCodeTime ?
960 sMaxChromeScriptRunTime : sMaxScriptRunTime)) {
961 return JS_TRUE;
964 if (!nsContentUtils::IsSafeToRunScript()) {
965 // If it isn't safe to run script, then it isn't safe to bring up the
966 // prompt (since that will cause the event loop to spin). In this case
967 // (which is rare), we just stop the script... But report a warning so
968 // that developers have some idea of what went wrong.
970 JS_ReportWarning(cx, "A long running script was terminated");
971 return JS_FALSE;
974 // If we get here we're most likely executing an infinite loop in JS,
975 // we'll tell the user about this and we'll give the user the option
976 // of stopping the execution of the script.
977 nsCOMPtr<nsIPrompt> prompt = GetPromptFromContext(ctx);
978 NS_ENSURE_TRUE(prompt, JS_TRUE);
980 // Check if we should offer the option to debug
981 JSStackFrame* fp = ::JS_GetScriptedCaller(cx, NULL);
982 PRBool debugPossible = (fp != nsnull && cx->debugHooks &&
983 cx->debugHooks->debuggerHandler != nsnull);
984 #ifdef MOZ_JSDEBUGGER
985 // Get the debugger service if necessary.
986 if (debugPossible) {
987 PRBool jsds_IsOn = PR_FALSE;
988 const char jsdServiceCtrID[] = "@mozilla.org/js/jsd/debugger-service;1";
989 nsCOMPtr<jsdIExecutionHook> jsdHook;
990 nsCOMPtr<jsdIDebuggerService> jsds = do_GetService(jsdServiceCtrID, &rv);
992 // Check if there's a user for the debugger service that's 'on' for us
993 if (NS_SUCCEEDED(rv)) {
994 jsds->GetDebuggerHook(getter_AddRefs(jsdHook));
995 jsds->GetIsOn(&jsds_IsOn);
998 // If there is a debug handler registered for this runtime AND
999 // ((jsd is on AND has a hook) OR (jsd isn't on (something else debugs)))
1000 // then something useful will be done with our request to debug.
1001 debugPossible = ((jsds_IsOn && (jsdHook != nsnull)) || !jsds_IsOn);
1003 #endif
1005 // Get localizable strings
1006 nsXPIDLString title, msg, stopButton, waitButton, debugButton, neverShowDlg;
1008 rv = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
1009 "KillScriptTitle",
1010 title);
1012 rv |= nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
1013 "StopScriptButton",
1014 stopButton);
1016 rv |= nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
1017 "WaitForScriptButton",
1018 waitButton);
1020 rv |= nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
1021 "DontAskAgain",
1022 neverShowDlg);
1025 if (debugPossible) {
1026 rv |= nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
1027 "DebugScriptButton",
1028 debugButton);
1030 rv |= nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
1031 "KillScriptWithDebugMessage",
1032 msg);
1034 else {
1035 rv |= nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
1036 "KillScriptMessage",
1037 msg);
1040 //GetStringFromName can return NS_OK and still give NULL string
1041 if (NS_FAILED(rv) || !title || !msg || !stopButton || !waitButton ||
1042 (!debugButton && debugPossible) || !neverShowDlg) {
1043 NS_ERROR("Failed to get localized strings.");
1044 return JS_TRUE;
1047 // Append file and line number information, if available
1048 JSScript *script = fp ? ::JS_GetFrameScript(cx, fp) : nsnull;
1049 if (script) {
1050 const char *filename = ::JS_GetScriptFilename(cx, script);
1051 if (filename) {
1052 nsXPIDLString scriptLocation;
1053 NS_ConvertUTF8toUTF16 filenameUTF16(filename);
1054 const PRUnichar *formatParams[] = { filenameUTF16.get() };
1055 rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES,
1056 "KillScriptLocation",
1057 formatParams, 1,
1058 scriptLocation);
1060 if (NS_SUCCEEDED(rv) && scriptLocation) {
1061 msg.AppendLiteral("\n\n");
1062 msg.Append(scriptLocation);
1064 JSStackFrame *fp, *iterator = nsnull;
1065 fp = ::JS_FrameIterator(cx, &iterator);
1066 if (fp) {
1067 jsbytecode *pc = ::JS_GetFramePC(cx, fp);
1068 if (pc) {
1069 PRUint32 lineno = ::JS_PCToLineNumber(cx, script, pc);
1070 msg.Append(':');
1071 msg.AppendInt(lineno);
1078 PRInt32 buttonPressed = 0; //In case user exits dialog by clicking X
1079 PRBool neverShowDlgChk = PR_FALSE;
1080 PRUint32 buttonFlags = nsIPrompt::BUTTON_POS_1_DEFAULT +
1081 (nsIPrompt::BUTTON_TITLE_IS_STRING *
1082 (nsIPrompt::BUTTON_POS_0 + nsIPrompt::BUTTON_POS_1));
1084 // Add a third button if necessary:
1085 if (debugPossible)
1086 buttonFlags += nsIPrompt::BUTTON_TITLE_IS_STRING * nsIPrompt::BUTTON_POS_2;
1088 // Null out the operation callback while we're re-entering JS here.
1089 ::JS_SetOperationCallback(cx, nsnull);
1091 // Open the dialog.
1092 rv = prompt->ConfirmEx(title, msg, buttonFlags, waitButton, stopButton,
1093 debugButton, neverShowDlg, &neverShowDlgChk,
1094 &buttonPressed);
1096 ::JS_SetOperationCallback(cx, DOMOperationCallback);
1098 if (NS_FAILED(rv) || (buttonPressed == 0)) {
1099 // Allow the script to continue running
1101 if (neverShowDlgChk) {
1102 nsIPrefBranch *prefBranch = nsContentUtils::GetPrefBranch();
1104 if (prefBranch) {
1105 prefBranch->SetIntPref(isTrackingChromeCodeTime ?
1106 "dom.max_chrome_script_run_time" :
1107 "dom.max_script_run_time", 0);
1111 ctx->mOperationCallbackTime = PR_Now();
1112 return JS_TRUE;
1114 else if ((buttonPressed == 2) && debugPossible) {
1115 // Debug the script
1116 jsval rval;
1117 switch(cx->debugHooks->debuggerHandler(cx, script, ::JS_GetFramePC(cx, fp),
1118 &rval,
1119 cx->debugHooks->
1120 debuggerHandlerData)) {
1121 case JSTRAP_RETURN:
1122 JS_SetFrameReturnValue(cx, fp, rval);
1123 return JS_TRUE;
1124 case JSTRAP_ERROR:
1125 cx->throwing = JS_FALSE;
1126 return JS_FALSE;
1127 case JSTRAP_THROW:
1128 JS_SetPendingException(cx, rval);
1129 return JS_FALSE;
1130 case JSTRAP_CONTINUE:
1131 default:
1132 return JS_TRUE;
1136 JS_ClearPendingException(cx);
1137 return JS_FALSE;
1140 void
1141 nsJSContext::EnterModalState()
1143 if (!mModalStateDepth) {
1144 mModalStateTime = mOperationCallbackTime ? PR_Now() : 0;
1146 ++mModalStateDepth;
1149 void
1150 nsJSContext::LeaveModalState()
1152 if (!mModalStateDepth) {
1153 NS_ERROR("Uh, mismatched LeaveModalState() call!");
1155 return;
1158 --mModalStateDepth;
1160 // If we're still in a modal dialog, or mOperationCallbackTime is still
1161 // uninitialized, do nothing.
1162 if (mModalStateDepth || !mOperationCallbackTime) {
1163 return;
1166 // If mOperationCallbackTime was set when we entered the first dialog
1167 // (and mModalStateTime is thus non-zero), adjust mOperationCallbackTime
1168 // to account for time spent in the dialog.
1169 // If mOperationCallbackTime got set while the modal dialog was open,
1170 // simply set mOperationCallbackTime to the closing time of the dialog so
1171 // that we never adjust mOperationCallbackTime to be in the future.
1172 if (mModalStateTime) {
1173 mOperationCallbackTime += PR_Now() - mModalStateTime;
1175 else {
1176 mOperationCallbackTime = PR_Now();
1180 #define JS_OPTIONS_DOT_STR "javascript.options."
1182 static const char js_options_dot_str[] = JS_OPTIONS_DOT_STR;
1183 static const char js_strict_option_str[] = JS_OPTIONS_DOT_STR "strict";
1184 #ifdef DEBUG
1185 static const char js_strict_debug_option_str[] = JS_OPTIONS_DOT_STR "strict.debug";
1186 #endif
1187 static const char js_werror_option_str[] = JS_OPTIONS_DOT_STR "werror";
1188 static const char js_relimit_option_str[]= JS_OPTIONS_DOT_STR "relimit";
1189 #ifdef JS_GC_ZEAL
1190 static const char js_zeal_option_str[] = JS_OPTIONS_DOT_STR "gczeal";
1191 #endif
1192 static const char js_tracejit_content_str[] = JS_OPTIONS_DOT_STR "tracejit.content";
1193 static const char js_tracejit_chrome_str[] = JS_OPTIONS_DOT_STR "tracejit.chrome";
1194 static const char js_methodjit_content_str[] = JS_OPTIONS_DOT_STR "methodjit.content";
1195 static const char js_methodjit_chrome_str[] = JS_OPTIONS_DOT_STR "methodjit.chrome";
1196 static const char js_profiling_content_str[] = JS_OPTIONS_DOT_STR "jitprofiling.content";
1197 static const char js_profiling_chrome_str[] = JS_OPTIONS_DOT_STR "jitprofiling.chrome";
1200 nsJSContext::JSOptionChangedCallback(const char *pref, void *data)
1202 nsJSContext *context = reinterpret_cast<nsJSContext *>(data);
1203 PRUint32 oldDefaultJSOptions = context->mDefaultJSOptions;
1204 PRUint32 newDefaultJSOptions = oldDefaultJSOptions;
1206 PRBool strict = nsContentUtils::GetBoolPref(js_strict_option_str);
1207 if (strict)
1208 newDefaultJSOptions |= JSOPTION_STRICT;
1209 else
1210 newDefaultJSOptions &= ~JSOPTION_STRICT;
1212 nsIScriptGlobalObject *global = context->GetGlobalObject();
1213 // XXX should we check for sysprin instead of a chrome window, to make
1214 // XXX components be covered by the chrome pref instead of the content one?
1215 nsCOMPtr<nsIDOMChromeWindow> chromeWindow(do_QueryInterface(global));
1217 PRBool useTraceJIT = nsContentUtils::GetBoolPref(chromeWindow ?
1218 js_tracejit_chrome_str :
1219 js_tracejit_content_str);
1220 PRBool useMethodJIT = nsContentUtils::GetBoolPref(chromeWindow ?
1221 js_methodjit_chrome_str :
1222 js_methodjit_content_str);
1223 PRBool useProfiling = nsContentUtils::GetBoolPref(chromeWindow ?
1224 js_profiling_chrome_str :
1225 js_profiling_content_str);
1226 nsCOMPtr<nsIXULRuntime> xr = do_GetService(XULRUNTIME_SERVICE_CONTRACTID);
1227 if (xr) {
1228 PRBool safeMode = PR_FALSE;
1229 xr->GetInSafeMode(&safeMode);
1230 if (safeMode) {
1231 useTraceJIT = PR_FALSE;
1232 useMethodJIT = PR_FALSE;
1233 useProfiling = PR_FALSE;
1237 if (useTraceJIT)
1238 newDefaultJSOptions |= JSOPTION_JIT;
1239 else
1240 newDefaultJSOptions &= ~JSOPTION_JIT;
1242 if (useMethodJIT)
1243 newDefaultJSOptions |= JSOPTION_METHODJIT;
1244 else
1245 newDefaultJSOptions &= ~JSOPTION_METHODJIT;
1247 if (useProfiling)
1248 newDefaultJSOptions |= JSOPTION_PROFILING;
1249 else
1250 newDefaultJSOptions &= ~JSOPTION_PROFILING;
1252 #ifdef DEBUG
1253 // In debug builds, warnings are enabled in chrome context if javascript.options.strict.debug is true
1254 PRBool strictDebug = nsContentUtils::GetBoolPref(js_strict_debug_option_str);
1255 // Note this callback is also called from context's InitClasses thus we don't
1256 // need to enable this directly from InitContext
1257 if (strictDebug && (newDefaultJSOptions & JSOPTION_STRICT) == 0) {
1258 if (chromeWindow)
1259 newDefaultJSOptions |= JSOPTION_STRICT;
1261 #endif
1263 PRBool werror = nsContentUtils::GetBoolPref(js_werror_option_str);
1264 if (werror)
1265 newDefaultJSOptions |= JSOPTION_WERROR;
1266 else
1267 newDefaultJSOptions &= ~JSOPTION_WERROR;
1269 PRBool relimit = nsContentUtils::GetBoolPref(js_relimit_option_str);
1270 if (relimit)
1271 newDefaultJSOptions |= JSOPTION_RELIMIT;
1272 else
1273 newDefaultJSOptions &= ~JSOPTION_RELIMIT;
1275 if (newDefaultJSOptions != oldDefaultJSOptions) {
1276 // Set options only if we used the old defaults; otherwise the page has
1277 // customized some via the options object and we defer to its wisdom.
1278 if (::JS_GetOptions(context->mContext) == oldDefaultJSOptions)
1279 ::JS_SetOptions(context->mContext, newDefaultJSOptions);
1281 // Save the new defaults for the next page load (InitContext).
1282 context->mDefaultJSOptions = newDefaultJSOptions;
1285 #ifdef JS_GC_ZEAL
1286 PRInt32 zeal = nsContentUtils::GetIntPref(js_zeal_option_str, -1);
1287 if (zeal >= 0)
1288 ::JS_SetGCZeal(context->mContext, (PRUint8)zeal);
1289 #endif
1291 return 0;
1294 nsJSContext::nsJSContext(JSRuntime *aRuntime)
1295 : mGCOnDestruction(PR_TRUE),
1296 mExecuteDepth(0)
1299 ++sContextCount;
1301 mDefaultJSOptions = JSOPTION_PRIVATE_IS_NSISUPPORTS | JSOPTION_ANONFUNFIX;
1303 mContext = ::JS_NewContext(aRuntime, gStackSize);
1304 if (mContext) {
1305 ::JS_SetContextPrivate(mContext, static_cast<nsIScriptContext *>(this));
1307 // Preserve any flags the context callback might have set.
1308 mDefaultJSOptions |= ::JS_GetOptions(mContext);
1310 // Make sure the new context gets the default context options
1311 ::JS_SetOptions(mContext, mDefaultJSOptions);
1313 // Watch for the JS boolean options
1314 nsContentUtils::RegisterPrefCallback(js_options_dot_str,
1315 JSOptionChangedCallback,
1316 this);
1318 ::JS_SetOperationCallback(mContext, DOMOperationCallback);
1320 static JSLocaleCallbacks localeCallbacks =
1322 LocaleToUpperCase,
1323 LocaleToLowerCase,
1324 LocaleCompare,
1325 LocaleToUnicode
1328 ::JS_SetLocaleCallbacks(mContext, &localeCallbacks);
1330 mIsInitialized = PR_FALSE;
1331 mNumEvaluations = 0;
1332 mTerminations = nsnull;
1333 mScriptsEnabled = PR_TRUE;
1334 mOperationCallbackTime = 0;
1335 mModalStateTime = 0;
1336 mModalStateDepth = 0;
1337 mProcessingScriptTag = PR_FALSE;
1340 nsJSContext::~nsJSContext()
1342 #ifdef DEBUG
1343 nsCycleCollector_DEBUG_wasFreed(static_cast<nsIScriptContext*>(this));
1344 #endif
1345 NS_PRECONDITION(!mTerminations, "Shouldn't have termination funcs by now");
1347 mGlobalObjectRef = nsnull;
1349 DestroyJSContext();
1351 --sContextCount;
1353 if (!sContextCount && sDidShutdown) {
1354 // The last context is being deleted, and we're already in the
1355 // process of shutting down, release the JS runtime service, and
1356 // the security manager.
1358 NS_IF_RELEASE(sRuntimeService);
1359 NS_IF_RELEASE(sSecurityManager);
1360 NS_IF_RELEASE(gCollation);
1361 NS_IF_RELEASE(gDecoder);
1365 void
1366 nsJSContext::DestroyJSContext()
1368 if (!mContext)
1369 return;
1371 // Clear our entry in the JSContext, bugzilla bug 66413
1372 ::JS_SetContextPrivate(mContext, nsnull);
1374 // Unregister our "javascript.options.*" pref-changed callback.
1375 nsContentUtils::UnregisterPrefCallback(js_options_dot_str,
1376 JSOptionChangedCallback,
1377 this);
1379 PRBool do_gc = mGCOnDestruction && !sGCTimer && sReadyForGC;
1381 // Let xpconnect destroy the JSContext when it thinks the time is right.
1382 nsIXPConnect *xpc = nsContentUtils::XPConnect();
1383 if (xpc) {
1384 xpc->ReleaseJSContext(mContext, !do_gc);
1385 } else if (do_gc) {
1386 ::JS_DestroyContext(mContext);
1387 } else {
1388 ::JS_DestroyContextNoGC(mContext);
1390 mContext = nsnull;
1393 // QueryInterface implementation for nsJSContext
1394 NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSContext)
1395 NS_IMPL_CYCLE_COLLECTION_ROOT_BEGIN(nsJSContext)
1396 NS_ASSERTION(!tmp->mContext || tmp->mContext->outstandingRequests == 0,
1397 "Trying to unlink a context with outstanding requests.");
1398 tmp->mIsInitialized = PR_FALSE;
1399 tmp->mGCOnDestruction = PR_FALSE;
1400 tmp->DestroyJSContext();
1401 NS_IMPL_CYCLE_COLLECTION_ROOT_END
1402 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSContext)
1403 NS_IMPL_CYCLE_COLLECTION_TRACE_END
1404 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSContext)
1405 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mGlobalObjectRef)
1406 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1407 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsJSContext)
1408 NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsJSContext, tmp->GetCCRefcnt())
1409 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mGlobalObjectRef)
1410 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mContext");
1411 nsContentUtils::XPConnect()->NoteJSContext(tmp->mContext, cb);
1412 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1414 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSContext)
1415 NS_INTERFACE_MAP_ENTRY(nsIScriptContext)
1416 NS_INTERFACE_MAP_ENTRY(nsIScriptContextPrincipal)
1417 NS_INTERFACE_MAP_ENTRY(nsIXPCScriptNotify)
1418 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIScriptContext)
1419 NS_INTERFACE_MAP_END
1422 NS_IMPL_CYCLE_COLLECTING_ADDREF_AMBIGUOUS(nsJSContext, nsIScriptContext)
1423 NS_IMPL_CYCLE_COLLECTING_RELEASE_AMBIGUOUS(nsJSContext, nsIScriptContext)
1425 nsrefcnt
1426 nsJSContext::GetCCRefcnt()
1428 nsrefcnt refcnt = mRefCnt.get();
1429 if (NS_LIKELY(mContext))
1430 refcnt += mContext->outstandingRequests;
1431 return refcnt;
1434 nsresult
1435 nsJSContext::EvaluateStringWithValue(const nsAString& aScript,
1436 void *aScopeObject,
1437 nsIPrincipal *aPrincipal,
1438 const char *aURL,
1439 PRUint32 aLineNo,
1440 PRUint32 aVersion,
1441 void* aRetValue,
1442 PRBool* aIsUndefined)
1444 NS_TIME_FUNCTION_MIN_FMT(1.0, "%s (line %d) (url: %s, line: %d)", MOZ_FUNCTION_NAME,
1445 __LINE__, aURL, aLineNo);
1447 NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED);
1449 if (!mScriptsEnabled) {
1450 if (aIsUndefined) {
1451 *aIsUndefined = PR_TRUE;
1454 return NS_OK;
1457 nsresult rv;
1458 if (!aScopeObject)
1459 aScopeObject = ::JS_GetGlobalObject(mContext);
1461 // Safety first: get an object representing the script's principals, i.e.,
1462 // the entities who signed this script, or the fully-qualified-domain-name
1463 // or "codebase" from which it was loaded.
1464 JSPrincipals *jsprin;
1465 nsIPrincipal *principal = aPrincipal;
1466 if (!aPrincipal) {
1467 nsIScriptGlobalObject *global = GetGlobalObject();
1468 if (!global)
1469 return NS_ERROR_FAILURE;
1470 nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal =
1471 do_QueryInterface(global, &rv);
1472 if (NS_FAILED(rv))
1473 return NS_ERROR_FAILURE;
1474 principal = objPrincipal->GetPrincipal();
1475 if (!principal)
1476 return NS_ERROR_FAILURE;
1479 principal->GetJSPrincipals(mContext, &jsprin);
1481 // From here on, we must JSPRINCIPALS_DROP(jsprin) before returning...
1483 PRBool ok = PR_FALSE;
1485 rv = sSecurityManager->CanExecuteScripts(mContext, principal, &ok);
1486 if (NS_FAILED(rv)) {
1487 JSPRINCIPALS_DROP(mContext, jsprin);
1488 return NS_ERROR_FAILURE;
1491 // Push our JSContext on the current thread's context stack so JS called
1492 // from native code via XPConnect uses the right context. Do this whether
1493 // or not the SecurityManager said "ok", in order to simplify control flow
1494 // below where we pop before returning.
1495 nsCOMPtr<nsIJSContextStack> stack =
1496 do_GetService("@mozilla.org/js/xpc/ContextStack;1", &rv);
1497 if (NS_FAILED(rv) || NS_FAILED(stack->Push(mContext))) {
1498 JSPRINCIPALS_DROP(mContext, jsprin);
1499 return NS_ERROR_FAILURE;
1502 jsval val;
1504 rv = sSecurityManager->PushContextPrincipal(mContext, nsnull, principal);
1505 NS_ENSURE_SUCCESS(rv, rv);
1507 nsJSContext::TerminationFuncHolder holder(this);
1509 // SecurityManager said "ok", but don't compile if aVersion is unknown.
1510 // Since the caller is responsible for parsing the version strings, we just
1511 // check it isn't JSVERSION_UNKNOWN.
1512 if (ok && ((JSVersion)aVersion) != JSVERSION_UNKNOWN) {
1514 JSAutoRequest ar(mContext);
1516 JSAutoEnterCompartment ac;
1517 if (!ac.enter(mContext, (JSObject *)aScopeObject)) {
1518 JSPRINCIPALS_DROP(mContext, jsprin);
1519 stack->Pop(nsnull);
1520 return NS_ERROR_FAILURE;
1523 ++mExecuteDepth;
1525 ok = ::JS_EvaluateUCScriptForPrincipalsVersion(mContext,
1526 (JSObject *)aScopeObject,
1527 jsprin,
1528 (jschar*)PromiseFlatString(aScript).get(),
1529 aScript.Length(),
1530 aURL,
1531 aLineNo,
1532 &val,
1533 JSVersion(aVersion));
1535 --mExecuteDepth;
1537 if (!ok) {
1538 // Tell XPConnect about any pending exceptions. This is needed
1539 // to avoid dropping JS exceptions in case we got here through
1540 // nested calls through XPConnect.
1542 ReportPendingException();
1546 // Whew! Finally done with these manually ref-counted things.
1547 JSPRINCIPALS_DROP(mContext, jsprin);
1549 // If all went well, convert val to a string (XXXbe unless undefined?).
1550 if (ok) {
1551 if (aIsUndefined) {
1552 *aIsUndefined = JSVAL_IS_VOID(val);
1555 *static_cast<jsval*>(aRetValue) = val;
1556 // XXX - nsScriptObjectHolder should be used once this method moves to
1557 // the new world order. However, use of 'jsval' appears to make this
1558 // tricky...
1560 else {
1561 if (aIsUndefined) {
1562 *aIsUndefined = PR_TRUE;
1566 sSecurityManager->PopContextPrincipal(mContext);
1568 // Pop here, after JS_ValueToString and any other possible evaluation.
1569 if (NS_FAILED(stack->Pop(nsnull)))
1570 rv = NS_ERROR_FAILURE;
1572 // ScriptEvaluated needs to come after we pop the stack
1573 ScriptEvaluated(PR_TRUE);
1575 return rv;
1579 // Helper function to convert a jsval to an nsAString, and set
1580 // exception flags if the conversion fails.
1581 static nsresult
1582 JSValueToAString(JSContext *cx, jsval val, nsAString *result,
1583 PRBool *isUndefined)
1585 if (isUndefined) {
1586 *isUndefined = JSVAL_IS_VOID(val);
1589 if (!result) {
1590 return NS_OK;
1593 JSString* jsstring = ::JS_ValueToString(cx, val);
1594 if (jsstring) {
1595 result->Assign(reinterpret_cast<const PRUnichar*>
1596 (::JS_GetStringChars(jsstring)),
1597 ::JS_GetStringLength(jsstring));
1598 } else {
1599 result->Truncate();
1601 // We failed to convert val to a string. We're either OOM, or the
1602 // security manager denied access to .toString(), or somesuch, on
1603 // an object. Treat this case as if the result were undefined.
1605 if (isUndefined) {
1606 *isUndefined = PR_TRUE;
1609 if (!::JS_IsExceptionPending(cx)) {
1610 // JS_ValueToString() returned null w/o an exception
1611 // pending. That means we're OOM.
1613 return NS_ERROR_OUT_OF_MEMORY;
1617 return NS_OK;
1620 nsIScriptObjectPrincipal*
1621 nsJSContext::GetObjectPrincipal()
1623 nsCOMPtr<nsIScriptObjectPrincipal> prin = do_QueryInterface(GetGlobalObject());
1624 return prin;
1627 nsresult
1628 nsJSContext::EvaluateString(const nsAString& aScript,
1629 void *aScopeObject,
1630 nsIPrincipal *aPrincipal,
1631 const char *aURL,
1632 PRUint32 aLineNo,
1633 PRUint32 aVersion,
1634 nsAString *aRetValue,
1635 PRBool* aIsUndefined)
1637 NS_TIME_FUNCTION_MIN_FMT(1.0, "%s (line %d) (url: %s, line: %d)", MOZ_FUNCTION_NAME,
1638 __LINE__, aURL, aLineNo);
1640 NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED);
1642 if (!mScriptsEnabled) {
1643 if (aIsUndefined) {
1644 *aIsUndefined = PR_TRUE;
1647 if (aRetValue) {
1648 aRetValue->Truncate();
1651 return NS_OK;
1654 nsresult rv;
1655 if (!aScopeObject)
1656 aScopeObject = ::JS_GetGlobalObject(mContext);
1658 // Safety first: get an object representing the script's principals, i.e.,
1659 // the entities who signed this script, or the fully-qualified-domain-name
1660 // or "codebase" from which it was loaded.
1661 JSPrincipals *jsprin;
1662 nsIPrincipal *principal = aPrincipal;
1663 if (aPrincipal) {
1664 aPrincipal->GetJSPrincipals(mContext, &jsprin);
1666 else {
1667 nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal =
1668 do_QueryInterface(GetGlobalObject(), &rv);
1669 if (NS_FAILED(rv))
1670 return NS_ERROR_FAILURE;
1671 principal = objPrincipal->GetPrincipal();
1672 if (!principal)
1673 return NS_ERROR_FAILURE;
1674 principal->GetJSPrincipals(mContext, &jsprin);
1677 // From here on, we must JSPRINCIPALS_DROP(jsprin) before returning...
1679 PRBool ok = PR_FALSE;
1681 rv = sSecurityManager->CanExecuteScripts(mContext, principal, &ok);
1682 if (NS_FAILED(rv)) {
1683 JSPRINCIPALS_DROP(mContext, jsprin);
1684 return NS_ERROR_FAILURE;
1687 // Push our JSContext on the current thread's context stack so JS called
1688 // from native code via XPConnect uses the right context. Do this whether
1689 // or not the SecurityManager said "ok", in order to simplify control flow
1690 // below where we pop before returning.
1691 nsCOMPtr<nsIJSContextStack> stack =
1692 do_GetService("@mozilla.org/js/xpc/ContextStack;1", &rv);
1693 if (NS_FAILED(rv) || NS_FAILED(stack->Push(mContext))) {
1694 JSPRINCIPALS_DROP(mContext, jsprin);
1695 return NS_ERROR_FAILURE;
1698 // The result of evaluation, used only if there were no errors. This need
1699 // not be a GC root currently, provided we run the GC only from the
1700 // operation callback or from ScriptEvaluated.
1701 jsval val = JSVAL_VOID;
1702 jsval* vp = aRetValue ? &val : NULL;
1704 rv = sSecurityManager->PushContextPrincipal(mContext, nsnull, principal);
1705 NS_ENSURE_SUCCESS(rv, rv);
1707 nsJSContext::TerminationFuncHolder holder(this);
1709 ++mExecuteDepth;
1711 // SecurityManager said "ok", but don't compile if aVersion is unknown.
1712 // Since the caller is responsible for parsing the version strings, we just
1713 // check it isn't JSVERSION_UNKNOWN.
1714 if (ok && ((JSVersion)aVersion) != JSVERSION_UNKNOWN) {
1715 JSAutoRequest ar(mContext);
1716 JSAutoEnterCompartment ac;
1717 if (!ac.enter(mContext, (JSObject *)aScopeObject)) {
1718 stack->Pop(nsnull);
1719 JSPRINCIPALS_DROP(mContext, jsprin);
1720 return NS_ERROR_FAILURE;
1723 ok = ::JS_EvaluateUCScriptForPrincipalsVersion(mContext,
1724 (JSObject *)aScopeObject,
1725 jsprin,
1726 (jschar*)PromiseFlatString(aScript).get(),
1727 aScript.Length(),
1728 aURL,
1729 aLineNo,
1731 JSVersion(aVersion));
1733 if (!ok) {
1734 // Tell XPConnect about any pending exceptions. This is needed
1735 // to avoid dropping JS exceptions in case we got here through
1736 // nested calls through XPConnect.
1738 ReportPendingException();
1742 // Whew! Finally done with these manually ref-counted things.
1743 JSPRINCIPALS_DROP(mContext, jsprin);
1745 // If all went well, convert val to a string if one is wanted.
1746 if (ok) {
1747 JSAutoRequest ar(mContext);
1748 JSAutoEnterCompartment ac;
1749 if (!ac.enter(mContext, (JSObject *)aScopeObject)) {
1750 stack->Pop(nsnull);
1752 rv = JSValueToAString(mContext, val, aRetValue, aIsUndefined);
1754 else {
1755 if (aIsUndefined) {
1756 *aIsUndefined = PR_TRUE;
1759 if (aRetValue) {
1760 aRetValue->Truncate();
1764 --mExecuteDepth;
1766 sSecurityManager->PopContextPrincipal(mContext);
1768 // Pop here, after JS_ValueToString and any other possible evaluation.
1769 if (NS_FAILED(stack->Pop(nsnull)))
1770 rv = NS_ERROR_FAILURE;
1772 // ScriptEvaluated needs to come after we pop the stack
1773 ScriptEvaluated(PR_TRUE);
1775 return rv;
1778 nsresult
1779 nsJSContext::CompileScript(const PRUnichar* aText,
1780 PRInt32 aTextLength,
1781 void *aScopeObject,
1782 nsIPrincipal *aPrincipal,
1783 const char *aURL,
1784 PRUint32 aLineNo,
1785 PRUint32 aVersion,
1786 nsScriptObjectHolder &aScriptObject)
1788 NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED);
1790 nsresult rv;
1791 NS_ENSURE_ARG_POINTER(aPrincipal);
1793 if (!aScopeObject)
1794 aScopeObject = ::JS_GetGlobalObject(mContext);
1796 JSPrincipals *jsprin;
1797 aPrincipal->GetJSPrincipals(mContext, &jsprin);
1798 // From here on, we must JSPRINCIPALS_DROP(jsprin) before returning...
1800 PRBool ok = PR_FALSE;
1802 rv = sSecurityManager->CanExecuteScripts(mContext, aPrincipal, &ok);
1803 if (NS_FAILED(rv)) {
1804 JSPRINCIPALS_DROP(mContext, jsprin);
1805 return NS_ERROR_FAILURE;
1808 aScriptObject.drop(); // ensure old object not used on failure...
1810 // SecurityManager said "ok", but don't compile if aVersion is unknown.
1811 // Since the caller is responsible for parsing the version strings, we just
1812 // check it isn't JSVERSION_UNKNOWN.
1813 if (ok && ((JSVersion)aVersion) != JSVERSION_UNKNOWN) {
1814 JSAutoRequest ar(mContext);
1816 JSScript* script =
1817 ::JS_CompileUCScriptForPrincipalsVersion(mContext,
1818 (JSObject *)aScopeObject,
1819 jsprin,
1820 (jschar*) aText,
1821 aTextLength,
1822 aURL,
1823 aLineNo,
1824 JSVersion(aVersion));
1825 if (script) {
1826 JSObject *scriptObject = ::JS_NewScriptObject(mContext, script);
1827 if (scriptObject) {
1828 NS_ASSERTION(aScriptObject.getScriptTypeID()==JAVASCRIPT,
1829 "Expecting JS script object holder");
1830 rv = aScriptObject.set(scriptObject);
1831 } else {
1832 ::JS_DestroyScript(mContext, script);
1833 script = nsnull;
1835 } else {
1836 rv = NS_ERROR_OUT_OF_MEMORY;
1840 // Whew! Finally done.
1841 JSPRINCIPALS_DROP(mContext, jsprin);
1842 return rv;
1845 nsresult
1846 nsJSContext::ExecuteScript(void *aScriptObject,
1847 void *aScopeObject,
1848 nsAString* aRetValue,
1849 PRBool* aIsUndefined)
1851 NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED);
1853 if (!mScriptsEnabled) {
1854 if (aIsUndefined) {
1855 *aIsUndefined = PR_TRUE;
1858 if (aRetValue) {
1859 aRetValue->Truncate();
1862 return NS_OK;
1865 nsresult rv;
1867 if (!aScopeObject)
1868 aScopeObject = ::JS_GetGlobalObject(mContext);
1870 // Push our JSContext on our thread's context stack, in case native code
1871 // called from JS calls back into JS via XPConnect.
1872 nsCOMPtr<nsIJSContextStack> stack =
1873 do_GetService("@mozilla.org/js/xpc/ContextStack;1", &rv);
1874 if (NS_FAILED(rv) || NS_FAILED(stack->Push(mContext))) {
1875 return NS_ERROR_FAILURE;
1878 // The result of evaluation, used only if there were no errors. This need
1879 // not be a GC root currently, provided we run the GC only from the
1880 // operation callback or from ScriptEvaluated.
1881 jsval val;
1882 JSBool ok;
1884 JSObject *scriptObj = (JSObject*)aScriptObject;
1885 nsCOMPtr<nsIPrincipal> principal;
1887 rv = sSecurityManager->GetObjectPrincipal(mContext, scriptObj, getter_AddRefs(principal));
1888 NS_ENSURE_SUCCESS(rv, rv);
1890 rv = sSecurityManager->PushContextPrincipal(mContext, nsnull, principal);
1891 NS_ENSURE_SUCCESS(rv, rv);
1893 nsJSContext::TerminationFuncHolder holder(this);
1894 JSAutoRequest ar(mContext);
1895 ++mExecuteDepth;
1896 ok = ::JS_ExecuteScript(mContext,
1897 (JSObject *)aScopeObject,
1898 (JSScript*)::JS_GetPrivate(mContext, scriptObj),
1899 &val);
1901 if (ok) {
1902 // If all went well, convert val to a string (XXXbe unless undefined?).
1903 rv = JSValueToAString(mContext, val, aRetValue, aIsUndefined);
1904 } else {
1905 if (aIsUndefined) {
1906 *aIsUndefined = PR_TRUE;
1909 if (aRetValue) {
1910 aRetValue->Truncate();
1914 --mExecuteDepth;
1916 sSecurityManager->PopContextPrincipal(mContext);
1918 // Pop here, after JS_ValueToString and any other possible evaluation.
1919 if (NS_FAILED(stack->Pop(nsnull)))
1920 rv = NS_ERROR_FAILURE;
1922 // ScriptEvaluated needs to come after we pop the stack
1923 ScriptEvaluated(PR_TRUE);
1925 return rv;
1929 #ifdef DEBUG
1930 PRBool
1931 AtomIsEventHandlerName(nsIAtom *aName)
1933 const PRUnichar *name = aName->GetUTF16String();
1935 const PRUnichar *cp;
1936 PRUnichar c;
1937 for (cp = name; *cp != '\0'; ++cp)
1939 c = *cp;
1940 if ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z'))
1941 return PR_FALSE;
1944 return PR_TRUE;
1946 #endif
1948 // Helper function to find the JSObject associated with a (presumably DOM)
1949 // interface.
1950 nsresult
1951 nsJSContext::JSObjectFromInterface(nsISupports* aTarget, void *aScope, JSObject **aRet)
1953 // It is legal to specify a null target.
1954 if (!aTarget) {
1955 *aRet = nsnull;
1956 return NS_OK;
1959 // Get the jsobject associated with this target
1960 // We don't wrap here because we trust the JS engine to wrap the target
1961 // later.
1962 nsresult rv;
1963 jsval v;
1964 rv = nsContentUtils::WrapNative(mContext, (JSObject *)aScope, aTarget, &v);
1965 NS_ENSURE_SUCCESS(rv, rv);
1967 #ifdef NS_DEBUG
1968 nsCOMPtr<nsISupports> targetSupp = do_QueryInterface(aTarget);
1969 nsCOMPtr<nsISupports> native =
1970 nsContentUtils::XPConnect()->GetNativeOfWrapper(mContext,
1971 JSVAL_TO_OBJECT(v));
1972 NS_ASSERTION(native == targetSupp, "Native should be the target!");
1973 #endif
1975 *aRet = JSVAL_TO_OBJECT(v);
1977 return NS_OK;
1981 nsresult
1982 nsJSContext::CompileEventHandler(nsIAtom *aName,
1983 PRUint32 aArgCount,
1984 const char** aArgNames,
1985 const nsAString& aBody,
1986 const char *aURL, PRUint32 aLineNo,
1987 PRUint32 aVersion,
1988 nsScriptObjectHolder &aHandler)
1990 NS_TIME_FUNCTION_MIN_FMT(1.0, "%s (line %d) (url: %s, line: %d)", MOZ_FUNCTION_NAME,
1991 __LINE__, aURL, aLineNo);
1993 NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED);
1995 NS_PRECONDITION(AtomIsEventHandlerName(aName), "Bad event name");
1996 NS_PRECONDITION(!::JS_IsExceptionPending(mContext),
1997 "Why are we being called with a pending exception?");
1999 if (!sSecurityManager) {
2000 NS_ERROR("Huh, we need a script security manager to compile "
2001 "an event handler!");
2003 return NS_ERROR_UNEXPECTED;
2006 // Don't compile if aVersion is unknown. Since the caller is responsible for
2007 // parsing the version strings, we just check it isn't JSVERSION_UNKNOWN.
2008 if ((JSVersion)aVersion == JSVERSION_UNKNOWN) {
2009 return NS_ERROR_ILLEGAL_VALUE;
2012 #ifdef DEBUG
2013 JSContext* top = nsContentUtils::GetCurrentJSContext();
2014 NS_ASSERTION(mContext == top, "Context not properly pushed!");
2015 #endif
2017 // Event handlers are always shared, and must be bound before use.
2018 // Therefore we never bother compiling with principals.
2019 // (that probably means we should avoid JS_CompileUCFunctionForPrincipals!)
2020 JSAutoRequest ar(mContext);
2022 JSFunction* fun =
2023 ::JS_CompileUCFunctionForPrincipalsVersion(mContext,
2024 nsnull, nsnull,
2025 nsAtomCString(aName).get(), aArgCount, aArgNames,
2026 (jschar*)PromiseFlatString(aBody).get(),
2027 aBody.Length(),
2028 aURL, aLineNo, JSVersion(aVersion));
2030 if (!fun) {
2031 ReportPendingException();
2032 return NS_ERROR_ILLEGAL_VALUE;
2035 JSObject *handler = ::JS_GetFunctionObject(fun);
2036 NS_ASSERTION(aHandler.getScriptTypeID()==JAVASCRIPT,
2037 "Expecting JS script object holder");
2038 return aHandler.set((void *)handler);
2041 // XXX - note that CompileFunction doesn't yet play the nsScriptObjectHolder
2042 // game - caller must still ensure JS GC root.
2043 nsresult
2044 nsJSContext::CompileFunction(void* aTarget,
2045 const nsACString& aName,
2046 PRUint32 aArgCount,
2047 const char** aArgArray,
2048 const nsAString& aBody,
2049 const char* aURL,
2050 PRUint32 aLineNo,
2051 PRUint32 aVersion,
2052 PRBool aShared,
2053 void** aFunctionObject)
2055 NS_TIME_FUNCTION_FMT(1.0, "%s (line %d) (function: %s, url: %s, line: %d)", MOZ_FUNCTION_NAME,
2056 __LINE__, aName.BeginReading(), aURL, aLineNo);
2058 NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED);
2060 // Don't compile if aVersion is unknown. Since the caller is responsible for
2061 // parsing the version strings, we just check it isn't JSVERSION_UNKNOWN.
2062 if ((JSVersion)aVersion == JSVERSION_UNKNOWN) {
2063 return NS_ERROR_ILLEGAL_VALUE;
2066 JSPrincipals *jsprin = nsnull;
2068 nsIScriptGlobalObject *global = GetGlobalObject();
2069 if (global) {
2070 // XXXbe why the two-step QI? speed up via a new GetGlobalObjectData func?
2071 nsCOMPtr<nsIScriptObjectPrincipal> globalData = do_QueryInterface(global);
2072 if (globalData) {
2073 nsIPrincipal *prin = globalData->GetPrincipal();
2074 if (!prin)
2075 return NS_ERROR_FAILURE;
2076 prin->GetJSPrincipals(mContext, &jsprin);
2080 JSObject *target = (JSObject*)aTarget;
2082 JSAutoRequest ar(mContext);
2084 JSFunction* fun =
2085 ::JS_CompileUCFunctionForPrincipalsVersion(mContext,
2086 aShared ? nsnull : target, jsprin,
2087 PromiseFlatCString(aName).get(),
2088 aArgCount, aArgArray,
2089 (jschar*)PromiseFlatString(aBody).get(),
2090 aBody.Length(),
2091 aURL, aLineNo,
2092 JSVersion(aVersion));
2094 if (jsprin)
2095 JSPRINCIPALS_DROP(mContext, jsprin);
2096 if (!fun)
2097 return NS_ERROR_FAILURE;
2099 JSObject *handler = ::JS_GetFunctionObject(fun);
2100 if (aFunctionObject)
2101 *aFunctionObject = (void*) handler;
2102 return NS_OK;
2105 nsresult
2106 nsJSContext::CallEventHandler(nsISupports* aTarget, void *aScope, void *aHandler,
2107 nsIArray *aargv, nsIVariant **arv)
2109 NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED);
2111 if (!mScriptsEnabled) {
2112 return NS_OK;
2115 #ifdef NS_FUNCTION_TIMER
2117 JSObject *obj = static_cast<JSObject *>(aHandler);
2118 JSString *id = JS_GetFunctionId(static_cast<JSFunction *>(JS_GetPrivate(mContext, obj)));
2119 JSAutoByteString bytes;
2120 const char *name = !id ? "anonymous" : bytes.encode(mContext, id) ? bytes.ptr() : "<error>";
2121 NS_TIME_FUNCTION_FMT(1.0, "%s (line %d) (function: %s)", MOZ_FUNCTION_NAME, __LINE__, name);
2123 #endif
2125 JSAutoRequest ar(mContext);
2126 JSObject* target = nsnull;
2127 nsresult rv = JSObjectFromInterface(aTarget, aScope, &target);
2128 NS_ENSURE_SUCCESS(rv, rv);
2130 js::AutoObjectRooter targetVal(mContext, target);
2131 jsval rval = JSVAL_VOID;
2133 // This one's a lot easier than EvaluateString because we don't have to
2134 // hassle with principals: they're already compiled into the JS function.
2135 // xxxmarkh - this comment is no longer true - principals are not used at
2136 // all now, and never were in some cases.
2138 nsCOMPtr<nsIJSContextStack> stack =
2139 do_GetService("@mozilla.org/js/xpc/ContextStack;1", &rv);
2140 if (NS_FAILED(rv) || NS_FAILED(stack->Push(mContext)))
2141 return NS_ERROR_FAILURE;
2143 // check if the event handler can be run on the object in question
2144 rv = sSecurityManager->CheckFunctionAccess(mContext, aHandler, target);
2146 nsJSContext::TerminationFuncHolder holder(this);
2148 if (NS_SUCCEEDED(rv)) {
2149 // Convert args to jsvals.
2150 PRUint32 argc = 0;
2151 jsval *argv = nsnull;
2153 js::LazilyConstructed<nsAutoPoolRelease> poolRelease;
2154 js::LazilyConstructed<js::AutoArrayRooter> tvr;
2156 // Use |target| as the scope for wrapping the arguments, since aScope is
2157 // the safe scope in many cases, which isn't very useful. Wrapping aTarget
2158 // was OK because those typically have PreCreate methods that give them the
2159 // right scope anyway, and we want to make sure that the arguments end up
2160 // in the same scope as aTarget.
2161 rv = ConvertSupportsTojsvals(aargv, target, &argc,
2162 &argv, poolRelease, tvr);
2163 if (NS_FAILED(rv)) {
2164 stack->Pop(nsnull);
2165 return rv;
2168 jsval funval = OBJECT_TO_JSVAL(static_cast<JSObject *>(aHandler));
2169 JSAutoEnterCompartment ac;
2170 if (!ac.enter(mContext, target)) {
2171 stack->Pop(nsnull);
2172 return NS_ERROR_FAILURE;
2175 ++mExecuteDepth;
2176 PRBool ok = ::JS_CallFunctionValue(mContext, target,
2177 funval, argc, argv, &rval);
2178 --mExecuteDepth;
2180 if (!ok) {
2181 // Tell XPConnect about any pending exceptions. This is needed
2182 // to avoid dropping JS exceptions in case we got here through
2183 // nested calls through XPConnect.
2185 ReportPendingException();
2187 // Don't pass back results from failed calls.
2188 rval = JSVAL_VOID;
2190 // Tell the caller that the handler threw an error.
2191 rv = NS_ERROR_FAILURE;
2195 if (NS_FAILED(stack->Pop(nsnull)))
2196 return NS_ERROR_FAILURE;
2198 // Convert to variant before calling ScriptEvaluated, as it may GC, meaning
2199 // we would need to root rval.
2200 if (NS_SUCCEEDED(rv)) {
2201 if (rval == JSVAL_NULL)
2202 *arv = nsnull;
2203 else
2204 rv = nsContentUtils::XPConnect()->JSToVariant(mContext, rval, arv);
2207 // ScriptEvaluated needs to come after we pop the stack
2208 ScriptEvaluated(PR_TRUE);
2210 return rv;
2213 nsresult
2214 nsJSContext::BindCompiledEventHandler(nsISupports* aTarget, void *aScope,
2215 nsIAtom *aName,
2216 void *aHandler)
2218 NS_ENSURE_ARG(aHandler);
2219 NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED);
2221 NS_PRECONDITION(AtomIsEventHandlerName(aName), "Bad event name");
2222 nsresult rv;
2224 // Get the jsobject associated with this target
2225 JSObject *target = nsnull;
2226 nsAutoGCRoot root(&target, &rv);
2227 NS_ENSURE_SUCCESS(rv, rv);
2228 rv = JSObjectFromInterface(aTarget, aScope, &target);
2229 NS_ENSURE_SUCCESS(rv, rv);
2231 JSObject *funobj = (JSObject*) aHandler;
2232 JSAutoRequest ar(mContext);
2234 #ifdef DEBUG
2236 JSAutoEnterCompartment ac;
2237 if (!ac.enter(mContext, funobj)) {
2238 return NS_ERROR_FAILURE;
2241 NS_ASSERTION(JS_TypeOfValue(mContext,
2242 OBJECT_TO_JSVAL(funobj)) == JSTYPE_FUNCTION,
2243 "Event handler object not a function");
2245 #endif
2247 JSAutoEnterCompartment ac;
2248 if (!ac.enter(mContext, target)) {
2249 return NS_ERROR_FAILURE;
2252 // Push our JSContext on our thread's context stack, in case native code
2253 // called from JS calls back into JS via XPConnect.
2254 nsCOMPtr<nsIJSContextStack> stack =
2255 do_GetService("@mozilla.org/js/xpc/ContextStack;1", &rv);
2256 if (NS_FAILED(rv) || NS_FAILED(stack->Push(mContext))) {
2257 return NS_ERROR_FAILURE;
2260 // Make sure the handler function is parented by its event target object
2261 if (funobj) { // && ::JS_GetParent(mContext, funobj) != target) {
2262 funobj = ::JS_CloneFunctionObject(mContext, funobj, target);
2263 if (!funobj)
2264 rv = NS_ERROR_OUT_OF_MEMORY;
2267 if (NS_SUCCEEDED(rv) &&
2268 // Make sure the flags here match those in nsEventReceiverSH::NewResolve
2269 !::JS_DefineProperty(mContext, target, nsAtomCString(aName).get(),
2270 OBJECT_TO_JSVAL(funobj), nsnull, nsnull,
2271 JSPROP_ENUMERATE | JSPROP_PERMANENT)) {
2272 ReportPendingException();
2273 rv = NS_ERROR_FAILURE;
2276 // XXXmarkh - ideally we should assert that the wrapped native is now
2277 // "long lived" - how to do that?
2279 if (NS_FAILED(stack->Pop(nsnull)) && NS_SUCCEEDED(rv)) {
2280 rv = NS_ERROR_FAILURE;
2283 return rv;
2286 nsresult
2287 nsJSContext::GetBoundEventHandler(nsISupports* aTarget, void *aScope,
2288 nsIAtom* aName,
2289 nsScriptObjectHolder &aHandler)
2291 NS_PRECONDITION(AtomIsEventHandlerName(aName), "Bad event name");
2293 nsresult rv;
2294 JSObject *obj = nsnull;
2295 nsAutoGCRoot root(&obj, &rv);
2296 NS_ENSURE_SUCCESS(rv, rv);
2297 JSAutoRequest ar(mContext);
2298 rv = JSObjectFromInterface(aTarget, aScope, &obj);
2299 NS_ENSURE_SUCCESS(rv, rv);
2301 JSAutoEnterCompartment ac;
2302 if (!ac.enter(mContext, obj)) {
2303 return NS_ERROR_FAILURE;
2306 jsval funval;
2307 if (!JS_LookupProperty(mContext, obj,
2308 nsAtomCString(aName).get(), &funval))
2309 return NS_ERROR_FAILURE;
2311 if (JS_TypeOfValue(mContext, funval) != JSTYPE_FUNCTION) {
2312 NS_WARNING("Event handler object not a function");
2313 aHandler.drop();
2314 return NS_OK;
2316 NS_ASSERTION(aHandler.getScriptTypeID()==JAVASCRIPT,
2317 "Expecting JS script object holder");
2318 return aHandler.set(JSVAL_TO_OBJECT(funval));
2321 // serialization
2322 nsresult
2323 nsJSContext::Serialize(nsIObjectOutputStream* aStream, void *aScriptObject)
2325 JSObject *mJSObject = (JSObject *)aScriptObject;
2326 if (!mJSObject)
2327 return NS_ERROR_FAILURE;
2329 nsresult rv;
2331 JSContext* cx = mContext;
2332 JSXDRState *xdr = ::JS_XDRNewMem(cx, JSXDR_ENCODE);
2333 if (! xdr)
2334 return NS_ERROR_OUT_OF_MEMORY;
2335 xdr->userdata = (void*) aStream;
2337 JSAutoRequest ar(cx);
2338 JSScript *script = reinterpret_cast<JSScript*>
2339 (::JS_GetPrivate(cx, mJSObject));
2340 if (! ::JS_XDRScript(xdr, &script)) {
2341 rv = NS_ERROR_FAILURE; // likely to be a principals serialization error
2342 } else {
2343 // Get the encoded JSXDRState data and write it. The JSXDRState owns
2344 // this buffer memory and will free it beneath ::JS_XDRDestroy.
2346 // If an XPCOM object needs to be written in the midst of the JS XDR
2347 // encoding process, the C++ code called back from the JS engine (e.g.,
2348 // nsEncodeJSPrincipals in caps/src/nsJSPrincipals.cpp) will flush data
2349 // from the JSXDRState to aStream, then write the object, then return
2350 // to JS XDR code with xdr reset so new JS data is encoded at the front
2351 // of the xdr's data buffer.
2353 // However many XPCOM objects are interleaved with JS XDR data in the
2354 // stream, when control returns here from ::JS_XDRScript, we'll have
2355 // one last buffer of data to write to aStream.
2357 uint32 size;
2358 const char* data = reinterpret_cast<const char*>
2359 (::JS_XDRMemGetData(xdr, &size));
2360 NS_ASSERTION(data, "no decoded JSXDRState data!");
2362 rv = aStream->Write32(size);
2363 if (NS_SUCCEEDED(rv))
2364 rv = aStream->WriteBytes(data, size);
2367 ::JS_XDRDestroy(xdr);
2368 if (NS_FAILED(rv)) return rv;
2370 return rv;
2373 nsresult
2374 nsJSContext::Deserialize(nsIObjectInputStream* aStream,
2375 nsScriptObjectHolder &aResult)
2377 JSObject *result = nsnull;
2378 nsresult rv;
2380 NS_TIME_FUNCTION_MIN(1.0);
2382 NS_TIMELINE_MARK_FUNCTION("js script deserialize");
2384 PRUint32 size;
2385 rv = aStream->Read32(&size);
2386 if (NS_FAILED(rv)) return rv;
2388 char* data;
2389 rv = aStream->ReadBytes(size, &data);
2390 if (NS_FAILED(rv)) return rv;
2392 JSContext* cx = mContext;
2394 JSXDRState *xdr = ::JS_XDRNewMem(cx, JSXDR_DECODE);
2395 if (! xdr) {
2396 rv = NS_ERROR_OUT_OF_MEMORY;
2397 } else {
2398 xdr->userdata = (void*) aStream;
2399 JSAutoRequest ar(cx);
2400 ::JS_XDRMemSetData(xdr, data, size);
2402 JSScript *script = nsnull;
2403 if (! ::JS_XDRScript(xdr, &script)) {
2404 rv = NS_ERROR_FAILURE; // principals deserialization error?
2405 } else {
2406 result = ::JS_NewScriptObject(cx, script);
2407 if (! result) {
2408 rv = NS_ERROR_OUT_OF_MEMORY; // certain error
2409 ::JS_DestroyScript(cx, script);
2413 // Update data in case ::JS_XDRScript called back into C++ code to
2414 // read an XPCOM object.
2416 // In that case, the serialization process must have flushed a run
2417 // of counted bytes containing JS data at the point where the XPCOM
2418 // object starts, after which an encoding C++ callback from the JS
2419 // XDR code must have written the XPCOM object directly into the
2420 // nsIObjectOutputStream.
2422 // The deserialization process will XDR-decode counted bytes up to
2423 // but not including the XPCOM object, then call back into C++ to
2424 // read the object, then read more counted bytes and hand them off
2425 // to the JSXDRState, so more JS data can be decoded.
2427 // This interleaving of JS XDR data and XPCOM object data may occur
2428 // several times beneath the call to ::JS_XDRScript, above. At the
2429 // end of the day, we need to free (via nsMemory) the data owned by
2430 // the JSXDRState. So we steal it back, nulling xdr's buffer so it
2431 // doesn't get passed to ::JS_free by ::JS_XDRDestroy.
2433 uint32 junk;
2434 data = (char*) ::JS_XDRMemGetData(xdr, &junk);
2435 if (data)
2436 ::JS_XDRMemSetData(xdr, NULL, 0);
2437 ::JS_XDRDestroy(xdr);
2440 // If data is null now, it must have been freed while deserializing an
2441 // XPCOM object (e.g., a principal) beneath ::JS_XDRScript.
2442 if (data)
2443 nsMemory::Free(data);
2444 NS_ASSERTION(aResult.getScriptTypeID()==JAVASCRIPT,
2445 "Expecting JS script object holder");
2447 // Now that we've cleaned up, handle the case when rv is a failure
2448 // code, which could happen for all sorts of reasons above.
2449 NS_ENSURE_SUCCESS(rv, rv);
2451 return aResult.set(result);
2454 void
2455 nsJSContext::SetDefaultLanguageVersion(PRUint32 aVersion)
2457 ::JS_SetVersion(mContext, (JSVersion)aVersion);
2460 nsIScriptGlobalObject *
2461 nsJSContext::GetGlobalObject()
2463 JSObject *global = ::JS_GetGlobalObject(mContext);
2465 if (!global) {
2466 return nsnull;
2469 OBJ_TO_INNER_OBJECT(mContext, global);
2470 if (!global) {
2471 return nsnull;
2474 JSClass *c = JS_GET_CLASS(mContext, global);
2476 if (!c || ((~c->flags) & (JSCLASS_HAS_PRIVATE |
2477 JSCLASS_PRIVATE_IS_NSISUPPORTS))) {
2478 return nsnull;
2481 JSAutoEnterCompartment ac;
2483 // NB: This AutoCrossCompartmentCall is only here to silence a warning. If
2484 // it fails, nothing bad will happen.
2485 ac.enterAndIgnoreErrors(mContext, global);
2487 nsCOMPtr<nsIScriptGlobalObject> sgo;
2488 nsISupports *priv =
2489 (nsISupports *)::JS_GetPrivate(mContext, global);
2491 nsCOMPtr<nsIXPConnectWrappedNative> wrapped_native =
2492 do_QueryInterface(priv);
2494 if (wrapped_native) {
2495 // The global object is a XPConnect wrapped native, the native in
2496 // the wrapper might be the nsIScriptGlobalObject
2498 sgo = do_QueryWrappedNative(wrapped_native);
2499 } else {
2500 sgo = do_QueryInterface(priv);
2503 // This'll return a pointer to something we're about to release, but
2504 // that's ok, the JS object will hold it alive long enough.
2505 nsCOMPtr<nsPIDOMWindow> pwin(do_QueryInterface(sgo));
2506 if (!pwin)
2507 return sgo;
2509 return static_cast<nsGlobalWindow *>(pwin->GetOuterWindow());
2512 void *
2513 nsJSContext::GetNativeGlobal()
2515 return ::JS_GetGlobalObject(mContext);
2518 nsresult
2519 nsJSContext::CreateNativeGlobalForInner(
2520 nsIScriptGlobalObject *aNewInner,
2521 PRBool aIsChrome,
2522 nsIPrincipal *aPrincipal,
2523 void **aNativeGlobal, nsISupports **aHolder)
2525 nsIXPConnect *xpc = nsContentUtils::XPConnect();
2526 PRUint32 flags = aIsChrome? nsIXPConnect::FLAG_SYSTEM_GLOBAL_OBJECT : 0;
2527 nsCOMPtr<nsIXPConnectJSObjectHolder> jsholder;
2529 nsCOMPtr<nsIPrincipal> systemPrincipal;
2530 if (aIsChrome) {
2531 nsIScriptSecurityManager *ssm = nsContentUtils::GetSecurityManager();
2532 ssm->GetSystemPrincipal(getter_AddRefs(systemPrincipal));
2535 nsresult rv = xpc->
2536 InitClassesWithNewWrappedGlobal(mContext,
2537 aNewInner, NS_GET_IID(nsISupports),
2538 aIsChrome ? systemPrincipal.get() : aPrincipal,
2539 nsnull, flags,
2540 getter_AddRefs(jsholder));
2541 if (NS_FAILED(rv))
2542 return rv;
2543 jsholder->GetJSObject(reinterpret_cast<JSObject **>(aNativeGlobal));
2544 *aHolder = jsholder.get();
2545 NS_ADDREF(*aHolder);
2546 return NS_OK;
2549 nsresult
2550 nsJSContext::ConnectToInner(nsIScriptGlobalObject *aNewInner, void *aOuterGlobal)
2552 NS_ENSURE_ARG(aNewInner);
2553 JSObject *newInnerJSObject = (JSObject *)aNewInner->GetScriptGlobal(JAVASCRIPT);
2554 JSObject *outerGlobal = (JSObject *)aOuterGlobal;
2556 // Make the inner and outer window both share the same
2557 // prototype. The prototype we share is the outer window's
2558 // prototype, this way XPConnect can still find the wrapper to
2559 // use when making a call like alert() (w/o qualifying it with
2560 // "window."). XPConnect looks up the wrapper based on the
2561 // function object's parent, which is the object the function
2562 // was called on, and when calling alert() we'll be calling the
2563 // alert() function from the outer window's prototype off of the
2564 // inner window. In this case XPConnect is able to find the
2565 // outer (through the JSExtendedClass hook outerObject), so this
2566 // prototype sharing works.
2568 // Now that we're connecting the outer global to the inner one,
2569 // we must have transplanted it. The JS engine tries to maintain
2570 // the global object's compartment as its default compartment,
2571 // so update that now since it might have changed.
2572 JS_SetGlobalObject(mContext, outerGlobal);
2574 // We do *not* want to use anything else out of the outer
2575 // object's prototype chain than the first prototype, which is
2576 // the XPConnect prototype. The rest we want from the inner
2577 // window's prototype, i.e. the global scope polluter and
2578 // Object.prototype. This way the outer also gets the benefits
2579 // of the global scope polluter, and the inner window's
2580 // Object.prototype.
2581 JSObject *proto = JS_GetPrototype(mContext, outerGlobal);
2582 JSObject *innerProto = JS_GetPrototype(mContext, newInnerJSObject);
2583 JSObject *innerProtoProto = JS_GetPrototype(mContext, innerProto);
2585 JS_SetPrototype(mContext, newInnerJSObject, proto);
2586 JS_SetPrototype(mContext, proto, innerProtoProto);
2588 return NS_OK;
2591 void *
2592 nsJSContext::GetNativeContext()
2594 return mContext;
2597 nsresult
2598 nsJSContext::InitContext()
2600 // Make sure callers of this use
2601 // WillInitializeContext/DidInitializeContext around this call.
2602 NS_ENSURE_TRUE(!mIsInitialized, NS_ERROR_ALREADY_INITIALIZED);
2604 if (!mContext)
2605 return NS_ERROR_OUT_OF_MEMORY;
2607 ::JS_SetErrorReporter(mContext, NS_ScriptErrorReporter);
2609 return NS_OK;
2612 nsresult
2613 nsJSContext::CreateOuterObject(nsIScriptGlobalObject *aGlobalObject,
2614 nsIScriptGlobalObject *aCurrentInner)
2616 mGlobalObjectRef = aGlobalObject;
2618 nsCOMPtr<nsIDOMChromeWindow> chromeWindow(do_QueryInterface(aGlobalObject));
2619 PRUint32 flags = 0;
2621 if (chromeWindow) {
2622 // Flag this window's global object and objects under it as "system",
2623 // for optional automated XPCNativeWrapper construction when chrome JS
2624 // views a content DOM.
2625 flags = nsIXPConnect::FLAG_SYSTEM_GLOBAL_OBJECT;
2627 // Always enable E4X for XUL and other chrome content -- there is no
2628 // need to preserve the <!-- script hiding hack from JS-in-HTML daze
2629 // (introduced in 1995 for graceful script degradation in Netscape 1,
2630 // Mosaic, and other pre-JS browsers).
2631 JS_SetOptions(mContext, JS_GetOptions(mContext) | JSOPTION_XML);
2634 nsIXPConnect *xpc = nsContentUtils::XPConnect();
2635 nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
2637 nsresult rv = xpc->WrapNative(mContext, aCurrentInner->GetGlobalJSObject(),
2638 aCurrentInner, NS_GET_IID(nsISupports),
2639 getter_AddRefs(holder));
2640 NS_ENSURE_SUCCESS(rv, rv);
2642 nsCOMPtr<nsIXPConnectWrappedNative> wrapper(do_QueryInterface(holder));
2643 NS_ABORT_IF_FALSE(wrapper, "bad wrapper");
2645 wrapper->RefreshPrototype();
2647 JSObject *outer =
2648 NS_NewOuterWindowProxy(mContext, aCurrentInner->GetGlobalJSObject());
2649 if (!outer) {
2650 return NS_ERROR_FAILURE;
2653 return SetOuterObject(outer);
2656 nsresult
2657 nsJSContext::SetOuterObject(void *aOuterObject)
2659 JSObject *outer = static_cast<JSObject *>(aOuterObject);
2661 // Force our context's global object to be the outer.
2662 JS_SetGlobalObject(mContext, outer);
2663 return NS_OK;
2666 nsresult
2667 nsJSContext::InitOuterWindow()
2669 JSObject *global = JS_GetGlobalObject(mContext);
2670 OBJ_TO_INNER_OBJECT(mContext, global);
2672 nsresult rv = InitClasses(global); // this will complete global object initialization
2673 NS_ENSURE_SUCCESS(rv, rv);
2675 return NS_OK;
2678 nsresult
2679 nsJSContext::InitializeExternalClasses()
2681 nsScriptNameSpaceManager *nameSpaceManager = nsJSRuntime::GetNameSpaceManager();
2682 NS_ENSURE_TRUE(nameSpaceManager, NS_ERROR_NOT_INITIALIZED);
2684 return nameSpaceManager->InitForContext(this);
2687 nsresult
2688 nsJSContext::SetProperty(void *aTarget, const char *aPropName, nsISupports *aArgs)
2690 PRUint32 argc;
2691 jsval *argv = nsnull;
2693 JSAutoRequest ar(mContext);
2695 js::LazilyConstructed<nsAutoPoolRelease> poolRelease;
2696 js::LazilyConstructed<js::AutoArrayRooter> tvr;
2698 nsresult rv;
2699 rv = ConvertSupportsTojsvals(aArgs, GetNativeGlobal(), &argc,
2700 &argv, poolRelease, tvr);
2701 NS_ENSURE_SUCCESS(rv, rv);
2703 jsval vargs;
2705 // got the arguments, now attach them.
2707 // window.dialogArguments is supposed to be an array if a JS array
2708 // was passed to showModalDialog(), deal with that here.
2709 if (strcmp(aPropName, "dialogArguments") == 0 && argc <= 1) {
2710 vargs = argc ? argv[0] : JSVAL_VOID;
2711 } else {
2712 for (PRUint32 i = 0; i < argc; ++i) {
2713 if (!JS_WrapValue(mContext, &argv[i])) {
2714 return NS_ERROR_FAILURE;
2718 JSObject *args = ::JS_NewArrayObject(mContext, argc, argv);
2719 vargs = OBJECT_TO_JSVAL(args);
2722 // Make sure to use JS_DefineProperty here so that we can override
2723 // readonly XPConnect properties here as well (read dialogArguments).
2724 rv = ::JS_DefineProperty(mContext, reinterpret_cast<JSObject *>(aTarget),
2725 aPropName, vargs, nsnull, nsnull, 0) ?
2726 NS_OK : NS_ERROR_FAILURE;
2728 return rv;
2731 nsresult
2732 nsJSContext::ConvertSupportsTojsvals(nsISupports *aArgs,
2733 void *aScope,
2734 PRUint32 *aArgc,
2735 jsval **aArgv,
2736 js::LazilyConstructed<nsAutoPoolRelease> &aPoolRelease,
2737 js::LazilyConstructed<js::AutoArrayRooter> &aRooter)
2739 nsresult rv = NS_OK;
2741 // If the array implements nsIJSArgArray, just grab the values directly.
2742 nsCOMPtr<nsIJSArgArray> fastArray = do_QueryInterface(aArgs);
2743 if (fastArray != nsnull)
2744 return fastArray->GetArgs(aArgc, reinterpret_cast<void **>(aArgv));
2746 // Take the slower path converting each item.
2747 // Handle only nsIArray and nsIVariant. nsIArray is only needed for
2748 // SetProperty('arguments', ...);
2750 *aArgv = nsnull;
2751 *aArgc = 0;
2753 nsIXPConnect *xpc = nsContentUtils::XPConnect();
2754 NS_ENSURE_TRUE(xpc, NS_ERROR_UNEXPECTED);
2756 if (!aArgs)
2757 return NS_OK;
2758 PRUint32 argCtr, argCount;
2759 // This general purpose function may need to convert an arg array
2760 // (window.arguments, event-handler args) and a generic property.
2761 nsCOMPtr<nsIArray> argsArray(do_QueryInterface(aArgs));
2763 if (argsArray) {
2764 rv = argsArray->GetLength(&argCount);
2765 NS_ENSURE_SUCCESS(rv, rv);
2766 if (argCount == 0)
2767 return NS_OK;
2768 } else {
2769 argCount = 1; // the nsISupports which is not an array
2772 void *mark = JS_ARENA_MARK(&mContext->tempPool);
2773 jsval *argv;
2774 size_t nbytes = argCount * sizeof(jsval);
2775 JS_ARENA_ALLOCATE_CAST(argv, jsval *, &mContext->tempPool, nbytes);
2776 NS_ENSURE_TRUE(argv, NS_ERROR_OUT_OF_MEMORY);
2777 memset(argv, 0, nbytes); /* initialize so GC-able */
2779 // Use the caller's auto guards to release and unroot.
2780 aPoolRelease.construct(&mContext->tempPool, mark);
2781 aRooter.construct(mContext, argCount, argv);
2783 if (argsArray) {
2784 for (argCtr = 0; argCtr < argCount && NS_SUCCEEDED(rv); argCtr++) {
2785 nsCOMPtr<nsISupports> arg;
2786 jsval *thisval = argv + argCtr;
2787 argsArray->QueryElementAt(argCtr, NS_GET_IID(nsISupports),
2788 getter_AddRefs(arg));
2789 if (!arg) {
2790 *thisval = JSVAL_NULL;
2791 continue;
2793 nsCOMPtr<nsIVariant> variant(do_QueryInterface(arg));
2794 if (variant != nsnull) {
2795 rv = xpc->VariantToJS(mContext, (JSObject *)aScope, variant,
2796 thisval);
2797 } else {
2798 // And finally, support the nsISupportsPrimitives supplied
2799 // by the AppShell. It generally will pass only strings, but
2800 // as we have code for handling all, we may as well use it.
2801 rv = AddSupportsPrimitiveTojsvals(arg, thisval);
2802 if (rv == NS_ERROR_NO_INTERFACE) {
2803 // something else - probably an event object or similar -
2804 // just wrap it.
2805 #ifdef NS_DEBUG
2806 // but first, check its not another nsISupportsPrimitive, as
2807 // these are now deprecated for use with script contexts.
2808 nsCOMPtr<nsISupportsPrimitive> prim(do_QueryInterface(arg));
2809 NS_ASSERTION(prim == nsnull,
2810 "Don't pass nsISupportsPrimitives - use nsIVariant!");
2811 #endif
2812 nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
2813 jsval v;
2814 rv = nsContentUtils::WrapNative(mContext, (JSObject *)aScope, arg,
2815 &v, getter_AddRefs(wrapper));
2816 if (NS_SUCCEEDED(rv)) {
2817 *thisval = v;
2822 } else {
2823 nsCOMPtr<nsIVariant> variant(do_QueryInterface(aArgs));
2824 if (variant)
2825 rv = xpc->VariantToJS(mContext, (JSObject *)aScope, variant, argv);
2826 else {
2827 NS_ERROR("Not an array, not an interface?");
2828 rv = NS_ERROR_UNEXPECTED;
2831 if (NS_FAILED(rv))
2832 return rv;
2833 *aArgv = argv;
2834 *aArgc = argCount;
2835 return NS_OK;
2838 // This really should go into xpconnect somewhere...
2839 nsresult
2840 nsJSContext::AddSupportsPrimitiveTojsvals(nsISupports *aArg, jsval *aArgv)
2842 NS_PRECONDITION(aArg, "Empty arg");
2844 nsCOMPtr<nsISupportsPrimitive> argPrimitive(do_QueryInterface(aArg));
2845 if (!argPrimitive)
2846 return NS_ERROR_NO_INTERFACE;
2848 JSContext *cx = mContext;
2849 PRUint16 type;
2850 argPrimitive->GetType(&type);
2852 switch(type) {
2853 case nsISupportsPrimitive::TYPE_CSTRING : {
2854 nsCOMPtr<nsISupportsCString> p(do_QueryInterface(argPrimitive));
2855 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
2857 nsCAutoString data;
2859 p->GetData(data);
2862 JSString *str = ::JS_NewStringCopyN(cx, data.get(), data.Length());
2863 NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY);
2865 *aArgv = STRING_TO_JSVAL(str);
2867 break;
2869 case nsISupportsPrimitive::TYPE_STRING : {
2870 nsCOMPtr<nsISupportsString> p(do_QueryInterface(argPrimitive));
2871 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
2873 nsAutoString data;
2875 p->GetData(data);
2877 // cast is probably safe since wchar_t and jschar are expected
2878 // to be equivalent; both unsigned 16-bit entities
2879 JSString *str =
2880 ::JS_NewUCStringCopyN(cx,
2881 reinterpret_cast<const jschar *>(data.get()),
2882 data.Length());
2883 NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY);
2885 *aArgv = STRING_TO_JSVAL(str);
2886 break;
2888 case nsISupportsPrimitive::TYPE_PRBOOL : {
2889 nsCOMPtr<nsISupportsPRBool> p(do_QueryInterface(argPrimitive));
2890 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
2892 PRBool data;
2894 p->GetData(&data);
2896 *aArgv = BOOLEAN_TO_JSVAL(data);
2898 break;
2900 case nsISupportsPrimitive::TYPE_PRUINT8 : {
2901 nsCOMPtr<nsISupportsPRUint8> p(do_QueryInterface(argPrimitive));
2902 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
2904 PRUint8 data;
2906 p->GetData(&data);
2908 *aArgv = INT_TO_JSVAL(data);
2910 break;
2912 case nsISupportsPrimitive::TYPE_PRUINT16 : {
2913 nsCOMPtr<nsISupportsPRUint16> p(do_QueryInterface(argPrimitive));
2914 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
2916 PRUint16 data;
2918 p->GetData(&data);
2920 *aArgv = INT_TO_JSVAL(data);
2922 break;
2924 case nsISupportsPrimitive::TYPE_PRUINT32 : {
2925 nsCOMPtr<nsISupportsPRUint32> p(do_QueryInterface(argPrimitive));
2926 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
2928 PRUint32 data;
2930 p->GetData(&data);
2932 *aArgv = INT_TO_JSVAL(data);
2934 break;
2936 case nsISupportsPrimitive::TYPE_CHAR : {
2937 nsCOMPtr<nsISupportsChar> p(do_QueryInterface(argPrimitive));
2938 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
2940 char data;
2942 p->GetData(&data);
2944 JSString *str = ::JS_NewStringCopyN(cx, &data, 1);
2945 NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY);
2947 *aArgv = STRING_TO_JSVAL(str);
2949 break;
2951 case nsISupportsPrimitive::TYPE_PRINT16 : {
2952 nsCOMPtr<nsISupportsPRInt16> p(do_QueryInterface(argPrimitive));
2953 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
2955 PRInt16 data;
2957 p->GetData(&data);
2959 *aArgv = INT_TO_JSVAL(data);
2961 break;
2963 case nsISupportsPrimitive::TYPE_PRINT32 : {
2964 nsCOMPtr<nsISupportsPRInt32> p(do_QueryInterface(argPrimitive));
2965 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
2967 PRInt32 data;
2969 p->GetData(&data);
2971 *aArgv = INT_TO_JSVAL(data);
2973 break;
2975 case nsISupportsPrimitive::TYPE_FLOAT : {
2976 nsCOMPtr<nsISupportsFloat> p(do_QueryInterface(argPrimitive));
2977 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
2979 float data;
2981 p->GetData(&data);
2983 JSBool ok = ::JS_NewNumberValue(cx, data, aArgv);
2984 NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
2986 break;
2988 case nsISupportsPrimitive::TYPE_DOUBLE : {
2989 nsCOMPtr<nsISupportsDouble> p(do_QueryInterface(argPrimitive));
2990 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
2992 double data;
2994 p->GetData(&data);
2996 JSBool ok = ::JS_NewNumberValue(cx, data, aArgv);
2997 NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
2999 break;
3001 case nsISupportsPrimitive::TYPE_INTERFACE_POINTER : {
3002 nsCOMPtr<nsISupportsInterfacePointer> p(do_QueryInterface(argPrimitive));
3003 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
3005 nsCOMPtr<nsISupports> data;
3006 nsIID *iid = nsnull;
3008 p->GetData(getter_AddRefs(data));
3009 p->GetDataIID(&iid);
3010 NS_ENSURE_TRUE(iid, NS_ERROR_UNEXPECTED);
3012 AutoFree iidGuard(iid); // Free iid upon destruction.
3014 nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
3015 jsval v;
3016 nsresult rv = nsContentUtils::WrapNative(cx, ::JS_GetGlobalObject(cx),
3017 data, iid, &v,
3018 getter_AddRefs(wrapper));
3019 NS_ENSURE_SUCCESS(rv, rv);
3021 *aArgv = v;
3023 break;
3025 case nsISupportsPrimitive::TYPE_ID :
3026 case nsISupportsPrimitive::TYPE_PRUINT64 :
3027 case nsISupportsPrimitive::TYPE_PRINT64 :
3028 case nsISupportsPrimitive::TYPE_PRTIME :
3029 case nsISupportsPrimitive::TYPE_VOID : {
3030 NS_WARNING("Unsupported primitive type used");
3031 *aArgv = JSVAL_NULL;
3032 break;
3034 default : {
3035 NS_WARNING("Unknown primitive type used");
3036 *aArgv = JSVAL_NULL;
3037 break;
3040 return NS_OK;
3043 static JSPropertySpec OptionsProperties[] = {
3044 {"strict", (int8)JSOPTION_STRICT, JSPROP_ENUMERATE | JSPROP_PERMANENT},
3045 {"werror", (int8)JSOPTION_WERROR, JSPROP_ENUMERATE | JSPROP_PERMANENT},
3046 {"relimit", (int8)JSOPTION_RELIMIT, JSPROP_ENUMERATE | JSPROP_PERMANENT},
3050 static JSBool
3051 GetOptionsProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
3053 if (JSID_IS_INT(id)) {
3054 uint32 optbit = (uint32) JSID_TO_INT(id);
3055 if (((optbit & (optbit - 1)) == 0 && optbit <= JSOPTION_WERROR) ||
3056 optbit == JSOPTION_RELIMIT)
3057 *vp = (JS_GetOptions(cx) & optbit) ? JSVAL_TRUE : JSVAL_FALSE;
3059 return JS_TRUE;
3062 static JSBool
3063 SetOptionsProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
3065 if (JSID_IS_INT(id)) {
3066 uint32 optbit = (uint32) JSID_TO_INT(id);
3068 // Don't let options other than strict, werror, or relimit be set -- it
3069 // would be bad if web page script could clear
3070 // JSOPTION_PRIVATE_IS_NSISUPPORTS!
3071 if (((optbit & (optbit - 1)) == 0 && optbit <= JSOPTION_WERROR) ||
3072 optbit == JSOPTION_RELIMIT) {
3073 JSBool optval;
3074 JS_ValueToBoolean(cx, *vp, &optval);
3076 uint32 optset = ::JS_GetOptions(cx);
3077 if (optval)
3078 optset |= optbit;
3079 else
3080 optset &= ~optbit;
3081 ::JS_SetOptions(cx, optset);
3084 return JS_TRUE;
3087 static JSClass OptionsClass = {
3088 "JSOptions",
3090 JS_PropertyStub, JS_PropertyStub, GetOptionsProperty, SetOptionsProperty,
3091 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, nsnull
3094 #ifdef NS_TRACE_MALLOC
3096 #include <errno.h> // XXX assume Linux if NS_TRACE_MALLOC
3097 #include <fcntl.h>
3098 #ifdef XP_UNIX
3099 #include <unistd.h>
3100 #endif
3101 #ifdef XP_WIN32
3102 #include <io.h>
3103 #endif
3104 #include "nsTraceMalloc.h"
3106 static JSBool
3107 CheckUniversalXPConnectForTraceMalloc(JSContext *cx)
3109 PRBool hasCap = PR_FALSE;
3110 nsresult rv = nsContentUtils::GetSecurityManager()->
3111 IsCapabilityEnabled("UniversalXPConnect", &hasCap);
3112 if (NS_SUCCEEDED(rv) && hasCap)
3113 return JS_TRUE;
3114 JS_ReportError(cx, "trace-malloc functions require UniversalXPConnect");
3115 return JS_FALSE;
3118 static JSBool
3119 TraceMallocDisable(JSContext *cx, uintN argc, jsval *vp)
3121 if (!CheckUniversalXPConnectForTraceMalloc(cx))
3122 return JS_FALSE;
3124 NS_TraceMallocDisable();
3125 JS_SET_RVAL(cx, vp, JSVAL_VOID);
3126 return JS_TRUE;
3129 static JSBool
3130 TraceMallocEnable(JSContext *cx, uintN argc, jsval *vp)
3132 if (!CheckUniversalXPConnectForTraceMalloc(cx))
3133 return JS_FALSE;
3135 NS_TraceMallocEnable();
3136 JS_SET_RVAL(cx, vp, JSVAL_VOID);
3137 return JS_TRUE;
3140 static JSBool
3141 TraceMallocOpenLogFile(JSContext *cx, uintN argc, jsval *vp)
3143 int fd;
3144 JSString *str;
3146 if (!CheckUniversalXPConnectForTraceMalloc(cx))
3147 return JS_FALSE;
3149 if (argc == 0) {
3150 fd = -1;
3151 } else {
3152 str = JS_ValueToString(cx, JS_ARGV(cx, vp)[0]);
3153 if (!str)
3154 return JS_FALSE;
3155 JSAutoByteString filename(cx, str);
3156 if (!filename)
3157 return JS_FALSE;
3158 fd = open(filename.ptr(), O_CREAT | O_WRONLY | O_TRUNC, 0644);
3159 if (fd < 0) {
3160 JS_ReportError(cx, "can't open %s: %s", filename.ptr(), strerror(errno));
3161 return JS_FALSE;
3164 JS_SET_RVAL(cx, vp, INT_TO_JSVAL(fd));
3165 return JS_TRUE;
3168 static JSBool
3169 TraceMallocChangeLogFD(JSContext *cx, uintN argc, jsval *vp)
3171 int32 fd, oldfd;
3173 if (!CheckUniversalXPConnectForTraceMalloc(cx))
3174 return JS_FALSE;
3176 if (argc == 0) {
3177 oldfd = -1;
3178 } else {
3179 if (!JS_ValueToECMAInt32(cx, JS_ARGV(cx, vp)[0], &fd))
3180 return JS_FALSE;
3181 oldfd = NS_TraceMallocChangeLogFD(fd);
3182 if (oldfd == -2) {
3183 JS_ReportOutOfMemory(cx);
3184 return JS_FALSE;
3187 JS_SET_RVAL(cx, vp, INT_TO_JSVAL(oldfd));
3188 return JS_TRUE;
3191 static JSBool
3192 TraceMallocCloseLogFD(JSContext *cx, uintN argc, jsval *vp)
3194 int32 fd;
3196 if (!CheckUniversalXPConnectForTraceMalloc(cx))
3197 return JS_FALSE;
3199 JS_SET_RVAL(cx, vp, JSVAL_VOID);
3200 if (argc == 0)
3201 return JS_TRUE;
3202 if (!JS_ValueToECMAInt32(cx, JS_ARGV(cx, vp)[0], &fd))
3203 return JS_FALSE;
3204 NS_TraceMallocCloseLogFD((int) fd);
3205 return JS_TRUE;
3208 static JSBool
3209 TraceMallocLogTimestamp(JSContext *cx, uintN argc, jsval *vp)
3211 if (!CheckUniversalXPConnectForTraceMalloc(cx))
3212 return JS_FALSE;
3214 JSString *str = JS_ValueToString(cx, argc ? JS_ARGV(cx, vp)[0] : JSVAL_VOID);
3215 if (!str)
3216 return JS_FALSE;
3217 JSAutoByteString caption(cx, str);
3218 if (!caption)
3219 return JS_FALSE;
3220 NS_TraceMallocLogTimestamp(caption.ptr());
3221 JS_SET_RVAL(cx, vp, JSVAL_VOID);
3222 return JS_TRUE;
3225 static JSBool
3226 TraceMallocDumpAllocations(JSContext *cx, uintN argc, jsval *vp)
3228 if (!CheckUniversalXPConnectForTraceMalloc(cx))
3229 return JS_FALSE;
3231 JSString *str = JS_ValueToString(cx, argc ? JS_ARGV(cx, vp)[0] : JSVAL_VOID);
3232 if (!str)
3233 return JS_FALSE;
3234 JSAutoByteString pathname(cx, str);
3235 if (!pathname)
3236 return JS_FALSE;
3237 if (NS_TraceMallocDumpAllocations(pathname.ptr()) < 0) {
3238 JS_ReportError(cx, "can't dump to %s: %s", pathname.ptr(), strerror(errno));
3239 return JS_FALSE;
3241 JS_SET_RVAL(cx, vp, JSVAL_VOID);
3242 return JS_TRUE;
3245 static JSFunctionSpec TraceMallocFunctions[] = {
3246 {"TraceMallocDisable", TraceMallocDisable, 0, 0},
3247 {"TraceMallocEnable", TraceMallocEnable, 0, 0},
3248 {"TraceMallocOpenLogFile", TraceMallocOpenLogFile, 1, 0},
3249 {"TraceMallocChangeLogFD", TraceMallocChangeLogFD, 1, 0},
3250 {"TraceMallocCloseLogFD", TraceMallocCloseLogFD, 1, 0},
3251 {"TraceMallocLogTimestamp", TraceMallocLogTimestamp, 1, 0},
3252 {"TraceMallocDumpAllocations", TraceMallocDumpAllocations, 1, 0},
3253 {nsnull, nsnull, 0, 0}
3256 #endif /* NS_TRACE_MALLOC */
3258 #ifdef MOZ_JPROF
3260 #include <signal.h>
3262 inline PRBool
3263 IsJProfAction(struct sigaction *action)
3265 return (action->sa_sigaction &&
3266 action->sa_flags == (SA_RESTART | SA_SIGINFO));
3269 void NS_JProfStartProfiling();
3270 void NS_JProfStopProfiling();
3272 static JSBool
3273 JProfStartProfilingJS(JSContext *cx, uintN argc, jsval *vp)
3275 NS_JProfStartProfiling();
3276 return JS_TRUE;
3279 void NS_JProfStartProfiling()
3281 // Figure out whether we're dealing with SIGPROF, SIGALRM, or
3282 // SIGPOLL profiling (SIGALRM for JP_REALTIME, SIGPOLL for
3283 // JP_RTC_HZ)
3284 struct sigaction action;
3286 sigaction(SIGALRM, nsnull, &action);
3287 if (IsJProfAction(&action)) {
3288 printf("Beginning real-time jprof profiling.\n");
3289 raise(SIGALRM);
3290 return;
3293 sigaction(SIGPROF, nsnull, &action);
3294 if (IsJProfAction(&action)) {
3295 printf("Beginning process-time jprof profiling.\n");
3296 raise(SIGPROF);
3297 return;
3300 sigaction(SIGPOLL, nsnull, &action);
3301 if (IsJProfAction(&action)) {
3302 printf("Beginning rtc-based jprof profiling.\n");
3303 raise(SIGPOLL);
3304 return;
3307 printf("Could not start jprof-profiling since JPROF_FLAGS was not set.\n");
3310 static JSBool
3311 JProfStopProfilingJS(JSContext *cx, uintN argc, jsval *vp)
3313 NS_JProfStopProfiling();
3314 return JS_TRUE;
3317 void
3318 NS_JProfStopProfiling()
3320 raise(SIGUSR1);
3321 printf("Stopped jprof profiling.\n");
3324 static JSFunctionSpec JProfFunctions[] = {
3325 {"JProfStartProfiling", JProfStartProfilingJS, 0, 0},
3326 {"JProfStopProfiling", JProfStopProfilingJS, 0, 0},
3327 {nsnull, nsnull, 0, 0}
3330 #endif /* defined(MOZ_JPROF) */
3332 #ifdef MOZ_SHARK
3333 static JSFunctionSpec SharkFunctions[] = {
3334 {"startShark", js_StartShark, 0, 0},
3335 {"stopShark", js_StopShark, 0, 0},
3336 {"connectShark", js_ConnectShark, 0, 0},
3337 {"disconnectShark", js_DisconnectShark, 0, 0},
3338 {nsnull, nsnull, 0, 0}
3340 #endif
3342 #ifdef MOZ_CALLGRIND
3343 static JSFunctionSpec CallgrindFunctions[] = {
3344 {"startCallgrind", js_StartCallgrind, 0, 0},
3345 {"stopCallgrind", js_StopCallgrind, 0, 0},
3346 {"dumpCallgrind", js_DumpCallgrind, 1, 0},
3347 {nsnull, nsnull, 0, 0}
3349 #endif
3351 #ifdef MOZ_VTUNE
3352 static JSFunctionSpec VtuneFunctions[] = {
3353 {"startVtune", js_StartVtune, 1, 0},
3354 {"stopVtune", js_StopVtune, 0, 0},
3355 {"pauseVtune", js_PauseVtune, 0, 0},
3356 {"resumeVtune", js_ResumeVtune, 0, 0},
3357 {nsnull, nsnull, 0, 0}
3359 #endif
3361 #ifdef MOZ_TRACEVIS
3362 static JSFunctionSpec EthogramFunctions[] = {
3363 {"initEthogram", js_InitEthogram, 0, 0},
3364 {"shutdownEthogram", js_ShutdownEthogram, 0, 0},
3365 {nsnull, nsnull, 0, 0}
3367 #endif
3369 nsresult
3370 nsJSContext::InitClasses(void *aGlobalObj)
3372 nsresult rv = NS_OK;
3374 JSObject *globalObj = static_cast<JSObject *>(aGlobalObj);
3376 rv = InitializeExternalClasses();
3377 NS_ENSURE_SUCCESS(rv, rv);
3379 JSAutoRequest ar(mContext);
3381 // Initialize the options object and set default options in mContext
3382 JSObject *optionsObj = ::JS_DefineObject(mContext, globalObj, "_options",
3383 &OptionsClass, nsnull, 0);
3384 if (optionsObj &&
3385 ::JS_DefineProperties(mContext, optionsObj, OptionsProperties)) {
3386 ::JS_SetOptions(mContext, mDefaultJSOptions);
3387 } else {
3388 rv = NS_ERROR_FAILURE;
3391 #ifdef NS_TRACE_MALLOC
3392 // Attempt to initialize TraceMalloc functions
3393 ::JS_DefineFunctions(mContext, globalObj, TraceMallocFunctions);
3394 #endif
3396 #ifdef MOZ_JPROF
3397 // Attempt to initialize JProf functions
3398 ::JS_DefineFunctions(mContext, globalObj, JProfFunctions);
3399 #endif
3401 #ifdef MOZ_SHARK
3402 // Attempt to initialize Shark functions
3403 ::JS_DefineFunctions(mContext, globalObj, SharkFunctions);
3404 #endif
3406 #ifdef MOZ_CALLGRIND
3407 // Attempt to initialize Callgrind functions
3408 ::JS_DefineFunctions(mContext, globalObj, CallgrindFunctions);
3409 #endif
3411 #ifdef MOZ_VTUNE
3412 // Attempt to initialize Vtune functions
3413 ::JS_DefineFunctions(mContext, globalObj, VtuneFunctions);
3414 #endif
3416 #ifdef MOZ_TRACEVIS
3417 // Attempt to initialize Ethogram functions
3418 ::JS_DefineFunctions(mContext, globalObj, EthogramFunctions);
3419 #endif
3421 JSOptionChangedCallback(js_options_dot_str, this);
3423 return rv;
3426 void
3427 nsJSContext::ClearScope(void *aGlobalObj, PRBool aClearFromProtoChain)
3429 // Push our JSContext on our thread's context stack.
3430 nsCOMPtr<nsIJSContextStack> stack =
3431 do_GetService("@mozilla.org/js/xpc/ContextStack;1");
3432 if (stack && NS_FAILED(stack->Push(mContext))) {
3433 stack = nsnull;
3436 if (aGlobalObj) {
3437 JSObject *obj = (JSObject *)aGlobalObj;
3438 JSAutoRequest ar(mContext);
3440 JSAutoEnterCompartment ac;
3441 ac.enterAndIgnoreErrors(mContext, obj);
3443 JS_ClearScope(mContext, obj);
3444 if (xpc::WrapperFactory::IsXrayWrapper(obj)) {
3445 JS_ClearScope(mContext, &obj->getProxyExtra().toObject());
3447 if (!obj->getParent()) {
3448 JS_ClearRegExpStatics(mContext, obj);
3451 // Always clear watchpoints, to deal with two cases:
3452 // 1. The first document for this window is loading, and a miscreant has
3453 // preset watchpoints on the window object in order to attack the new
3454 // document's privileged information.
3455 // 2. A document loaded and used watchpoints on its own window, leaving
3456 // them set until the next document loads. We must clean up window
3457 // watchpoints here.
3458 // Watchpoints set on document and subordinate objects are all cleared
3459 // when those sub-window objects are finalized, after JS_ClearScope and
3460 // a GC run that finds them to be garbage.
3461 ::JS_ClearWatchPointsForObject(mContext, obj);
3463 // Since the prototype chain is shared between inner and outer (and
3464 // stays with the inner), we don't clear things from the prototype
3465 // chain when we're clearing an outer window whose current inner we
3466 // still want.
3467 if (aClearFromProtoChain) {
3468 nsWindowSH::InvalidateGlobalScopePolluter(mContext, obj);
3470 // Clear up obj's prototype chain, but not Object.prototype.
3471 for (JSObject *o = ::JS_GetPrototype(mContext, obj), *next;
3472 o && (next = ::JS_GetPrototype(mContext, o)); o = next)
3473 ::JS_ClearScope(mContext, o);
3477 if (stack) {
3478 stack->Pop(nsnull);
3482 void
3483 nsJSContext::WillInitializeContext()
3485 mIsInitialized = PR_FALSE;
3488 void
3489 nsJSContext::DidInitializeContext()
3491 mIsInitialized = PR_TRUE;
3494 PRBool
3495 nsJSContext::IsContextInitialized()
3497 return mIsInitialized;
3500 void
3501 nsJSContext::FinalizeContext()
3506 void
3507 nsJSContext::GC()
3509 FireGCTimer(PR_FALSE);
3512 void
3513 nsJSContext::ScriptEvaluated(PRBool aTerminated)
3515 if (aTerminated && mTerminations) {
3516 // Make sure to null out mTerminations before doing anything that
3517 // might cause new termination funcs to be added!
3518 nsJSContext::TerminationFuncClosure* start = mTerminations;
3519 mTerminations = nsnull;
3521 for (nsJSContext::TerminationFuncClosure* cur = start;
3522 cur;
3523 cur = cur->mNext) {
3524 (*(cur->mTerminationFunc))(cur->mTerminationFuncArg);
3526 delete start;
3529 mNumEvaluations++;
3531 #ifdef JS_GC_ZEAL
3532 if (mContext->runtime->gcZeal >= 2) {
3533 MaybeGC(mContext);
3534 } else
3535 #endif
3536 if (mNumEvaluations > 20) {
3537 mNumEvaluations = 0;
3538 MaybeGC(mContext);
3541 if (aTerminated) {
3542 mOperationCallbackTime = 0;
3543 mModalStateTime = 0;
3547 nsresult
3548 nsJSContext::SetTerminationFunction(nsScriptTerminationFunc aFunc,
3549 nsISupports* aRef)
3551 NS_PRECONDITION(JS_IsRunning(mContext), "should be executing script");
3553 nsJSContext::TerminationFuncClosure* newClosure =
3554 new nsJSContext::TerminationFuncClosure(aFunc, aRef, mTerminations);
3555 if (!newClosure) {
3556 return NS_ERROR_OUT_OF_MEMORY;
3559 mTerminations = newClosure;
3560 return NS_OK;
3563 PRBool
3564 nsJSContext::GetScriptsEnabled()
3566 return mScriptsEnabled;
3569 void
3570 nsJSContext::SetScriptsEnabled(PRBool aEnabled, PRBool aFireTimeouts)
3572 // eeek - this seems the wrong way around - the global should callback
3573 // into each context, so every language is disabled.
3574 mScriptsEnabled = aEnabled;
3576 nsIScriptGlobalObject *global = GetGlobalObject();
3578 if (global) {
3579 global->SetScriptsEnabled(aEnabled, aFireTimeouts);
3584 PRBool
3585 nsJSContext::GetProcessingScriptTag()
3587 return mProcessingScriptTag;
3590 void
3591 nsJSContext::SetProcessingScriptTag(PRBool aFlag)
3593 mProcessingScriptTag = aFlag;
3596 PRBool
3597 nsJSContext::GetExecutingScript()
3599 return JS_IsRunning(mContext) || mExecuteDepth > 0;
3602 void
3603 nsJSContext::SetGCOnDestruction(PRBool aGCOnDestruction)
3605 mGCOnDestruction = aGCOnDestruction;
3608 NS_IMETHODIMP
3609 nsJSContext::ScriptExecuted()
3611 ScriptEvaluated(!::JS_IsRunning(mContext));
3613 return NS_OK;
3616 //static
3617 void
3618 nsJSContext::CC(nsICycleCollectorListener *aListener)
3620 NS_TIME_FUNCTION_MIN(1.0);
3622 ++sCCollectCount;
3623 #ifdef DEBUG_smaug
3624 printf("Will run cycle collector (%i), %lldms since previous.\n",
3625 sCCollectCount, (PR_Now() - sPreviousCCTime) / PR_USEC_PER_MSEC);
3626 #endif
3627 sPreviousCCTime = PR_Now();
3628 sDelayedCCollectCount = 0;
3629 sCCSuspectChanges = 0;
3630 // nsCycleCollector_collect() no longer forces a JS garbage collection,
3631 // so we have to do it ourselves here.
3632 if (nsContentUtils::XPConnect()) {
3633 nsContentUtils::XPConnect()->GarbageCollect();
3635 sCollectedObjectsCounts = nsCycleCollector_collect(aListener);
3636 sCCSuspectedCount = nsCycleCollector_suspectedCount();
3637 if (nsJSRuntime::sRuntime) {
3638 sSavedGCCount = JS_GetGCParameter(nsJSRuntime::sRuntime, JSGC_NUMBER);
3640 #ifdef DEBUG_smaug
3641 printf("Collected %u objects, %u suspected objects, took %lldms\n",
3642 sCollectedObjectsCounts, sCCSuspectedCount,
3643 (PR_Now() - sPreviousCCTime) / PR_USEC_PER_MSEC);
3644 #endif
3647 static inline uint32
3648 GetGCRunsSinceLastCC()
3650 // To avoid crash if nsJSRuntime is not properly initialized.
3651 // See the bug 474586
3652 if (!nsJSRuntime::sRuntime)
3653 return 0;
3655 // Since JS_GetGCParameter() and sSavedGCCount are unsigned, the following
3656 // gives the correct result even when the GC counter wraps around
3657 // UINT32_MAX since the last call to JS_GetGCParameter().
3658 return JS_GetGCParameter(nsJSRuntime::sRuntime, JSGC_NUMBER) -
3659 sSavedGCCount;
3662 //static
3663 PRBool
3664 nsJSContext::MaybeCC(PRBool aHigherProbability)
3666 ++sDelayedCCollectCount;
3668 // Don't check suspected count if CC will be called anyway.
3669 if (sCCSuspectChanges <= NS_MIN_SUSPECT_CHANGES ||
3670 GetGCRunsSinceLastCC() <= NS_MAX_GC_COUNT) {
3671 #ifdef DEBUG_smaug
3672 PRTime now = PR_Now();
3673 #endif
3674 PRUint32 suspected = nsCycleCollector_suspectedCount();
3675 #ifdef DEBUG_smaug
3676 printf("%u suspected objects (%lldms), sCCSuspectedCount %u\n",
3677 suspected, (PR_Now() - now) / PR_USEC_PER_MSEC,
3678 sCCSuspectedCount);
3679 #endif
3680 // Update only when suspected count has increased.
3681 if (suspected > sCCSuspectedCount) {
3682 sCCSuspectChanges += (suspected - sCCSuspectedCount);
3683 sCCSuspectedCount = suspected;
3686 #ifdef DEBUG_smaug
3687 printf("sCCSuspectChanges %u, GC runs %u\n",
3688 sCCSuspectChanges, GetGCRunsSinceLastCC());
3689 #endif
3691 // Increase the probability also if the previous call to cycle collector
3692 // collected something.
3693 if (aHigherProbability ||
3694 sCollectedObjectsCounts > NS_COLLECTED_OBJECTS_LIMIT) {
3695 sDelayedCCollectCount *= NS_PROBABILITY_MULTIPLIER;
3696 } else if (!sUserIsActive && sCCSuspectChanges > NS_MAX_SUSPECT_CHANGES) {
3697 // If user is inactive and there are lots of new suspected objects,
3698 // increase the probability for cycle collection.
3699 sDelayedCCollectCount += (sCCSuspectChanges / NS_MAX_SUSPECT_CHANGES);
3702 if (!sGCTimer &&
3703 (sDelayedCCollectCount > NS_MAX_DELAYED_CCOLLECT) &&
3704 ((sCCSuspectChanges > NS_MIN_SUSPECT_CHANGES &&
3705 GetGCRunsSinceLastCC() > NS_MAX_GC_COUNT) ||
3706 (sCCSuspectChanges > NS_MAX_SUSPECT_CHANGES))) {
3707 return IntervalCC();
3709 return PR_FALSE;
3712 //static
3713 void
3714 nsJSContext::CCIfUserInactive()
3716 if (sUserIsActive) {
3717 MaybeCC(PR_TRUE);
3718 } else {
3719 IntervalCC();
3723 //static
3724 void
3725 nsJSContext::MaybeCCIfUserInactive()
3727 if (!sUserIsActive) {
3728 MaybeCC(PR_FALSE);
3732 //static
3733 PRBool
3734 nsJSContext::IntervalCC()
3736 if ((PR_Now() - sPreviousCCTime) >=
3737 PRTime(NS_MIN_CC_INTERVAL * PR_USEC_PER_MSEC)) {
3738 nsJSContext::CC(nsnull);
3739 return PR_TRUE;
3741 #ifdef DEBUG_smaug
3742 printf("Running CC was delayed because of NS_MIN_CC_INTERVAL.\n");
3743 #endif
3744 return PR_FALSE;
3747 // static
3748 void
3749 GCTimerFired(nsITimer *aTimer, void *aClosure)
3751 NS_RELEASE(sGCTimer);
3753 if (sPendingLoadCount == 0 || sLoadInProgressGCTimer) {
3754 sLoadInProgressGCTimer = PR_FALSE;
3756 // Reset sPendingLoadCount in case the timer that fired was a
3757 // timer we scheduled due to a normal GC timer firing while
3758 // documents were loading. If this happens we're waiting for a
3759 // document that is taking a long time to load, and we effectively
3760 // ignore the fact that the currently loading documents are still
3761 // loading and move on as if they weren't.
3762 sPendingLoadCount = 0;
3764 nsJSContext::CCIfUserInactive();
3765 } else {
3766 nsJSContext::FireGCTimer(PR_TRUE);
3769 sReadyForGC = PR_TRUE;
3772 // static
3773 void
3774 nsJSContext::LoadStart()
3776 ++sPendingLoadCount;
3779 // static
3780 void
3781 nsJSContext::LoadEnd()
3783 // sPendingLoadCount is not a well managed load counter (and doesn't
3784 // need to be), so make sure we don't make it wrap backwards here.
3785 if (sPendingLoadCount > 0) {
3786 --sPendingLoadCount;
3789 if (!sPendingLoadCount && sLoadInProgressGCTimer) {
3790 sGCTimer->Cancel();
3791 NS_RELEASE(sGCTimer);
3792 sLoadInProgressGCTimer = PR_FALSE;
3794 CCIfUserInactive();
3798 // static
3799 void
3800 nsJSContext::FireGCTimer(PRBool aLoadInProgress)
3802 if (sGCTimer) {
3803 // There's already a timer for GC'ing, just return
3804 return;
3807 CallCreateInstance("@mozilla.org/timer;1", &sGCTimer);
3809 if (!sGCTimer) {
3810 NS_WARNING("Failed to create timer");
3812 // Reset sLoadInProgressGCTimer since we're not able to fire the
3813 // timer.
3814 sLoadInProgressGCTimer = PR_FALSE;
3816 CCIfUserInactive();
3817 return;
3820 static PRBool first = PR_TRUE;
3822 sGCTimer->InitWithFuncCallback(GCTimerFired, nsnull,
3823 first ? NS_FIRST_GC_DELAY :
3824 aLoadInProgress ? NS_LOAD_IN_PROCESS_GC_DELAY :
3825 NS_GC_DELAY,
3826 nsITimer::TYPE_ONE_SHOT);
3828 sLoadInProgressGCTimer = aLoadInProgress;
3830 first = PR_FALSE;
3833 static JSBool
3834 DOMGCCallback(JSContext *cx, JSGCStatus status)
3836 JSBool result = gOldJSGCCallback ? gOldJSGCCallback(cx, status) : JS_TRUE;
3838 if (status == JSGC_BEGIN && !NS_IsMainThread())
3839 return JS_FALSE;
3841 return result;
3844 // Script object mananagement - note duplicate implementation
3845 // in nsJSRuntime below...
3846 nsresult
3847 nsJSContext::HoldScriptObject(void* aScriptObject)
3849 NS_ASSERTION(sIsInitialized, "runtime not initialized");
3850 if (! nsJSRuntime::sRuntime) {
3851 NS_NOTREACHED("couldn't add GC root - no runtime");
3852 return NS_ERROR_FAILURE;
3855 ::JS_LockGCThingRT(nsJSRuntime::sRuntime, aScriptObject);
3856 return NS_OK;
3859 nsresult
3860 nsJSContext::DropScriptObject(void* aScriptObject)
3862 NS_ASSERTION(sIsInitialized, "runtime not initialized");
3863 if (! nsJSRuntime::sRuntime) {
3864 NS_NOTREACHED("couldn't remove GC root");
3865 return NS_ERROR_FAILURE;
3868 ::JS_UnlockGCThingRT(nsJSRuntime::sRuntime, aScriptObject);
3869 return NS_OK;
3872 void
3873 nsJSContext::ReportPendingException()
3875 // set aside the frame chain, since it has nothing to do with the
3876 // exception we're reporting.
3877 if (mIsInitialized && ::JS_IsExceptionPending(mContext)) {
3878 JSStackFrame* frame = JS_SaveFrameChain(mContext);
3879 ::JS_ReportPendingException(mContext);
3880 JS_RestoreFrameChain(mContext, frame);
3884 /**********************************************************************
3885 * nsJSRuntime implementation
3886 *********************************************************************/
3888 // QueryInterface implementation for nsJSRuntime
3889 NS_INTERFACE_MAP_BEGIN(nsJSRuntime)
3890 NS_INTERFACE_MAP_ENTRY(nsIScriptRuntime)
3891 NS_INTERFACE_MAP_END
3894 NS_IMPL_ADDREF(nsJSRuntime)
3895 NS_IMPL_RELEASE(nsJSRuntime)
3897 nsresult
3898 nsJSRuntime::CreateContext(nsIScriptContext **aContext)
3900 nsCOMPtr<nsIScriptContext> scriptContext;
3902 *aContext = new nsJSContext(sRuntime);
3903 NS_ENSURE_TRUE(*aContext, NS_ERROR_OUT_OF_MEMORY);
3904 NS_ADDREF(*aContext);
3905 return NS_OK;
3908 nsresult
3909 nsJSRuntime::ParseVersion(const nsString &aVersionStr, PRUint32 *flags)
3911 NS_PRECONDITION(flags, "Null flags param?");
3912 JSVersion jsVersion = JSVERSION_UNKNOWN;
3913 if (aVersionStr.Length() != 3 || aVersionStr[0] != '1' || aVersionStr[1] != '.')
3914 jsVersion = JSVERSION_UNKNOWN;
3915 else switch (aVersionStr[2]) {
3916 case '0': jsVersion = JSVERSION_1_0; break;
3917 case '1': jsVersion = JSVERSION_1_1; break;
3918 case '2': jsVersion = JSVERSION_1_2; break;
3919 case '3': jsVersion = JSVERSION_1_3; break;
3920 case '4': jsVersion = JSVERSION_1_4; break;
3921 case '5': jsVersion = JSVERSION_1_5; break;
3922 case '6': jsVersion = JSVERSION_1_6; break;
3923 case '7': jsVersion = JSVERSION_1_7; break;
3924 case '8': jsVersion = JSVERSION_1_8; break;
3925 default: jsVersion = JSVERSION_UNKNOWN;
3927 *flags = (PRUint32)jsVersion;
3928 return NS_OK;
3931 //static
3932 void
3933 nsJSRuntime::Startup()
3935 // initialize all our statics, so that we can restart XPCOM
3936 sDelayedCCollectCount = 0;
3937 sCCollectCount = 0;
3938 sUserIsActive = PR_FALSE;
3939 sPreviousCCTime = PR_Now();
3940 sCollectedObjectsCounts = 0;
3941 sSavedGCCount = 0;
3942 sCCSuspectChanges = 0;
3943 sCCSuspectedCount = 0;
3944 sGCTimer = nsnull;
3945 sReadyForGC = PR_FALSE;
3946 sLoadInProgressGCTimer = PR_FALSE;
3947 sPendingLoadCount = 0;
3948 gNameSpaceManager = nsnull;
3949 sRuntimeService = nsnull;
3950 sRuntime = nsnull;
3951 gOldJSGCCallback = nsnull;
3952 sIsInitialized = PR_FALSE;
3953 sDidShutdown = PR_FALSE;
3954 sContextCount = 0;
3955 sSecurityManager = nsnull;
3956 gCollation = nsnull;
3959 static int
3960 MaxScriptRunTimePrefChangedCallback(const char *aPrefName, void *aClosure)
3962 // Default limit on script run time to 10 seconds. 0 means let
3963 // scripts run forever.
3964 PRBool isChromePref =
3965 strcmp(aPrefName, "dom.max_chrome_script_run_time") == 0;
3966 PRInt32 time = nsContentUtils::GetIntPref(aPrefName, isChromePref ? 20 : 10);
3968 PRTime t;
3969 if (time <= 0) {
3970 // Let scripts run for a really, really long time.
3971 t = LL_INIT(0x40000000, 0);
3972 } else {
3973 t = time * PR_USEC_PER_SEC;
3976 if (isChromePref) {
3977 sMaxChromeScriptRunTime = t;
3978 } else {
3979 sMaxScriptRunTime = t;
3982 return 0;
3985 static int
3986 ReportAllJSExceptionsPrefChangedCallback(const char* aPrefName, void* aClosure)
3988 PRBool reportAll = nsContentUtils::GetBoolPref(aPrefName, PR_FALSE);
3989 nsContentUtils::XPConnect()->SetReportAllJSExceptions(reportAll);
3990 return 0;
3993 static int
3994 SetMemoryHighWaterMarkPrefChangedCallback(const char* aPrefName, void* aClosure)
3996 PRInt32 highwatermark = nsContentUtils::GetIntPref(aPrefName, 32);
3998 if (highwatermark >= 32) {
4000 * There are two ways to allocate memory in SpiderMonkey. One is
4001 * to use jsmalloc() and the other is to use GC-owned memory
4002 * (e.g. js_NewGCThing()).
4004 * In the browser, we don't cap the amount of GC-owned memory.
4006 JS_SetGCParameter(nsJSRuntime::sRuntime, JSGC_MAX_MALLOC_BYTES,
4007 128L * 1024L * 1024L);
4008 JS_SetGCParameter(nsJSRuntime::sRuntime, JSGC_MAX_BYTES,
4009 0xffffffff);
4010 } else {
4011 JS_SetGCParameter(nsJSRuntime::sRuntime, JSGC_MAX_MALLOC_BYTES,
4012 highwatermark * 1024L * 1024L);
4013 JS_SetGCParameter(nsJSRuntime::sRuntime, JSGC_MAX_BYTES,
4014 highwatermark * 1024L * 1024L);
4016 return 0;
4019 static int
4020 SetMemoryGCFrequencyPrefChangedCallback(const char* aPrefName, void* aClosure)
4022 PRInt32 triggerFactor = nsContentUtils::GetIntPref(aPrefName, 300);
4023 JS_SetGCParameter(nsJSRuntime::sRuntime, JSGC_TRIGGER_FACTOR, triggerFactor);
4024 return 0;
4027 static JSPrincipals *
4028 ObjectPrincipalFinder(JSContext *cx, JSObject *obj)
4030 if (!sSecurityManager)
4031 return nsnull;
4033 nsCOMPtr<nsIPrincipal> principal;
4034 nsresult rv =
4035 sSecurityManager->GetObjectPrincipal(cx, obj,
4036 getter_AddRefs(principal));
4038 if (NS_FAILED(rv) || !principal) {
4039 return nsnull;
4042 JSPrincipals *jsPrincipals = nsnull;
4043 principal->GetJSPrincipals(cx, &jsPrincipals);
4045 // nsIPrincipal::GetJSPrincipals() returns a strong reference to the
4046 // JS principals, but the caller of this function expects a weak
4047 // reference. So we need to release here.
4049 JSPRINCIPALS_DROP(cx, jsPrincipals);
4051 return jsPrincipals;
4054 static JSObject*
4055 DOMReadStructuredClone(JSContext* cx,
4056 JSStructuredCloneReader* reader,
4057 uint32 tag,
4058 uint32 data)
4060 // We don't currently support any extensions to structured cloning.
4061 nsDOMClassInfo::ThrowJSException(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
4062 return nsnull;
4065 static JSBool
4066 DOMWriteStructuredClone(JSContext* cx,
4067 JSStructuredCloneWriter* writer,
4068 JSObject* obj)
4070 // We don't currently support any extensions to structured cloning.
4071 nsDOMClassInfo::ThrowJSException(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
4072 return JS_FALSE;
4075 static void
4076 DOMStructuredCloneError(JSContext* cx,
4077 uint32 errorid)
4079 // We don't currently support any extensions to structured cloning.
4080 nsDOMClassInfo::ThrowJSException(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
4083 //static
4084 nsresult
4085 nsJSRuntime::Init()
4087 if (sIsInitialized) {
4088 if (!nsContentUtils::XPConnect())
4089 return NS_ERROR_NOT_AVAILABLE;
4091 return NS_OK;
4094 nsresult rv = CallGetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID,
4095 &sSecurityManager);
4096 NS_ENSURE_SUCCESS(rv, rv);
4098 rv = CallGetService(kJSRuntimeServiceContractID, &sRuntimeService);
4099 // get the JSRuntime from the runtime svc, if possible
4100 NS_ENSURE_SUCCESS(rv, rv);
4102 rv = sRuntimeService->GetRuntime(&sRuntime);
4103 NS_ENSURE_SUCCESS(rv, rv);
4105 // Let's make sure that our main thread is the same as the xpcom main thread.
4106 NS_ASSERTION(NS_IsMainThread(), "bad");
4108 NS_ASSERTION(!gOldJSGCCallback,
4109 "nsJSRuntime initialized more than once");
4111 sSavedGCCount = JS_GetGCParameter(nsJSRuntime::sRuntime, JSGC_NUMBER);
4113 // Save the old GC callback to chain to it, for GC-observing generality.
4114 gOldJSGCCallback = ::JS_SetGCCallbackRT(sRuntime, DOMGCCallback);
4116 JSSecurityCallbacks *callbacks = JS_GetRuntimeSecurityCallbacks(sRuntime);
4117 NS_ASSERTION(callbacks, "SecMan should have set security callbacks!");
4119 callbacks->findObjectPrincipals = ObjectPrincipalFinder;
4121 // Set up the structured clone callbacks.
4122 static JSStructuredCloneCallbacks cloneCallbacks = {
4123 DOMReadStructuredClone,
4124 DOMWriteStructuredClone,
4125 DOMStructuredCloneError
4127 JS_SetStructuredCloneCallbacks(sRuntime, &cloneCallbacks);
4129 // Set these global xpconnect options...
4130 nsContentUtils::RegisterPrefCallback("dom.max_script_run_time",
4131 MaxScriptRunTimePrefChangedCallback,
4132 nsnull);
4133 MaxScriptRunTimePrefChangedCallback("dom.max_script_run_time", nsnull);
4135 nsContentUtils::RegisterPrefCallback("dom.max_chrome_script_run_time",
4136 MaxScriptRunTimePrefChangedCallback,
4137 nsnull);
4138 MaxScriptRunTimePrefChangedCallback("dom.max_chrome_script_run_time",
4139 nsnull);
4141 nsContentUtils::RegisterPrefCallback("dom.report_all_js_exceptions",
4142 ReportAllJSExceptionsPrefChangedCallback,
4143 nsnull);
4144 ReportAllJSExceptionsPrefChangedCallback("dom.report_all_js_exceptions",
4145 nsnull);
4147 nsContentUtils::RegisterPrefCallback("javascript.options.mem.high_water_mark",
4148 SetMemoryHighWaterMarkPrefChangedCallback,
4149 nsnull);
4150 SetMemoryHighWaterMarkPrefChangedCallback("javascript.options.mem.high_water_mark",
4151 nsnull);
4153 nsContentUtils::RegisterPrefCallback("javascript.options.mem.gc_frequency",
4154 SetMemoryGCFrequencyPrefChangedCallback,
4155 nsnull);
4156 SetMemoryGCFrequencyPrefChangedCallback("javascript.options.mem.gc_frequency",
4157 nsnull);
4159 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
4160 if (!obs)
4161 return NS_ERROR_FAILURE;
4162 nsIObserver* activityObserver = new nsUserActivityObserver();
4163 NS_ENSURE_TRUE(activityObserver, NS_ERROR_OUT_OF_MEMORY);
4164 obs->AddObserver(activityObserver, "user-interaction-inactive", PR_FALSE);
4165 obs->AddObserver(activityObserver, "user-interaction-active", PR_FALSE);
4166 obs->AddObserver(activityObserver, "xpcom-shutdown", PR_FALSE);
4168 nsIObserver* ccMemPressureObserver = new nsCCMemoryPressureObserver();
4169 NS_ENSURE_TRUE(ccMemPressureObserver, NS_ERROR_OUT_OF_MEMORY);
4170 obs->AddObserver(ccMemPressureObserver, "memory-pressure", PR_FALSE);
4172 sIsInitialized = PR_TRUE;
4174 return NS_OK;
4177 //static
4178 nsScriptNameSpaceManager*
4179 nsJSRuntime::GetNameSpaceManager()
4181 if (sDidShutdown)
4182 return nsnull;
4184 if (!gNameSpaceManager) {
4185 gNameSpaceManager = new nsScriptNameSpaceManager;
4186 NS_ADDREF(gNameSpaceManager);
4188 nsresult rv = gNameSpaceManager->Init();
4189 NS_ENSURE_SUCCESS(rv, nsnull);
4192 return gNameSpaceManager;
4195 /* static */
4196 void
4197 nsJSRuntime::Shutdown()
4199 if (sGCTimer) {
4200 // We're being shut down, if we have a GC timer scheduled, cancel
4201 // it. The DOM factory will do one final GC once it's shut down.
4203 sGCTimer->Cancel();
4205 NS_RELEASE(sGCTimer);
4207 sLoadInProgressGCTimer = PR_FALSE;
4210 NS_IF_RELEASE(gNameSpaceManager);
4212 if (!sContextCount) {
4213 // We're being shutdown, and there are no more contexts
4214 // alive, release the JS runtime service and the security manager.
4216 if (sRuntimeService && sSecurityManager) {
4217 JSSecurityCallbacks *callbacks = JS_GetRuntimeSecurityCallbacks(sRuntime);
4218 if (callbacks) {
4219 NS_ASSERTION(callbacks->findObjectPrincipals == ObjectPrincipalFinder,
4220 "Fighting over the findObjectPrincipals callback!");
4221 callbacks->findObjectPrincipals = NULL;
4224 NS_IF_RELEASE(sRuntimeService);
4225 NS_IF_RELEASE(sSecurityManager);
4226 NS_IF_RELEASE(gCollation);
4227 NS_IF_RELEASE(gDecoder);
4230 sDidShutdown = PR_TRUE;
4233 // Script object mananagement - note duplicate implementation
4234 // in nsJSContext above...
4235 nsresult
4236 nsJSRuntime::HoldScriptObject(void* aScriptObject)
4238 NS_ASSERTION(sIsInitialized, "runtime not initialized");
4239 if (! sRuntime) {
4240 NS_NOTREACHED("couldn't remove GC root - no runtime");
4241 return NS_ERROR_FAILURE;
4244 ::JS_LockGCThingRT(sRuntime, aScriptObject);
4245 return NS_OK;
4248 nsresult
4249 nsJSRuntime::DropScriptObject(void* aScriptObject)
4251 NS_ASSERTION(sIsInitialized, "runtime not initialized");
4252 if (! sRuntime) {
4253 NS_NOTREACHED("couldn't remove GC root");
4254 return NS_ERROR_FAILURE;
4257 ::JS_UnlockGCThingRT(sRuntime, aScriptObject);
4258 return NS_OK;
4261 // A factory for the runtime.
4262 nsresult NS_CreateJSRuntime(nsIScriptRuntime **aRuntime)
4264 nsresult rv = nsJSRuntime::Init();
4265 NS_ENSURE_SUCCESS(rv, rv);
4267 *aRuntime = new nsJSRuntime();
4268 if (*aRuntime == nsnull)
4269 return NS_ERROR_OUT_OF_MEMORY;
4270 NS_IF_ADDREF(*aRuntime);
4271 return NS_OK;
4274 // A fast-array class for JS. This class supports both nsIJSScriptArray and
4275 // nsIArray. If it is JS itself providing and consuming this class, all work
4276 // can be done via nsIJSScriptArray, and avoid the conversion of elements
4277 // to/from nsISupports.
4278 // When consumed by non-JS (eg, another script language), conversion is done
4279 // on-the-fly.
4280 class nsJSArgArray : public nsIJSArgArray, public nsIArray {
4281 public:
4282 nsJSArgArray(JSContext *aContext, PRUint32 argc, jsval *argv, nsresult *prv);
4283 ~nsJSArgArray();
4284 // nsISupports
4285 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
4286 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsJSArgArray,
4287 nsIJSArgArray)
4289 // nsIArray
4290 NS_DECL_NSIARRAY
4292 // nsIJSArgArray
4293 nsresult GetArgs(PRUint32 *argc, void **argv);
4295 void ReleaseJSObjects();
4297 protected:
4298 JSContext *mContext;
4299 jsval *mArgv;
4300 PRUint32 mArgc;
4303 nsJSArgArray::nsJSArgArray(JSContext *aContext, PRUint32 argc, jsval *argv,
4304 nsresult *prv) :
4305 mContext(aContext),
4306 mArgv(nsnull),
4307 mArgc(argc)
4309 // copy the array - we don't know its lifetime, and ours is tied to xpcom
4310 // refcounting. Alloc zero'd array so cleanup etc is safe.
4311 if (argc) {
4312 mArgv = (jsval *) PR_CALLOC(argc * sizeof(jsval));
4313 if (!mArgv) {
4314 *prv = NS_ERROR_OUT_OF_MEMORY;
4315 return;
4319 // Callers are allowed to pass in a null argv even for argc > 0. They can
4320 // then use GetArgs to initialize the values.
4321 if (argv) {
4322 for (PRUint32 i = 0; i < argc; ++i)
4323 mArgv[i] = argv[i];
4326 *prv = argc > 0 ? NS_HOLD_JS_OBJECTS(this, nsJSArgArray) : NS_OK;
4329 nsJSArgArray::~nsJSArgArray()
4331 ReleaseJSObjects();
4334 void
4335 nsJSArgArray::ReleaseJSObjects()
4337 if (mArgc > 0)
4338 NS_DROP_JS_OBJECTS(this, nsJSArgArray);
4339 if (mArgv) {
4340 PR_DELETE(mArgv);
4342 mArgc = 0;
4345 // QueryInterface implementation for nsJSArgArray
4346 NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSArgArray)
4347 NS_IMPL_CYCLE_COLLECTION_ROOT_BEGIN(nsJSArgArray)
4348 tmp->ReleaseJSObjects();
4349 NS_IMPL_CYCLE_COLLECTION_ROOT_END
4350 NS_IMPL_CYCLE_COLLECTION_UNLINK_0(nsJSArgArray)
4351 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsJSArgArray)
4352 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
4353 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
4355 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSArgArray)
4356 jsval *argv = tmp->mArgv;
4357 if (argv) {
4358 jsval *end;
4359 for (end = argv + tmp->mArgc; argv < end; ++argv) {
4360 if (JSVAL_IS_GCTHING(*argv))
4361 NS_IMPL_CYCLE_COLLECTION_TRACE_CALLBACK(JAVASCRIPT,
4362 JSVAL_TO_GCTHING(*argv))
4365 NS_IMPL_CYCLE_COLLECTION_TRACE_END
4367 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSArgArray)
4368 NS_INTERFACE_MAP_ENTRY(nsIArray)
4369 NS_INTERFACE_MAP_ENTRY(nsIJSArgArray)
4370 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIJSArgArray)
4371 NS_INTERFACE_MAP_END
4373 NS_IMPL_CYCLE_COLLECTING_ADDREF_AMBIGUOUS(nsJSArgArray, nsIJSArgArray)
4374 NS_IMPL_CYCLE_COLLECTING_RELEASE_AMBIGUOUS(nsJSArgArray, nsIJSArgArray)
4376 nsresult
4377 nsJSArgArray::GetArgs(PRUint32 *argc, void **argv)
4379 if (!mArgv) {
4380 NS_WARNING("nsJSArgArray has no argv!");
4381 return NS_ERROR_UNEXPECTED;
4383 *argv = (void *)mArgv;
4384 *argc = mArgc;
4385 return NS_OK;
4388 // nsIArray impl
4389 NS_IMETHODIMP nsJSArgArray::GetLength(PRUint32 *aLength)
4391 *aLength = mArgc;
4392 return NS_OK;
4395 /* void queryElementAt (in unsigned long index, in nsIIDRef uuid, [iid_is (uuid), retval] out nsQIResult result); */
4396 NS_IMETHODIMP nsJSArgArray::QueryElementAt(PRUint32 index, const nsIID & uuid, void * *result)
4398 *result = nsnull;
4399 if (index >= mArgc)
4400 return NS_ERROR_INVALID_ARG;
4402 if (uuid.Equals(NS_GET_IID(nsIVariant)) || uuid.Equals(NS_GET_IID(nsISupports))) {
4403 return nsContentUtils::XPConnect()->JSToVariant(mContext, mArgv[index],
4404 (nsIVariant **)result);
4406 NS_WARNING("nsJSArgArray only handles nsIVariant");
4407 return NS_ERROR_NO_INTERFACE;
4410 /* unsigned long indexOf (in unsigned long startIndex, in nsISupports element); */
4411 NS_IMETHODIMP nsJSArgArray::IndexOf(PRUint32 startIndex, nsISupports *element, PRUint32 *_retval)
4413 return NS_ERROR_NOT_IMPLEMENTED;
4416 /* nsISimpleEnumerator enumerate (); */
4417 NS_IMETHODIMP nsJSArgArray::Enumerate(nsISimpleEnumerator **_retval)
4419 return NS_ERROR_NOT_IMPLEMENTED;
4422 // The factory function
4423 nsresult NS_CreateJSArgv(JSContext *aContext, PRUint32 argc, void *argv,
4424 nsIArray **aArray)
4426 nsresult rv;
4427 nsJSArgArray *ret = new nsJSArgArray(aContext, argc,
4428 static_cast<jsval *>(argv), &rv);
4429 if (ret == nsnull)
4430 return NS_ERROR_OUT_OF_MEMORY;
4431 if (NS_FAILED(rv)) {
4432 delete ret;
4433 return rv;
4435 return ret->QueryInterface(NS_GET_IID(nsIArray), (void **)aArray);