Bug 627938: Fix nsGlobalChromeWindow cleanup. (r=smaug, a=jst)
[mozilla-central.git] / dom / base / nsJSEnvironment.cpp
blob6a638c9d830077c4bad5775dd8abd4f59c044a67
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 #include "nsDOMClassInfo.h"
89 #include "jsdbgapi.h" // for JS_ClearWatchPointsForObject
90 #include "jsxdrapi.h"
91 #include "nsIArray.h"
92 #include "nsIObjectInputStream.h"
93 #include "nsIObjectOutputStream.h"
94 #include "nsITimelineService.h"
95 #include "nsDOMScriptObjectHolder.h"
96 #include "prmem.h"
97 #include "WrapperFactory.h"
98 #include "nsGlobalWindow.h"
100 #ifdef XP_MACOSX
101 // AssertMacros.h defines 'check' and conflicts with AccessCheck.h
102 #undef check
103 #endif
104 #include "AccessCheck.h"
106 #ifdef MOZ_JSDEBUGGER
107 #include "jsdIDebuggerService.h"
108 #endif
109 #ifdef MOZ_LOGGING
110 // Force PR_LOGGING so we can get JS strict warnings even in release builds
111 #define FORCE_PR_LOG 1
112 #endif
113 #include "prlog.h"
114 #include "prthread.h"
116 #include "mozilla/FunctionTimer.h"
118 const size_t gStackSize = 8192;
120 #ifdef PR_LOGGING
121 static PRLogModuleInfo* gJSDiagnostics;
122 #endif
124 // Thank you Microsoft!
125 #ifndef WINCE
126 #ifdef CompareString
127 #undef CompareString
128 #endif
129 #endif // WINCE
131 // The amount of time we wait between a request to GC (due to leaving
132 // a page) and doing the actual GC.
133 #define NS_GC_DELAY 2000 // ms
135 // The amount of time we wait until we force a GC in case the previous
136 // GC timer happened to fire while we were in the middle of loading a
137 // page (we'll GC once the page is loaded if that happens before this
138 // amount of time has passed).
139 #define NS_LOAD_IN_PROCESS_GC_DELAY 4000 // ms
141 // The amount of time we wait from the first request to GC to actually
142 // doing the first GC.
143 #define NS_FIRST_GC_DELAY 10000 // ms
145 #define JAVASCRIPT nsIProgrammingLanguage::JAVASCRIPT
147 // The max number of delayed cycle collects..
148 #define NS_MAX_DELAYED_CCOLLECT 45
149 // The max number of user interaction notifications in inactive state before
150 // we try to call cycle collector more aggressively.
151 #define NS_CC_SOFT_LIMIT_INACTIVE 6
152 // The max number of user interaction notifications in active state before
153 // we try to call cycle collector more aggressively.
154 #define NS_CC_SOFT_LIMIT_ACTIVE 12
155 // When higher probability MaybeCC is used, the number of sDelayedCCollectCount
156 // is multiplied with this number.
157 #define NS_PROBABILITY_MULTIPLIER 3
158 // Cycle collector is never called more often than every NS_MIN_CC_INTERVAL
159 // milliseconds. Exceptions are low memory situation and memory pressure
160 // notification.
161 #define NS_MIN_CC_INTERVAL 10000 // ms
162 // If previous cycle collection collected more than this number of objects,
163 // the next collection will happen somewhat soon.
164 #define NS_COLLECTED_OBJECTS_LIMIT 5000
165 // CC will be called if GC has been called at least this number of times and
166 // there are at least NS_MIN_SUSPECT_CHANGES new suspected objects.
167 #define NS_MAX_GC_COUNT 5
168 #define NS_MIN_SUSPECT_CHANGES 10
169 // CC will be called if there are at least NS_MAX_SUSPECT_CHANGES new suspected
170 // objects.
171 #define NS_MAX_SUSPECT_CHANGES 100
173 // if you add statics here, add them to the list in nsJSRuntime::Startup
175 static PRUint32 sDelayedCCollectCount;
176 static PRUint32 sCCollectCount;
177 static PRBool sUserIsActive;
178 static PRTime sPreviousCCTime;
179 static PRUint32 sCollectedObjectsCounts;
180 static PRUint32 sSavedGCCount;
181 static PRUint32 sCCSuspectChanges;
182 static PRUint32 sCCSuspectedCount;
183 static nsITimer *sGCTimer;
184 static PRBool sReadyForGC;
186 // The number of currently pending document loads. This count isn't
187 // guaranteed to always reflect reality and can't easily as we don't
188 // have an easy place to know when a load ends or is interrupted in
189 // all cases. This counter also gets reset if we end up GC'ing while
190 // we're waiting for a slow page to load. IOW, this count may be 0
191 // even when there are pending loads.
192 static PRUint32 sPendingLoadCount;
194 // Boolean that tells us whether or not the current GC timer
195 // (sGCTimer) was scheduled due to a GC timer firing while we were in
196 // the middle of loading a page.
197 static PRBool sLoadInProgressGCTimer;
199 nsScriptNameSpaceManager *gNameSpaceManager;
201 static nsIJSRuntimeService *sRuntimeService;
202 JSRuntime *nsJSRuntime::sRuntime;
204 static const char kJSRuntimeServiceContractID[] =
205 "@mozilla.org/js/xpc/RuntimeService;1";
207 static JSGCCallback gOldJSGCCallback;
209 static PRBool sIsInitialized;
210 static PRBool sDidShutdown;
212 static PRInt32 sContextCount;
214 static PRTime sMaxScriptRunTime;
215 static PRTime sMaxChromeScriptRunTime;
217 static nsIScriptSecurityManager *sSecurityManager;
219 // nsUserActivityObserver observes user-interaction-active and
220 // user-interaction-inactive notifications. It counts the number of
221 // notifications and if the number is bigger than NS_CC_SOFT_LIMIT_ACTIVE
222 // (in case the current notification is user-interaction-active) or
223 // NS_CC_SOFT_LIMIT_INACTIVE (current notification is user-interaction-inactive)
224 // MaybeCC is called with aHigherParameter set to PR_TRUE, otherwise PR_FALSE.
226 // When moving from active state to inactive, nsJSContext::IntervalCC() is
227 // called unless the timer related to page load is active.
229 class nsUserActivityObserver : public nsIObserver
231 public:
232 nsUserActivityObserver()
233 : mUserActivityCounter(0), mOldCCollectCount(0) {}
234 NS_DECL_ISUPPORTS
235 NS_DECL_NSIOBSERVER
236 private:
237 PRUint32 mUserActivityCounter;
238 PRUint32 mOldCCollectCount;
241 NS_IMPL_ISUPPORTS1(nsUserActivityObserver, nsIObserver)
243 NS_IMETHODIMP
244 nsUserActivityObserver::Observe(nsISupports* aSubject, const char* aTopic,
245 const PRUnichar* aData)
247 if (mOldCCollectCount != sCCollectCount) {
248 mOldCCollectCount = sCCollectCount;
249 // Cycle collector was called between user interaction notifications, so
250 // we can reset the counter.
251 mUserActivityCounter = 0;
253 PRBool higherProbability = PR_FALSE;
254 ++mUserActivityCounter;
255 if (!strcmp(aTopic, "user-interaction-inactive")) {
256 #ifdef DEBUG_smaug
257 printf("user-interaction-inactive\n");
258 #endif
259 if (sUserIsActive) {
260 sUserIsActive = PR_FALSE;
261 if (!sGCTimer) {
262 nsJSContext::IntervalCC();
263 return NS_OK;
266 higherProbability = (mUserActivityCounter > NS_CC_SOFT_LIMIT_INACTIVE);
267 } else if (!strcmp(aTopic, "user-interaction-active")) {
268 #ifdef DEBUG_smaug
269 printf("user-interaction-active\n");
270 #endif
271 sUserIsActive = PR_TRUE;
272 higherProbability = (mUserActivityCounter > NS_CC_SOFT_LIMIT_ACTIVE);
273 } else if (!strcmp(aTopic, "xpcom-shutdown")) {
274 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
275 if (obs) {
276 obs->RemoveObserver(this, "user-interaction-active");
277 obs->RemoveObserver(this, "user-interaction-inactive");
278 obs->RemoveObserver(this, "xpcom-shutdown");
280 return NS_OK;
282 nsJSContext::MaybeCC(higherProbability);
283 return NS_OK;
286 // nsCCMemoryPressureObserver observes the memory-pressure notifications
287 // and forces a cycle collection when it happens.
289 class nsCCMemoryPressureObserver : public nsIObserver
291 public:
292 NS_DECL_ISUPPORTS
293 NS_DECL_NSIOBSERVER
296 NS_IMPL_ISUPPORTS1(nsCCMemoryPressureObserver, nsIObserver)
298 NS_IMETHODIMP
299 nsCCMemoryPressureObserver::Observe(nsISupports* aSubject, const char* aTopic,
300 const PRUnichar* aData)
302 nsJSContext::CC(nsnull);
303 return NS_OK;
306 /****************************************************************
307 ************************** AutoFree ****************************
308 ****************************************************************/
310 class AutoFree {
311 public:
312 AutoFree(void *aPtr) : mPtr(aPtr) {
314 ~AutoFree() {
315 if (mPtr)
316 nsMemory::Free(mPtr);
318 void Invalidate() {
319 mPtr = 0;
321 private:
322 void *mPtr;
325 class nsAutoPoolRelease {
326 public:
327 nsAutoPoolRelease(JSArenaPool *p, void *m) : mPool(p), mMark(m) {}
328 ~nsAutoPoolRelease() { JS_ARENA_RELEASE(mPool, mMark); }
329 private:
330 JSArenaPool *mPool;
331 void *mMark;
334 // A utility function for script languages to call. Although it looks small,
335 // the use of nsIDocShell and nsPresContext triggers a huge number of
336 // dependencies that most languages would not otherwise need.
337 // XXXmarkh - This function is mis-placed!
338 PRBool
339 NS_HandleScriptError(nsIScriptGlobalObject *aScriptGlobal,
340 nsScriptErrorEvent *aErrorEvent,
341 nsEventStatus *aStatus)
343 PRBool called = PR_FALSE;
344 nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(aScriptGlobal));
345 nsIDocShell *docShell = win ? win->GetDocShell() : nsnull;
346 if (docShell) {
347 nsRefPtr<nsPresContext> presContext;
348 docShell->GetPresContext(getter_AddRefs(presContext));
350 static PRInt32 errorDepth; // Recursion prevention
351 ++errorDepth;
353 if (presContext && errorDepth < 2) {
354 // Dispatch() must be synchronous for the recursion block
355 // (errorDepth) to work.
356 nsEventDispatcher::Dispatch(win, presContext, aErrorEvent, nsnull,
357 aStatus);
358 called = PR_TRUE;
360 --errorDepth;
362 return called;
365 class ScriptErrorEvent : public nsRunnable
367 public:
368 ScriptErrorEvent(nsIScriptGlobalObject* aScriptGlobal,
369 PRUint32 aLineNr, PRUint32 aColumn, PRUint32 aFlags,
370 const nsAString& aErrorMsg,
371 const nsAString& aFileName,
372 const nsAString& aSourceLine,
373 PRBool aDispatchEvent,
374 PRUint64 aWindowID)
375 : mScriptGlobal(aScriptGlobal), mLineNr(aLineNr), mColumn(aColumn),
376 mFlags(aFlags), mErrorMsg(aErrorMsg), mFileName(aFileName),
377 mSourceLine(aSourceLine), mDispatchEvent(aDispatchEvent),
378 mWindowID(aWindowID)
381 NS_IMETHOD Run()
383 nsEventStatus status = nsEventStatus_eIgnore;
384 // First, notify the DOM that we have a script error.
385 if (mDispatchEvent) {
386 nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(mScriptGlobal));
387 nsIDocShell* docShell = win ? win->GetDocShell() : nsnull;
388 if (docShell &&
389 !JSREPORT_IS_WARNING(mFlags) &&
390 !sHandlingScriptError) {
391 sHandlingScriptError = PR_TRUE; // Recursion prevention
393 nsRefPtr<nsPresContext> presContext;
394 docShell->GetPresContext(getter_AddRefs(presContext));
396 if (presContext) {
397 nsScriptErrorEvent errorevent(PR_TRUE, NS_LOAD_ERROR);
399 errorevent.fileName = mFileName.get();
401 nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(win));
402 NS_ENSURE_STATE(sop);
403 nsIPrincipal* p = sop->GetPrincipal();
404 NS_ENSURE_STATE(p);
406 PRBool sameOrigin = mFileName.IsVoid();
408 if (p && !sameOrigin) {
409 nsCOMPtr<nsIURI> errorURI;
410 NS_NewURI(getter_AddRefs(errorURI), mFileName);
411 if (errorURI) {
412 // FIXME: Once error reports contain the origin of the
413 // error (principals) we should change this to do the
414 // security check based on the principals and not
415 // URIs. See bug 387476.
416 sameOrigin = NS_SUCCEEDED(p->CheckMayLoad(errorURI, PR_FALSE));
420 NS_NAMED_LITERAL_STRING(xoriginMsg, "Script error.");
421 if (sameOrigin) {
422 errorevent.errorMsg = mErrorMsg.get();
423 errorevent.lineNr = mLineNr;
424 } else {
425 NS_WARNING("Not same origin error!");
426 errorevent.errorMsg = xoriginMsg.get();
427 errorevent.lineNr = 0;
428 // FIXME: once the principal of the script is not tied to
429 // the filename, we can stop using the post-redirect
430 // filename if we want and remove this line. Note that
431 // apparently we can't handle null filenames in the error
432 // event dispatching code.
433 static PRUnichar nullFilename[] = { PRUnichar(0) };
434 errorevent.fileName = nullFilename;
437 nsEventDispatcher::Dispatch(win, presContext, &errorevent, nsnull,
438 &status);
441 sHandlingScriptError = PR_FALSE;
445 if (status != nsEventStatus_eConsumeNoDefault) {
446 // Make an nsIScriptError and populate it with information from
447 // this error.
448 nsCOMPtr<nsIScriptError> errorObject =
449 do_CreateInstance("@mozilla.org/scripterror;1");
451 if (errorObject != nsnull) {
452 nsresult rv = NS_ERROR_NOT_AVAILABLE;
454 // Set category to chrome or content
455 nsCOMPtr<nsIScriptObjectPrincipal> scriptPrincipal =
456 do_QueryInterface(mScriptGlobal);
457 NS_ASSERTION(scriptPrincipal, "Global objects must implement "
458 "nsIScriptObjectPrincipal");
459 nsCOMPtr<nsIPrincipal> systemPrincipal;
460 sSecurityManager->GetSystemPrincipal(getter_AddRefs(systemPrincipal));
461 const char * category =
462 scriptPrincipal->GetPrincipal() == systemPrincipal
463 ? "chrome javascript"
464 : "content javascript";
466 nsCOMPtr<nsIScriptError2> error2(do_QueryInterface(errorObject));
467 if (error2) {
468 rv = error2->InitWithWindowID(mErrorMsg.get(), mFileName.get(),
469 mSourceLine.get(),
470 mLineNr, mColumn, mFlags,
471 category, mWindowID);
472 } else {
473 rv = errorObject->Init(mErrorMsg.get(), mFileName.get(),
474 mSourceLine.get(),
475 mLineNr, mColumn, mFlags,
476 category);
479 if (NS_SUCCEEDED(rv)) {
480 nsCOMPtr<nsIConsoleService> consoleService =
481 do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv);
482 if (NS_SUCCEEDED(rv)) {
483 consoleService->LogMessage(errorObject);
488 return NS_OK;
492 nsCOMPtr<nsIScriptGlobalObject> mScriptGlobal;
493 PRUint32 mLineNr;
494 PRUint32 mColumn;
495 PRUint32 mFlags;
496 nsString mErrorMsg;
497 nsString mFileName;
498 nsString mSourceLine;
499 PRBool mDispatchEvent;
500 PRUint64 mWindowID;
502 static PRBool sHandlingScriptError;
505 PRBool ScriptErrorEvent::sHandlingScriptError = PR_FALSE;
507 // NOTE: This function could be refactored to use the above. The only reason
508 // it has not been done is that the code below only fills the error event
509 // after it has a good nsPresContext - whereas using the above function
510 // would involve always filling it. Is that a concern?
511 void
512 NS_ScriptErrorReporter(JSContext *cx,
513 const char *message,
514 JSErrorReport *report)
516 // We don't want to report exceptions too eagerly, but warnings in the
517 // absence of werror are swallowed whole, so report those now.
518 if (!JSREPORT_IS_WARNING(report->flags)) {
519 JSStackFrame * fp = nsnull;
520 while ((fp = JS_FrameIterator(cx, &fp))) {
521 if (JS_IsScriptFrame(cx, fp)) {
522 return;
526 nsIXPConnect* xpc = nsContentUtils::XPConnect();
527 if (xpc) {
528 nsAXPCNativeCallContext *cc = nsnull;
529 xpc->GetCurrentNativeCallContext(&cc);
530 if (cc) {
531 nsAXPCNativeCallContext *prev = cc;
532 while (NS_SUCCEEDED(prev->GetPreviousCallContext(&prev)) && prev) {
533 PRUint16 lang;
534 if (NS_SUCCEEDED(prev->GetLanguage(&lang)) &&
535 lang == nsAXPCNativeCallContext::LANG_JS) {
536 return;
543 // XXX this means we are not going to get error reports on non DOM contexts
544 nsIScriptContext *context = nsJSUtils::GetDynamicScriptContext(cx);
546 // Note: we must do this before running any more code on cx (if cx is the
547 // dynamic script context).
548 ::JS_ClearPendingException(cx);
550 if (context) {
551 nsIScriptGlobalObject *globalObject = context->GetGlobalObject();
553 if (globalObject) {
554 nsAutoString fileName, msg;
555 if (!report->filename) {
556 fileName.SetIsVoid(PR_TRUE);
557 } else {
558 fileName.AssignWithConversion(report->filename);
561 const PRUnichar *m = reinterpret_cast<const PRUnichar*>
562 (report->ucmessage);
563 if (m) {
564 msg.Assign(m);
567 if (msg.IsEmpty() && message) {
568 msg.AssignWithConversion(message);
572 /* We do not try to report Out Of Memory via a dom
573 * event because the dom event handler would encounter
574 * an OOM exception trying to process the event, and
575 * then we'd need to generate a new OOM event for that
576 * new OOM instance -- this isn't pretty.
578 nsAutoString sourceLine;
579 sourceLine.Assign(reinterpret_cast<const PRUnichar*>(report->uclinebuf));
580 nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(globalObject);
581 PRUint64 windowID = win ? win->WindowID() : 0;
582 nsContentUtils::AddScriptRunner(
583 new ScriptErrorEvent(globalObject, report->lineno,
584 report->uctokenptr - report->uclinebuf,
585 report->flags, msg, fileName, sourceLine,
586 report->errorNumber != JSMSG_OUT_OF_MEMORY,
587 windowID));
591 #ifdef DEBUG
592 // Print it to stderr as well, for the benefit of those invoking
593 // mozilla with -console.
594 nsCAutoString error;
595 error.Assign("JavaScript ");
596 if (JSREPORT_IS_STRICT(report->flags))
597 error.Append("strict ");
598 if (JSREPORT_IS_WARNING(report->flags))
599 error.Append("warning: ");
600 else
601 error.Append("error: ");
602 error.Append(report->filename);
603 error.Append(", line ");
604 error.AppendInt(report->lineno, 10);
605 error.Append(": ");
606 if (report->ucmessage) {
607 AppendUTF16toUTF8(reinterpret_cast<const PRUnichar*>(report->ucmessage),
608 error);
609 } else {
610 error.Append(message);
613 fprintf(stderr, "%s\n", error.get());
614 fflush(stderr);
615 #endif
617 #ifdef PR_LOGGING
618 if (!gJSDiagnostics)
619 gJSDiagnostics = PR_NewLogModule("JSDiagnostics");
621 if (gJSDiagnostics) {
622 PR_LOG(gJSDiagnostics,
623 JSREPORT_IS_WARNING(report->flags) ? PR_LOG_WARNING : PR_LOG_ERROR,
624 ("file %s, line %u: %s\n%s%s",
625 report->filename, report->lineno, message,
626 report->linebuf ? report->linebuf : "",
627 (report->linebuf &&
628 report->linebuf[strlen(report->linebuf)-1] != '\n')
629 ? "\n"
630 : ""));
632 #endif
635 #ifdef DEBUG
636 // A couple of useful functions to call when you're debugging.
637 nsGlobalWindow *
638 JSObject2Win(JSContext *cx, JSObject *obj)
640 nsIXPConnect *xpc = nsContentUtils::XPConnect();
641 if (!xpc) {
642 return nsnull;
645 nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
646 xpc->GetWrappedNativeOfJSObject(cx, obj, getter_AddRefs(wrapper));
647 if (wrapper) {
648 nsCOMPtr<nsPIDOMWindow> win = do_QueryWrappedNative(wrapper);
649 if (win) {
650 return static_cast<nsGlobalWindow *>
651 (static_cast<nsPIDOMWindow *>(win));
655 return nsnull;
658 void
659 PrintWinURI(nsGlobalWindow *win)
661 if (!win) {
662 printf("No window passed in.\n");
663 return;
666 nsCOMPtr<nsIDocument> doc = do_QueryInterface(win->GetExtantDocument());
667 if (!doc) {
668 printf("No document in the window.\n");
669 return;
672 nsIURI *uri = doc->GetDocumentURI();
673 if (!uri) {
674 printf("Document doesn't have a URI.\n");
675 return;
678 nsCAutoString spec;
679 uri->GetSpec(spec);
680 printf("%s\n", spec.get());
683 void
684 PrintWinCodebase(nsGlobalWindow *win)
686 if (!win) {
687 printf("No window passed in.\n");
688 return;
691 nsIPrincipal *prin = win->GetPrincipal();
692 if (!prin) {
693 printf("Window doesn't have principals.\n");
694 return;
697 nsCOMPtr<nsIURI> uri;
698 prin->GetURI(getter_AddRefs(uri));
699 if (!uri) {
700 printf("No URI, maybe the system principal.\n");
701 return;
704 nsCAutoString spec;
705 uri->GetSpec(spec);
706 printf("%s\n", spec.get());
709 void
710 DumpString(const nsAString &str)
712 printf("%s\n", NS_ConvertUTF16toUTF8(str).get());
714 #endif
716 static already_AddRefed<nsIPrompt>
717 GetPromptFromContext(nsJSContext* ctx)
719 nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(ctx->GetGlobalObject()));
720 NS_ENSURE_TRUE(win, nsnull);
722 nsIDocShell *docShell = win->GetDocShell();
723 NS_ENSURE_TRUE(docShell, nsnull);
725 nsCOMPtr<nsIInterfaceRequestor> ireq(do_QueryInterface(docShell));
726 NS_ENSURE_TRUE(ireq, nsnull);
728 // Get the nsIPrompt interface from the docshell
729 nsIPrompt* prompt;
730 ireq->GetInterface(NS_GET_IID(nsIPrompt), (void**)&prompt);
731 return prompt;
734 JSBool
735 nsJSContext::DOMOperationCallback(JSContext *cx)
737 nsresult rv;
739 // Get the native context
740 nsJSContext *ctx = static_cast<nsJSContext *>(::JS_GetContextPrivate(cx));
742 if (!ctx) {
743 // Can happen; see bug 355811
744 return JS_TRUE;
747 // XXX Save the operation callback time so we can restore it after the GC,
748 // because GCing can cause JS to run on our context, causing our
749 // ScriptEvaluated to be called, and clearing our operation callback time.
750 // See bug 302333.
751 PRTime callbackTime = ctx->mOperationCallbackTime;
752 PRTime modalStateTime = ctx->mModalStateTime;
754 JS_MaybeGC(cx);
756 // Now restore the callback time and count, in case they got reset.
757 ctx->mOperationCallbackTime = callbackTime;
758 ctx->mModalStateTime = modalStateTime;
760 PRTime now = PR_Now();
762 if (callbackTime == 0) {
763 // Initialize mOperationCallbackTime to start timing how long the
764 // script has run
765 ctx->mOperationCallbackTime = now;
766 return JS_TRUE;
769 if (ctx->mModalStateDepth) {
770 // We're waiting on a modal dialog, nothing more to do here.
771 return JS_TRUE;
774 PRTime duration = now - callbackTime;
776 // Check the amount of time this script has been running, or if the
777 // dialog is disabled.
778 JSObject* global = ::JS_GetGlobalForScopeChain(cx);
779 PRBool isTrackingChromeCodeTime =
780 global && xpc::AccessCheck::isChrome(global->getCompartment());
781 if (duration < (isTrackingChromeCodeTime ?
782 sMaxChromeScriptRunTime : sMaxScriptRunTime)) {
783 return JS_TRUE;
786 if (!nsContentUtils::IsSafeToRunScript()) {
787 // If it isn't safe to run script, then it isn't safe to bring up the
788 // prompt (since that will cause the event loop to spin). In this case
789 // (which is rare), we just stop the script... But report a warning so
790 // that developers have some idea of what went wrong.
792 JS_ReportWarning(cx, "A long running script was terminated");
793 return JS_FALSE;
796 // If we get here we're most likely executing an infinite loop in JS,
797 // we'll tell the user about this and we'll give the user the option
798 // of stopping the execution of the script.
799 nsCOMPtr<nsIPrompt> prompt = GetPromptFromContext(ctx);
800 NS_ENSURE_TRUE(prompt, JS_TRUE);
802 // Check if we should offer the option to debug
803 JSStackFrame* fp = ::JS_GetScriptedCaller(cx, NULL);
804 PRBool debugPossible = (fp != nsnull && cx->debugHooks &&
805 cx->debugHooks->debuggerHandler != nsnull);
806 #ifdef MOZ_JSDEBUGGER
807 // Get the debugger service if necessary.
808 if (debugPossible) {
809 PRBool jsds_IsOn = PR_FALSE;
810 const char jsdServiceCtrID[] = "@mozilla.org/js/jsd/debugger-service;1";
811 nsCOMPtr<jsdIExecutionHook> jsdHook;
812 nsCOMPtr<jsdIDebuggerService> jsds = do_GetService(jsdServiceCtrID, &rv);
814 // Check if there's a user for the debugger service that's 'on' for us
815 if (NS_SUCCEEDED(rv)) {
816 jsds->GetDebuggerHook(getter_AddRefs(jsdHook));
817 jsds->GetIsOn(&jsds_IsOn);
820 // If there is a debug handler registered for this runtime AND
821 // ((jsd is on AND has a hook) OR (jsd isn't on (something else debugs)))
822 // then something useful will be done with our request to debug.
823 debugPossible = ((jsds_IsOn && (jsdHook != nsnull)) || !jsds_IsOn);
825 #endif
827 // Get localizable strings
828 nsXPIDLString title, msg, stopButton, waitButton, debugButton, neverShowDlg;
830 rv = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
831 "KillScriptTitle",
832 title);
834 rv |= nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
835 "StopScriptButton",
836 stopButton);
838 rv |= nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
839 "WaitForScriptButton",
840 waitButton);
842 rv |= nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
843 "DontAskAgain",
844 neverShowDlg);
847 if (debugPossible) {
848 rv |= nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
849 "DebugScriptButton",
850 debugButton);
852 rv |= nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
853 "KillScriptWithDebugMessage",
854 msg);
856 else {
857 rv |= nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
858 "KillScriptMessage",
859 msg);
862 //GetStringFromName can return NS_OK and still give NULL string
863 if (NS_FAILED(rv) || !title || !msg || !stopButton || !waitButton ||
864 (!debugButton && debugPossible) || !neverShowDlg) {
865 NS_ERROR("Failed to get localized strings.");
866 return JS_TRUE;
869 // Append file and line number information, if available
870 JSScript *script = fp ? ::JS_GetFrameScript(cx, fp) : nsnull;
871 if (script) {
872 const char *filename = ::JS_GetScriptFilename(cx, script);
873 if (filename) {
874 nsXPIDLString scriptLocation;
875 NS_ConvertUTF8toUTF16 filenameUTF16(filename);
876 const PRUnichar *formatParams[] = { filenameUTF16.get() };
877 rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES,
878 "KillScriptLocation",
879 formatParams, 1,
880 scriptLocation);
882 if (NS_SUCCEEDED(rv) && scriptLocation) {
883 msg.AppendLiteral("\n\n");
884 msg.Append(scriptLocation);
886 JSStackFrame *fp, *iterator = nsnull;
887 fp = ::JS_FrameIterator(cx, &iterator);
888 if (fp) {
889 jsbytecode *pc = ::JS_GetFramePC(cx, fp);
890 if (pc) {
891 PRUint32 lineno = ::JS_PCToLineNumber(cx, script, pc);
892 msg.Append(':');
893 msg.AppendInt(lineno);
900 PRInt32 buttonPressed = 0; //In case user exits dialog by clicking X
901 PRBool neverShowDlgChk = PR_FALSE;
902 PRUint32 buttonFlags = nsIPrompt::BUTTON_POS_1_DEFAULT +
903 (nsIPrompt::BUTTON_TITLE_IS_STRING *
904 (nsIPrompt::BUTTON_POS_0 + nsIPrompt::BUTTON_POS_1));
906 // Add a third button if necessary:
907 if (debugPossible)
908 buttonFlags += nsIPrompt::BUTTON_TITLE_IS_STRING * nsIPrompt::BUTTON_POS_2;
910 // Null out the operation callback while we're re-entering JS here.
911 ::JS_SetOperationCallback(cx, nsnull);
913 // Open the dialog.
914 rv = prompt->ConfirmEx(title, msg, buttonFlags, waitButton, stopButton,
915 debugButton, neverShowDlg, &neverShowDlgChk,
916 &buttonPressed);
918 ::JS_SetOperationCallback(cx, DOMOperationCallback);
920 if (NS_FAILED(rv) || (buttonPressed == 0)) {
921 // Allow the script to continue running
923 if (neverShowDlgChk) {
924 nsIPrefBranch *prefBranch = nsContentUtils::GetPrefBranch();
926 if (prefBranch) {
927 prefBranch->SetIntPref(isTrackingChromeCodeTime ?
928 "dom.max_chrome_script_run_time" :
929 "dom.max_script_run_time", 0);
933 ctx->mOperationCallbackTime = PR_Now();
934 return JS_TRUE;
936 else if ((buttonPressed == 2) && debugPossible) {
937 // Debug the script
938 jsval rval;
939 switch(cx->debugHooks->debuggerHandler(cx, script, ::JS_GetFramePC(cx, fp),
940 &rval,
941 cx->debugHooks->
942 debuggerHandlerData)) {
943 case JSTRAP_RETURN:
944 JS_SetFrameReturnValue(cx, fp, rval);
945 return JS_TRUE;
946 case JSTRAP_ERROR:
947 JS_ClearPendingException(cx);
948 return JS_FALSE;
949 case JSTRAP_THROW:
950 JS_SetPendingException(cx, rval);
951 return JS_FALSE;
952 case JSTRAP_CONTINUE:
953 default:
954 return JS_TRUE;
958 JS_ClearPendingException(cx);
959 return JS_FALSE;
962 void
963 nsJSContext::EnterModalState()
965 if (!mModalStateDepth) {
966 mModalStateTime = mOperationCallbackTime ? PR_Now() : 0;
968 ++mModalStateDepth;
971 void
972 nsJSContext::LeaveModalState()
974 if (!mModalStateDepth) {
975 NS_ERROR("Uh, mismatched LeaveModalState() call!");
977 return;
980 --mModalStateDepth;
982 // If we're still in a modal dialog, or mOperationCallbackTime is still
983 // uninitialized, do nothing.
984 if (mModalStateDepth || !mOperationCallbackTime) {
985 return;
988 // If mOperationCallbackTime was set when we entered the first dialog
989 // (and mModalStateTime is thus non-zero), adjust mOperationCallbackTime
990 // to account for time spent in the dialog.
991 // If mOperationCallbackTime got set while the modal dialog was open,
992 // simply set mOperationCallbackTime to the closing time of the dialog so
993 // that we never adjust mOperationCallbackTime to be in the future.
994 if (mModalStateTime) {
995 mOperationCallbackTime += PR_Now() - mModalStateTime;
997 else {
998 mOperationCallbackTime = PR_Now();
1002 #define JS_OPTIONS_DOT_STR "javascript.options."
1004 static const char js_options_dot_str[] = JS_OPTIONS_DOT_STR;
1005 static const char js_strict_option_str[] = JS_OPTIONS_DOT_STR "strict";
1006 #ifdef DEBUG
1007 static const char js_strict_debug_option_str[] = JS_OPTIONS_DOT_STR "strict.debug";
1008 #endif
1009 static const char js_werror_option_str[] = JS_OPTIONS_DOT_STR "werror";
1010 static const char js_relimit_option_str[]= JS_OPTIONS_DOT_STR "relimit";
1011 #ifdef JS_GC_ZEAL
1012 static const char js_zeal_option_str[] = JS_OPTIONS_DOT_STR "gczeal";
1013 #endif
1014 static const char js_tracejit_content_str[] = JS_OPTIONS_DOT_STR "tracejit.content";
1015 static const char js_tracejit_chrome_str[] = JS_OPTIONS_DOT_STR "tracejit.chrome";
1016 static const char js_methodjit_content_str[] = JS_OPTIONS_DOT_STR "methodjit.content";
1017 static const char js_methodjit_chrome_str[] = JS_OPTIONS_DOT_STR "methodjit.chrome";
1018 static const char js_profiling_content_str[] = JS_OPTIONS_DOT_STR "jitprofiling.content";
1019 static const char js_profiling_chrome_str[] = JS_OPTIONS_DOT_STR "jitprofiling.chrome";
1022 nsJSContext::JSOptionChangedCallback(const char *pref, void *data)
1024 nsJSContext *context = reinterpret_cast<nsJSContext *>(data);
1025 PRUint32 oldDefaultJSOptions = context->mDefaultJSOptions;
1026 PRUint32 newDefaultJSOptions = oldDefaultJSOptions;
1028 PRBool strict = nsContentUtils::GetBoolPref(js_strict_option_str);
1029 if (strict)
1030 newDefaultJSOptions |= JSOPTION_STRICT;
1031 else
1032 newDefaultJSOptions &= ~JSOPTION_STRICT;
1034 nsIScriptGlobalObject *global = context->GetGlobalObject();
1035 // XXX should we check for sysprin instead of a chrome window, to make
1036 // XXX components be covered by the chrome pref instead of the content one?
1037 nsCOMPtr<nsIDOMChromeWindow> chromeWindow(do_QueryInterface(global));
1039 PRBool useTraceJIT = nsContentUtils::GetBoolPref(chromeWindow ?
1040 js_tracejit_chrome_str :
1041 js_tracejit_content_str);
1042 PRBool useMethodJIT = nsContentUtils::GetBoolPref(chromeWindow ?
1043 js_methodjit_chrome_str :
1044 js_methodjit_content_str);
1045 PRBool useProfiling = nsContentUtils::GetBoolPref(chromeWindow ?
1046 js_profiling_chrome_str :
1047 js_profiling_content_str);
1048 nsCOMPtr<nsIXULRuntime> xr = do_GetService(XULRUNTIME_SERVICE_CONTRACTID);
1049 if (xr) {
1050 PRBool safeMode = PR_FALSE;
1051 xr->GetInSafeMode(&safeMode);
1052 if (safeMode) {
1053 useTraceJIT = PR_FALSE;
1054 useMethodJIT = PR_FALSE;
1055 useProfiling = PR_FALSE;
1059 if (useTraceJIT)
1060 newDefaultJSOptions |= JSOPTION_JIT;
1061 else
1062 newDefaultJSOptions &= ~JSOPTION_JIT;
1064 if (useMethodJIT)
1065 newDefaultJSOptions |= JSOPTION_METHODJIT;
1066 else
1067 newDefaultJSOptions &= ~JSOPTION_METHODJIT;
1069 if (useProfiling)
1070 newDefaultJSOptions |= JSOPTION_PROFILING;
1071 else
1072 newDefaultJSOptions &= ~JSOPTION_PROFILING;
1074 #ifdef DEBUG
1075 // In debug builds, warnings are enabled in chrome context if javascript.options.strict.debug is true
1076 PRBool strictDebug = nsContentUtils::GetBoolPref(js_strict_debug_option_str);
1077 // Note this callback is also called from context's InitClasses thus we don't
1078 // need to enable this directly from InitContext
1079 if (strictDebug && (newDefaultJSOptions & JSOPTION_STRICT) == 0) {
1080 if (chromeWindow)
1081 newDefaultJSOptions |= JSOPTION_STRICT;
1083 #endif
1085 PRBool werror = nsContentUtils::GetBoolPref(js_werror_option_str);
1086 if (werror)
1087 newDefaultJSOptions |= JSOPTION_WERROR;
1088 else
1089 newDefaultJSOptions &= ~JSOPTION_WERROR;
1091 PRBool relimit = nsContentUtils::GetBoolPref(js_relimit_option_str);
1092 if (relimit)
1093 newDefaultJSOptions |= JSOPTION_RELIMIT;
1094 else
1095 newDefaultJSOptions &= ~JSOPTION_RELIMIT;
1097 if (newDefaultJSOptions != oldDefaultJSOptions) {
1098 // Set options only if we used the old defaults; otherwise the page has
1099 // customized some via the options object and we defer to its wisdom.
1100 if (::JS_GetOptions(context->mContext) == oldDefaultJSOptions)
1101 ::JS_SetOptions(context->mContext, newDefaultJSOptions);
1103 // Save the new defaults for the next page load (InitContext).
1104 context->mDefaultJSOptions = newDefaultJSOptions;
1107 #ifdef JS_GC_ZEAL
1108 PRInt32 zeal = nsContentUtils::GetIntPref(js_zeal_option_str, -1);
1109 if (zeal >= 0)
1110 ::JS_SetGCZeal(context->mContext, (PRUint8)zeal);
1111 #endif
1113 return 0;
1116 nsJSContext::nsJSContext(JSRuntime *aRuntime)
1117 : mGCOnDestruction(PR_TRUE),
1118 mExecuteDepth(0)
1121 ++sContextCount;
1123 mDefaultJSOptions = JSOPTION_PRIVATE_IS_NSISUPPORTS | JSOPTION_ANONFUNFIX;
1125 mContext = ::JS_NewContext(aRuntime, gStackSize);
1126 if (mContext) {
1127 ::JS_SetContextPrivate(mContext, static_cast<nsIScriptContext *>(this));
1129 // Preserve any flags the context callback might have set.
1130 mDefaultJSOptions |= ::JS_GetOptions(mContext);
1132 // Make sure the new context gets the default context options
1133 ::JS_SetOptions(mContext, mDefaultJSOptions);
1135 // Watch for the JS boolean options
1136 nsContentUtils::RegisterPrefCallback(js_options_dot_str,
1137 JSOptionChangedCallback,
1138 this);
1140 ::JS_SetOperationCallback(mContext, DOMOperationCallback);
1142 xpc_LocalizeContext(mContext);
1144 mIsInitialized = PR_FALSE;
1145 mTerminations = nsnull;
1146 mScriptsEnabled = PR_TRUE;
1147 mOperationCallbackTime = 0;
1148 mModalStateTime = 0;
1149 mModalStateDepth = 0;
1150 mProcessingScriptTag = PR_FALSE;
1153 nsJSContext::~nsJSContext()
1155 #ifdef DEBUG
1156 nsCycleCollector_DEBUG_wasFreed(static_cast<nsIScriptContext*>(this));
1157 #endif
1158 NS_PRECONDITION(!mTerminations, "Shouldn't have termination funcs by now");
1160 mGlobalObjectRef = nsnull;
1162 DestroyJSContext();
1164 --sContextCount;
1166 if (!sContextCount && sDidShutdown) {
1167 // The last context is being deleted, and we're already in the
1168 // process of shutting down, release the JS runtime service, and
1169 // the security manager.
1171 NS_IF_RELEASE(sRuntimeService);
1172 NS_IF_RELEASE(sSecurityManager);
1176 void
1177 nsJSContext::DestroyJSContext()
1179 if (!mContext)
1180 return;
1182 // Clear our entry in the JSContext, bugzilla bug 66413
1183 ::JS_SetContextPrivate(mContext, nsnull);
1185 // Unregister our "javascript.options.*" pref-changed callback.
1186 nsContentUtils::UnregisterPrefCallback(js_options_dot_str,
1187 JSOptionChangedCallback,
1188 this);
1190 PRBool do_gc = mGCOnDestruction && !sGCTimer && sReadyForGC;
1192 // Let xpconnect destroy the JSContext when it thinks the time is right.
1193 nsIXPConnect *xpc = nsContentUtils::XPConnect();
1194 if (xpc) {
1195 xpc->ReleaseJSContext(mContext, !do_gc);
1196 } else if (do_gc) {
1197 ::JS_DestroyContext(mContext);
1198 } else {
1199 ::JS_DestroyContextNoGC(mContext);
1201 mContext = nsnull;
1204 // QueryInterface implementation for nsJSContext
1205 NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSContext)
1206 NS_IMPL_CYCLE_COLLECTION_ROOT_BEGIN(nsJSContext)
1207 NS_ASSERTION(!tmp->mContext || tmp->mContext->outstandingRequests == 0,
1208 "Trying to unlink a context with outstanding requests.");
1209 tmp->mIsInitialized = PR_FALSE;
1210 tmp->mGCOnDestruction = PR_FALSE;
1211 tmp->DestroyJSContext();
1212 NS_IMPL_CYCLE_COLLECTION_ROOT_END
1213 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSContext)
1214 NS_IMPL_CYCLE_COLLECTION_TRACE_END
1215 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSContext)
1216 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mGlobalObjectRef)
1217 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1218 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsJSContext)
1219 NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsJSContext, tmp->GetCCRefcnt())
1220 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mGlobalObjectRef)
1221 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mContext");
1222 nsContentUtils::XPConnect()->NoteJSContext(tmp->mContext, cb);
1223 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1225 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSContext)
1226 NS_INTERFACE_MAP_ENTRY(nsIScriptContext)
1227 NS_INTERFACE_MAP_ENTRY(nsIScriptContextPrincipal)
1228 NS_INTERFACE_MAP_ENTRY(nsIXPCScriptNotify)
1229 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIScriptContext)
1230 NS_INTERFACE_MAP_END
1233 NS_IMPL_CYCLE_COLLECTING_ADDREF_AMBIGUOUS(nsJSContext, nsIScriptContext)
1234 NS_IMPL_CYCLE_COLLECTING_RELEASE_AMBIGUOUS(nsJSContext, nsIScriptContext)
1236 nsrefcnt
1237 nsJSContext::GetCCRefcnt()
1239 nsrefcnt refcnt = mRefCnt.get();
1240 if (NS_LIKELY(mContext))
1241 refcnt += mContext->outstandingRequests;
1242 return refcnt;
1245 nsresult
1246 nsJSContext::EvaluateStringWithValue(const nsAString& aScript,
1247 void *aScopeObject,
1248 nsIPrincipal *aPrincipal,
1249 const char *aURL,
1250 PRUint32 aLineNo,
1251 PRUint32 aVersion,
1252 void* aRetValue,
1253 PRBool* aIsUndefined)
1255 NS_TIME_FUNCTION_MIN_FMT(1.0, "%s (line %d) (url: %s, line: %d)", MOZ_FUNCTION_NAME,
1256 __LINE__, aURL, aLineNo);
1258 NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED);
1260 if (!mScriptsEnabled) {
1261 if (aIsUndefined) {
1262 *aIsUndefined = PR_TRUE;
1265 return NS_OK;
1268 nsresult rv;
1269 if (!aScopeObject)
1270 aScopeObject = ::JS_GetGlobalObject(mContext);
1272 // Safety first: get an object representing the script's principals, i.e.,
1273 // the entities who signed this script, or the fully-qualified-domain-name
1274 // or "codebase" from which it was loaded.
1275 JSPrincipals *jsprin;
1276 nsIPrincipal *principal = aPrincipal;
1277 if (!aPrincipal) {
1278 nsIScriptGlobalObject *global = GetGlobalObject();
1279 if (!global)
1280 return NS_ERROR_FAILURE;
1281 nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal =
1282 do_QueryInterface(global, &rv);
1283 if (NS_FAILED(rv))
1284 return NS_ERROR_FAILURE;
1285 principal = objPrincipal->GetPrincipal();
1286 if (!principal)
1287 return NS_ERROR_FAILURE;
1290 principal->GetJSPrincipals(mContext, &jsprin);
1292 // From here on, we must JSPRINCIPALS_DROP(jsprin) before returning...
1294 PRBool ok = PR_FALSE;
1296 rv = sSecurityManager->CanExecuteScripts(mContext, principal, &ok);
1297 if (NS_FAILED(rv)) {
1298 JSPRINCIPALS_DROP(mContext, jsprin);
1299 return NS_ERROR_FAILURE;
1302 // Push our JSContext on the current thread's context stack so JS called
1303 // from native code via XPConnect uses the right context. Do this whether
1304 // or not the SecurityManager said "ok", in order to simplify control flow
1305 // below where we pop before returning.
1306 nsCOMPtr<nsIJSContextStack> stack =
1307 do_GetService("@mozilla.org/js/xpc/ContextStack;1", &rv);
1308 if (NS_FAILED(rv) || NS_FAILED(stack->Push(mContext))) {
1309 JSPRINCIPALS_DROP(mContext, jsprin);
1310 return NS_ERROR_FAILURE;
1313 jsval val;
1315 rv = sSecurityManager->PushContextPrincipal(mContext, nsnull, principal);
1316 NS_ENSURE_SUCCESS(rv, rv);
1318 nsJSContext::TerminationFuncHolder holder(this);
1320 // SecurityManager said "ok", but don't compile if aVersion is unknown.
1321 // Since the caller is responsible for parsing the version strings, we just
1322 // check it isn't JSVERSION_UNKNOWN.
1323 if (ok && ((JSVersion)aVersion) != JSVERSION_UNKNOWN) {
1325 JSAutoRequest ar(mContext);
1327 JSAutoEnterCompartment ac;
1328 if (!ac.enter(mContext, (JSObject *)aScopeObject)) {
1329 JSPRINCIPALS_DROP(mContext, jsprin);
1330 stack->Pop(nsnull);
1331 return NS_ERROR_FAILURE;
1334 ++mExecuteDepth;
1336 ok = ::JS_EvaluateUCScriptForPrincipalsVersion(mContext,
1337 (JSObject *)aScopeObject,
1338 jsprin,
1339 (jschar*)PromiseFlatString(aScript).get(),
1340 aScript.Length(),
1341 aURL,
1342 aLineNo,
1343 &val,
1344 JSVersion(aVersion));
1346 --mExecuteDepth;
1348 if (!ok) {
1349 // Tell XPConnect about any pending exceptions. This is needed
1350 // to avoid dropping JS exceptions in case we got here through
1351 // nested calls through XPConnect.
1353 ReportPendingException();
1357 // Whew! Finally done with these manually ref-counted things.
1358 JSPRINCIPALS_DROP(mContext, jsprin);
1360 // If all went well, convert val to a string (XXXbe unless undefined?).
1361 if (ok) {
1362 if (aIsUndefined) {
1363 *aIsUndefined = JSVAL_IS_VOID(val);
1366 *static_cast<jsval*>(aRetValue) = val;
1367 // XXX - nsScriptObjectHolder should be used once this method moves to
1368 // the new world order. However, use of 'jsval' appears to make this
1369 // tricky...
1371 else {
1372 if (aIsUndefined) {
1373 *aIsUndefined = PR_TRUE;
1377 sSecurityManager->PopContextPrincipal(mContext);
1379 // Pop here, after JS_ValueToString and any other possible evaluation.
1380 if (NS_FAILED(stack->Pop(nsnull)))
1381 rv = NS_ERROR_FAILURE;
1383 // ScriptEvaluated needs to come after we pop the stack
1384 ScriptEvaluated(PR_TRUE);
1386 return rv;
1390 // Helper function to convert a jsval to an nsAString, and set
1391 // exception flags if the conversion fails.
1392 static nsresult
1393 JSValueToAString(JSContext *cx, jsval val, nsAString *result,
1394 PRBool *isUndefined)
1396 if (isUndefined) {
1397 *isUndefined = JSVAL_IS_VOID(val);
1400 if (!result) {
1401 return NS_OK;
1404 JSString* jsstring = ::JS_ValueToString(cx, val);
1405 if (!jsstring) {
1406 goto error;
1409 size_t length;
1410 const jschar *chars;
1411 chars = ::JS_GetStringCharsAndLength(cx, jsstring, &length);
1412 if (!chars) {
1413 goto error;
1416 result->Assign(chars, length);
1417 return NS_OK;
1419 error:
1420 // We failed to convert val to a string. We're either OOM, or the
1421 // security manager denied access to .toString(), or somesuch, on
1422 // an object. Treat this case as if the result were undefined.
1424 result->Truncate();
1426 if (isUndefined) {
1427 *isUndefined = PR_TRUE;
1430 if (!::JS_IsExceptionPending(cx)) {
1431 // JS_ValueToString()/JS_GetStringCharsAndLength returned null w/o an
1432 // exception pending. That means we're OOM.
1434 return NS_ERROR_OUT_OF_MEMORY;
1437 return NS_OK;
1440 nsIScriptObjectPrincipal*
1441 nsJSContext::GetObjectPrincipal()
1443 nsCOMPtr<nsIScriptObjectPrincipal> prin = do_QueryInterface(GetGlobalObject());
1444 return prin;
1447 nsresult
1448 nsJSContext::EvaluateString(const nsAString& aScript,
1449 void *aScopeObject,
1450 nsIPrincipal *aPrincipal,
1451 const char *aURL,
1452 PRUint32 aLineNo,
1453 PRUint32 aVersion,
1454 nsAString *aRetValue,
1455 PRBool* aIsUndefined)
1457 NS_TIME_FUNCTION_MIN_FMT(1.0, "%s (line %d) (url: %s, line: %d)", MOZ_FUNCTION_NAME,
1458 __LINE__, aURL, aLineNo);
1460 NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED);
1462 if (!mScriptsEnabled) {
1463 if (aIsUndefined) {
1464 *aIsUndefined = PR_TRUE;
1467 if (aRetValue) {
1468 aRetValue->Truncate();
1471 return NS_OK;
1474 nsresult rv;
1475 if (!aScopeObject)
1476 aScopeObject = ::JS_GetGlobalObject(mContext);
1478 // Safety first: get an object representing the script's principals, i.e.,
1479 // the entities who signed this script, or the fully-qualified-domain-name
1480 // or "codebase" from which it was loaded.
1481 JSPrincipals *jsprin;
1482 nsIPrincipal *principal = aPrincipal;
1483 if (aPrincipal) {
1484 aPrincipal->GetJSPrincipals(mContext, &jsprin);
1486 else {
1487 nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal =
1488 do_QueryInterface(GetGlobalObject(), &rv);
1489 if (NS_FAILED(rv))
1490 return NS_ERROR_FAILURE;
1491 principal = objPrincipal->GetPrincipal();
1492 if (!principal)
1493 return NS_ERROR_FAILURE;
1494 principal->GetJSPrincipals(mContext, &jsprin);
1497 // From here on, we must JSPRINCIPALS_DROP(jsprin) before returning...
1499 PRBool ok = PR_FALSE;
1501 rv = sSecurityManager->CanExecuteScripts(mContext, principal, &ok);
1502 if (NS_FAILED(rv)) {
1503 JSPRINCIPALS_DROP(mContext, jsprin);
1504 return NS_ERROR_FAILURE;
1507 // Push our JSContext on the current thread's context stack so JS called
1508 // from native code via XPConnect uses the right context. Do this whether
1509 // or not the SecurityManager said "ok", in order to simplify control flow
1510 // below where we pop before returning.
1511 nsCOMPtr<nsIJSContextStack> stack =
1512 do_GetService("@mozilla.org/js/xpc/ContextStack;1", &rv);
1513 if (NS_FAILED(rv) || NS_FAILED(stack->Push(mContext))) {
1514 JSPRINCIPALS_DROP(mContext, jsprin);
1515 return NS_ERROR_FAILURE;
1518 // The result of evaluation, used only if there were no errors. This need
1519 // not be a GC root currently, provided we run the GC only from the
1520 // operation callback or from ScriptEvaluated.
1521 jsval val = JSVAL_VOID;
1522 jsval* vp = aRetValue ? &val : NULL;
1524 rv = sSecurityManager->PushContextPrincipal(mContext, nsnull, principal);
1525 NS_ENSURE_SUCCESS(rv, rv);
1527 nsJSContext::TerminationFuncHolder holder(this);
1529 ++mExecuteDepth;
1531 // SecurityManager said "ok", but don't compile if aVersion is unknown.
1532 // Since the caller is responsible for parsing the version strings, we just
1533 // check it isn't JSVERSION_UNKNOWN.
1534 if (ok && ((JSVersion)aVersion) != JSVERSION_UNKNOWN) {
1535 JSAutoRequest ar(mContext);
1536 JSAutoEnterCompartment ac;
1537 if (!ac.enter(mContext, (JSObject *)aScopeObject)) {
1538 stack->Pop(nsnull);
1539 JSPRINCIPALS_DROP(mContext, jsprin);
1540 return NS_ERROR_FAILURE;
1543 ok = ::JS_EvaluateUCScriptForPrincipalsVersion(mContext,
1544 (JSObject *)aScopeObject,
1545 jsprin,
1546 (jschar*)PromiseFlatString(aScript).get(),
1547 aScript.Length(),
1548 aURL,
1549 aLineNo,
1551 JSVersion(aVersion));
1553 if (!ok) {
1554 // Tell XPConnect about any pending exceptions. This is needed
1555 // to avoid dropping JS exceptions in case we got here through
1556 // nested calls through XPConnect.
1558 ReportPendingException();
1562 // Whew! Finally done with these manually ref-counted things.
1563 JSPRINCIPALS_DROP(mContext, jsprin);
1565 // If all went well, convert val to a string if one is wanted.
1566 if (ok) {
1567 JSAutoRequest ar(mContext);
1568 JSAutoEnterCompartment ac;
1569 if (!ac.enter(mContext, (JSObject *)aScopeObject)) {
1570 stack->Pop(nsnull);
1572 rv = JSValueToAString(mContext, val, aRetValue, aIsUndefined);
1574 else {
1575 if (aIsUndefined) {
1576 *aIsUndefined = PR_TRUE;
1579 if (aRetValue) {
1580 aRetValue->Truncate();
1584 --mExecuteDepth;
1586 sSecurityManager->PopContextPrincipal(mContext);
1588 // Pop here, after JS_ValueToString and any other possible evaluation.
1589 if (NS_FAILED(stack->Pop(nsnull)))
1590 rv = NS_ERROR_FAILURE;
1592 // ScriptEvaluated needs to come after we pop the stack
1593 ScriptEvaluated(PR_TRUE);
1595 return rv;
1598 nsresult
1599 nsJSContext::CompileScript(const PRUnichar* aText,
1600 PRInt32 aTextLength,
1601 void *aScopeObject,
1602 nsIPrincipal *aPrincipal,
1603 const char *aURL,
1604 PRUint32 aLineNo,
1605 PRUint32 aVersion,
1606 nsScriptObjectHolder &aScriptObject)
1608 NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED);
1610 nsresult rv;
1611 NS_ENSURE_ARG_POINTER(aPrincipal);
1613 if (!aScopeObject)
1614 aScopeObject = ::JS_GetGlobalObject(mContext);
1616 JSPrincipals *jsprin;
1617 aPrincipal->GetJSPrincipals(mContext, &jsprin);
1618 // From here on, we must JSPRINCIPALS_DROP(jsprin) before returning...
1620 PRBool ok = PR_FALSE;
1622 rv = sSecurityManager->CanExecuteScripts(mContext, aPrincipal, &ok);
1623 if (NS_FAILED(rv)) {
1624 JSPRINCIPALS_DROP(mContext, jsprin);
1625 return NS_ERROR_FAILURE;
1628 aScriptObject.drop(); // ensure old object not used on failure...
1630 // SecurityManager said "ok", but don't compile if aVersion is unknown.
1631 // Since the caller is responsible for parsing the version strings, we just
1632 // check it isn't JSVERSION_UNKNOWN.
1633 if (ok && ((JSVersion)aVersion) != JSVERSION_UNKNOWN) {
1634 JSAutoRequest ar(mContext);
1636 JSScript* script =
1637 ::JS_CompileUCScriptForPrincipalsVersion(mContext,
1638 (JSObject *)aScopeObject,
1639 jsprin,
1640 (jschar*) aText,
1641 aTextLength,
1642 aURL,
1643 aLineNo,
1644 JSVersion(aVersion));
1645 if (script) {
1646 JSObject *scriptObject = ::JS_NewScriptObject(mContext, script);
1647 if (scriptObject) {
1648 NS_ASSERTION(aScriptObject.getScriptTypeID()==JAVASCRIPT,
1649 "Expecting JS script object holder");
1650 rv = aScriptObject.set(scriptObject);
1651 } else {
1652 ::JS_DestroyScript(mContext, script);
1653 script = nsnull;
1655 } else {
1656 rv = NS_ERROR_OUT_OF_MEMORY;
1660 // Whew! Finally done.
1661 JSPRINCIPALS_DROP(mContext, jsprin);
1662 return rv;
1665 nsresult
1666 nsJSContext::ExecuteScript(void *aScriptObject,
1667 void *aScopeObject,
1668 nsAString* aRetValue,
1669 PRBool* aIsUndefined)
1671 NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED);
1673 if (!mScriptsEnabled) {
1674 if (aIsUndefined) {
1675 *aIsUndefined = PR_TRUE;
1678 if (aRetValue) {
1679 aRetValue->Truncate();
1682 return NS_OK;
1685 nsresult rv;
1687 if (!aScopeObject)
1688 aScopeObject = ::JS_GetGlobalObject(mContext);
1690 // Push our JSContext on our thread's context stack, in case native code
1691 // called from JS calls back into JS via XPConnect.
1692 nsCOMPtr<nsIJSContextStack> stack =
1693 do_GetService("@mozilla.org/js/xpc/ContextStack;1", &rv);
1694 if (NS_FAILED(rv) || NS_FAILED(stack->Push(mContext))) {
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;
1702 JSBool ok;
1704 JSObject *scriptObj = (JSObject*)aScriptObject;
1705 nsCOMPtr<nsIPrincipal> principal;
1707 rv = sSecurityManager->GetObjectPrincipal(mContext, scriptObj, getter_AddRefs(principal));
1708 NS_ENSURE_SUCCESS(rv, rv);
1710 rv = sSecurityManager->PushContextPrincipal(mContext, nsnull, principal);
1711 NS_ENSURE_SUCCESS(rv, rv);
1713 nsJSContext::TerminationFuncHolder holder(this);
1714 JSAutoRequest ar(mContext);
1715 ++mExecuteDepth;
1716 ok = ::JS_ExecuteScript(mContext,
1717 (JSObject *)aScopeObject,
1718 (JSScript*)::JS_GetPrivate(mContext, scriptObj),
1719 &val);
1721 if (ok) {
1722 // If all went well, convert val to a string (XXXbe unless undefined?).
1723 rv = JSValueToAString(mContext, val, aRetValue, aIsUndefined);
1724 } else {
1725 if (aIsUndefined) {
1726 *aIsUndefined = PR_TRUE;
1729 if (aRetValue) {
1730 aRetValue->Truncate();
1734 --mExecuteDepth;
1736 sSecurityManager->PopContextPrincipal(mContext);
1738 // Pop here, after JS_ValueToString and any other possible evaluation.
1739 if (NS_FAILED(stack->Pop(nsnull)))
1740 rv = NS_ERROR_FAILURE;
1742 // ScriptEvaluated needs to come after we pop the stack
1743 ScriptEvaluated(PR_TRUE);
1745 return rv;
1749 #ifdef DEBUG
1750 PRBool
1751 AtomIsEventHandlerName(nsIAtom *aName)
1753 const PRUnichar *name = aName->GetUTF16String();
1755 const PRUnichar *cp;
1756 PRUnichar c;
1757 for (cp = name; *cp != '\0'; ++cp)
1759 c = *cp;
1760 if ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z'))
1761 return PR_FALSE;
1764 return PR_TRUE;
1766 #endif
1768 // Helper function to find the JSObject associated with a (presumably DOM)
1769 // interface.
1770 nsresult
1771 nsJSContext::JSObjectFromInterface(nsISupports* aTarget, void *aScope, JSObject **aRet)
1773 // It is legal to specify a null target.
1774 if (!aTarget) {
1775 *aRet = nsnull;
1776 return NS_OK;
1779 // Get the jsobject associated with this target
1780 // We don't wrap here because we trust the JS engine to wrap the target
1781 // later.
1782 nsresult rv;
1783 jsval v;
1784 rv = nsContentUtils::WrapNative(mContext, (JSObject *)aScope, aTarget, &v);
1785 NS_ENSURE_SUCCESS(rv, rv);
1787 #ifdef NS_DEBUG
1788 nsCOMPtr<nsISupports> targetSupp = do_QueryInterface(aTarget);
1789 nsCOMPtr<nsISupports> native =
1790 nsContentUtils::XPConnect()->GetNativeOfWrapper(mContext,
1791 JSVAL_TO_OBJECT(v));
1792 NS_ASSERTION(native == targetSupp, "Native should be the target!");
1793 #endif
1795 *aRet = JSVAL_TO_OBJECT(v);
1797 return NS_OK;
1801 nsresult
1802 nsJSContext::CompileEventHandler(nsIAtom *aName,
1803 PRUint32 aArgCount,
1804 const char** aArgNames,
1805 const nsAString& aBody,
1806 const char *aURL, PRUint32 aLineNo,
1807 PRUint32 aVersion,
1808 nsScriptObjectHolder &aHandler)
1810 NS_TIME_FUNCTION_MIN_FMT(1.0, "%s (line %d) (url: %s, line: %d)", MOZ_FUNCTION_NAME,
1811 __LINE__, aURL, aLineNo);
1813 NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED);
1815 NS_PRECONDITION(AtomIsEventHandlerName(aName), "Bad event name");
1816 NS_PRECONDITION(!::JS_IsExceptionPending(mContext),
1817 "Why are we being called with a pending exception?");
1819 if (!sSecurityManager) {
1820 NS_ERROR("Huh, we need a script security manager to compile "
1821 "an event handler!");
1823 return NS_ERROR_UNEXPECTED;
1826 // Don't compile if aVersion is unknown. Since the caller is responsible for
1827 // parsing the version strings, we just check it isn't JSVERSION_UNKNOWN.
1828 if ((JSVersion)aVersion == JSVERSION_UNKNOWN) {
1829 return NS_ERROR_ILLEGAL_VALUE;
1832 #ifdef DEBUG
1833 JSContext* top = nsContentUtils::GetCurrentJSContext();
1834 NS_ASSERTION(mContext == top, "Context not properly pushed!");
1835 #endif
1837 // Event handlers are always shared, and must be bound before use.
1838 // Therefore we never bother compiling with principals.
1839 // (that probably means we should avoid JS_CompileUCFunctionForPrincipals!)
1840 JSAutoRequest ar(mContext);
1842 JSFunction* fun =
1843 ::JS_CompileUCFunctionForPrincipalsVersion(mContext,
1844 nsnull, nsnull,
1845 nsAtomCString(aName).get(), aArgCount, aArgNames,
1846 (jschar*)PromiseFlatString(aBody).get(),
1847 aBody.Length(),
1848 aURL, aLineNo, JSVersion(aVersion));
1850 if (!fun) {
1851 ReportPendingException();
1852 return NS_ERROR_ILLEGAL_VALUE;
1855 JSObject *handler = ::JS_GetFunctionObject(fun);
1856 NS_ASSERTION(aHandler.getScriptTypeID()==JAVASCRIPT,
1857 "Expecting JS script object holder");
1858 return aHandler.set((void *)handler);
1861 // XXX - note that CompileFunction doesn't yet play the nsScriptObjectHolder
1862 // game - caller must still ensure JS GC root.
1863 nsresult
1864 nsJSContext::CompileFunction(void* aTarget,
1865 const nsACString& aName,
1866 PRUint32 aArgCount,
1867 const char** aArgArray,
1868 const nsAString& aBody,
1869 const char* aURL,
1870 PRUint32 aLineNo,
1871 PRUint32 aVersion,
1872 PRBool aShared,
1873 void** aFunctionObject)
1875 NS_TIME_FUNCTION_FMT(1.0, "%s (line %d) (function: %s, url: %s, line: %d)", MOZ_FUNCTION_NAME,
1876 __LINE__, aName.BeginReading(), aURL, aLineNo);
1878 NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED);
1880 // Don't compile if aVersion is unknown. Since the caller is responsible for
1881 // parsing the version strings, we just check it isn't JSVERSION_UNKNOWN.
1882 if ((JSVersion)aVersion == JSVERSION_UNKNOWN) {
1883 return NS_ERROR_ILLEGAL_VALUE;
1886 JSPrincipals *jsprin = nsnull;
1888 nsIScriptGlobalObject *global = GetGlobalObject();
1889 if (global) {
1890 // XXXbe why the two-step QI? speed up via a new GetGlobalObjectData func?
1891 nsCOMPtr<nsIScriptObjectPrincipal> globalData = do_QueryInterface(global);
1892 if (globalData) {
1893 nsIPrincipal *prin = globalData->GetPrincipal();
1894 if (!prin)
1895 return NS_ERROR_FAILURE;
1896 prin->GetJSPrincipals(mContext, &jsprin);
1900 JSObject *target = (JSObject*)aTarget;
1902 JSAutoRequest ar(mContext);
1904 JSFunction* fun =
1905 ::JS_CompileUCFunctionForPrincipalsVersion(mContext,
1906 aShared ? nsnull : target, jsprin,
1907 PromiseFlatCString(aName).get(),
1908 aArgCount, aArgArray,
1909 (jschar*)PromiseFlatString(aBody).get(),
1910 aBody.Length(),
1911 aURL, aLineNo,
1912 JSVersion(aVersion));
1914 if (jsprin)
1915 JSPRINCIPALS_DROP(mContext, jsprin);
1916 if (!fun)
1917 return NS_ERROR_FAILURE;
1919 JSObject *handler = ::JS_GetFunctionObject(fun);
1920 if (aFunctionObject)
1921 *aFunctionObject = (void*) handler;
1922 return NS_OK;
1925 nsresult
1926 nsJSContext::CallEventHandler(nsISupports* aTarget, void *aScope, void *aHandler,
1927 nsIArray *aargv, nsIVariant **arv)
1929 NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED);
1931 if (!mScriptsEnabled) {
1932 return NS_OK;
1935 #ifdef NS_FUNCTION_TIMER
1937 JSObject *obj = static_cast<JSObject *>(aHandler);
1938 JSString *id = JS_GetFunctionId(static_cast<JSFunction *>(JS_GetPrivate(mContext, obj)));
1939 JSAutoByteString bytes;
1940 const char *name = !id ? "anonymous" : bytes.encode(mContext, id) ? bytes.ptr() : "<error>";
1941 NS_TIME_FUNCTION_FMT(1.0, "%s (line %d) (function: %s)", MOZ_FUNCTION_NAME, __LINE__, name);
1943 #endif
1945 JSAutoRequest ar(mContext);
1946 JSObject* target = nsnull;
1947 nsresult rv = JSObjectFromInterface(aTarget, aScope, &target);
1948 NS_ENSURE_SUCCESS(rv, rv);
1950 js::AutoObjectRooter targetVal(mContext, target);
1951 jsval rval = JSVAL_VOID;
1953 // This one's a lot easier than EvaluateString because we don't have to
1954 // hassle with principals: they're already compiled into the JS function.
1955 // xxxmarkh - this comment is no longer true - principals are not used at
1956 // all now, and never were in some cases.
1958 nsCxPusher pusher;
1959 if (!pusher.Push(mContext, PR_TRUE))
1960 return NS_ERROR_FAILURE;
1962 // check if the event handler can be run on the object in question
1963 rv = sSecurityManager->CheckFunctionAccess(mContext, aHandler, target);
1965 nsJSContext::TerminationFuncHolder holder(this);
1967 if (NS_SUCCEEDED(rv)) {
1968 // Convert args to jsvals.
1969 PRUint32 argc = 0;
1970 jsval *argv = nsnull;
1972 js::LazilyConstructed<nsAutoPoolRelease> poolRelease;
1973 js::LazilyConstructed<js::AutoArrayRooter> tvr;
1975 // Use |target| as the scope for wrapping the arguments, since aScope is
1976 // the safe scope in many cases, which isn't very useful. Wrapping aTarget
1977 // was OK because those typically have PreCreate methods that give them the
1978 // right scope anyway, and we want to make sure that the arguments end up
1979 // in the same scope as aTarget.
1980 rv = ConvertSupportsTojsvals(aargv, target, &argc,
1981 &argv, poolRelease, tvr);
1982 NS_ENSURE_SUCCESS(rv, rv);
1984 JSObject *funobj = static_cast<JSObject *>(aHandler);
1985 nsCOMPtr<nsIPrincipal> principal;
1986 rv = sSecurityManager->GetObjectPrincipal(mContext, funobj,
1987 getter_AddRefs(principal));
1988 NS_ENSURE_SUCCESS(rv, rv);
1990 JSStackFrame *currentfp = nsnull;
1991 rv = sSecurityManager->PushContextPrincipal(mContext,
1992 JS_FrameIterator(mContext, &currentfp),
1993 principal);
1994 NS_ENSURE_SUCCESS(rv, rv);
1996 jsval funval = OBJECT_TO_JSVAL(funobj);
1997 JSAutoEnterCompartment ac;
1998 if (!ac.enter(mContext, target)) {
1999 sSecurityManager->PopContextPrincipal(mContext);
2000 return NS_ERROR_FAILURE;
2003 ++mExecuteDepth;
2004 PRBool ok = ::JS_CallFunctionValue(mContext, target,
2005 funval, argc, argv, &rval);
2006 --mExecuteDepth;
2008 if (!ok) {
2009 // Tell XPConnect about any pending exceptions. This is needed
2010 // to avoid dropping JS exceptions in case we got here through
2011 // nested calls through XPConnect.
2013 ReportPendingException();
2015 // Don't pass back results from failed calls.
2016 rval = JSVAL_VOID;
2018 // Tell the caller that the handler threw an error.
2019 rv = NS_ERROR_FAILURE;
2022 sSecurityManager->PopContextPrincipal(mContext);
2025 pusher.Pop();
2027 // Convert to variant before calling ScriptEvaluated, as it may GC, meaning
2028 // we would need to root rval.
2029 if (NS_SUCCEEDED(rv)) {
2030 if (rval == JSVAL_NULL)
2031 *arv = nsnull;
2032 else
2033 rv = nsContentUtils::XPConnect()->JSToVariant(mContext, rval, arv);
2036 // ScriptEvaluated needs to come after we pop the stack
2037 ScriptEvaluated(PR_TRUE);
2039 return rv;
2042 nsresult
2043 nsJSContext::BindCompiledEventHandler(nsISupports* aTarget, void *aScope,
2044 nsIAtom *aName,
2045 void *aHandler)
2047 NS_ENSURE_ARG(aHandler);
2048 NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED);
2050 NS_PRECONDITION(AtomIsEventHandlerName(aName), "Bad event name");
2052 JSAutoRequest ar(mContext);
2054 // Get the jsobject associated with this target
2055 JSObject *target = nsnull;
2056 nsresult rv = JSObjectFromInterface(aTarget, aScope, &target);
2057 NS_ENSURE_SUCCESS(rv, rv);
2059 JSObject *funobj = (JSObject*) aHandler;
2061 #ifdef DEBUG
2063 JSAutoEnterCompartment ac;
2064 if (!ac.enter(mContext, funobj)) {
2065 return NS_ERROR_FAILURE;
2068 NS_ASSERTION(JS_TypeOfValue(mContext,
2069 OBJECT_TO_JSVAL(funobj)) == JSTYPE_FUNCTION,
2070 "Event handler object not a function");
2072 #endif
2074 JSAutoEnterCompartment ac;
2075 if (!ac.enter(mContext, target)) {
2076 return NS_ERROR_FAILURE;
2079 // Push our JSContext on our thread's context stack, in case native code
2080 // called from JS calls back into JS via XPConnect.
2081 nsCOMPtr<nsIJSContextStack> stack =
2082 do_GetService("@mozilla.org/js/xpc/ContextStack;1", &rv);
2083 if (NS_FAILED(rv) || NS_FAILED(stack->Push(mContext))) {
2084 return NS_ERROR_FAILURE;
2087 // Make sure the handler function is parented by its event target object
2088 if (funobj) { // && ::JS_GetParent(mContext, funobj) != target) {
2089 funobj = ::JS_CloneFunctionObject(mContext, funobj, target);
2090 if (!funobj)
2091 rv = NS_ERROR_OUT_OF_MEMORY;
2094 if (NS_SUCCEEDED(rv) &&
2095 // Make sure the flags here match those in nsEventReceiverSH::NewResolve
2096 !::JS_DefineProperty(mContext, target, nsAtomCString(aName).get(),
2097 OBJECT_TO_JSVAL(funobj), nsnull, nsnull,
2098 JSPROP_ENUMERATE | JSPROP_PERMANENT)) {
2099 ReportPendingException();
2100 rv = NS_ERROR_FAILURE;
2103 // XXXmarkh - ideally we should assert that the wrapped native is now
2104 // "long lived" - how to do that?
2106 if (NS_FAILED(stack->Pop(nsnull)) && NS_SUCCEEDED(rv)) {
2107 rv = NS_ERROR_FAILURE;
2110 return rv;
2113 nsresult
2114 nsJSContext::GetBoundEventHandler(nsISupports* aTarget, void *aScope,
2115 nsIAtom* aName,
2116 nsScriptObjectHolder &aHandler)
2118 NS_PRECONDITION(AtomIsEventHandlerName(aName), "Bad event name");
2120 JSAutoRequest ar(mContext);
2121 JSObject *obj = nsnull;
2122 nsresult rv = JSObjectFromInterface(aTarget, aScope, &obj);
2123 NS_ENSURE_SUCCESS(rv, rv);
2125 JSAutoEnterCompartment ac;
2126 if (!ac.enter(mContext, obj)) {
2127 return NS_ERROR_FAILURE;
2130 jsval funval;
2131 if (!JS_LookupProperty(mContext, obj,
2132 nsAtomCString(aName).get(), &funval))
2133 return NS_ERROR_FAILURE;
2135 if (JS_TypeOfValue(mContext, funval) != JSTYPE_FUNCTION) {
2136 NS_WARNING("Event handler object not a function");
2137 aHandler.drop();
2138 return NS_OK;
2140 NS_ASSERTION(aHandler.getScriptTypeID()==JAVASCRIPT,
2141 "Expecting JS script object holder");
2142 return aHandler.set(JSVAL_TO_OBJECT(funval));
2145 // serialization
2146 nsresult
2147 nsJSContext::Serialize(nsIObjectOutputStream* aStream, void *aScriptObject)
2149 JSObject *mJSObject = (JSObject *)aScriptObject;
2150 if (!mJSObject)
2151 return NS_ERROR_FAILURE;
2153 nsresult rv;
2155 JSContext* cx = mContext;
2156 JSXDRState *xdr = ::JS_XDRNewMem(cx, JSXDR_ENCODE);
2157 if (! xdr)
2158 return NS_ERROR_OUT_OF_MEMORY;
2159 xdr->userdata = (void*) aStream;
2161 JSAutoRequest ar(cx);
2162 JSScript *script = reinterpret_cast<JSScript*>
2163 (::JS_GetPrivate(cx, mJSObject));
2164 if (! ::JS_XDRScript(xdr, &script)) {
2165 rv = NS_ERROR_FAILURE; // likely to be a principals serialization error
2166 } else {
2167 // Get the encoded JSXDRState data and write it. The JSXDRState owns
2168 // this buffer memory and will free it beneath ::JS_XDRDestroy.
2170 // If an XPCOM object needs to be written in the midst of the JS XDR
2171 // encoding process, the C++ code called back from the JS engine (e.g.,
2172 // nsEncodeJSPrincipals in caps/src/nsJSPrincipals.cpp) will flush data
2173 // from the JSXDRState to aStream, then write the object, then return
2174 // to JS XDR code with xdr reset so new JS data is encoded at the front
2175 // of the xdr's data buffer.
2177 // However many XPCOM objects are interleaved with JS XDR data in the
2178 // stream, when control returns here from ::JS_XDRScript, we'll have
2179 // one last buffer of data to write to aStream.
2181 uint32 size;
2182 const char* data = reinterpret_cast<const char*>
2183 (::JS_XDRMemGetData(xdr, &size));
2184 NS_ASSERTION(data, "no decoded JSXDRState data!");
2186 rv = aStream->Write32(size);
2187 if (NS_SUCCEEDED(rv))
2188 rv = aStream->WriteBytes(data, size);
2191 ::JS_XDRDestroy(xdr);
2192 if (NS_FAILED(rv)) return rv;
2194 return rv;
2197 nsresult
2198 nsJSContext::Deserialize(nsIObjectInputStream* aStream,
2199 nsScriptObjectHolder &aResult)
2201 JSObject *result = nsnull;
2202 nsresult rv;
2204 NS_TIME_FUNCTION_MIN(1.0);
2206 NS_TIMELINE_MARK_FUNCTION("js script deserialize");
2208 PRUint32 size;
2209 rv = aStream->Read32(&size);
2210 if (NS_FAILED(rv)) return rv;
2212 char* data;
2213 rv = aStream->ReadBytes(size, &data);
2214 if (NS_FAILED(rv)) return rv;
2216 JSContext* cx = mContext;
2218 JSXDRState *xdr = ::JS_XDRNewMem(cx, JSXDR_DECODE);
2219 if (! xdr) {
2220 rv = NS_ERROR_OUT_OF_MEMORY;
2221 } else {
2222 xdr->userdata = (void*) aStream;
2223 JSAutoRequest ar(cx);
2224 ::JS_XDRMemSetData(xdr, data, size);
2226 JSScript *script = nsnull;
2227 if (! ::JS_XDRScript(xdr, &script)) {
2228 rv = NS_ERROR_FAILURE; // principals deserialization error?
2229 } else {
2230 result = ::JS_NewScriptObject(cx, script);
2231 if (! result) {
2232 rv = NS_ERROR_OUT_OF_MEMORY; // certain error
2233 ::JS_DestroyScript(cx, script);
2237 // Update data in case ::JS_XDRScript called back into C++ code to
2238 // read an XPCOM object.
2240 // In that case, the serialization process must have flushed a run
2241 // of counted bytes containing JS data at the point where the XPCOM
2242 // object starts, after which an encoding C++ callback from the JS
2243 // XDR code must have written the XPCOM object directly into the
2244 // nsIObjectOutputStream.
2246 // The deserialization process will XDR-decode counted bytes up to
2247 // but not including the XPCOM object, then call back into C++ to
2248 // read the object, then read more counted bytes and hand them off
2249 // to the JSXDRState, so more JS data can be decoded.
2251 // This interleaving of JS XDR data and XPCOM object data may occur
2252 // several times beneath the call to ::JS_XDRScript, above. At the
2253 // end of the day, we need to free (via nsMemory) the data owned by
2254 // the JSXDRState. So we steal it back, nulling xdr's buffer so it
2255 // doesn't get passed to ::JS_free by ::JS_XDRDestroy.
2257 uint32 junk;
2258 data = (char*) ::JS_XDRMemGetData(xdr, &junk);
2259 if (data)
2260 ::JS_XDRMemSetData(xdr, NULL, 0);
2261 ::JS_XDRDestroy(xdr);
2264 // If data is null now, it must have been freed while deserializing an
2265 // XPCOM object (e.g., a principal) beneath ::JS_XDRScript.
2266 if (data)
2267 nsMemory::Free(data);
2268 NS_ASSERTION(aResult.getScriptTypeID()==JAVASCRIPT,
2269 "Expecting JS script object holder");
2271 // Now that we've cleaned up, handle the case when rv is a failure
2272 // code, which could happen for all sorts of reasons above.
2273 NS_ENSURE_SUCCESS(rv, rv);
2275 return aResult.set(result);
2278 void
2279 nsJSContext::SetDefaultLanguageVersion(PRUint32 aVersion)
2281 ::JS_SetVersion(mContext, (JSVersion)aVersion);
2284 nsIScriptGlobalObject *
2285 nsJSContext::GetGlobalObject()
2287 JSObject *global = ::JS_GetGlobalObject(mContext);
2289 if (!global) {
2290 return nsnull;
2293 OBJ_TO_INNER_OBJECT(mContext, global);
2294 if (!global) {
2295 return nsnull;
2298 JSClass *c = JS_GET_CLASS(mContext, global);
2300 if (!c || ((~c->flags) & (JSCLASS_HAS_PRIVATE |
2301 JSCLASS_PRIVATE_IS_NSISUPPORTS))) {
2302 return nsnull;
2305 JSAutoEnterCompartment ac;
2307 // NB: This AutoCrossCompartmentCall is only here to silence a warning. If
2308 // it fails, nothing bad will happen.
2309 ac.enterAndIgnoreErrors(mContext, global);
2311 nsCOMPtr<nsIScriptGlobalObject> sgo;
2312 nsISupports *priv =
2313 (nsISupports *)::JS_GetPrivate(mContext, global);
2315 nsCOMPtr<nsIXPConnectWrappedNative> wrapped_native =
2316 do_QueryInterface(priv);
2318 if (wrapped_native) {
2319 // The global object is a XPConnect wrapped native, the native in
2320 // the wrapper might be the nsIScriptGlobalObject
2322 sgo = do_QueryWrappedNative(wrapped_native);
2323 } else {
2324 sgo = do_QueryInterface(priv);
2327 // This'll return a pointer to something we're about to release, but
2328 // that's ok, the JS object will hold it alive long enough.
2329 nsCOMPtr<nsPIDOMWindow> pwin(do_QueryInterface(sgo));
2330 if (!pwin)
2331 return sgo;
2333 return static_cast<nsGlobalWindow *>(pwin->GetOuterWindow());
2336 void *
2337 nsJSContext::GetNativeGlobal()
2339 return ::JS_GetGlobalObject(mContext);
2342 nsresult
2343 nsJSContext::CreateNativeGlobalForInner(
2344 nsIScriptGlobalObject *aNewInner,
2345 PRBool aIsChrome,
2346 nsIPrincipal *aPrincipal,
2347 void **aNativeGlobal, nsISupports **aHolder)
2349 nsIXPConnect *xpc = nsContentUtils::XPConnect();
2350 PRUint32 flags = aIsChrome? nsIXPConnect::FLAG_SYSTEM_GLOBAL_OBJECT : 0;
2351 nsCOMPtr<nsIXPConnectJSObjectHolder> jsholder;
2353 nsCOMPtr<nsIPrincipal> systemPrincipal;
2354 if (aIsChrome) {
2355 nsIScriptSecurityManager *ssm = nsContentUtils::GetSecurityManager();
2356 ssm->GetSystemPrincipal(getter_AddRefs(systemPrincipal));
2359 nsresult rv = xpc->
2360 InitClassesWithNewWrappedGlobal(mContext,
2361 aNewInner, NS_GET_IID(nsISupports),
2362 aIsChrome ? systemPrincipal.get() : aPrincipal,
2363 nsnull, flags,
2364 getter_AddRefs(jsholder));
2365 if (NS_FAILED(rv))
2366 return rv;
2367 jsholder->GetJSObject(reinterpret_cast<JSObject **>(aNativeGlobal));
2368 *aHolder = jsholder.get();
2369 NS_ADDREF(*aHolder);
2370 return NS_OK;
2373 nsresult
2374 nsJSContext::ConnectToInner(nsIScriptGlobalObject *aNewInner, void *aOuterGlobal)
2376 NS_ENSURE_ARG(aNewInner);
2377 JSObject *newInnerJSObject = (JSObject *)aNewInner->GetScriptGlobal(JAVASCRIPT);
2378 JSObject *outerGlobal = (JSObject *)aOuterGlobal;
2380 // Now that we're connecting the outer global to the inner one,
2381 // we must have transplanted it. The JS engine tries to maintain
2382 // the global object's compartment as its default compartment,
2383 // so update that now since it might have changed.
2384 JS_SetGlobalObject(mContext, outerGlobal);
2385 NS_ASSERTION(JS_GetPrototype(mContext, outerGlobal) ==
2386 JS_GetPrototype(mContext, newInnerJSObject),
2387 "outer and inner globals should have the same prototype");
2389 return NS_OK;
2392 void *
2393 nsJSContext::GetNativeContext()
2395 return mContext;
2398 nsresult
2399 nsJSContext::InitContext()
2401 // Make sure callers of this use
2402 // WillInitializeContext/DidInitializeContext around this call.
2403 NS_ENSURE_TRUE(!mIsInitialized, NS_ERROR_ALREADY_INITIALIZED);
2405 if (!mContext)
2406 return NS_ERROR_OUT_OF_MEMORY;
2408 ::JS_SetErrorReporter(mContext, NS_ScriptErrorReporter);
2410 return NS_OK;
2413 nsresult
2414 nsJSContext::CreateOuterObject(nsIScriptGlobalObject *aGlobalObject,
2415 nsIScriptGlobalObject *aCurrentInner)
2417 mGlobalObjectRef = aGlobalObject;
2419 nsCOMPtr<nsIDOMChromeWindow> chromeWindow(do_QueryInterface(aGlobalObject));
2420 PRUint32 flags = 0;
2422 if (chromeWindow) {
2423 // Flag this window's global object and objects under it as "system",
2424 // for optional automated XPCNativeWrapper construction when chrome JS
2425 // views a content DOM.
2426 flags = nsIXPConnect::FLAG_SYSTEM_GLOBAL_OBJECT;
2428 // Always enable E4X for XUL and other chrome content -- there is no
2429 // need to preserve the <!-- script hiding hack from JS-in-HTML daze
2430 // (introduced in 1995 for graceful script degradation in Netscape 1,
2431 // Mosaic, and other pre-JS browsers).
2432 JS_SetOptions(mContext, JS_GetOptions(mContext) | JSOPTION_XML);
2435 JSObject *outer =
2436 NS_NewOuterWindowProxy(mContext, aCurrentInner->GetGlobalJSObject());
2437 if (!outer) {
2438 return NS_ERROR_FAILURE;
2441 return SetOuterObject(outer);
2444 nsresult
2445 nsJSContext::SetOuterObject(void *aOuterObject)
2447 JSObject *outer = static_cast<JSObject *>(aOuterObject);
2449 // Force our context's global object to be the outer.
2450 JS_SetGlobalObject(mContext, outer);
2452 // NB: JS_SetGlobalObject sets mContext->compartment.
2453 JSObject *inner = JS_GetParent(mContext, outer);
2455 nsIXPConnect *xpc = nsContentUtils::XPConnect();
2456 nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
2457 nsresult rv = xpc->GetWrappedNativeOfJSObject(mContext, inner,
2458 getter_AddRefs(wrapper));
2459 NS_ENSURE_SUCCESS(rv, rv);
2460 NS_ABORT_IF_FALSE(wrapper, "bad wrapper");
2462 wrapper->RefreshPrototype();
2463 JS_SetPrototype(mContext, outer, JS_GetPrototype(mContext, inner));
2465 return NS_OK;
2468 nsresult
2469 nsJSContext::InitOuterWindow()
2471 JSObject *global = JS_GetGlobalObject(mContext);
2472 OBJ_TO_INNER_OBJECT(mContext, global);
2474 nsresult rv = InitClasses(global); // this will complete global object initialization
2475 NS_ENSURE_SUCCESS(rv, rv);
2477 return NS_OK;
2480 nsresult
2481 nsJSContext::InitializeExternalClasses()
2483 nsScriptNameSpaceManager *nameSpaceManager = nsJSRuntime::GetNameSpaceManager();
2484 NS_ENSURE_TRUE(nameSpaceManager, NS_ERROR_NOT_INITIALIZED);
2486 return nameSpaceManager->InitForContext(this);
2489 nsresult
2490 nsJSContext::SetProperty(void *aTarget, const char *aPropName, nsISupports *aArgs)
2492 PRUint32 argc;
2493 jsval *argv = nsnull;
2495 JSAutoRequest ar(mContext);
2497 js::LazilyConstructed<nsAutoPoolRelease> poolRelease;
2498 js::LazilyConstructed<js::AutoArrayRooter> tvr;
2500 nsresult rv;
2501 rv = ConvertSupportsTojsvals(aArgs, GetNativeGlobal(), &argc,
2502 &argv, poolRelease, tvr);
2503 NS_ENSURE_SUCCESS(rv, rv);
2505 jsval vargs;
2507 // got the arguments, now attach them.
2509 // window.dialogArguments is supposed to be an array if a JS array
2510 // was passed to showModalDialog(), deal with that here.
2511 if (strcmp(aPropName, "dialogArguments") == 0 && argc <= 1) {
2512 vargs = argc ? argv[0] : JSVAL_VOID;
2513 } else {
2514 for (PRUint32 i = 0; i < argc; ++i) {
2515 if (!JS_WrapValue(mContext, &argv[i])) {
2516 return NS_ERROR_FAILURE;
2520 JSObject *args = ::JS_NewArrayObject(mContext, argc, argv);
2521 vargs = OBJECT_TO_JSVAL(args);
2524 // Make sure to use JS_DefineProperty here so that we can override
2525 // readonly XPConnect properties here as well (read dialogArguments).
2526 rv = ::JS_DefineProperty(mContext, reinterpret_cast<JSObject *>(aTarget),
2527 aPropName, vargs, nsnull, nsnull, 0) ?
2528 NS_OK : NS_ERROR_FAILURE;
2530 return rv;
2533 nsresult
2534 nsJSContext::ConvertSupportsTojsvals(nsISupports *aArgs,
2535 void *aScope,
2536 PRUint32 *aArgc,
2537 jsval **aArgv,
2538 js::LazilyConstructed<nsAutoPoolRelease> &aPoolRelease,
2539 js::LazilyConstructed<js::AutoArrayRooter> &aRooter)
2541 nsresult rv = NS_OK;
2543 // If the array implements nsIJSArgArray, just grab the values directly.
2544 nsCOMPtr<nsIJSArgArray> fastArray = do_QueryInterface(aArgs);
2545 if (fastArray != nsnull)
2546 return fastArray->GetArgs(aArgc, reinterpret_cast<void **>(aArgv));
2548 // Take the slower path converting each item.
2549 // Handle only nsIArray and nsIVariant. nsIArray is only needed for
2550 // SetProperty('arguments', ...);
2552 *aArgv = nsnull;
2553 *aArgc = 0;
2555 nsIXPConnect *xpc = nsContentUtils::XPConnect();
2556 NS_ENSURE_TRUE(xpc, NS_ERROR_UNEXPECTED);
2558 if (!aArgs)
2559 return NS_OK;
2560 PRUint32 argCtr, argCount;
2561 // This general purpose function may need to convert an arg array
2562 // (window.arguments, event-handler args) and a generic property.
2563 nsCOMPtr<nsIArray> argsArray(do_QueryInterface(aArgs));
2565 if (argsArray) {
2566 rv = argsArray->GetLength(&argCount);
2567 NS_ENSURE_SUCCESS(rv, rv);
2568 if (argCount == 0)
2569 return NS_OK;
2570 } else {
2571 argCount = 1; // the nsISupports which is not an array
2574 void *mark = JS_ARENA_MARK(&mContext->tempPool);
2575 jsval *argv;
2576 size_t nbytes = argCount * sizeof(jsval);
2577 JS_ARENA_ALLOCATE_CAST(argv, jsval *, &mContext->tempPool, nbytes);
2578 NS_ENSURE_TRUE(argv, NS_ERROR_OUT_OF_MEMORY);
2579 memset(argv, 0, nbytes); /* initialize so GC-able */
2581 // Use the caller's auto guards to release and unroot.
2582 aPoolRelease.construct(&mContext->tempPool, mark);
2583 aRooter.construct(mContext, argCount, argv);
2585 if (argsArray) {
2586 for (argCtr = 0; argCtr < argCount && NS_SUCCEEDED(rv); argCtr++) {
2587 nsCOMPtr<nsISupports> arg;
2588 jsval *thisval = argv + argCtr;
2589 argsArray->QueryElementAt(argCtr, NS_GET_IID(nsISupports),
2590 getter_AddRefs(arg));
2591 if (!arg) {
2592 *thisval = JSVAL_NULL;
2593 continue;
2595 nsCOMPtr<nsIVariant> variant(do_QueryInterface(arg));
2596 if (variant != nsnull) {
2597 rv = xpc->VariantToJS(mContext, (JSObject *)aScope, variant,
2598 thisval);
2599 } else {
2600 // And finally, support the nsISupportsPrimitives supplied
2601 // by the AppShell. It generally will pass only strings, but
2602 // as we have code for handling all, we may as well use it.
2603 rv = AddSupportsPrimitiveTojsvals(arg, thisval);
2604 if (rv == NS_ERROR_NO_INTERFACE) {
2605 // something else - probably an event object or similar -
2606 // just wrap it.
2607 #ifdef NS_DEBUG
2608 // but first, check its not another nsISupportsPrimitive, as
2609 // these are now deprecated for use with script contexts.
2610 nsCOMPtr<nsISupportsPrimitive> prim(do_QueryInterface(arg));
2611 NS_ASSERTION(prim == nsnull,
2612 "Don't pass nsISupportsPrimitives - use nsIVariant!");
2613 #endif
2614 nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
2615 jsval v;
2616 rv = nsContentUtils::WrapNative(mContext, (JSObject *)aScope, arg,
2617 &v, getter_AddRefs(wrapper));
2618 if (NS_SUCCEEDED(rv)) {
2619 *thisval = v;
2624 } else {
2625 nsCOMPtr<nsIVariant> variant(do_QueryInterface(aArgs));
2626 if (variant)
2627 rv = xpc->VariantToJS(mContext, (JSObject *)aScope, variant, argv);
2628 else {
2629 NS_ERROR("Not an array, not an interface?");
2630 rv = NS_ERROR_UNEXPECTED;
2633 if (NS_FAILED(rv))
2634 return rv;
2635 *aArgv = argv;
2636 *aArgc = argCount;
2637 return NS_OK;
2640 // This really should go into xpconnect somewhere...
2641 nsresult
2642 nsJSContext::AddSupportsPrimitiveTojsvals(nsISupports *aArg, jsval *aArgv)
2644 NS_PRECONDITION(aArg, "Empty arg");
2646 nsCOMPtr<nsISupportsPrimitive> argPrimitive(do_QueryInterface(aArg));
2647 if (!argPrimitive)
2648 return NS_ERROR_NO_INTERFACE;
2650 JSContext *cx = mContext;
2651 PRUint16 type;
2652 argPrimitive->GetType(&type);
2654 switch(type) {
2655 case nsISupportsPrimitive::TYPE_CSTRING : {
2656 nsCOMPtr<nsISupportsCString> p(do_QueryInterface(argPrimitive));
2657 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
2659 nsCAutoString data;
2661 p->GetData(data);
2664 JSString *str = ::JS_NewStringCopyN(cx, data.get(), data.Length());
2665 NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY);
2667 *aArgv = STRING_TO_JSVAL(str);
2669 break;
2671 case nsISupportsPrimitive::TYPE_STRING : {
2672 nsCOMPtr<nsISupportsString> p(do_QueryInterface(argPrimitive));
2673 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
2675 nsAutoString data;
2677 p->GetData(data);
2679 // cast is probably safe since wchar_t and jschar are expected
2680 // to be equivalent; both unsigned 16-bit entities
2681 JSString *str =
2682 ::JS_NewUCStringCopyN(cx,
2683 reinterpret_cast<const jschar *>(data.get()),
2684 data.Length());
2685 NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY);
2687 *aArgv = STRING_TO_JSVAL(str);
2688 break;
2690 case nsISupportsPrimitive::TYPE_PRBOOL : {
2691 nsCOMPtr<nsISupportsPRBool> p(do_QueryInterface(argPrimitive));
2692 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
2694 PRBool data;
2696 p->GetData(&data);
2698 *aArgv = BOOLEAN_TO_JSVAL(data);
2700 break;
2702 case nsISupportsPrimitive::TYPE_PRUINT8 : {
2703 nsCOMPtr<nsISupportsPRUint8> p(do_QueryInterface(argPrimitive));
2704 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
2706 PRUint8 data;
2708 p->GetData(&data);
2710 *aArgv = INT_TO_JSVAL(data);
2712 break;
2714 case nsISupportsPrimitive::TYPE_PRUINT16 : {
2715 nsCOMPtr<nsISupportsPRUint16> p(do_QueryInterface(argPrimitive));
2716 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
2718 PRUint16 data;
2720 p->GetData(&data);
2722 *aArgv = INT_TO_JSVAL(data);
2724 break;
2726 case nsISupportsPrimitive::TYPE_PRUINT32 : {
2727 nsCOMPtr<nsISupportsPRUint32> p(do_QueryInterface(argPrimitive));
2728 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
2730 PRUint32 data;
2732 p->GetData(&data);
2734 *aArgv = INT_TO_JSVAL(data);
2736 break;
2738 case nsISupportsPrimitive::TYPE_CHAR : {
2739 nsCOMPtr<nsISupportsChar> p(do_QueryInterface(argPrimitive));
2740 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
2742 char data;
2744 p->GetData(&data);
2746 JSString *str = ::JS_NewStringCopyN(cx, &data, 1);
2747 NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY);
2749 *aArgv = STRING_TO_JSVAL(str);
2751 break;
2753 case nsISupportsPrimitive::TYPE_PRINT16 : {
2754 nsCOMPtr<nsISupportsPRInt16> p(do_QueryInterface(argPrimitive));
2755 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
2757 PRInt16 data;
2759 p->GetData(&data);
2761 *aArgv = INT_TO_JSVAL(data);
2763 break;
2765 case nsISupportsPrimitive::TYPE_PRINT32 : {
2766 nsCOMPtr<nsISupportsPRInt32> p(do_QueryInterface(argPrimitive));
2767 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
2769 PRInt32 data;
2771 p->GetData(&data);
2773 *aArgv = INT_TO_JSVAL(data);
2775 break;
2777 case nsISupportsPrimitive::TYPE_FLOAT : {
2778 nsCOMPtr<nsISupportsFloat> p(do_QueryInterface(argPrimitive));
2779 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
2781 float data;
2783 p->GetData(&data);
2785 JSBool ok = ::JS_NewNumberValue(cx, data, aArgv);
2786 NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
2788 break;
2790 case nsISupportsPrimitive::TYPE_DOUBLE : {
2791 nsCOMPtr<nsISupportsDouble> p(do_QueryInterface(argPrimitive));
2792 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
2794 double data;
2796 p->GetData(&data);
2798 JSBool ok = ::JS_NewNumberValue(cx, data, aArgv);
2799 NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
2801 break;
2803 case nsISupportsPrimitive::TYPE_INTERFACE_POINTER : {
2804 nsCOMPtr<nsISupportsInterfacePointer> p(do_QueryInterface(argPrimitive));
2805 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
2807 nsCOMPtr<nsISupports> data;
2808 nsIID *iid = nsnull;
2810 p->GetData(getter_AddRefs(data));
2811 p->GetDataIID(&iid);
2812 NS_ENSURE_TRUE(iid, NS_ERROR_UNEXPECTED);
2814 AutoFree iidGuard(iid); // Free iid upon destruction.
2816 nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
2817 jsval v;
2818 nsresult rv = nsContentUtils::WrapNative(cx, ::JS_GetGlobalObject(cx),
2819 data, iid, &v,
2820 getter_AddRefs(wrapper));
2821 NS_ENSURE_SUCCESS(rv, rv);
2823 *aArgv = v;
2825 break;
2827 case nsISupportsPrimitive::TYPE_ID :
2828 case nsISupportsPrimitive::TYPE_PRUINT64 :
2829 case nsISupportsPrimitive::TYPE_PRINT64 :
2830 case nsISupportsPrimitive::TYPE_PRTIME :
2831 case nsISupportsPrimitive::TYPE_VOID : {
2832 NS_WARNING("Unsupported primitive type used");
2833 *aArgv = JSVAL_NULL;
2834 break;
2836 default : {
2837 NS_WARNING("Unknown primitive type used");
2838 *aArgv = JSVAL_NULL;
2839 break;
2842 return NS_OK;
2845 static JSPropertySpec OptionsProperties[] = {
2846 {"strict", (int8)JSOPTION_STRICT, JSPROP_ENUMERATE | JSPROP_PERMANENT},
2847 {"werror", (int8)JSOPTION_WERROR, JSPROP_ENUMERATE | JSPROP_PERMANENT},
2848 {"relimit", (int8)JSOPTION_RELIMIT, JSPROP_ENUMERATE | JSPROP_PERMANENT},
2852 static JSBool
2853 GetOptionsProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
2855 if (JSID_IS_INT(id)) {
2856 uint32 optbit = (uint32) JSID_TO_INT(id);
2857 if (((optbit & (optbit - 1)) == 0 && optbit <= JSOPTION_WERROR) ||
2858 optbit == JSOPTION_RELIMIT)
2859 *vp = (JS_GetOptions(cx) & optbit) ? JSVAL_TRUE : JSVAL_FALSE;
2861 return JS_TRUE;
2864 static JSBool
2865 SetOptionsProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
2867 if (JSID_IS_INT(id)) {
2868 uint32 optbit = (uint32) JSID_TO_INT(id);
2870 // Don't let options other than strict, werror, or relimit be set -- it
2871 // would be bad if web page script could clear
2872 // JSOPTION_PRIVATE_IS_NSISUPPORTS!
2873 if (((optbit & (optbit - 1)) == 0 && optbit <= JSOPTION_WERROR) ||
2874 optbit == JSOPTION_RELIMIT) {
2875 JSBool optval;
2876 JS_ValueToBoolean(cx, *vp, &optval);
2878 uint32 optset = ::JS_GetOptions(cx);
2879 if (optval)
2880 optset |= optbit;
2881 else
2882 optset &= ~optbit;
2883 ::JS_SetOptions(cx, optset);
2886 return JS_TRUE;
2889 static JSClass OptionsClass = {
2890 "JSOptions",
2892 JS_PropertyStub, JS_PropertyStub, GetOptionsProperty, SetOptionsProperty,
2893 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, nsnull
2896 #ifdef NS_TRACE_MALLOC
2898 #include <errno.h> // XXX assume Linux if NS_TRACE_MALLOC
2899 #include <fcntl.h>
2900 #ifdef XP_UNIX
2901 #include <unistd.h>
2902 #endif
2903 #ifdef XP_WIN32
2904 #include <io.h>
2905 #endif
2906 #include "nsTraceMalloc.h"
2908 static JSBool
2909 CheckUniversalXPConnectForTraceMalloc(JSContext *cx)
2911 PRBool hasCap = PR_FALSE;
2912 nsresult rv = nsContentUtils::GetSecurityManager()->
2913 IsCapabilityEnabled("UniversalXPConnect", &hasCap);
2914 if (NS_SUCCEEDED(rv) && hasCap)
2915 return JS_TRUE;
2916 JS_ReportError(cx, "trace-malloc functions require UniversalXPConnect");
2917 return JS_FALSE;
2920 static JSBool
2921 TraceMallocDisable(JSContext *cx, uintN argc, jsval *vp)
2923 if (!CheckUniversalXPConnectForTraceMalloc(cx))
2924 return JS_FALSE;
2926 NS_TraceMallocDisable();
2927 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2928 return JS_TRUE;
2931 static JSBool
2932 TraceMallocEnable(JSContext *cx, uintN argc, jsval *vp)
2934 if (!CheckUniversalXPConnectForTraceMalloc(cx))
2935 return JS_FALSE;
2937 NS_TraceMallocEnable();
2938 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2939 return JS_TRUE;
2942 static JSBool
2943 TraceMallocOpenLogFile(JSContext *cx, uintN argc, jsval *vp)
2945 int fd;
2946 JSString *str;
2948 if (!CheckUniversalXPConnectForTraceMalloc(cx))
2949 return JS_FALSE;
2951 if (argc == 0) {
2952 fd = -1;
2953 } else {
2954 str = JS_ValueToString(cx, JS_ARGV(cx, vp)[0]);
2955 if (!str)
2956 return JS_FALSE;
2957 JSAutoByteString filename(cx, str);
2958 if (!filename)
2959 return JS_FALSE;
2960 fd = open(filename.ptr(), O_CREAT | O_WRONLY | O_TRUNC, 0644);
2961 if (fd < 0) {
2962 JS_ReportError(cx, "can't open %s: %s", filename.ptr(), strerror(errno));
2963 return JS_FALSE;
2966 JS_SET_RVAL(cx, vp, INT_TO_JSVAL(fd));
2967 return JS_TRUE;
2970 static JSBool
2971 TraceMallocChangeLogFD(JSContext *cx, uintN argc, jsval *vp)
2973 int32 fd, oldfd;
2975 if (!CheckUniversalXPConnectForTraceMalloc(cx))
2976 return JS_FALSE;
2978 if (argc == 0) {
2979 oldfd = -1;
2980 } else {
2981 if (!JS_ValueToECMAInt32(cx, JS_ARGV(cx, vp)[0], &fd))
2982 return JS_FALSE;
2983 oldfd = NS_TraceMallocChangeLogFD(fd);
2984 if (oldfd == -2) {
2985 JS_ReportOutOfMemory(cx);
2986 return JS_FALSE;
2989 JS_SET_RVAL(cx, vp, INT_TO_JSVAL(oldfd));
2990 return JS_TRUE;
2993 static JSBool
2994 TraceMallocCloseLogFD(JSContext *cx, uintN argc, jsval *vp)
2996 int32 fd;
2998 if (!CheckUniversalXPConnectForTraceMalloc(cx))
2999 return JS_FALSE;
3001 JS_SET_RVAL(cx, vp, JSVAL_VOID);
3002 if (argc == 0)
3003 return JS_TRUE;
3004 if (!JS_ValueToECMAInt32(cx, JS_ARGV(cx, vp)[0], &fd))
3005 return JS_FALSE;
3006 NS_TraceMallocCloseLogFD((int) fd);
3007 return JS_TRUE;
3010 static JSBool
3011 TraceMallocLogTimestamp(JSContext *cx, uintN argc, jsval *vp)
3013 if (!CheckUniversalXPConnectForTraceMalloc(cx))
3014 return JS_FALSE;
3016 JSString *str = JS_ValueToString(cx, argc ? JS_ARGV(cx, vp)[0] : JSVAL_VOID);
3017 if (!str)
3018 return JS_FALSE;
3019 JSAutoByteString caption(cx, str);
3020 if (!caption)
3021 return JS_FALSE;
3022 NS_TraceMallocLogTimestamp(caption.ptr());
3023 JS_SET_RVAL(cx, vp, JSVAL_VOID);
3024 return JS_TRUE;
3027 static JSBool
3028 TraceMallocDumpAllocations(JSContext *cx, uintN argc, jsval *vp)
3030 if (!CheckUniversalXPConnectForTraceMalloc(cx))
3031 return JS_FALSE;
3033 JSString *str = JS_ValueToString(cx, argc ? JS_ARGV(cx, vp)[0] : JSVAL_VOID);
3034 if (!str)
3035 return JS_FALSE;
3036 JSAutoByteString pathname(cx, str);
3037 if (!pathname)
3038 return JS_FALSE;
3039 if (NS_TraceMallocDumpAllocations(pathname.ptr()) < 0) {
3040 JS_ReportError(cx, "can't dump to %s: %s", pathname.ptr(), strerror(errno));
3041 return JS_FALSE;
3043 JS_SET_RVAL(cx, vp, JSVAL_VOID);
3044 return JS_TRUE;
3047 static JSFunctionSpec TraceMallocFunctions[] = {
3048 {"TraceMallocDisable", TraceMallocDisable, 0, 0},
3049 {"TraceMallocEnable", TraceMallocEnable, 0, 0},
3050 {"TraceMallocOpenLogFile", TraceMallocOpenLogFile, 1, 0},
3051 {"TraceMallocChangeLogFD", TraceMallocChangeLogFD, 1, 0},
3052 {"TraceMallocCloseLogFD", TraceMallocCloseLogFD, 1, 0},
3053 {"TraceMallocLogTimestamp", TraceMallocLogTimestamp, 1, 0},
3054 {"TraceMallocDumpAllocations", TraceMallocDumpAllocations, 1, 0},
3055 {nsnull, nsnull, 0, 0}
3058 #endif /* NS_TRACE_MALLOC */
3060 #ifdef MOZ_JPROF
3062 #include <signal.h>
3064 inline PRBool
3065 IsJProfAction(struct sigaction *action)
3067 return (action->sa_sigaction &&
3068 action->sa_flags == (SA_RESTART | SA_SIGINFO));
3071 void NS_JProfStartProfiling();
3072 void NS_JProfStopProfiling();
3074 static JSBool
3075 JProfStartProfilingJS(JSContext *cx, uintN argc, jsval *vp)
3077 NS_JProfStartProfiling();
3078 return JS_TRUE;
3081 void NS_JProfStartProfiling()
3083 // Figure out whether we're dealing with SIGPROF, SIGALRM, or
3084 // SIGPOLL profiling (SIGALRM for JP_REALTIME, SIGPOLL for
3085 // JP_RTC_HZ)
3086 struct sigaction action;
3088 sigaction(SIGALRM, nsnull, &action);
3089 if (IsJProfAction(&action)) {
3090 printf("Beginning real-time jprof profiling.\n");
3091 raise(SIGALRM);
3092 return;
3095 sigaction(SIGPROF, nsnull, &action);
3096 if (IsJProfAction(&action)) {
3097 printf("Beginning process-time jprof profiling.\n");
3098 raise(SIGPROF);
3099 return;
3102 sigaction(SIGPOLL, nsnull, &action);
3103 if (IsJProfAction(&action)) {
3104 printf("Beginning rtc-based jprof profiling.\n");
3105 raise(SIGPOLL);
3106 return;
3109 printf("Could not start jprof-profiling since JPROF_FLAGS was not set.\n");
3112 static JSBool
3113 JProfStopProfilingJS(JSContext *cx, uintN argc, jsval *vp)
3115 NS_JProfStopProfiling();
3116 return JS_TRUE;
3119 void
3120 NS_JProfStopProfiling()
3122 raise(SIGUSR1);
3123 printf("Stopped jprof profiling.\n");
3126 static JSFunctionSpec JProfFunctions[] = {
3127 {"JProfStartProfiling", JProfStartProfilingJS, 0, 0},
3128 {"JProfStopProfiling", JProfStopProfilingJS, 0, 0},
3129 {nsnull, nsnull, 0, 0}
3132 #endif /* defined(MOZ_JPROF) */
3134 #ifdef MOZ_CALLGRIND
3135 static JSFunctionSpec CallgrindFunctions[] = {
3136 {"startCallgrind", js_StartCallgrind, 0, 0},
3137 {"stopCallgrind", js_StopCallgrind, 0, 0},
3138 {"dumpCallgrind", js_DumpCallgrind, 1, 0},
3139 {nsnull, nsnull, 0, 0}
3141 #endif
3143 #ifdef MOZ_VTUNE
3144 static JSFunctionSpec VtuneFunctions[] = {
3145 {"startVtune", js_StartVtune, 1, 0},
3146 {"stopVtune", js_StopVtune, 0, 0},
3147 {"pauseVtune", js_PauseVtune, 0, 0},
3148 {"resumeVtune", js_ResumeVtune, 0, 0},
3149 {nsnull, nsnull, 0, 0}
3151 #endif
3153 #ifdef MOZ_TRACEVIS
3154 static JSFunctionSpec EthogramFunctions[] = {
3155 {"initEthogram", js_InitEthogram, 0, 0},
3156 {"shutdownEthogram", js_ShutdownEthogram, 0, 0},
3157 {nsnull, nsnull, 0, 0}
3159 #endif
3161 nsresult
3162 nsJSContext::InitClasses(void *aGlobalObj)
3164 nsresult rv = NS_OK;
3166 JSObject *globalObj = static_cast<JSObject *>(aGlobalObj);
3168 rv = InitializeExternalClasses();
3169 NS_ENSURE_SUCCESS(rv, rv);
3171 JSAutoRequest ar(mContext);
3173 // Initialize the options object and set default options in mContext
3174 JSObject *optionsObj = ::JS_DefineObject(mContext, globalObj, "_options",
3175 &OptionsClass, nsnull, 0);
3176 if (optionsObj &&
3177 ::JS_DefineProperties(mContext, optionsObj, OptionsProperties)) {
3178 ::JS_SetOptions(mContext, mDefaultJSOptions);
3179 } else {
3180 rv = NS_ERROR_FAILURE;
3183 // Attempt to initialize profiling functions
3184 ::JS_DefineProfilingFunctions(mContext, globalObj);
3186 #ifdef NS_TRACE_MALLOC
3187 // Attempt to initialize TraceMalloc functions
3188 ::JS_DefineFunctions(mContext, globalObj, TraceMallocFunctions);
3189 #endif
3191 #ifdef MOZ_JPROF
3192 // Attempt to initialize JProf functions
3193 ::JS_DefineFunctions(mContext, globalObj, JProfFunctions);
3194 #endif
3196 #ifdef MOZ_CALLGRIND
3197 // Attempt to initialize Callgrind functions
3198 ::JS_DefineFunctions(mContext, globalObj, CallgrindFunctions);
3199 #endif
3201 #ifdef MOZ_VTUNE
3202 // Attempt to initialize Vtune functions
3203 ::JS_DefineFunctions(mContext, globalObj, VtuneFunctions);
3204 #endif
3206 #ifdef MOZ_TRACEVIS
3207 // Attempt to initialize Ethogram functions
3208 ::JS_DefineFunctions(mContext, globalObj, EthogramFunctions);
3209 #endif
3211 JSOptionChangedCallback(js_options_dot_str, this);
3213 return rv;
3216 void
3217 nsJSContext::ClearScope(void *aGlobalObj, PRBool aClearFromProtoChain)
3219 // Push our JSContext on our thread's context stack.
3220 nsCOMPtr<nsIJSContextStack> stack =
3221 do_GetService("@mozilla.org/js/xpc/ContextStack;1");
3222 if (stack && NS_FAILED(stack->Push(mContext))) {
3223 stack = nsnull;
3226 if (aGlobalObj) {
3227 JSObject *obj = (JSObject *)aGlobalObj;
3228 JSAutoRequest ar(mContext);
3230 JSAutoEnterCompartment ac;
3231 ac.enterAndIgnoreErrors(mContext, obj);
3233 // Grab a reference to the window property, which is the outer
3234 // window, so that we can re-define it once we've cleared
3235 // scope. This is what keeps the outer window alive in cases where
3236 // nothing else does.
3237 jsval window;
3238 if (!JS_GetProperty(mContext, obj, "window", &window)) {
3239 window = JSVAL_VOID;
3241 JS_ClearPendingException(mContext);
3244 JS_ClearScope(mContext, obj);
3245 if (xpc::WrapperFactory::IsXrayWrapper(obj)) {
3246 JS_ClearScope(mContext, &obj->getProxyExtra().toObject());
3249 if (window != JSVAL_VOID) {
3250 if (!JS_DefineProperty(mContext, obj, "window", window,
3251 JS_PropertyStub, JS_PropertyStub,
3252 JSPROP_ENUMERATE | JSPROP_READONLY |
3253 JSPROP_PERMANENT)) {
3254 JS_ClearPendingException(mContext);
3258 if (!obj->getParent()) {
3259 JS_ClearRegExpStatics(mContext, obj);
3262 // Always clear watchpoints, to deal with two cases:
3263 // 1. The first document for this window is loading, and a miscreant has
3264 // preset watchpoints on the window object in order to attack the new
3265 // document's privileged information.
3266 // 2. A document loaded and used watchpoints on its own window, leaving
3267 // them set until the next document loads. We must clean up window
3268 // watchpoints here.
3269 // Watchpoints set on document and subordinate objects are all cleared
3270 // when those sub-window objects are finalized, after JS_ClearScope and
3271 // a GC run that finds them to be garbage.
3272 ::JS_ClearWatchPointsForObject(mContext, obj);
3274 // Since the prototype chain is shared between inner and outer (and
3275 // stays with the inner), we don't clear things from the prototype
3276 // chain when we're clearing an outer window whose current inner we
3277 // still want.
3278 if (aClearFromProtoChain) {
3279 nsWindowSH::InvalidateGlobalScopePolluter(mContext, obj);
3281 // Clear up obj's prototype chain, but not Object.prototype.
3282 for (JSObject *o = ::JS_GetPrototype(mContext, obj), *next;
3283 o && (next = ::JS_GetPrototype(mContext, o)); o = next)
3284 ::JS_ClearScope(mContext, o);
3288 if (stack) {
3289 stack->Pop(nsnull);
3293 void
3294 nsJSContext::WillInitializeContext()
3296 mIsInitialized = PR_FALSE;
3299 void
3300 nsJSContext::DidInitializeContext()
3302 mIsInitialized = PR_TRUE;
3305 PRBool
3306 nsJSContext::IsContextInitialized()
3308 return mIsInitialized;
3311 void
3312 nsJSContext::FinalizeContext()
3317 void
3318 nsJSContext::GC()
3320 FireGCTimer(PR_FALSE);
3323 void
3324 nsJSContext::ScriptEvaluated(PRBool aTerminated)
3326 if (aTerminated && mTerminations) {
3327 // Make sure to null out mTerminations before doing anything that
3328 // might cause new termination funcs to be added!
3329 nsJSContext::TerminationFuncClosure* start = mTerminations;
3330 mTerminations = nsnull;
3332 for (nsJSContext::TerminationFuncClosure* cur = start;
3333 cur;
3334 cur = cur->mNext) {
3335 (*(cur->mTerminationFunc))(cur->mTerminationFuncArg);
3337 delete start;
3340 #ifdef JS_GC_ZEAL
3341 if (mContext->runtime->gcZeal >= 2) {
3342 JS_MaybeGC(mContext);
3344 #endif
3346 if (aTerminated) {
3347 mOperationCallbackTime = 0;
3348 mModalStateTime = 0;
3352 nsresult
3353 nsJSContext::SetTerminationFunction(nsScriptTerminationFunc aFunc,
3354 nsISupports* aRef)
3356 NS_PRECONDITION(JS_IsRunning(mContext), "should be executing script");
3358 nsJSContext::TerminationFuncClosure* newClosure =
3359 new nsJSContext::TerminationFuncClosure(aFunc, aRef, mTerminations);
3360 if (!newClosure) {
3361 return NS_ERROR_OUT_OF_MEMORY;
3364 mTerminations = newClosure;
3365 return NS_OK;
3368 PRBool
3369 nsJSContext::GetScriptsEnabled()
3371 return mScriptsEnabled;
3374 void
3375 nsJSContext::SetScriptsEnabled(PRBool aEnabled, PRBool aFireTimeouts)
3377 // eeek - this seems the wrong way around - the global should callback
3378 // into each context, so every language is disabled.
3379 mScriptsEnabled = aEnabled;
3381 nsIScriptGlobalObject *global = GetGlobalObject();
3383 if (global) {
3384 global->SetScriptsEnabled(aEnabled, aFireTimeouts);
3389 PRBool
3390 nsJSContext::GetProcessingScriptTag()
3392 return mProcessingScriptTag;
3395 void
3396 nsJSContext::SetProcessingScriptTag(PRBool aFlag)
3398 mProcessingScriptTag = aFlag;
3401 PRBool
3402 nsJSContext::GetExecutingScript()
3404 return JS_IsRunning(mContext) || mExecuteDepth > 0;
3407 void
3408 nsJSContext::SetGCOnDestruction(PRBool aGCOnDestruction)
3410 mGCOnDestruction = aGCOnDestruction;
3413 NS_IMETHODIMP
3414 nsJSContext::ScriptExecuted()
3416 ScriptEvaluated(!::JS_IsRunning(mContext));
3418 return NS_OK;
3421 //static
3422 void
3423 nsJSContext::CC(nsICycleCollectorListener *aListener)
3425 NS_TIME_FUNCTION_MIN(1.0);
3427 ++sCCollectCount;
3428 #ifdef DEBUG_smaug
3429 printf("Will run cycle collector (%i), %lldms since previous.\n",
3430 sCCollectCount, (PR_Now() - sPreviousCCTime) / PR_USEC_PER_MSEC);
3431 #endif
3432 sPreviousCCTime = PR_Now();
3433 sDelayedCCollectCount = 0;
3434 sCCSuspectChanges = 0;
3435 // nsCycleCollector_collect() no longer forces a JS garbage collection,
3436 // so we have to do it ourselves here.
3437 if (nsContentUtils::XPConnect()) {
3438 nsContentUtils::XPConnect()->GarbageCollect();
3440 sCollectedObjectsCounts = nsCycleCollector_collect(aListener);
3441 sCCSuspectedCount = nsCycleCollector_suspectedCount();
3442 if (nsJSRuntime::sRuntime) {
3443 sSavedGCCount = JS_GetGCParameter(nsJSRuntime::sRuntime, JSGC_NUMBER);
3445 #ifdef DEBUG_smaug
3446 printf("Collected %u objects, %u suspected objects, took %lldms\n",
3447 sCollectedObjectsCounts, sCCSuspectedCount,
3448 (PR_Now() - sPreviousCCTime) / PR_USEC_PER_MSEC);
3449 #endif
3452 static inline uint32
3453 GetGCRunsSinceLastCC()
3455 // To avoid crash if nsJSRuntime is not properly initialized.
3456 // See the bug 474586
3457 if (!nsJSRuntime::sRuntime)
3458 return 0;
3460 // Since JS_GetGCParameter() and sSavedGCCount are unsigned, the following
3461 // gives the correct result even when the GC counter wraps around
3462 // UINT32_MAX since the last call to JS_GetGCParameter().
3463 return JS_GetGCParameter(nsJSRuntime::sRuntime, JSGC_NUMBER) -
3464 sSavedGCCount;
3467 //static
3468 PRBool
3469 nsJSContext::MaybeCC(PRBool aHigherProbability)
3471 ++sDelayedCCollectCount;
3473 // Don't check suspected count if CC will be called anyway.
3474 if (sCCSuspectChanges <= NS_MIN_SUSPECT_CHANGES ||
3475 GetGCRunsSinceLastCC() <= NS_MAX_GC_COUNT) {
3476 #ifdef DEBUG_smaug
3477 PRTime now = PR_Now();
3478 #endif
3479 PRUint32 suspected = nsCycleCollector_suspectedCount();
3480 #ifdef DEBUG_smaug
3481 printf("%u suspected objects (%lldms), sCCSuspectedCount %u\n",
3482 suspected, (PR_Now() - now) / PR_USEC_PER_MSEC,
3483 sCCSuspectedCount);
3484 #endif
3485 // Update only when suspected count has increased.
3486 if (suspected > sCCSuspectedCount) {
3487 sCCSuspectChanges += (suspected - sCCSuspectedCount);
3488 sCCSuspectedCount = suspected;
3491 #ifdef DEBUG_smaug
3492 printf("sCCSuspectChanges %u, GC runs %u\n",
3493 sCCSuspectChanges, GetGCRunsSinceLastCC());
3494 #endif
3496 // Increase the probability also if the previous call to cycle collector
3497 // collected something.
3498 if (aHigherProbability ||
3499 sCollectedObjectsCounts > NS_COLLECTED_OBJECTS_LIMIT) {
3500 sDelayedCCollectCount *= NS_PROBABILITY_MULTIPLIER;
3501 } else if (!sUserIsActive && sCCSuspectChanges > NS_MAX_SUSPECT_CHANGES) {
3502 // If user is inactive and there are lots of new suspected objects,
3503 // increase the probability for cycle collection.
3504 sDelayedCCollectCount += (sCCSuspectChanges / NS_MAX_SUSPECT_CHANGES);
3507 if (!sGCTimer &&
3508 (sDelayedCCollectCount > NS_MAX_DELAYED_CCOLLECT) &&
3509 ((sCCSuspectChanges > NS_MIN_SUSPECT_CHANGES &&
3510 GetGCRunsSinceLastCC() > NS_MAX_GC_COUNT) ||
3511 (sCCSuspectChanges > NS_MAX_SUSPECT_CHANGES))) {
3512 return IntervalCC();
3514 return PR_FALSE;
3517 //static
3518 void
3519 nsJSContext::CCIfUserInactive()
3521 if (sUserIsActive) {
3522 MaybeCC(PR_TRUE);
3523 } else {
3524 IntervalCC();
3528 //static
3529 void
3530 nsJSContext::MaybeCCIfUserInactive()
3532 if (!sUserIsActive) {
3533 MaybeCC(PR_FALSE);
3537 //static
3538 PRBool
3539 nsJSContext::IntervalCC()
3541 if ((PR_Now() - sPreviousCCTime) >=
3542 PRTime(NS_MIN_CC_INTERVAL * PR_USEC_PER_MSEC)) {
3543 nsJSContext::CC(nsnull);
3544 return PR_TRUE;
3546 #ifdef DEBUG_smaug
3547 printf("Running CC was delayed because of NS_MIN_CC_INTERVAL.\n");
3548 #endif
3549 return PR_FALSE;
3552 // static
3553 void
3554 GCTimerFired(nsITimer *aTimer, void *aClosure)
3556 NS_RELEASE(sGCTimer);
3558 if (sPendingLoadCount == 0 || sLoadInProgressGCTimer) {
3559 sLoadInProgressGCTimer = PR_FALSE;
3561 // Reset sPendingLoadCount in case the timer that fired was a
3562 // timer we scheduled due to a normal GC timer firing while
3563 // documents were loading. If this happens we're waiting for a
3564 // document that is taking a long time to load, and we effectively
3565 // ignore the fact that the currently loading documents are still
3566 // loading and move on as if they weren't.
3567 sPendingLoadCount = 0;
3569 nsJSContext::CCIfUserInactive();
3570 } else {
3571 nsJSContext::FireGCTimer(PR_TRUE);
3574 sReadyForGC = PR_TRUE;
3577 // static
3578 void
3579 nsJSContext::LoadStart()
3581 ++sPendingLoadCount;
3584 // static
3585 void
3586 nsJSContext::LoadEnd()
3588 // sPendingLoadCount is not a well managed load counter (and doesn't
3589 // need to be), so make sure we don't make it wrap backwards here.
3590 if (sPendingLoadCount > 0) {
3591 --sPendingLoadCount;
3594 if (!sPendingLoadCount && sLoadInProgressGCTimer) {
3595 sGCTimer->Cancel();
3596 NS_RELEASE(sGCTimer);
3597 sLoadInProgressGCTimer = PR_FALSE;
3599 CCIfUserInactive();
3603 // static
3604 void
3605 nsJSContext::FireGCTimer(PRBool aLoadInProgress)
3607 if (sGCTimer) {
3608 // There's already a timer for GC'ing, just return
3609 return;
3612 CallCreateInstance("@mozilla.org/timer;1", &sGCTimer);
3614 if (!sGCTimer) {
3615 NS_WARNING("Failed to create timer");
3617 // Reset sLoadInProgressGCTimer since we're not able to fire the
3618 // timer.
3619 sLoadInProgressGCTimer = PR_FALSE;
3621 CCIfUserInactive();
3622 return;
3625 static PRBool first = PR_TRUE;
3627 sGCTimer->InitWithFuncCallback(GCTimerFired, nsnull,
3628 first ? NS_FIRST_GC_DELAY :
3629 aLoadInProgress ? NS_LOAD_IN_PROCESS_GC_DELAY :
3630 NS_GC_DELAY,
3631 nsITimer::TYPE_ONE_SHOT);
3633 sLoadInProgressGCTimer = aLoadInProgress;
3635 first = PR_FALSE;
3638 static JSBool
3639 DOMGCCallback(JSContext *cx, JSGCStatus status)
3641 JSBool result = gOldJSGCCallback ? gOldJSGCCallback(cx, status) : JS_TRUE;
3643 if (status == JSGC_BEGIN && !NS_IsMainThread())
3644 return JS_FALSE;
3646 return result;
3649 // Script object mananagement - note duplicate implementation
3650 // in nsJSRuntime below...
3651 nsresult
3652 nsJSContext::HoldScriptObject(void* aScriptObject)
3654 NS_ASSERTION(sIsInitialized, "runtime not initialized");
3655 if (! nsJSRuntime::sRuntime) {
3656 NS_NOTREACHED("couldn't add GC root - no runtime");
3657 return NS_ERROR_FAILURE;
3660 ::JS_LockGCThingRT(nsJSRuntime::sRuntime, aScriptObject);
3661 return NS_OK;
3664 nsresult
3665 nsJSContext::DropScriptObject(void* aScriptObject)
3667 NS_ASSERTION(sIsInitialized, "runtime not initialized");
3668 if (! nsJSRuntime::sRuntime) {
3669 NS_NOTREACHED("couldn't remove GC root");
3670 return NS_ERROR_FAILURE;
3673 ::JS_UnlockGCThingRT(nsJSRuntime::sRuntime, aScriptObject);
3674 return NS_OK;
3677 void
3678 nsJSContext::ReportPendingException()
3680 // set aside the frame chain, since it has nothing to do with the
3681 // exception we're reporting.
3682 if (mIsInitialized && ::JS_IsExceptionPending(mContext)) {
3683 JSStackFrame* frame = JS_SaveFrameChain(mContext);
3684 ::JS_ReportPendingException(mContext);
3685 JS_RestoreFrameChain(mContext, frame);
3689 /**********************************************************************
3690 * nsJSRuntime implementation
3691 *********************************************************************/
3693 // QueryInterface implementation for nsJSRuntime
3694 NS_INTERFACE_MAP_BEGIN(nsJSRuntime)
3695 NS_INTERFACE_MAP_ENTRY(nsIScriptRuntime)
3696 NS_INTERFACE_MAP_END
3699 NS_IMPL_ADDREF(nsJSRuntime)
3700 NS_IMPL_RELEASE(nsJSRuntime)
3702 nsresult
3703 nsJSRuntime::CreateContext(nsIScriptContext **aContext)
3705 nsCOMPtr<nsIScriptContext> scriptContext;
3707 *aContext = new nsJSContext(sRuntime);
3708 NS_ENSURE_TRUE(*aContext, NS_ERROR_OUT_OF_MEMORY);
3709 NS_ADDREF(*aContext);
3710 return NS_OK;
3713 nsresult
3714 nsJSRuntime::ParseVersion(const nsString &aVersionStr, PRUint32 *flags)
3716 NS_PRECONDITION(flags, "Null flags param?");
3717 JSVersion jsVersion = JSVERSION_UNKNOWN;
3718 if (aVersionStr.Length() != 3 || aVersionStr[0] != '1' || aVersionStr[1] != '.')
3719 jsVersion = JSVERSION_UNKNOWN;
3720 else switch (aVersionStr[2]) {
3721 case '0': jsVersion = JSVERSION_1_0; break;
3722 case '1': jsVersion = JSVERSION_1_1; break;
3723 case '2': jsVersion = JSVERSION_1_2; break;
3724 case '3': jsVersion = JSVERSION_1_3; break;
3725 case '4': jsVersion = JSVERSION_1_4; break;
3726 case '5': jsVersion = JSVERSION_1_5; break;
3727 case '6': jsVersion = JSVERSION_1_6; break;
3728 case '7': jsVersion = JSVERSION_1_7; break;
3729 case '8': jsVersion = JSVERSION_1_8; break;
3730 default: jsVersion = JSVERSION_UNKNOWN;
3732 *flags = (PRUint32)jsVersion;
3733 return NS_OK;
3736 //static
3737 void
3738 nsJSRuntime::Startup()
3740 // initialize all our statics, so that we can restart XPCOM
3741 sDelayedCCollectCount = 0;
3742 sCCollectCount = 0;
3743 sUserIsActive = PR_FALSE;
3744 sPreviousCCTime = PR_Now();
3745 sCollectedObjectsCounts = 0;
3746 sSavedGCCount = 0;
3747 sCCSuspectChanges = 0;
3748 sCCSuspectedCount = 0;
3749 sGCTimer = nsnull;
3750 sReadyForGC = PR_FALSE;
3751 sLoadInProgressGCTimer = PR_FALSE;
3752 sPendingLoadCount = 0;
3753 gNameSpaceManager = nsnull;
3754 sRuntimeService = nsnull;
3755 sRuntime = nsnull;
3756 gOldJSGCCallback = nsnull;
3757 sIsInitialized = PR_FALSE;
3758 sDidShutdown = PR_FALSE;
3759 sContextCount = 0;
3760 sSecurityManager = nsnull;
3763 static int
3764 MaxScriptRunTimePrefChangedCallback(const char *aPrefName, void *aClosure)
3766 // Default limit on script run time to 10 seconds. 0 means let
3767 // scripts run forever.
3768 PRBool isChromePref =
3769 strcmp(aPrefName, "dom.max_chrome_script_run_time") == 0;
3770 PRInt32 time = nsContentUtils::GetIntPref(aPrefName, isChromePref ? 20 : 10);
3772 PRTime t;
3773 if (time <= 0) {
3774 // Let scripts run for a really, really long time.
3775 t = LL_INIT(0x40000000, 0);
3776 } else {
3777 t = time * PR_USEC_PER_SEC;
3780 if (isChromePref) {
3781 sMaxChromeScriptRunTime = t;
3782 } else {
3783 sMaxScriptRunTime = t;
3786 return 0;
3789 static int
3790 ReportAllJSExceptionsPrefChangedCallback(const char* aPrefName, void* aClosure)
3792 PRBool reportAll = nsContentUtils::GetBoolPref(aPrefName, PR_FALSE);
3793 nsContentUtils::XPConnect()->SetReportAllJSExceptions(reportAll);
3794 return 0;
3797 static int
3798 SetMemoryHighWaterMarkPrefChangedCallback(const char* aPrefName, void* aClosure)
3800 PRInt32 highwatermark = nsContentUtils::GetIntPref(aPrefName, 128);
3802 JS_SetGCParameter(nsJSRuntime::sRuntime, JSGC_MAX_MALLOC_BYTES,
3803 highwatermark * 1024L * 1024L);
3804 return 0;
3807 static int
3808 SetMemoryMaxPrefChangedCallback(const char* aPrefName, void* aClosure)
3810 PRInt32 pref = nsContentUtils::GetIntPref(aPrefName, -1);
3811 // handle overflow and negative pref values
3812 PRUint32 max = (pref <= 0 || pref >= 0x1000) ? -1 : (PRUint32)pref * 1024 * 1024;
3813 JS_SetGCParameter(nsJSRuntime::sRuntime, JSGC_MAX_BYTES, max);
3814 return 0;
3817 static int
3818 SetMemoryGCFrequencyPrefChangedCallback(const char* aPrefName, void* aClosure)
3820 PRInt32 triggerFactor = nsContentUtils::GetIntPref(aPrefName, 300);
3821 JS_SetGCParameter(nsJSRuntime::sRuntime, JSGC_TRIGGER_FACTOR, triggerFactor);
3822 return 0;
3825 static int
3826 SetMemoryGCModePrefChangedCallback(const char* aPrefName, void* aClosure)
3828 PRBool enableCompartmentGC = nsContentUtils::GetBoolPref(aPrefName);
3829 JS_SetGCParameter(nsJSRuntime::sRuntime, JSGC_MODE, enableCompartmentGC
3830 ? JSGC_MODE_COMPARTMENT
3831 : JSGC_MODE_GLOBAL);
3832 return 0;
3835 static JSPrincipals *
3836 ObjectPrincipalFinder(JSContext *cx, JSObject *obj)
3838 if (!sSecurityManager)
3839 return nsnull;
3841 nsCOMPtr<nsIPrincipal> principal;
3842 nsresult rv =
3843 sSecurityManager->GetObjectPrincipal(cx, obj,
3844 getter_AddRefs(principal));
3846 if (NS_FAILED(rv) || !principal) {
3847 return nsnull;
3850 JSPrincipals *jsPrincipals = nsnull;
3851 principal->GetJSPrincipals(cx, &jsPrincipals);
3853 // nsIPrincipal::GetJSPrincipals() returns a strong reference to the
3854 // JS principals, but the caller of this function expects a weak
3855 // reference. So we need to release here.
3857 JSPRINCIPALS_DROP(cx, jsPrincipals);
3859 return jsPrincipals;
3862 static JSObject*
3863 DOMReadStructuredClone(JSContext* cx,
3864 JSStructuredCloneReader* reader,
3865 uint32 tag,
3866 uint32 data,
3867 void* closure)
3869 // We don't currently support any extensions to structured cloning.
3870 nsDOMClassInfo::ThrowJSException(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
3871 return nsnull;
3874 static JSBool
3875 DOMWriteStructuredClone(JSContext* cx,
3876 JSStructuredCloneWriter* writer,
3877 JSObject* obj,
3878 void *closure)
3880 // We don't currently support any extensions to structured cloning.
3881 nsDOMClassInfo::ThrowJSException(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
3882 return JS_FALSE;
3885 static void
3886 DOMStructuredCloneError(JSContext* cx,
3887 uint32 errorid)
3889 // We don't currently support any extensions to structured cloning.
3890 nsDOMClassInfo::ThrowJSException(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
3893 //static
3894 nsresult
3895 nsJSRuntime::Init()
3897 if (sIsInitialized) {
3898 if (!nsContentUtils::XPConnect())
3899 return NS_ERROR_NOT_AVAILABLE;
3901 return NS_OK;
3904 nsresult rv = CallGetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID,
3905 &sSecurityManager);
3906 NS_ENSURE_SUCCESS(rv, rv);
3908 rv = CallGetService(kJSRuntimeServiceContractID, &sRuntimeService);
3909 // get the JSRuntime from the runtime svc, if possible
3910 NS_ENSURE_SUCCESS(rv, rv);
3912 rv = sRuntimeService->GetRuntime(&sRuntime);
3913 NS_ENSURE_SUCCESS(rv, rv);
3915 // Let's make sure that our main thread is the same as the xpcom main thread.
3916 NS_ASSERTION(NS_IsMainThread(), "bad");
3918 NS_ASSERTION(!gOldJSGCCallback,
3919 "nsJSRuntime initialized more than once");
3921 sSavedGCCount = JS_GetGCParameter(nsJSRuntime::sRuntime, JSGC_NUMBER);
3923 // Save the old GC callback to chain to it, for GC-observing generality.
3924 gOldJSGCCallback = ::JS_SetGCCallbackRT(sRuntime, DOMGCCallback);
3926 JSSecurityCallbacks *callbacks = JS_GetRuntimeSecurityCallbacks(sRuntime);
3927 NS_ASSERTION(callbacks, "SecMan should have set security callbacks!");
3929 callbacks->findObjectPrincipals = ObjectPrincipalFinder;
3931 // Set up the structured clone callbacks.
3932 static JSStructuredCloneCallbacks cloneCallbacks = {
3933 DOMReadStructuredClone,
3934 DOMWriteStructuredClone,
3935 DOMStructuredCloneError
3937 JS_SetStructuredCloneCallbacks(sRuntime, &cloneCallbacks);
3939 // Set these global xpconnect options...
3940 nsContentUtils::RegisterPrefCallback("dom.max_script_run_time",
3941 MaxScriptRunTimePrefChangedCallback,
3942 nsnull);
3943 MaxScriptRunTimePrefChangedCallback("dom.max_script_run_time", nsnull);
3945 nsContentUtils::RegisterPrefCallback("dom.max_chrome_script_run_time",
3946 MaxScriptRunTimePrefChangedCallback,
3947 nsnull);
3948 MaxScriptRunTimePrefChangedCallback("dom.max_chrome_script_run_time",
3949 nsnull);
3951 nsContentUtils::RegisterPrefCallback("dom.report_all_js_exceptions",
3952 ReportAllJSExceptionsPrefChangedCallback,
3953 nsnull);
3954 ReportAllJSExceptionsPrefChangedCallback("dom.report_all_js_exceptions",
3955 nsnull);
3957 nsContentUtils::RegisterPrefCallback("javascript.options.mem.high_water_mark",
3958 SetMemoryHighWaterMarkPrefChangedCallback,
3959 nsnull);
3960 SetMemoryHighWaterMarkPrefChangedCallback("javascript.options.mem.high_water_mark",
3961 nsnull);
3963 nsContentUtils::RegisterPrefCallback("javascript.options.mem.max",
3964 SetMemoryMaxPrefChangedCallback,
3965 nsnull);
3966 SetMemoryMaxPrefChangedCallback("javascript.options.mem.max",
3967 nsnull);
3969 nsContentUtils::RegisterPrefCallback("javascript.options.mem.gc_frequency",
3970 SetMemoryGCFrequencyPrefChangedCallback,
3971 nsnull);
3972 SetMemoryGCFrequencyPrefChangedCallback("javascript.options.mem.gc_frequency",
3973 nsnull);
3975 nsContentUtils::RegisterPrefCallback("javascript.options.mem.gc_per_compartment",
3976 SetMemoryGCModePrefChangedCallback,
3977 nsnull);
3978 SetMemoryGCModePrefChangedCallback("javascript.options.mem.gc_per_compartment",
3979 nsnull);
3981 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
3982 if (!obs)
3983 return NS_ERROR_FAILURE;
3984 nsIObserver* activityObserver = new nsUserActivityObserver();
3985 NS_ENSURE_TRUE(activityObserver, NS_ERROR_OUT_OF_MEMORY);
3986 obs->AddObserver(activityObserver, "user-interaction-inactive", PR_FALSE);
3987 obs->AddObserver(activityObserver, "user-interaction-active", PR_FALSE);
3988 obs->AddObserver(activityObserver, "xpcom-shutdown", PR_FALSE);
3990 nsIObserver* ccMemPressureObserver = new nsCCMemoryPressureObserver();
3991 NS_ENSURE_TRUE(ccMemPressureObserver, NS_ERROR_OUT_OF_MEMORY);
3992 obs->AddObserver(ccMemPressureObserver, "memory-pressure", PR_FALSE);
3994 sIsInitialized = PR_TRUE;
3996 return NS_OK;
3999 //static
4000 nsScriptNameSpaceManager*
4001 nsJSRuntime::GetNameSpaceManager()
4003 if (sDidShutdown)
4004 return nsnull;
4006 if (!gNameSpaceManager) {
4007 gNameSpaceManager = new nsScriptNameSpaceManager;
4008 NS_ADDREF(gNameSpaceManager);
4010 nsresult rv = gNameSpaceManager->Init();
4011 NS_ENSURE_SUCCESS(rv, nsnull);
4014 return gNameSpaceManager;
4017 /* static */
4018 void
4019 nsJSRuntime::Shutdown()
4021 if (sGCTimer) {
4022 // We're being shut down, if we have a GC timer scheduled, cancel
4023 // it. The DOM factory will do one final GC once it's shut down.
4025 sGCTimer->Cancel();
4027 NS_RELEASE(sGCTimer);
4029 sLoadInProgressGCTimer = PR_FALSE;
4032 NS_IF_RELEASE(gNameSpaceManager);
4034 if (!sContextCount) {
4035 // We're being shutdown, and there are no more contexts
4036 // alive, release the JS runtime service and the security manager.
4038 if (sRuntimeService && sSecurityManager) {
4039 JSSecurityCallbacks *callbacks = JS_GetRuntimeSecurityCallbacks(sRuntime);
4040 if (callbacks) {
4041 NS_ASSERTION(callbacks->findObjectPrincipals == ObjectPrincipalFinder,
4042 "Fighting over the findObjectPrincipals callback!");
4043 callbacks->findObjectPrincipals = NULL;
4046 NS_IF_RELEASE(sRuntimeService);
4047 NS_IF_RELEASE(sSecurityManager);
4050 sDidShutdown = PR_TRUE;
4053 // Script object mananagement - note duplicate implementation
4054 // in nsJSContext above...
4055 nsresult
4056 nsJSRuntime::HoldScriptObject(void* aScriptObject)
4058 NS_ASSERTION(sIsInitialized, "runtime not initialized");
4059 if (! sRuntime) {
4060 NS_NOTREACHED("couldn't remove GC root - no runtime");
4061 return NS_ERROR_FAILURE;
4064 ::JS_LockGCThingRT(sRuntime, aScriptObject);
4065 return NS_OK;
4068 nsresult
4069 nsJSRuntime::DropScriptObject(void* aScriptObject)
4071 NS_ASSERTION(sIsInitialized, "runtime not initialized");
4072 if (! sRuntime) {
4073 NS_NOTREACHED("couldn't remove GC root");
4074 return NS_ERROR_FAILURE;
4077 ::JS_UnlockGCThingRT(sRuntime, aScriptObject);
4078 return NS_OK;
4081 // A factory for the runtime.
4082 nsresult NS_CreateJSRuntime(nsIScriptRuntime **aRuntime)
4084 nsresult rv = nsJSRuntime::Init();
4085 NS_ENSURE_SUCCESS(rv, rv);
4087 *aRuntime = new nsJSRuntime();
4088 if (*aRuntime == nsnull)
4089 return NS_ERROR_OUT_OF_MEMORY;
4090 NS_IF_ADDREF(*aRuntime);
4091 return NS_OK;
4094 // A fast-array class for JS. This class supports both nsIJSScriptArray and
4095 // nsIArray. If it is JS itself providing and consuming this class, all work
4096 // can be done via nsIJSScriptArray, and avoid the conversion of elements
4097 // to/from nsISupports.
4098 // When consumed by non-JS (eg, another script language), conversion is done
4099 // on-the-fly.
4100 class nsJSArgArray : public nsIJSArgArray, public nsIArray {
4101 public:
4102 nsJSArgArray(JSContext *aContext, PRUint32 argc, jsval *argv, nsresult *prv);
4103 ~nsJSArgArray();
4104 // nsISupports
4105 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
4106 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsJSArgArray,
4107 nsIJSArgArray)
4109 // nsIArray
4110 NS_DECL_NSIARRAY
4112 // nsIJSArgArray
4113 nsresult GetArgs(PRUint32 *argc, void **argv);
4115 void ReleaseJSObjects();
4117 protected:
4118 JSContext *mContext;
4119 jsval *mArgv;
4120 PRUint32 mArgc;
4123 nsJSArgArray::nsJSArgArray(JSContext *aContext, PRUint32 argc, jsval *argv,
4124 nsresult *prv) :
4125 mContext(aContext),
4126 mArgv(nsnull),
4127 mArgc(argc)
4129 // copy the array - we don't know its lifetime, and ours is tied to xpcom
4130 // refcounting. Alloc zero'd array so cleanup etc is safe.
4131 if (argc) {
4132 mArgv = (jsval *) PR_CALLOC(argc * sizeof(jsval));
4133 if (!mArgv) {
4134 *prv = NS_ERROR_OUT_OF_MEMORY;
4135 return;
4139 // Callers are allowed to pass in a null argv even for argc > 0. They can
4140 // then use GetArgs to initialize the values.
4141 if (argv) {
4142 for (PRUint32 i = 0; i < argc; ++i)
4143 mArgv[i] = argv[i];
4146 *prv = argc > 0 ? NS_HOLD_JS_OBJECTS(this, nsJSArgArray) : NS_OK;
4149 nsJSArgArray::~nsJSArgArray()
4151 ReleaseJSObjects();
4154 void
4155 nsJSArgArray::ReleaseJSObjects()
4157 if (mArgc > 0)
4158 NS_DROP_JS_OBJECTS(this, nsJSArgArray);
4159 if (mArgv) {
4160 PR_DELETE(mArgv);
4162 mArgc = 0;
4165 // QueryInterface implementation for nsJSArgArray
4166 NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSArgArray)
4167 NS_IMPL_CYCLE_COLLECTION_ROOT_BEGIN(nsJSArgArray)
4168 tmp->ReleaseJSObjects();
4169 NS_IMPL_CYCLE_COLLECTION_ROOT_END
4170 NS_IMPL_CYCLE_COLLECTION_UNLINK_0(nsJSArgArray)
4171 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsJSArgArray)
4172 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
4173 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
4175 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSArgArray)
4176 jsval *argv = tmp->mArgv;
4177 if (argv) {
4178 jsval *end;
4179 for (end = argv + tmp->mArgc; argv < end; ++argv) {
4180 if (JSVAL_IS_GCTHING(*argv))
4181 NS_IMPL_CYCLE_COLLECTION_TRACE_CALLBACK(JAVASCRIPT,
4182 JSVAL_TO_GCTHING(*argv))
4185 NS_IMPL_CYCLE_COLLECTION_TRACE_END
4187 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSArgArray)
4188 NS_INTERFACE_MAP_ENTRY(nsIArray)
4189 NS_INTERFACE_MAP_ENTRY(nsIJSArgArray)
4190 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIJSArgArray)
4191 NS_INTERFACE_MAP_END
4193 NS_IMPL_CYCLE_COLLECTING_ADDREF_AMBIGUOUS(nsJSArgArray, nsIJSArgArray)
4194 NS_IMPL_CYCLE_COLLECTING_RELEASE_AMBIGUOUS(nsJSArgArray, nsIJSArgArray)
4196 nsresult
4197 nsJSArgArray::GetArgs(PRUint32 *argc, void **argv)
4199 if (!mArgv) {
4200 NS_WARNING("nsJSArgArray has no argv!");
4201 return NS_ERROR_UNEXPECTED;
4203 *argv = (void *)mArgv;
4204 *argc = mArgc;
4205 return NS_OK;
4208 // nsIArray impl
4209 NS_IMETHODIMP nsJSArgArray::GetLength(PRUint32 *aLength)
4211 *aLength = mArgc;
4212 return NS_OK;
4215 /* void queryElementAt (in unsigned long index, in nsIIDRef uuid, [iid_is (uuid), retval] out nsQIResult result); */
4216 NS_IMETHODIMP nsJSArgArray::QueryElementAt(PRUint32 index, const nsIID & uuid, void * *result)
4218 *result = nsnull;
4219 if (index >= mArgc)
4220 return NS_ERROR_INVALID_ARG;
4222 if (uuid.Equals(NS_GET_IID(nsIVariant)) || uuid.Equals(NS_GET_IID(nsISupports))) {
4223 return nsContentUtils::XPConnect()->JSToVariant(mContext, mArgv[index],
4224 (nsIVariant **)result);
4226 NS_WARNING("nsJSArgArray only handles nsIVariant");
4227 return NS_ERROR_NO_INTERFACE;
4230 /* unsigned long indexOf (in unsigned long startIndex, in nsISupports element); */
4231 NS_IMETHODIMP nsJSArgArray::IndexOf(PRUint32 startIndex, nsISupports *element, PRUint32 *_retval)
4233 return NS_ERROR_NOT_IMPLEMENTED;
4236 /* nsISimpleEnumerator enumerate (); */
4237 NS_IMETHODIMP nsJSArgArray::Enumerate(nsISimpleEnumerator **_retval)
4239 return NS_ERROR_NOT_IMPLEMENTED;
4242 // The factory function
4243 nsresult NS_CreateJSArgv(JSContext *aContext, PRUint32 argc, void *argv,
4244 nsIArray **aArray)
4246 nsresult rv;
4247 nsJSArgArray *ret = new nsJSArgArray(aContext, argc,
4248 static_cast<jsval *>(argv), &rv);
4249 if (ret == nsnull)
4250 return NS_ERROR_OUT_OF_MEMORY;
4251 if (NS_FAILED(rv)) {
4252 delete ret;
4253 return rv;
4255 return ret->QueryInterface(NS_GET_IID(nsIArray), (void **)aArray);