Merge mozilla-central and tracemonkey. (a=blockers)
[mozilla-central.git] / dom / base / nsJSEnvironment.cpp
blobf035dc48f25340a24613993531a3095fcca028d5
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"
88 #include "xpcpublic.h"
90 #include "jsdbgapi.h" // for JS_ClearWatchPointsForObject
91 #include "jsxdrapi.h"
92 #include "nsIArray.h"
93 #include "nsIObjectInputStream.h"
94 #include "nsIObjectOutputStream.h"
95 #include "nsITimelineService.h"
96 #include "nsDOMScriptObjectHolder.h"
97 #include "prmem.h"
98 #include "WrapperFactory.h"
99 #include "nsGlobalWindow.h"
101 #ifdef XP_MACOSX
102 // AssertMacros.h defines 'check' and conflicts with AccessCheck.h
103 #undef check
104 #endif
105 #include "AccessCheck.h"
107 #ifdef MOZ_JSDEBUGGER
108 #include "jsdIDebuggerService.h"
109 #endif
110 #ifdef MOZ_LOGGING
111 // Force PR_LOGGING so we can get JS strict warnings even in release builds
112 #define FORCE_PR_LOG 1
113 #endif
114 #include "prlog.h"
115 #include "prthread.h"
117 #include "mozilla/FunctionTimer.h"
119 const size_t gStackSize = 8192;
121 #ifdef PR_LOGGING
122 static PRLogModuleInfo* gJSDiagnostics;
123 #endif
125 // Thank you Microsoft!
126 #ifndef WINCE
127 #ifdef CompareString
128 #undef CompareString
129 #endif
130 #endif // WINCE
132 // The amount of time we wait between a request to GC (due to leaving
133 // a page) and doing the actual GC.
134 #define NS_GC_DELAY 4000 // ms
136 // The amount of time we wait from the first request to GC to actually
137 // doing the first GC.
138 #define NS_FIRST_GC_DELAY 10000 // ms
140 // The amount of time we wait between a request to CC (after GC ran)
141 // and doing the actual CC.
142 #define NS_CC_DELAY 5000 // ms
144 #define JAVASCRIPT nsIProgrammingLanguage::JAVASCRIPT
146 // if you add statics here, add them to the list in nsJSRuntime::Startup
148 static nsITimer *sGCTimer;
149 static nsITimer *sCCTimer;
151 // The number of currently pending document loads. This count isn't
152 // guaranteed to always reflect reality and can't easily as we don't
153 // have an easy place to know when a load ends or is interrupted in
154 // all cases. This counter also gets reset if we end up GC'ing while
155 // we're waiting for a slow page to load. IOW, this count may be 0
156 // even when there are pending loads.
157 static PRUint32 sPendingLoadCount;
158 static PRBool sLoadingInProgress;
160 static PRBool sPostGCEventsToConsole;
162 nsScriptNameSpaceManager *gNameSpaceManager;
164 static nsIJSRuntimeService *sRuntimeService;
165 JSRuntime *nsJSRuntime::sRuntime;
167 static const char kJSRuntimeServiceContractID[] =
168 "@mozilla.org/js/xpc/RuntimeService;1";
170 static JSGCCallback gOldJSGCCallback;
172 static PRBool sIsInitialized;
173 static PRBool sDidShutdown;
175 static PRInt32 sContextCount;
177 static PRTime sMaxScriptRunTime;
178 static PRTime sMaxChromeScriptRunTime;
180 static nsIScriptSecurityManager *sSecurityManager;
182 // nsMemoryPressureObserver observes the memory-pressure notifications
183 // and forces a garbage collection and cycle collection when it happens.
185 class nsMemoryPressureObserver : public nsIObserver
187 public:
188 NS_DECL_ISUPPORTS
189 NS_DECL_NSIOBSERVER
192 NS_IMPL_ISUPPORTS1(nsMemoryPressureObserver, nsIObserver)
194 NS_IMETHODIMP
195 nsMemoryPressureObserver::Observe(nsISupports* aSubject, const char* aTopic,
196 const PRUnichar* aData)
198 nsJSContext::GarbageCollectNow();
199 nsJSContext::CycleCollectNow();
200 return NS_OK;
203 /****************************************************************
204 ************************** AutoFree ****************************
205 ****************************************************************/
207 class AutoFree {
208 public:
209 AutoFree(void *aPtr) : mPtr(aPtr) {
211 ~AutoFree() {
212 if (mPtr)
213 nsMemory::Free(mPtr);
215 void Invalidate() {
216 mPtr = 0;
218 private:
219 void *mPtr;
222 class nsAutoPoolRelease {
223 public:
224 nsAutoPoolRelease(JSArenaPool *p, void *m) : mPool(p), mMark(m) {}
225 ~nsAutoPoolRelease() { JS_ARENA_RELEASE(mPool, mMark); }
226 private:
227 JSArenaPool *mPool;
228 void *mMark;
231 // A utility function for script languages to call. Although it looks small,
232 // the use of nsIDocShell and nsPresContext triggers a huge number of
233 // dependencies that most languages would not otherwise need.
234 // XXXmarkh - This function is mis-placed!
235 PRBool
236 NS_HandleScriptError(nsIScriptGlobalObject *aScriptGlobal,
237 nsScriptErrorEvent *aErrorEvent,
238 nsEventStatus *aStatus)
240 PRBool called = PR_FALSE;
241 nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(aScriptGlobal));
242 nsIDocShell *docShell = win ? win->GetDocShell() : nsnull;
243 if (docShell) {
244 nsRefPtr<nsPresContext> presContext;
245 docShell->GetPresContext(getter_AddRefs(presContext));
247 static PRInt32 errorDepth; // Recursion prevention
248 ++errorDepth;
250 if (presContext && errorDepth < 2) {
251 // Dispatch() must be synchronous for the recursion block
252 // (errorDepth) to work.
253 nsEventDispatcher::Dispatch(win, presContext, aErrorEvent, nsnull,
254 aStatus);
255 called = PR_TRUE;
257 --errorDepth;
259 return called;
262 class ScriptErrorEvent : public nsRunnable
264 public:
265 ScriptErrorEvent(nsIScriptGlobalObject* aScriptGlobal,
266 PRUint32 aLineNr, PRUint32 aColumn, PRUint32 aFlags,
267 const nsAString& aErrorMsg,
268 const nsAString& aFileName,
269 const nsAString& aSourceLine,
270 PRBool aDispatchEvent,
271 PRUint64 aWindowID)
272 : mScriptGlobal(aScriptGlobal), mLineNr(aLineNr), mColumn(aColumn),
273 mFlags(aFlags), mErrorMsg(aErrorMsg), mFileName(aFileName),
274 mSourceLine(aSourceLine), mDispatchEvent(aDispatchEvent),
275 mWindowID(aWindowID)
278 NS_IMETHOD Run()
280 nsEventStatus status = nsEventStatus_eIgnore;
281 // First, notify the DOM that we have a script error.
282 if (mDispatchEvent) {
283 nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(mScriptGlobal));
284 nsIDocShell* docShell = win ? win->GetDocShell() : nsnull;
285 if (docShell &&
286 !JSREPORT_IS_WARNING(mFlags) &&
287 !sHandlingScriptError) {
288 sHandlingScriptError = PR_TRUE; // Recursion prevention
290 nsRefPtr<nsPresContext> presContext;
291 docShell->GetPresContext(getter_AddRefs(presContext));
293 if (presContext) {
294 nsScriptErrorEvent errorevent(PR_TRUE, NS_LOAD_ERROR);
296 errorevent.fileName = mFileName.get();
298 nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(win));
299 NS_ENSURE_STATE(sop);
300 nsIPrincipal* p = sop->GetPrincipal();
301 NS_ENSURE_STATE(p);
303 PRBool sameOrigin = mFileName.IsVoid();
305 if (p && !sameOrigin) {
306 nsCOMPtr<nsIURI> errorURI;
307 NS_NewURI(getter_AddRefs(errorURI), mFileName);
308 if (errorURI) {
309 // FIXME: Once error reports contain the origin of the
310 // error (principals) we should change this to do the
311 // security check based on the principals and not
312 // URIs. See bug 387476.
313 sameOrigin = NS_SUCCEEDED(p->CheckMayLoad(errorURI, PR_FALSE));
317 NS_NAMED_LITERAL_STRING(xoriginMsg, "Script error.");
318 if (sameOrigin) {
319 errorevent.errorMsg = mErrorMsg.get();
320 errorevent.lineNr = mLineNr;
321 } else {
322 NS_WARNING("Not same origin error!");
323 errorevent.errorMsg = xoriginMsg.get();
324 errorevent.lineNr = 0;
325 // FIXME: once the principal of the script is not tied to
326 // the filename, we can stop using the post-redirect
327 // filename if we want and remove this line. Note that
328 // apparently we can't handle null filenames in the error
329 // event dispatching code.
330 static PRUnichar nullFilename[] = { PRUnichar(0) };
331 errorevent.fileName = nullFilename;
334 nsEventDispatcher::Dispatch(win, presContext, &errorevent, nsnull,
335 &status);
338 sHandlingScriptError = PR_FALSE;
342 if (status != nsEventStatus_eConsumeNoDefault) {
343 // Make an nsIScriptError and populate it with information from
344 // this error.
345 nsCOMPtr<nsIScriptError> errorObject =
346 do_CreateInstance("@mozilla.org/scripterror;1");
348 if (errorObject != nsnull) {
349 nsresult rv = NS_ERROR_NOT_AVAILABLE;
351 // Set category to chrome or content
352 nsCOMPtr<nsIScriptObjectPrincipal> scriptPrincipal =
353 do_QueryInterface(mScriptGlobal);
354 NS_ASSERTION(scriptPrincipal, "Global objects must implement "
355 "nsIScriptObjectPrincipal");
356 nsCOMPtr<nsIPrincipal> systemPrincipal;
357 sSecurityManager->GetSystemPrincipal(getter_AddRefs(systemPrincipal));
358 const char * category =
359 scriptPrincipal->GetPrincipal() == systemPrincipal
360 ? "chrome javascript"
361 : "content javascript";
363 nsCOMPtr<nsIScriptError2> error2(do_QueryInterface(errorObject));
364 if (error2) {
365 rv = error2->InitWithWindowID(mErrorMsg.get(), mFileName.get(),
366 mSourceLine.get(),
367 mLineNr, mColumn, mFlags,
368 category, mWindowID);
369 } else {
370 rv = errorObject->Init(mErrorMsg.get(), mFileName.get(),
371 mSourceLine.get(),
372 mLineNr, mColumn, mFlags,
373 category);
376 if (NS_SUCCEEDED(rv)) {
377 nsCOMPtr<nsIConsoleService> consoleService =
378 do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv);
379 if (NS_SUCCEEDED(rv)) {
380 consoleService->LogMessage(errorObject);
385 return NS_OK;
389 nsCOMPtr<nsIScriptGlobalObject> mScriptGlobal;
390 PRUint32 mLineNr;
391 PRUint32 mColumn;
392 PRUint32 mFlags;
393 nsString mErrorMsg;
394 nsString mFileName;
395 nsString mSourceLine;
396 PRBool mDispatchEvent;
397 PRUint64 mWindowID;
399 static PRBool sHandlingScriptError;
402 PRBool ScriptErrorEvent::sHandlingScriptError = PR_FALSE;
404 // NOTE: This function could be refactored to use the above. The only reason
405 // it has not been done is that the code below only fills the error event
406 // after it has a good nsPresContext - whereas using the above function
407 // would involve always filling it. Is that a concern?
408 void
409 NS_ScriptErrorReporter(JSContext *cx,
410 const char *message,
411 JSErrorReport *report)
413 // We don't want to report exceptions too eagerly, but warnings in the
414 // absence of werror are swallowed whole, so report those now.
415 if (!JSREPORT_IS_WARNING(report->flags)) {
416 JSStackFrame * fp = nsnull;
417 while ((fp = JS_FrameIterator(cx, &fp))) {
418 if (JS_IsScriptFrame(cx, fp)) {
419 return;
423 nsIXPConnect* xpc = nsContentUtils::XPConnect();
424 if (xpc) {
425 nsAXPCNativeCallContext *cc = nsnull;
426 xpc->GetCurrentNativeCallContext(&cc);
427 if (cc) {
428 nsAXPCNativeCallContext *prev = cc;
429 while (NS_SUCCEEDED(prev->GetPreviousCallContext(&prev)) && prev) {
430 PRUint16 lang;
431 if (NS_SUCCEEDED(prev->GetLanguage(&lang)) &&
432 lang == nsAXPCNativeCallContext::LANG_JS) {
433 return;
440 // XXX this means we are not going to get error reports on non DOM contexts
441 nsIScriptContext *context = nsJSUtils::GetDynamicScriptContext(cx);
443 // Note: we must do this before running any more code on cx (if cx is the
444 // dynamic script context).
445 ::JS_ClearPendingException(cx);
447 if (context) {
448 nsIScriptGlobalObject *globalObject = context->GetGlobalObject();
450 if (globalObject) {
451 nsAutoString fileName, msg;
452 if (!report->filename) {
453 fileName.SetIsVoid(PR_TRUE);
454 } else {
455 fileName.AssignWithConversion(report->filename);
458 const PRUnichar *m = reinterpret_cast<const PRUnichar*>
459 (report->ucmessage);
460 if (m) {
461 msg.Assign(m);
464 if (msg.IsEmpty() && message) {
465 msg.AssignWithConversion(message);
469 /* We do not try to report Out Of Memory via a dom
470 * event because the dom event handler would encounter
471 * an OOM exception trying to process the event, and
472 * then we'd need to generate a new OOM event for that
473 * new OOM instance -- this isn't pretty.
475 nsAutoString sourceLine;
476 sourceLine.Assign(reinterpret_cast<const PRUnichar*>(report->uclinebuf));
477 nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(globalObject);
478 PRUint64 windowID = win ? win->WindowID() : 0;
479 nsContentUtils::AddScriptRunner(
480 new ScriptErrorEvent(globalObject, report->lineno,
481 report->uctokenptr - report->uclinebuf,
482 report->flags, msg, fileName, sourceLine,
483 report->errorNumber != JSMSG_OUT_OF_MEMORY,
484 windowID));
488 #ifdef DEBUG
489 // Print it to stderr as well, for the benefit of those invoking
490 // mozilla with -console.
491 nsCAutoString error;
492 error.Assign("JavaScript ");
493 if (JSREPORT_IS_STRICT(report->flags))
494 error.Append("strict ");
495 if (JSREPORT_IS_WARNING(report->flags))
496 error.Append("warning: ");
497 else
498 error.Append("error: ");
499 error.Append(report->filename);
500 error.Append(", line ");
501 error.AppendInt(report->lineno, 10);
502 error.Append(": ");
503 if (report->ucmessage) {
504 AppendUTF16toUTF8(reinterpret_cast<const PRUnichar*>(report->ucmessage),
505 error);
506 } else {
507 error.Append(message);
510 fprintf(stderr, "%s\n", error.get());
511 fflush(stderr);
512 #endif
514 #ifdef PR_LOGGING
515 if (!gJSDiagnostics)
516 gJSDiagnostics = PR_NewLogModule("JSDiagnostics");
518 if (gJSDiagnostics) {
519 PR_LOG(gJSDiagnostics,
520 JSREPORT_IS_WARNING(report->flags) ? PR_LOG_WARNING : PR_LOG_ERROR,
521 ("file %s, line %u: %s\n%s%s",
522 report->filename, report->lineno, message,
523 report->linebuf ? report->linebuf : "",
524 (report->linebuf &&
525 report->linebuf[strlen(report->linebuf)-1] != '\n')
526 ? "\n"
527 : ""));
529 #endif
532 #ifdef DEBUG
533 // A couple of useful functions to call when you're debugging.
534 nsGlobalWindow *
535 JSObject2Win(JSContext *cx, JSObject *obj)
537 nsIXPConnect *xpc = nsContentUtils::XPConnect();
538 if (!xpc) {
539 return nsnull;
542 nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
543 xpc->GetWrappedNativeOfJSObject(cx, obj, getter_AddRefs(wrapper));
544 if (wrapper) {
545 nsCOMPtr<nsPIDOMWindow> win = do_QueryWrappedNative(wrapper);
546 if (win) {
547 return static_cast<nsGlobalWindow *>
548 (static_cast<nsPIDOMWindow *>(win));
552 return nsnull;
555 void
556 PrintWinURI(nsGlobalWindow *win)
558 if (!win) {
559 printf("No window passed in.\n");
560 return;
563 nsCOMPtr<nsIDocument> doc = do_QueryInterface(win->GetExtantDocument());
564 if (!doc) {
565 printf("No document in the window.\n");
566 return;
569 nsIURI *uri = doc->GetDocumentURI();
570 if (!uri) {
571 printf("Document doesn't have a URI.\n");
572 return;
575 nsCAutoString spec;
576 uri->GetSpec(spec);
577 printf("%s\n", spec.get());
580 void
581 PrintWinCodebase(nsGlobalWindow *win)
583 if (!win) {
584 printf("No window passed in.\n");
585 return;
588 nsIPrincipal *prin = win->GetPrincipal();
589 if (!prin) {
590 printf("Window doesn't have principals.\n");
591 return;
594 nsCOMPtr<nsIURI> uri;
595 prin->GetURI(getter_AddRefs(uri));
596 if (!uri) {
597 printf("No URI, maybe the system principal.\n");
598 return;
601 nsCAutoString spec;
602 uri->GetSpec(spec);
603 printf("%s\n", spec.get());
606 void
607 DumpString(const nsAString &str)
609 printf("%s\n", NS_ConvertUTF16toUTF8(str).get());
611 #endif
613 static already_AddRefed<nsIPrompt>
614 GetPromptFromContext(nsJSContext* ctx)
616 nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(ctx->GetGlobalObject()));
617 NS_ENSURE_TRUE(win, nsnull);
619 nsIDocShell *docShell = win->GetDocShell();
620 NS_ENSURE_TRUE(docShell, nsnull);
622 nsCOMPtr<nsIInterfaceRequestor> ireq(do_QueryInterface(docShell));
623 NS_ENSURE_TRUE(ireq, nsnull);
625 // Get the nsIPrompt interface from the docshell
626 nsIPrompt* prompt;
627 ireq->GetInterface(NS_GET_IID(nsIPrompt), (void**)&prompt);
628 return prompt;
631 JSBool
632 nsJSContext::DOMOperationCallback(JSContext *cx)
634 nsresult rv;
636 // Get the native context
637 nsJSContext *ctx = static_cast<nsJSContext *>(::JS_GetContextPrivate(cx));
639 if (!ctx) {
640 // Can happen; see bug 355811
641 return JS_TRUE;
644 // XXX Save the operation callback time so we can restore it after the GC,
645 // because GCing can cause JS to run on our context, causing our
646 // ScriptEvaluated to be called, and clearing our operation callback time.
647 // See bug 302333.
648 PRTime callbackTime = ctx->mOperationCallbackTime;
649 PRTime modalStateTime = ctx->mModalStateTime;
651 // Now restore the callback time and count, in case they got reset.
652 ctx->mOperationCallbackTime = callbackTime;
653 ctx->mModalStateTime = modalStateTime;
655 PRTime now = PR_Now();
657 if (callbackTime == 0) {
658 // Initialize mOperationCallbackTime to start timing how long the
659 // script has run
660 ctx->mOperationCallbackTime = now;
661 return JS_TRUE;
664 if (ctx->mModalStateDepth) {
665 // We're waiting on a modal dialog, nothing more to do here.
666 return JS_TRUE;
669 PRTime duration = now - callbackTime;
671 // Check the amount of time this script has been running, or if the
672 // dialog is disabled.
673 JSObject* global = ::JS_GetGlobalForScopeChain(cx);
674 PRBool isTrackingChromeCodeTime =
675 global && xpc::AccessCheck::isChrome(global->getCompartment());
676 if (duration < (isTrackingChromeCodeTime ?
677 sMaxChromeScriptRunTime : sMaxScriptRunTime)) {
678 return JS_TRUE;
681 if (!nsContentUtils::IsSafeToRunScript()) {
682 // If it isn't safe to run script, then it isn't safe to bring up the
683 // prompt (since that will cause the event loop to spin). In this case
684 // (which is rare), we just stop the script... But report a warning so
685 // that developers have some idea of what went wrong.
687 JS_ReportWarning(cx, "A long running script was terminated");
688 return JS_FALSE;
691 // If we get here we're most likely executing an infinite loop in JS,
692 // we'll tell the user about this and we'll give the user the option
693 // of stopping the execution of the script.
694 nsCOMPtr<nsIPrompt> prompt = GetPromptFromContext(ctx);
695 NS_ENSURE_TRUE(prompt, JS_TRUE);
697 // Check if we should offer the option to debug
698 JSStackFrame* fp = ::JS_GetScriptedCaller(cx, NULL);
699 PRBool debugPossible = (fp != nsnull && cx->debugHooks &&
700 cx->debugHooks->debuggerHandler != nsnull);
701 #ifdef MOZ_JSDEBUGGER
702 // Get the debugger service if necessary.
703 if (debugPossible) {
704 PRBool jsds_IsOn = PR_FALSE;
705 const char jsdServiceCtrID[] = "@mozilla.org/js/jsd/debugger-service;1";
706 nsCOMPtr<jsdIExecutionHook> jsdHook;
707 nsCOMPtr<jsdIDebuggerService> jsds = do_GetService(jsdServiceCtrID, &rv);
709 // Check if there's a user for the debugger service that's 'on' for us
710 if (NS_SUCCEEDED(rv)) {
711 jsds->GetDebuggerHook(getter_AddRefs(jsdHook));
712 jsds->GetIsOn(&jsds_IsOn);
715 // If there is a debug handler registered for this runtime AND
716 // ((jsd is on AND has a hook) OR (jsd isn't on (something else debugs)))
717 // then something useful will be done with our request to debug.
718 debugPossible = ((jsds_IsOn && (jsdHook != nsnull)) || !jsds_IsOn);
720 #endif
722 // Get localizable strings
723 nsXPIDLString title, msg, stopButton, waitButton, debugButton, neverShowDlg;
725 rv = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
726 "KillScriptTitle",
727 title);
729 rv |= nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
730 "StopScriptButton",
731 stopButton);
733 rv |= nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
734 "WaitForScriptButton",
735 waitButton);
737 rv |= nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
738 "DontAskAgain",
739 neverShowDlg);
742 if (debugPossible) {
743 rv |= nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
744 "DebugScriptButton",
745 debugButton);
747 rv |= nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
748 "KillScriptWithDebugMessage",
749 msg);
751 else {
752 rv |= nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
753 "KillScriptMessage",
754 msg);
757 //GetStringFromName can return NS_OK and still give NULL string
758 if (NS_FAILED(rv) || !title || !msg || !stopButton || !waitButton ||
759 (!debugButton && debugPossible) || !neverShowDlg) {
760 NS_ERROR("Failed to get localized strings.");
761 return JS_TRUE;
764 // Append file and line number information, if available
765 JSScript *script = fp ? ::JS_GetFrameScript(cx, fp) : nsnull;
766 if (script) {
767 const char *filename = ::JS_GetScriptFilename(cx, script);
768 if (filename) {
769 nsXPIDLString scriptLocation;
770 NS_ConvertUTF8toUTF16 filenameUTF16(filename);
771 const PRUnichar *formatParams[] = { filenameUTF16.get() };
772 rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES,
773 "KillScriptLocation",
774 formatParams, 1,
775 scriptLocation);
777 if (NS_SUCCEEDED(rv) && scriptLocation) {
778 msg.AppendLiteral("\n\n");
779 msg.Append(scriptLocation);
781 JSStackFrame *fp, *iterator = nsnull;
782 fp = ::JS_FrameIterator(cx, &iterator);
783 if (fp) {
784 jsbytecode *pc = ::JS_GetFramePC(cx, fp);
785 if (pc) {
786 PRUint32 lineno = ::JS_PCToLineNumber(cx, script, pc);
787 msg.Append(':');
788 msg.AppendInt(lineno);
795 PRInt32 buttonPressed = 0; //In case user exits dialog by clicking X
796 PRBool neverShowDlgChk = PR_FALSE;
797 PRUint32 buttonFlags = nsIPrompt::BUTTON_POS_1_DEFAULT +
798 (nsIPrompt::BUTTON_TITLE_IS_STRING *
799 (nsIPrompt::BUTTON_POS_0 + nsIPrompt::BUTTON_POS_1));
801 // Add a third button if necessary:
802 if (debugPossible)
803 buttonFlags += nsIPrompt::BUTTON_TITLE_IS_STRING * nsIPrompt::BUTTON_POS_2;
805 // Null out the operation callback while we're re-entering JS here.
806 ::JS_SetOperationCallback(cx, nsnull);
808 // Open the dialog.
809 rv = prompt->ConfirmEx(title, msg, buttonFlags, waitButton, stopButton,
810 debugButton, neverShowDlg, &neverShowDlgChk,
811 &buttonPressed);
813 ::JS_SetOperationCallback(cx, DOMOperationCallback);
815 if (NS_FAILED(rv) || (buttonPressed == 0)) {
816 // Allow the script to continue running
818 if (neverShowDlgChk) {
819 nsIPrefBranch *prefBranch = nsContentUtils::GetPrefBranch();
821 if (prefBranch) {
822 prefBranch->SetIntPref(isTrackingChromeCodeTime ?
823 "dom.max_chrome_script_run_time" :
824 "dom.max_script_run_time", 0);
828 ctx->mOperationCallbackTime = PR_Now();
829 return JS_TRUE;
831 else if ((buttonPressed == 2) && debugPossible) {
832 // Debug the script
833 jsval rval;
834 switch(cx->debugHooks->debuggerHandler(cx, script, ::JS_GetFramePC(cx, fp),
835 &rval,
836 cx->debugHooks->
837 debuggerHandlerData)) {
838 case JSTRAP_RETURN:
839 JS_SetFrameReturnValue(cx, fp, rval);
840 return JS_TRUE;
841 case JSTRAP_ERROR:
842 JS_ClearPendingException(cx);
843 return JS_FALSE;
844 case JSTRAP_THROW:
845 JS_SetPendingException(cx, rval);
846 return JS_FALSE;
847 case JSTRAP_CONTINUE:
848 default:
849 return JS_TRUE;
853 JS_ClearPendingException(cx);
854 return JS_FALSE;
857 void
858 nsJSContext::EnterModalState()
860 if (!mModalStateDepth) {
861 mModalStateTime = mOperationCallbackTime ? PR_Now() : 0;
863 ++mModalStateDepth;
866 void
867 nsJSContext::LeaveModalState()
869 if (!mModalStateDepth) {
870 NS_ERROR("Uh, mismatched LeaveModalState() call!");
872 return;
875 --mModalStateDepth;
877 // If we're still in a modal dialog, or mOperationCallbackTime is still
878 // uninitialized, do nothing.
879 if (mModalStateDepth || !mOperationCallbackTime) {
880 return;
883 // If mOperationCallbackTime was set when we entered the first dialog
884 // (and mModalStateTime is thus non-zero), adjust mOperationCallbackTime
885 // to account for time spent in the dialog.
886 // If mOperationCallbackTime got set while the modal dialog was open,
887 // simply set mOperationCallbackTime to the closing time of the dialog so
888 // that we never adjust mOperationCallbackTime to be in the future.
889 if (mModalStateTime) {
890 mOperationCallbackTime += PR_Now() - mModalStateTime;
892 else {
893 mOperationCallbackTime = PR_Now();
897 #define JS_OPTIONS_DOT_STR "javascript.options."
899 static const char js_options_dot_str[] = JS_OPTIONS_DOT_STR;
900 static const char js_strict_option_str[] = JS_OPTIONS_DOT_STR "strict";
901 #ifdef DEBUG
902 static const char js_strict_debug_option_str[] = JS_OPTIONS_DOT_STR "strict.debug";
903 #endif
904 static const char js_werror_option_str[] = JS_OPTIONS_DOT_STR "werror";
905 static const char js_relimit_option_str[]= JS_OPTIONS_DOT_STR "relimit";
906 #ifdef JS_GC_ZEAL
907 static const char js_zeal_option_str[] = JS_OPTIONS_DOT_STR "gczeal";
908 #endif
909 static const char js_tracejit_content_str[] = JS_OPTIONS_DOT_STR "tracejit.content";
910 static const char js_tracejit_chrome_str[] = JS_OPTIONS_DOT_STR "tracejit.chrome";
911 static const char js_methodjit_content_str[] = JS_OPTIONS_DOT_STR "methodjit.content";
912 static const char js_methodjit_chrome_str[] = JS_OPTIONS_DOT_STR "methodjit.chrome";
913 static const char js_profiling_content_str[] = JS_OPTIONS_DOT_STR "jitprofiling.content";
914 static const char js_profiling_chrome_str[] = JS_OPTIONS_DOT_STR "jitprofiling.chrome";
915 static const char js_methodjit_always_str[] = JS_OPTIONS_DOT_STR "methodjit_always";
916 static const char js_memlog_option_str[] = JS_OPTIONS_DOT_STR "mem.log";
919 nsJSContext::JSOptionChangedCallback(const char *pref, void *data)
921 nsJSContext *context = reinterpret_cast<nsJSContext *>(data);
922 PRUint32 oldDefaultJSOptions = context->mDefaultJSOptions;
923 PRUint32 newDefaultJSOptions = oldDefaultJSOptions;
925 sPostGCEventsToConsole = nsContentUtils::GetBoolPref(js_memlog_option_str);
927 PRBool strict = nsContentUtils::GetBoolPref(js_strict_option_str);
928 if (strict)
929 newDefaultJSOptions |= JSOPTION_STRICT;
930 else
931 newDefaultJSOptions &= ~JSOPTION_STRICT;
933 nsIScriptGlobalObject *global = context->GetGlobalObject();
934 // XXX should we check for sysprin instead of a chrome window, to make
935 // XXX components be covered by the chrome pref instead of the content one?
936 nsCOMPtr<nsIDOMChromeWindow> chromeWindow(do_QueryInterface(global));
938 PRBool useTraceJIT = nsContentUtils::GetBoolPref(chromeWindow ?
939 js_tracejit_chrome_str :
940 js_tracejit_content_str);
941 PRBool useMethodJIT = nsContentUtils::GetBoolPref(chromeWindow ?
942 js_methodjit_chrome_str :
943 js_methodjit_content_str);
944 PRBool useProfiling = nsContentUtils::GetBoolPref(chromeWindow ?
945 js_profiling_chrome_str :
946 js_profiling_content_str);
947 PRBool useMethodJITAlways = nsContentUtils::GetBoolPref(js_methodjit_always_str);
948 nsCOMPtr<nsIXULRuntime> xr = do_GetService(XULRUNTIME_SERVICE_CONTRACTID);
949 if (xr) {
950 PRBool safeMode = PR_FALSE;
951 xr->GetInSafeMode(&safeMode);
952 if (safeMode) {
953 useTraceJIT = PR_FALSE;
954 useMethodJIT = PR_FALSE;
955 useProfiling = PR_FALSE;
956 useMethodJITAlways = PR_TRUE;
960 if (useTraceJIT)
961 newDefaultJSOptions |= JSOPTION_JIT;
962 else
963 newDefaultJSOptions &= ~JSOPTION_JIT;
965 if (useMethodJIT)
966 newDefaultJSOptions |= JSOPTION_METHODJIT;
967 else
968 newDefaultJSOptions &= ~JSOPTION_METHODJIT;
970 if (useProfiling)
971 newDefaultJSOptions |= JSOPTION_PROFILING;
972 else
973 newDefaultJSOptions &= ~JSOPTION_PROFILING;
975 if (useMethodJITAlways)
976 newDefaultJSOptions |= JSOPTION_METHODJIT_ALWAYS;
977 else
978 newDefaultJSOptions &= ~JSOPTION_METHODJIT_ALWAYS;
980 #ifdef DEBUG
981 // In debug builds, warnings are enabled in chrome context if
982 // javascript.options.strict.debug is true
983 PRBool strictDebug = nsContentUtils::GetBoolPref(js_strict_debug_option_str);
984 // Note this callback is also called from context's InitClasses thus we don't
985 // need to enable this directly from InitContext
986 if (strictDebug && (newDefaultJSOptions & JSOPTION_STRICT) == 0) {
987 if (chromeWindow)
988 newDefaultJSOptions |= JSOPTION_STRICT;
990 #endif
992 PRBool werror = nsContentUtils::GetBoolPref(js_werror_option_str);
993 if (werror)
994 newDefaultJSOptions |= JSOPTION_WERROR;
995 else
996 newDefaultJSOptions &= ~JSOPTION_WERROR;
998 PRBool relimit = nsContentUtils::GetBoolPref(js_relimit_option_str);
999 if (relimit)
1000 newDefaultJSOptions |= JSOPTION_RELIMIT;
1001 else
1002 newDefaultJSOptions &= ~JSOPTION_RELIMIT;
1004 ::JS_SetOptions(context->mContext, newDefaultJSOptions & JSRUNOPTION_MASK);
1006 // Save the new defaults for the next page load (InitContext).
1007 context->mDefaultJSOptions = newDefaultJSOptions;
1009 #ifdef JS_GC_ZEAL
1010 PRInt32 zeal = nsContentUtils::GetIntPref(js_zeal_option_str, -1);
1011 if (zeal >= 0)
1012 ::JS_SetGCZeal(context->mContext, (PRUint8)zeal);
1013 #endif
1015 return 0;
1018 nsJSContext::nsJSContext(JSRuntime *aRuntime)
1019 : mGCOnDestruction(PR_TRUE),
1020 mExecuteDepth(0)
1023 ++sContextCount;
1025 mDefaultJSOptions = JSOPTION_PRIVATE_IS_NSISUPPORTS | JSOPTION_ANONFUNFIX;
1027 mContext = ::JS_NewContext(aRuntime, gStackSize);
1028 if (mContext) {
1029 ::JS_SetContextPrivate(mContext, static_cast<nsIScriptContext *>(this));
1031 // Preserve any flags the context callback might have set.
1032 mDefaultJSOptions |= ::JS_GetOptions(mContext);
1034 // Make sure the new context gets the default context options
1035 ::JS_SetOptions(mContext, mDefaultJSOptions);
1037 // Watch for the JS boolean options
1038 nsContentUtils::RegisterPrefCallback(js_options_dot_str,
1039 JSOptionChangedCallback,
1040 this);
1042 ::JS_SetOperationCallback(mContext, DOMOperationCallback);
1044 xpc_LocalizeContext(mContext);
1046 mIsInitialized = PR_FALSE;
1047 mTerminations = nsnull;
1048 mScriptsEnabled = PR_TRUE;
1049 mOperationCallbackTime = 0;
1050 mModalStateTime = 0;
1051 mModalStateDepth = 0;
1052 mProcessingScriptTag = PR_FALSE;
1055 nsJSContext::~nsJSContext()
1057 #ifdef DEBUG
1058 nsCycleCollector_DEBUG_wasFreed(static_cast<nsIScriptContext*>(this));
1059 #endif
1061 // We may still have pending termination functions if the context is destroyed
1062 // before they could be executed. In this case, free the references to their
1063 // parameters, but don't execute the functions (see bug 622326).
1064 delete mTerminations;
1066 mGlobalObjectRef = nsnull;
1068 DestroyJSContext();
1070 --sContextCount;
1072 if (!sContextCount && sDidShutdown) {
1073 // The last context is being deleted, and we're already in the
1074 // process of shutting down, release the JS runtime service, and
1075 // the security manager.
1077 NS_IF_RELEASE(sRuntimeService);
1078 NS_IF_RELEASE(sSecurityManager);
1082 void
1083 nsJSContext::DestroyJSContext()
1085 if (!mContext)
1086 return;
1088 // Clear our entry in the JSContext, bugzilla bug 66413
1089 ::JS_SetContextPrivate(mContext, nsnull);
1091 // Unregister our "javascript.options.*" pref-changed callback.
1092 nsContentUtils::UnregisterPrefCallback(js_options_dot_str,
1093 JSOptionChangedCallback,
1094 this);
1096 PRBool do_gc = mGCOnDestruction && !sGCTimer;
1098 // Let xpconnect destroy the JSContext when it thinks the time is right.
1099 nsIXPConnect *xpc = nsContentUtils::XPConnect();
1100 if (xpc) {
1101 xpc->ReleaseJSContext(mContext, !do_gc);
1102 } else if (do_gc) {
1103 ::JS_DestroyContext(mContext);
1104 } else {
1105 ::JS_DestroyContextNoGC(mContext);
1107 mContext = nsnull;
1110 // QueryInterface implementation for nsJSContext
1111 NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSContext)
1112 NS_IMPL_CYCLE_COLLECTION_ROOT_BEGIN(nsJSContext)
1113 NS_ASSERTION(!tmp->mContext || tmp->mContext->outstandingRequests == 0,
1114 "Trying to unlink a context with outstanding requests.");
1115 tmp->mIsInitialized = PR_FALSE;
1116 tmp->mGCOnDestruction = PR_FALSE;
1117 tmp->DestroyJSContext();
1118 NS_IMPL_CYCLE_COLLECTION_ROOT_END
1119 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSContext)
1120 NS_IMPL_CYCLE_COLLECTION_TRACE_END
1121 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSContext)
1122 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mGlobalObjectRef)
1123 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1124 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsJSContext)
1125 NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsJSContext, tmp->GetCCRefcnt())
1126 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mGlobalObjectRef)
1127 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mContext");
1128 nsContentUtils::XPConnect()->NoteJSContext(tmp->mContext, cb);
1129 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1131 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSContext)
1132 NS_INTERFACE_MAP_ENTRY(nsIScriptContext)
1133 NS_INTERFACE_MAP_ENTRY(nsIScriptContextPrincipal)
1134 NS_INTERFACE_MAP_ENTRY(nsIXPCScriptNotify)
1135 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIScriptContext)
1136 NS_INTERFACE_MAP_END
1139 NS_IMPL_CYCLE_COLLECTING_ADDREF_AMBIGUOUS(nsJSContext, nsIScriptContext)
1140 NS_IMPL_CYCLE_COLLECTING_RELEASE_AMBIGUOUS(nsJSContext, nsIScriptContext)
1142 nsrefcnt
1143 nsJSContext::GetCCRefcnt()
1145 nsrefcnt refcnt = mRefCnt.get();
1146 if (NS_LIKELY(mContext))
1147 refcnt += mContext->outstandingRequests;
1148 return refcnt;
1151 nsresult
1152 nsJSContext::EvaluateStringWithValue(const nsAString& aScript,
1153 void *aScopeObject,
1154 nsIPrincipal *aPrincipal,
1155 const char *aURL,
1156 PRUint32 aLineNo,
1157 PRUint32 aVersion,
1158 void* aRetValue,
1159 PRBool* aIsUndefined)
1161 NS_TIME_FUNCTION_MIN_FMT(1.0, "%s (line %d) (url: %s, line: %d)", MOZ_FUNCTION_NAME,
1162 __LINE__, aURL, aLineNo);
1164 NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED);
1166 if (!mScriptsEnabled) {
1167 if (aIsUndefined) {
1168 *aIsUndefined = PR_TRUE;
1171 return NS_OK;
1174 nsresult rv;
1175 if (!aScopeObject)
1176 aScopeObject = ::JS_GetGlobalObject(mContext);
1178 // Safety first: get an object representing the script's principals, i.e.,
1179 // the entities who signed this script, or the fully-qualified-domain-name
1180 // or "codebase" from which it was loaded.
1181 JSPrincipals *jsprin;
1182 nsIPrincipal *principal = aPrincipal;
1183 if (!aPrincipal) {
1184 nsIScriptGlobalObject *global = GetGlobalObject();
1185 if (!global)
1186 return NS_ERROR_FAILURE;
1187 nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal =
1188 do_QueryInterface(global, &rv);
1189 if (NS_FAILED(rv))
1190 return NS_ERROR_FAILURE;
1191 principal = objPrincipal->GetPrincipal();
1192 if (!principal)
1193 return NS_ERROR_FAILURE;
1196 principal->GetJSPrincipals(mContext, &jsprin);
1198 // From here on, we must JSPRINCIPALS_DROP(jsprin) before returning...
1200 PRBool ok = PR_FALSE;
1202 rv = sSecurityManager->CanExecuteScripts(mContext, principal, &ok);
1203 if (NS_FAILED(rv)) {
1204 JSPRINCIPALS_DROP(mContext, jsprin);
1205 return NS_ERROR_FAILURE;
1208 // Push our JSContext on the current thread's context stack so JS called
1209 // from native code via XPConnect uses the right context. Do this whether
1210 // or not the SecurityManager said "ok", in order to simplify control flow
1211 // below where we pop before returning.
1212 nsCOMPtr<nsIJSContextStack> stack =
1213 do_GetService("@mozilla.org/js/xpc/ContextStack;1", &rv);
1214 if (NS_FAILED(rv) || NS_FAILED(stack->Push(mContext))) {
1215 JSPRINCIPALS_DROP(mContext, jsprin);
1216 return NS_ERROR_FAILURE;
1219 jsval val;
1221 rv = sSecurityManager->PushContextPrincipal(mContext, nsnull, principal);
1222 NS_ENSURE_SUCCESS(rv, rv);
1224 nsJSContext::TerminationFuncHolder holder(this);
1226 // SecurityManager said "ok", but don't compile if aVersion is unknown.
1227 // Since the caller is responsible for parsing the version strings, we just
1228 // check it isn't JSVERSION_UNKNOWN.
1229 if (ok && ((JSVersion)aVersion) != JSVERSION_UNKNOWN) {
1231 JSAutoRequest ar(mContext);
1233 JSAutoEnterCompartment ac;
1234 if (!ac.enter(mContext, (JSObject *)aScopeObject)) {
1235 JSPRINCIPALS_DROP(mContext, jsprin);
1236 stack->Pop(nsnull);
1237 return NS_ERROR_FAILURE;
1240 ++mExecuteDepth;
1242 ok = ::JS_EvaluateUCScriptForPrincipalsVersion(mContext,
1243 (JSObject *)aScopeObject,
1244 jsprin,
1245 (jschar*)PromiseFlatString(aScript).get(),
1246 aScript.Length(),
1247 aURL,
1248 aLineNo,
1249 &val,
1250 JSVersion(aVersion));
1252 --mExecuteDepth;
1254 if (!ok) {
1255 // Tell XPConnect about any pending exceptions. This is needed
1256 // to avoid dropping JS exceptions in case we got here through
1257 // nested calls through XPConnect.
1259 ReportPendingException();
1263 // Whew! Finally done with these manually ref-counted things.
1264 JSPRINCIPALS_DROP(mContext, jsprin);
1266 // If all went well, convert val to a string (XXXbe unless undefined?).
1267 if (ok) {
1268 if (aIsUndefined) {
1269 *aIsUndefined = JSVAL_IS_VOID(val);
1272 *static_cast<jsval*>(aRetValue) = val;
1273 // XXX - nsScriptObjectHolder should be used once this method moves to
1274 // the new world order. However, use of 'jsval' appears to make this
1275 // tricky...
1277 else {
1278 if (aIsUndefined) {
1279 *aIsUndefined = PR_TRUE;
1283 sSecurityManager->PopContextPrincipal(mContext);
1285 // Pop here, after JS_ValueToString and any other possible evaluation.
1286 if (NS_FAILED(stack->Pop(nsnull)))
1287 rv = NS_ERROR_FAILURE;
1289 // ScriptEvaluated needs to come after we pop the stack
1290 ScriptEvaluated(PR_TRUE);
1292 return rv;
1296 // Helper function to convert a jsval to an nsAString, and set
1297 // exception flags if the conversion fails.
1298 static nsresult
1299 JSValueToAString(JSContext *cx, jsval val, nsAString *result,
1300 PRBool *isUndefined)
1302 if (isUndefined) {
1303 *isUndefined = JSVAL_IS_VOID(val);
1306 if (!result) {
1307 return NS_OK;
1310 JSString* jsstring = ::JS_ValueToString(cx, val);
1311 if (!jsstring) {
1312 goto error;
1315 size_t length;
1316 const jschar *chars;
1317 chars = ::JS_GetStringCharsAndLength(cx, jsstring, &length);
1318 if (!chars) {
1319 goto error;
1322 result->Assign(chars, length);
1323 return NS_OK;
1325 error:
1326 // We failed to convert val to a string. We're either OOM, or the
1327 // security manager denied access to .toString(), or somesuch, on
1328 // an object. Treat this case as if the result were undefined.
1330 result->Truncate();
1332 if (isUndefined) {
1333 *isUndefined = PR_TRUE;
1336 if (!::JS_IsExceptionPending(cx)) {
1337 // JS_ValueToString()/JS_GetStringCharsAndLength returned null w/o an
1338 // exception pending. That means we're OOM.
1340 return NS_ERROR_OUT_OF_MEMORY;
1343 return NS_OK;
1346 nsIScriptObjectPrincipal*
1347 nsJSContext::GetObjectPrincipal()
1349 nsCOMPtr<nsIScriptObjectPrincipal> prin = do_QueryInterface(GetGlobalObject());
1350 return prin;
1353 nsresult
1354 nsJSContext::EvaluateString(const nsAString& aScript,
1355 void *aScopeObject,
1356 nsIPrincipal *aPrincipal,
1357 const char *aURL,
1358 PRUint32 aLineNo,
1359 PRUint32 aVersion,
1360 nsAString *aRetValue,
1361 PRBool* aIsUndefined)
1363 NS_TIME_FUNCTION_MIN_FMT(1.0, "%s (line %d) (url: %s, line: %d)", MOZ_FUNCTION_NAME,
1364 __LINE__, aURL, aLineNo);
1366 NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED);
1368 if (!mScriptsEnabled) {
1369 if (aIsUndefined) {
1370 *aIsUndefined = PR_TRUE;
1373 if (aRetValue) {
1374 aRetValue->Truncate();
1377 return NS_OK;
1380 nsresult rv;
1381 if (!aScopeObject)
1382 aScopeObject = ::JS_GetGlobalObject(mContext);
1384 // Safety first: get an object representing the script's principals, i.e.,
1385 // the entities who signed this script, or the fully-qualified-domain-name
1386 // or "codebase" from which it was loaded.
1387 JSPrincipals *jsprin;
1388 nsIPrincipal *principal = aPrincipal;
1389 if (aPrincipal) {
1390 aPrincipal->GetJSPrincipals(mContext, &jsprin);
1392 else {
1393 nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal =
1394 do_QueryInterface(GetGlobalObject(), &rv);
1395 if (NS_FAILED(rv))
1396 return NS_ERROR_FAILURE;
1397 principal = objPrincipal->GetPrincipal();
1398 if (!principal)
1399 return NS_ERROR_FAILURE;
1400 principal->GetJSPrincipals(mContext, &jsprin);
1403 // From here on, we must JSPRINCIPALS_DROP(jsprin) before returning...
1405 PRBool ok = PR_FALSE;
1407 rv = sSecurityManager->CanExecuteScripts(mContext, principal, &ok);
1408 if (NS_FAILED(rv)) {
1409 JSPRINCIPALS_DROP(mContext, jsprin);
1410 return NS_ERROR_FAILURE;
1413 // Push our JSContext on the current thread's context stack so JS called
1414 // from native code via XPConnect uses the right context. Do this whether
1415 // or not the SecurityManager said "ok", in order to simplify control flow
1416 // below where we pop before returning.
1417 nsCOMPtr<nsIJSContextStack> stack =
1418 do_GetService("@mozilla.org/js/xpc/ContextStack;1", &rv);
1419 if (NS_FAILED(rv) || NS_FAILED(stack->Push(mContext))) {
1420 JSPRINCIPALS_DROP(mContext, jsprin);
1421 return NS_ERROR_FAILURE;
1424 // The result of evaluation, used only if there were no errors. This need
1425 // not be a GC root currently, provided we run the GC only from the
1426 // operation callback or from ScriptEvaluated.
1427 jsval val = JSVAL_VOID;
1428 jsval* vp = aRetValue ? &val : NULL;
1430 rv = sSecurityManager->PushContextPrincipal(mContext, nsnull, principal);
1431 NS_ENSURE_SUCCESS(rv, rv);
1433 nsJSContext::TerminationFuncHolder holder(this);
1435 ++mExecuteDepth;
1437 // SecurityManager said "ok", but don't compile if aVersion is unknown.
1438 // Since the caller is responsible for parsing the version strings, we just
1439 // check it isn't JSVERSION_UNKNOWN.
1440 if (ok && ((JSVersion)aVersion) != JSVERSION_UNKNOWN) {
1441 JSAutoRequest ar(mContext);
1442 JSAutoEnterCompartment ac;
1443 if (!ac.enter(mContext, (JSObject *)aScopeObject)) {
1444 stack->Pop(nsnull);
1445 JSPRINCIPALS_DROP(mContext, jsprin);
1446 return NS_ERROR_FAILURE;
1449 ok = ::JS_EvaluateUCScriptForPrincipalsVersion(mContext,
1450 (JSObject *)aScopeObject,
1451 jsprin,
1452 (jschar*)PromiseFlatString(aScript).get(),
1453 aScript.Length(),
1454 aURL,
1455 aLineNo,
1457 JSVersion(aVersion));
1459 if (!ok) {
1460 // Tell XPConnect about any pending exceptions. This is needed
1461 // to avoid dropping JS exceptions in case we got here through
1462 // nested calls through XPConnect.
1464 ReportPendingException();
1468 // Whew! Finally done with these manually ref-counted things.
1469 JSPRINCIPALS_DROP(mContext, jsprin);
1471 // If all went well, convert val to a string if one is wanted.
1472 if (ok) {
1473 JSAutoRequest ar(mContext);
1474 JSAutoEnterCompartment ac;
1475 if (!ac.enter(mContext, (JSObject *)aScopeObject)) {
1476 stack->Pop(nsnull);
1478 rv = JSValueToAString(mContext, val, aRetValue, aIsUndefined);
1480 else {
1481 if (aIsUndefined) {
1482 *aIsUndefined = PR_TRUE;
1485 if (aRetValue) {
1486 aRetValue->Truncate();
1490 --mExecuteDepth;
1492 sSecurityManager->PopContextPrincipal(mContext);
1494 // Pop here, after JS_ValueToString and any other possible evaluation.
1495 if (NS_FAILED(stack->Pop(nsnull)))
1496 rv = NS_ERROR_FAILURE;
1498 // ScriptEvaluated needs to come after we pop the stack
1499 ScriptEvaluated(PR_TRUE);
1501 return rv;
1504 nsresult
1505 nsJSContext::CompileScript(const PRUnichar* aText,
1506 PRInt32 aTextLength,
1507 void *aScopeObject,
1508 nsIPrincipal *aPrincipal,
1509 const char *aURL,
1510 PRUint32 aLineNo,
1511 PRUint32 aVersion,
1512 nsScriptObjectHolder &aScriptObject)
1514 NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED);
1516 nsresult rv;
1517 NS_ENSURE_ARG_POINTER(aPrincipal);
1519 if (!aScopeObject)
1520 aScopeObject = ::JS_GetGlobalObject(mContext);
1522 JSPrincipals *jsprin;
1523 aPrincipal->GetJSPrincipals(mContext, &jsprin);
1524 // From here on, we must JSPRINCIPALS_DROP(jsprin) before returning...
1526 PRBool ok = PR_FALSE;
1528 rv = sSecurityManager->CanExecuteScripts(mContext, aPrincipal, &ok);
1529 if (NS_FAILED(rv)) {
1530 JSPRINCIPALS_DROP(mContext, jsprin);
1531 return NS_ERROR_FAILURE;
1534 aScriptObject.drop(); // ensure old object not used on failure...
1536 // SecurityManager said "ok", but don't compile if aVersion is unknown.
1537 // Since the caller is responsible for parsing the version strings, we just
1538 // check it isn't JSVERSION_UNKNOWN.
1539 if (ok && ((JSVersion)aVersion) != JSVERSION_UNKNOWN) {
1540 JSAutoRequest ar(mContext);
1542 JSScript* script =
1543 ::JS_CompileUCScriptForPrincipalsVersion(mContext,
1544 (JSObject *)aScopeObject,
1545 jsprin,
1546 (jschar*) aText,
1547 aTextLength,
1548 aURL,
1549 aLineNo,
1550 JSVersion(aVersion));
1551 if (script) {
1552 JSObject *scriptObject = ::JS_NewScriptObject(mContext, script);
1553 if (scriptObject) {
1554 NS_ASSERTION(aScriptObject.getScriptTypeID()==JAVASCRIPT,
1555 "Expecting JS script object holder");
1556 rv = aScriptObject.set(scriptObject);
1557 } else {
1558 ::JS_DestroyScript(mContext, script);
1559 script = nsnull;
1561 } else {
1562 rv = NS_ERROR_OUT_OF_MEMORY;
1566 // Whew! Finally done.
1567 JSPRINCIPALS_DROP(mContext, jsprin);
1568 return rv;
1571 nsresult
1572 nsJSContext::ExecuteScript(void *aScriptObject,
1573 void *aScopeObject,
1574 nsAString* aRetValue,
1575 PRBool* aIsUndefined)
1577 NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED);
1579 if (!mScriptsEnabled) {
1580 if (aIsUndefined) {
1581 *aIsUndefined = PR_TRUE;
1584 if (aRetValue) {
1585 aRetValue->Truncate();
1588 return NS_OK;
1591 nsresult rv;
1593 if (!aScopeObject)
1594 aScopeObject = ::JS_GetGlobalObject(mContext);
1596 // Push our JSContext on our thread's context stack, in case native code
1597 // called from JS calls back into JS via XPConnect.
1598 nsCOMPtr<nsIJSContextStack> stack =
1599 do_GetService("@mozilla.org/js/xpc/ContextStack;1", &rv);
1600 if (NS_FAILED(rv) || NS_FAILED(stack->Push(mContext))) {
1601 return NS_ERROR_FAILURE;
1604 // The result of evaluation, used only if there were no errors. This need
1605 // not be a GC root currently, provided we run the GC only from the
1606 // operation callback or from ScriptEvaluated.
1607 jsval val;
1608 JSBool ok;
1610 JSObject *scriptObj = (JSObject*)aScriptObject;
1611 nsCOMPtr<nsIPrincipal> principal;
1613 rv = sSecurityManager->GetObjectPrincipal(mContext, scriptObj, getter_AddRefs(principal));
1614 NS_ENSURE_SUCCESS(rv, rv);
1616 rv = sSecurityManager->PushContextPrincipal(mContext, nsnull, principal);
1617 NS_ENSURE_SUCCESS(rv, rv);
1619 nsJSContext::TerminationFuncHolder holder(this);
1620 JSAutoRequest ar(mContext);
1621 ++mExecuteDepth;
1622 ok = ::JS_ExecuteScript(mContext,
1623 (JSObject *)aScopeObject,
1624 (JSScript*)::JS_GetPrivate(mContext, scriptObj),
1625 &val);
1627 if (ok) {
1628 // If all went well, convert val to a string (XXXbe unless undefined?).
1629 rv = JSValueToAString(mContext, val, aRetValue, aIsUndefined);
1630 } else {
1631 if (aIsUndefined) {
1632 *aIsUndefined = PR_TRUE;
1635 if (aRetValue) {
1636 aRetValue->Truncate();
1640 --mExecuteDepth;
1642 sSecurityManager->PopContextPrincipal(mContext);
1644 // Pop here, after JS_ValueToString and any other possible evaluation.
1645 if (NS_FAILED(stack->Pop(nsnull)))
1646 rv = NS_ERROR_FAILURE;
1648 // ScriptEvaluated needs to come after we pop the stack
1649 ScriptEvaluated(PR_TRUE);
1651 return rv;
1655 #ifdef DEBUG
1656 PRBool
1657 AtomIsEventHandlerName(nsIAtom *aName)
1659 const PRUnichar *name = aName->GetUTF16String();
1661 const PRUnichar *cp;
1662 PRUnichar c;
1663 for (cp = name; *cp != '\0'; ++cp)
1665 c = *cp;
1666 if ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z'))
1667 return PR_FALSE;
1670 return PR_TRUE;
1672 #endif
1674 // Helper function to find the JSObject associated with a (presumably DOM)
1675 // interface.
1676 nsresult
1677 nsJSContext::JSObjectFromInterface(nsISupports* aTarget, void *aScope, JSObject **aRet)
1679 // It is legal to specify a null target.
1680 if (!aTarget) {
1681 *aRet = nsnull;
1682 return NS_OK;
1685 // Get the jsobject associated with this target
1686 // We don't wrap here because we trust the JS engine to wrap the target
1687 // later.
1688 nsresult rv;
1689 jsval v;
1690 rv = nsContentUtils::WrapNative(mContext, (JSObject *)aScope, aTarget, &v);
1691 NS_ENSURE_SUCCESS(rv, rv);
1693 #ifdef NS_DEBUG
1694 nsCOMPtr<nsISupports> targetSupp = do_QueryInterface(aTarget);
1695 nsCOMPtr<nsISupports> native =
1696 nsContentUtils::XPConnect()->GetNativeOfWrapper(mContext,
1697 JSVAL_TO_OBJECT(v));
1698 NS_ASSERTION(native == targetSupp, "Native should be the target!");
1699 #endif
1701 *aRet = JSVAL_TO_OBJECT(v);
1703 return NS_OK;
1707 nsresult
1708 nsJSContext::CompileEventHandler(nsIAtom *aName,
1709 PRUint32 aArgCount,
1710 const char** aArgNames,
1711 const nsAString& aBody,
1712 const char *aURL, PRUint32 aLineNo,
1713 PRUint32 aVersion,
1714 nsScriptObjectHolder &aHandler)
1716 NS_TIME_FUNCTION_MIN_FMT(1.0, "%s (line %d) (url: %s, line: %d)", MOZ_FUNCTION_NAME,
1717 __LINE__, aURL, aLineNo);
1719 NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED);
1721 NS_PRECONDITION(AtomIsEventHandlerName(aName), "Bad event name");
1722 NS_PRECONDITION(!::JS_IsExceptionPending(mContext),
1723 "Why are we being called with a pending exception?");
1725 if (!sSecurityManager) {
1726 NS_ERROR("Huh, we need a script security manager to compile "
1727 "an event handler!");
1729 return NS_ERROR_UNEXPECTED;
1732 // Don't compile if aVersion is unknown. Since the caller is responsible for
1733 // parsing the version strings, we just check it isn't JSVERSION_UNKNOWN.
1734 if ((JSVersion)aVersion == JSVERSION_UNKNOWN) {
1735 return NS_ERROR_ILLEGAL_VALUE;
1738 #ifdef DEBUG
1739 JSContext* top = nsContentUtils::GetCurrentJSContext();
1740 NS_ASSERTION(mContext == top, "Context not properly pushed!");
1741 #endif
1743 // Event handlers are always shared, and must be bound before use.
1744 // Therefore we never bother compiling with principals.
1745 // (that probably means we should avoid JS_CompileUCFunctionForPrincipals!)
1746 JSAutoRequest ar(mContext);
1748 JSFunction* fun =
1749 ::JS_CompileUCFunctionForPrincipalsVersion(mContext,
1750 nsnull, nsnull,
1751 nsAtomCString(aName).get(), aArgCount, aArgNames,
1752 (jschar*)PromiseFlatString(aBody).get(),
1753 aBody.Length(),
1754 aURL, aLineNo, JSVersion(aVersion));
1756 if (!fun) {
1757 ReportPendingException();
1758 return NS_ERROR_ILLEGAL_VALUE;
1761 JSObject *handler = ::JS_GetFunctionObject(fun);
1762 NS_ASSERTION(aHandler.getScriptTypeID()==JAVASCRIPT,
1763 "Expecting JS script object holder");
1764 return aHandler.set((void *)handler);
1767 // XXX - note that CompileFunction doesn't yet play the nsScriptObjectHolder
1768 // game - caller must still ensure JS GC root.
1769 nsresult
1770 nsJSContext::CompileFunction(void* aTarget,
1771 const nsACString& aName,
1772 PRUint32 aArgCount,
1773 const char** aArgArray,
1774 const nsAString& aBody,
1775 const char* aURL,
1776 PRUint32 aLineNo,
1777 PRUint32 aVersion,
1778 PRBool aShared,
1779 void** aFunctionObject)
1781 NS_TIME_FUNCTION_FMT(1.0, "%s (line %d) (function: %s, url: %s, line: %d)", MOZ_FUNCTION_NAME,
1782 __LINE__, aName.BeginReading(), aURL, aLineNo);
1784 NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED);
1786 // Don't compile if aVersion is unknown. Since the caller is responsible for
1787 // parsing the version strings, we just check it isn't JSVERSION_UNKNOWN.
1788 if ((JSVersion)aVersion == JSVERSION_UNKNOWN) {
1789 return NS_ERROR_ILLEGAL_VALUE;
1792 JSPrincipals *jsprin = nsnull;
1794 nsIScriptGlobalObject *global = GetGlobalObject();
1795 if (global) {
1796 // XXXbe why the two-step QI? speed up via a new GetGlobalObjectData func?
1797 nsCOMPtr<nsIScriptObjectPrincipal> globalData = do_QueryInterface(global);
1798 if (globalData) {
1799 nsIPrincipal *prin = globalData->GetPrincipal();
1800 if (!prin)
1801 return NS_ERROR_FAILURE;
1802 prin->GetJSPrincipals(mContext, &jsprin);
1806 JSObject *target = (JSObject*)aTarget;
1808 JSAutoRequest ar(mContext);
1810 JSFunction* fun =
1811 ::JS_CompileUCFunctionForPrincipalsVersion(mContext,
1812 aShared ? nsnull : target, jsprin,
1813 PromiseFlatCString(aName).get(),
1814 aArgCount, aArgArray,
1815 (jschar*)PromiseFlatString(aBody).get(),
1816 aBody.Length(),
1817 aURL, aLineNo,
1818 JSVersion(aVersion));
1820 if (jsprin)
1821 JSPRINCIPALS_DROP(mContext, jsprin);
1822 if (!fun)
1823 return NS_ERROR_FAILURE;
1825 JSObject *handler = ::JS_GetFunctionObject(fun);
1826 if (aFunctionObject)
1827 *aFunctionObject = (void*) handler;
1828 return NS_OK;
1831 nsresult
1832 nsJSContext::CallEventHandler(nsISupports* aTarget, void *aScope, void *aHandler,
1833 nsIArray *aargv, nsIVariant **arv)
1835 NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED);
1837 if (!mScriptsEnabled) {
1838 return NS_OK;
1841 #ifdef NS_FUNCTION_TIMER
1843 JSObject *obj = static_cast<JSObject *>(aHandler);
1844 JSString *id = JS_GetFunctionId(static_cast<JSFunction *>(JS_GetPrivate(mContext, obj)));
1845 JSAutoByteString bytes;
1846 const char *name = !id ? "anonymous" : bytes.encode(mContext, id) ? bytes.ptr() : "<error>";
1847 NS_TIME_FUNCTION_FMT(1.0, "%s (line %d) (function: %s)", MOZ_FUNCTION_NAME, __LINE__, name);
1849 #endif
1851 JSAutoRequest ar(mContext);
1852 JSObject* target = nsnull;
1853 nsresult rv = JSObjectFromInterface(aTarget, aScope, &target);
1854 NS_ENSURE_SUCCESS(rv, rv);
1856 js::AutoObjectRooter targetVal(mContext, target);
1857 jsval rval = JSVAL_VOID;
1859 // This one's a lot easier than EvaluateString because we don't have to
1860 // hassle with principals: they're already compiled into the JS function.
1861 // xxxmarkh - this comment is no longer true - principals are not used at
1862 // all now, and never were in some cases.
1864 nsCxPusher pusher;
1865 if (!pusher.Push(mContext, PR_TRUE))
1866 return NS_ERROR_FAILURE;
1868 // check if the event handler can be run on the object in question
1869 rv = sSecurityManager->CheckFunctionAccess(mContext, aHandler, target);
1871 nsJSContext::TerminationFuncHolder holder(this);
1873 if (NS_SUCCEEDED(rv)) {
1874 // Convert args to jsvals.
1875 PRUint32 argc = 0;
1876 jsval *argv = nsnull;
1878 JSObject *funobj = static_cast<JSObject *>(aHandler);
1879 nsCOMPtr<nsIPrincipal> principal;
1880 rv = sSecurityManager->GetObjectPrincipal(mContext, funobj,
1881 getter_AddRefs(principal));
1882 NS_ENSURE_SUCCESS(rv, rv);
1884 JSStackFrame *currentfp = nsnull;
1885 rv = sSecurityManager->PushContextPrincipal(mContext,
1886 JS_FrameIterator(mContext, &currentfp),
1887 principal);
1888 NS_ENSURE_SUCCESS(rv, rv);
1890 jsval funval = OBJECT_TO_JSVAL(funobj);
1891 JSAutoEnterCompartment ac;
1892 if (!ac.enter(mContext, funobj) || !JS_WrapObject(mContext, &target)) {
1893 sSecurityManager->PopContextPrincipal(mContext);
1894 return NS_ERROR_FAILURE;
1897 js::LazilyConstructed<nsAutoPoolRelease> poolRelease;
1898 js::LazilyConstructed<js::AutoArrayRooter> tvr;
1900 // Use |target| as the scope for wrapping the arguments, since aScope is
1901 // the safe scope in many cases, which isn't very useful. Wrapping aTarget
1902 // was OK because those typically have PreCreate methods that give them the
1903 // right scope anyway, and we want to make sure that the arguments end up
1904 // in the same scope as aTarget.
1905 rv = ConvertSupportsTojsvals(aargv, target, &argc,
1906 &argv, poolRelease, tvr);
1907 NS_ENSURE_SUCCESS(rv, rv);
1909 ++mExecuteDepth;
1910 PRBool ok = ::JS_CallFunctionValue(mContext, target,
1911 funval, argc, argv, &rval);
1912 --mExecuteDepth;
1914 if (!ok) {
1915 // Tell XPConnect about any pending exceptions. This is needed
1916 // to avoid dropping JS exceptions in case we got here through
1917 // nested calls through XPConnect.
1919 ReportPendingException();
1921 // Don't pass back results from failed calls.
1922 rval = JSVAL_VOID;
1924 // Tell the caller that the handler threw an error.
1925 rv = NS_ERROR_FAILURE;
1928 sSecurityManager->PopContextPrincipal(mContext);
1931 pusher.Pop();
1933 // Convert to variant before calling ScriptEvaluated, as it may GC, meaning
1934 // we would need to root rval.
1935 if (NS_SUCCEEDED(rv)) {
1936 if (rval == JSVAL_NULL)
1937 *arv = nsnull;
1938 else
1939 rv = nsContentUtils::XPConnect()->JSToVariant(mContext, rval, arv);
1942 // ScriptEvaluated needs to come after we pop the stack
1943 ScriptEvaluated(PR_TRUE);
1945 return rv;
1948 nsresult
1949 nsJSContext::BindCompiledEventHandler(nsISupports* aTarget, void *aScope,
1950 nsIAtom *aName,
1951 void *aHandler)
1953 NS_ENSURE_ARG(aHandler);
1954 NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED);
1956 NS_PRECONDITION(AtomIsEventHandlerName(aName), "Bad event name");
1958 JSAutoRequest ar(mContext);
1960 // Get the jsobject associated with this target
1961 JSObject *target = nsnull;
1962 nsresult rv = JSObjectFromInterface(aTarget, aScope, &target);
1963 NS_ENSURE_SUCCESS(rv, rv);
1965 JSObject *funobj = (JSObject*) aHandler;
1967 #ifdef DEBUG
1969 JSAutoEnterCompartment ac;
1970 if (!ac.enter(mContext, funobj)) {
1971 return NS_ERROR_FAILURE;
1974 NS_ASSERTION(JS_TypeOfValue(mContext,
1975 OBJECT_TO_JSVAL(funobj)) == JSTYPE_FUNCTION,
1976 "Event handler object not a function");
1978 #endif
1980 JSAutoEnterCompartment ac;
1981 if (!ac.enter(mContext, target)) {
1982 return NS_ERROR_FAILURE;
1985 // Push our JSContext on our thread's context stack, in case native code
1986 // called from JS calls back into JS via XPConnect.
1987 nsCOMPtr<nsIJSContextStack> stack =
1988 do_GetService("@mozilla.org/js/xpc/ContextStack;1", &rv);
1989 if (NS_FAILED(rv) || NS_FAILED(stack->Push(mContext))) {
1990 return NS_ERROR_FAILURE;
1993 // Make sure the handler function is parented by its event target object
1994 if (funobj) { // && ::JS_GetParent(mContext, funobj) != target) {
1995 funobj = ::JS_CloneFunctionObject(mContext, funobj, target);
1996 if (!funobj)
1997 rv = NS_ERROR_OUT_OF_MEMORY;
2000 if (NS_SUCCEEDED(rv) &&
2001 // Make sure the flags here match those in nsEventReceiverSH::NewResolve
2002 !::JS_DefineProperty(mContext, target, nsAtomCString(aName).get(),
2003 OBJECT_TO_JSVAL(funobj), nsnull, nsnull,
2004 JSPROP_ENUMERATE | JSPROP_PERMANENT)) {
2005 ReportPendingException();
2006 rv = NS_ERROR_FAILURE;
2009 // XXXmarkh - ideally we should assert that the wrapped native is now
2010 // "long lived" - how to do that?
2012 if (NS_FAILED(stack->Pop(nsnull)) && NS_SUCCEEDED(rv)) {
2013 rv = NS_ERROR_FAILURE;
2016 return rv;
2019 nsresult
2020 nsJSContext::GetBoundEventHandler(nsISupports* aTarget, void *aScope,
2021 nsIAtom* aName,
2022 nsScriptObjectHolder &aHandler)
2024 NS_PRECONDITION(AtomIsEventHandlerName(aName), "Bad event name");
2026 JSAutoRequest ar(mContext);
2027 JSObject *obj = nsnull;
2028 nsresult rv = JSObjectFromInterface(aTarget, aScope, &obj);
2029 NS_ENSURE_SUCCESS(rv, rv);
2031 JSAutoEnterCompartment ac;
2032 if (!ac.enter(mContext, obj)) {
2033 return NS_ERROR_FAILURE;
2036 jsval funval;
2037 if (!JS_LookupProperty(mContext, obj,
2038 nsAtomCString(aName).get(), &funval))
2039 return NS_ERROR_FAILURE;
2041 if (JS_TypeOfValue(mContext, funval) != JSTYPE_FUNCTION) {
2042 NS_WARNING("Event handler object not a function");
2043 aHandler.drop();
2044 return NS_OK;
2046 NS_ASSERTION(aHandler.getScriptTypeID()==JAVASCRIPT,
2047 "Expecting JS script object holder");
2048 return aHandler.set(JSVAL_TO_OBJECT(funval));
2051 // serialization
2052 nsresult
2053 nsJSContext::Serialize(nsIObjectOutputStream* aStream, void *aScriptObject)
2055 JSObject *mJSObject = (JSObject *)aScriptObject;
2056 if (!mJSObject)
2057 return NS_ERROR_FAILURE;
2059 nsresult rv;
2061 JSContext* cx = mContext;
2062 JSXDRState *xdr = ::JS_XDRNewMem(cx, JSXDR_ENCODE);
2063 if (! xdr)
2064 return NS_ERROR_OUT_OF_MEMORY;
2065 xdr->userdata = (void*) aStream;
2067 JSAutoRequest ar(cx);
2068 JSScript *script = reinterpret_cast<JSScript*>
2069 (::JS_GetPrivate(cx, mJSObject));
2070 if (! ::JS_XDRScript(xdr, &script)) {
2071 rv = NS_ERROR_FAILURE; // likely to be a principals serialization error
2072 } else {
2073 // Get the encoded JSXDRState data and write it. The JSXDRState owns
2074 // this buffer memory and will free it beneath ::JS_XDRDestroy.
2076 // If an XPCOM object needs to be written in the midst of the JS XDR
2077 // encoding process, the C++ code called back from the JS engine (e.g.,
2078 // nsEncodeJSPrincipals in caps/src/nsJSPrincipals.cpp) will flush data
2079 // from the JSXDRState to aStream, then write the object, then return
2080 // to JS XDR code with xdr reset so new JS data is encoded at the front
2081 // of the xdr's data buffer.
2083 // However many XPCOM objects are interleaved with JS XDR data in the
2084 // stream, when control returns here from ::JS_XDRScript, we'll have
2085 // one last buffer of data to write to aStream.
2087 uint32 size;
2088 const char* data = reinterpret_cast<const char*>
2089 (::JS_XDRMemGetData(xdr, &size));
2090 NS_ASSERTION(data, "no decoded JSXDRState data!");
2092 rv = aStream->Write32(size);
2093 if (NS_SUCCEEDED(rv))
2094 rv = aStream->WriteBytes(data, size);
2097 ::JS_XDRDestroy(xdr);
2098 if (NS_FAILED(rv)) return rv;
2100 return rv;
2103 nsresult
2104 nsJSContext::Deserialize(nsIObjectInputStream* aStream,
2105 nsScriptObjectHolder &aResult)
2107 JSObject *result = nsnull;
2108 nsresult rv;
2110 NS_TIME_FUNCTION_MIN(1.0);
2112 NS_TIMELINE_MARK_FUNCTION("js script deserialize");
2114 PRUint32 size;
2115 rv = aStream->Read32(&size);
2116 if (NS_FAILED(rv)) return rv;
2118 char* data;
2119 rv = aStream->ReadBytes(size, &data);
2120 if (NS_FAILED(rv)) return rv;
2122 JSContext* cx = mContext;
2124 JSXDRState *xdr = ::JS_XDRNewMem(cx, JSXDR_DECODE);
2125 if (! xdr) {
2126 rv = NS_ERROR_OUT_OF_MEMORY;
2127 } else {
2128 xdr->userdata = (void*) aStream;
2129 JSAutoRequest ar(cx);
2130 ::JS_XDRMemSetData(xdr, data, size);
2132 JSScript *script = nsnull;
2133 if (! ::JS_XDRScript(xdr, &script)) {
2134 rv = NS_ERROR_FAILURE; // principals deserialization error?
2135 } else {
2136 result = ::JS_NewScriptObject(cx, script);
2137 if (! result) {
2138 rv = NS_ERROR_OUT_OF_MEMORY; // certain error
2139 ::JS_DestroyScript(cx, script);
2143 // Update data in case ::JS_XDRScript called back into C++ code to
2144 // read an XPCOM object.
2146 // In that case, the serialization process must have flushed a run
2147 // of counted bytes containing JS data at the point where the XPCOM
2148 // object starts, after which an encoding C++ callback from the JS
2149 // XDR code must have written the XPCOM object directly into the
2150 // nsIObjectOutputStream.
2152 // The deserialization process will XDR-decode counted bytes up to
2153 // but not including the XPCOM object, then call back into C++ to
2154 // read the object, then read more counted bytes and hand them off
2155 // to the JSXDRState, so more JS data can be decoded.
2157 // This interleaving of JS XDR data and XPCOM object data may occur
2158 // several times beneath the call to ::JS_XDRScript, above. At the
2159 // end of the day, we need to free (via nsMemory) the data owned by
2160 // the JSXDRState. So we steal it back, nulling xdr's buffer so it
2161 // doesn't get passed to ::JS_free by ::JS_XDRDestroy.
2163 uint32 junk;
2164 data = (char*) ::JS_XDRMemGetData(xdr, &junk);
2165 if (data)
2166 ::JS_XDRMemSetData(xdr, NULL, 0);
2167 ::JS_XDRDestroy(xdr);
2170 // If data is null now, it must have been freed while deserializing an
2171 // XPCOM object (e.g., a principal) beneath ::JS_XDRScript.
2172 if (data)
2173 nsMemory::Free(data);
2174 NS_ASSERTION(aResult.getScriptTypeID()==JAVASCRIPT,
2175 "Expecting JS script object holder");
2177 // Now that we've cleaned up, handle the case when rv is a failure
2178 // code, which could happen for all sorts of reasons above.
2179 NS_ENSURE_SUCCESS(rv, rv);
2181 return aResult.set(result);
2184 void
2185 nsJSContext::SetDefaultLanguageVersion(PRUint32 aVersion)
2187 ::JS_SetVersion(mContext, (JSVersion)aVersion);
2190 nsIScriptGlobalObject *
2191 nsJSContext::GetGlobalObject()
2193 JSObject *global = ::JS_GetGlobalObject(mContext);
2195 if (!global) {
2196 return nsnull;
2199 OBJ_TO_INNER_OBJECT(mContext, global);
2200 if (!global) {
2201 return nsnull;
2204 JSClass *c = JS_GET_CLASS(mContext, global);
2206 if (!c || ((~c->flags) & (JSCLASS_HAS_PRIVATE |
2207 JSCLASS_PRIVATE_IS_NSISUPPORTS))) {
2208 return nsnull;
2211 nsISupports *priv = (nsISupports *)global->getPrivate();
2213 nsCOMPtr<nsIXPConnectWrappedNative> wrapped_native =
2214 do_QueryInterface(priv);
2216 nsCOMPtr<nsIScriptGlobalObject> sgo;
2217 if (wrapped_native) {
2218 // The global object is a XPConnect wrapped native, the native in
2219 // the wrapper might be the nsIScriptGlobalObject
2221 sgo = do_QueryWrappedNative(wrapped_native);
2222 } else {
2223 sgo = do_QueryInterface(priv);
2226 // This'll return a pointer to something we're about to release, but
2227 // that's ok, the JS object will hold it alive long enough.
2228 nsCOMPtr<nsPIDOMWindow> pwin(do_QueryInterface(sgo));
2229 if (!pwin)
2230 return sgo;
2232 return static_cast<nsGlobalWindow *>(pwin->GetOuterWindow());
2235 void *
2236 nsJSContext::GetNativeGlobal()
2238 return ::JS_GetGlobalObject(mContext);
2241 nsresult
2242 nsJSContext::CreateNativeGlobalForInner(
2243 nsIScriptGlobalObject *aNewInner,
2244 PRBool aIsChrome,
2245 nsIPrincipal *aPrincipal,
2246 void **aNativeGlobal, nsISupports **aHolder)
2248 nsIXPConnect *xpc = nsContentUtils::XPConnect();
2249 PRUint32 flags = aIsChrome? nsIXPConnect::FLAG_SYSTEM_GLOBAL_OBJECT : 0;
2250 nsCOMPtr<nsIXPConnectJSObjectHolder> jsholder;
2252 nsCOMPtr<nsIPrincipal> systemPrincipal;
2253 if (aIsChrome) {
2254 nsIScriptSecurityManager *ssm = nsContentUtils::GetSecurityManager();
2255 ssm->GetSystemPrincipal(getter_AddRefs(systemPrincipal));
2258 nsresult rv = xpc->
2259 InitClassesWithNewWrappedGlobal(mContext,
2260 aNewInner, NS_GET_IID(nsISupports),
2261 aIsChrome ? systemPrincipal.get() : aPrincipal,
2262 nsnull, flags,
2263 getter_AddRefs(jsholder));
2264 if (NS_FAILED(rv))
2265 return rv;
2266 jsholder->GetJSObject(reinterpret_cast<JSObject **>(aNativeGlobal));
2267 *aHolder = jsholder.get();
2268 NS_ADDREF(*aHolder);
2269 return NS_OK;
2272 nsresult
2273 nsJSContext::ConnectToInner(nsIScriptGlobalObject *aNewInner, void *aOuterGlobal)
2275 NS_ENSURE_ARG(aNewInner);
2276 JSObject *newInnerJSObject = (JSObject *)aNewInner->GetScriptGlobal(JAVASCRIPT);
2277 JSObject *outerGlobal = (JSObject *)aOuterGlobal;
2279 // Now that we're connecting the outer global to the inner one,
2280 // we must have transplanted it. The JS engine tries to maintain
2281 // the global object's compartment as its default compartment,
2282 // so update that now since it might have changed.
2283 JS_SetGlobalObject(mContext, outerGlobal);
2284 NS_ASSERTION(JS_GetPrototype(mContext, outerGlobal) ==
2285 JS_GetPrototype(mContext, newInnerJSObject),
2286 "outer and inner globals should have the same prototype");
2288 return NS_OK;
2291 void *
2292 nsJSContext::GetNativeContext()
2294 return mContext;
2297 nsresult
2298 nsJSContext::InitContext()
2300 // Make sure callers of this use
2301 // WillInitializeContext/DidInitializeContext around this call.
2302 NS_ENSURE_TRUE(!mIsInitialized, NS_ERROR_ALREADY_INITIALIZED);
2304 if (!mContext)
2305 return NS_ERROR_OUT_OF_MEMORY;
2307 ::JS_SetErrorReporter(mContext, NS_ScriptErrorReporter);
2309 return NS_OK;
2312 nsresult
2313 nsJSContext::CreateOuterObject(nsIScriptGlobalObject *aGlobalObject,
2314 nsIScriptGlobalObject *aCurrentInner)
2316 mGlobalObjectRef = aGlobalObject;
2318 nsCOMPtr<nsIDOMChromeWindow> chromeWindow(do_QueryInterface(aGlobalObject));
2319 PRUint32 flags = 0;
2321 if (chromeWindow) {
2322 // Flag this window's global object and objects under it as "system",
2323 // for optional automated XPCNativeWrapper construction when chrome JS
2324 // views a content DOM.
2325 flags = nsIXPConnect::FLAG_SYSTEM_GLOBAL_OBJECT;
2327 // Always enable E4X for XUL and other chrome content -- there is no
2328 // need to preserve the <!-- script hiding hack from JS-in-HTML daze
2329 // (introduced in 1995 for graceful script degradation in Netscape 1,
2330 // Mosaic, and other pre-JS browsers).
2331 JS_SetOptions(mContext, JS_GetOptions(mContext) | JSOPTION_XML);
2334 JSObject *outer =
2335 NS_NewOuterWindowProxy(mContext, aCurrentInner->GetGlobalJSObject());
2336 if (!outer) {
2337 return NS_ERROR_FAILURE;
2340 return SetOuterObject(outer);
2343 nsresult
2344 nsJSContext::SetOuterObject(void *aOuterObject)
2346 JSObject *outer = static_cast<JSObject *>(aOuterObject);
2348 // Force our context's global object to be the outer.
2349 JS_SetGlobalObject(mContext, outer);
2351 // NB: JS_SetGlobalObject sets mContext->compartment.
2352 JSObject *inner = JS_GetParent(mContext, outer);
2354 nsIXPConnect *xpc = nsContentUtils::XPConnect();
2355 nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
2356 nsresult rv = xpc->GetWrappedNativeOfJSObject(mContext, inner,
2357 getter_AddRefs(wrapper));
2358 NS_ENSURE_SUCCESS(rv, rv);
2359 NS_ABORT_IF_FALSE(wrapper, "bad wrapper");
2361 wrapper->RefreshPrototype();
2362 JS_SetPrototype(mContext, outer, JS_GetPrototype(mContext, inner));
2364 return NS_OK;
2367 nsresult
2368 nsJSContext::InitOuterWindow()
2370 JSObject *global = JS_GetGlobalObject(mContext);
2371 OBJ_TO_INNER_OBJECT(mContext, global);
2373 nsresult rv = InitClasses(global); // this will complete global object initialization
2374 NS_ENSURE_SUCCESS(rv, rv);
2376 return NS_OK;
2379 nsresult
2380 nsJSContext::InitializeExternalClasses()
2382 nsScriptNameSpaceManager *nameSpaceManager = nsJSRuntime::GetNameSpaceManager();
2383 NS_ENSURE_TRUE(nameSpaceManager, NS_ERROR_NOT_INITIALIZED);
2385 return nameSpaceManager->InitForContext(this);
2388 nsresult
2389 nsJSContext::SetProperty(void *aTarget, const char *aPropName, nsISupports *aArgs)
2391 PRUint32 argc;
2392 jsval *argv = nsnull;
2394 JSAutoRequest ar(mContext);
2396 js::LazilyConstructed<nsAutoPoolRelease> poolRelease;
2397 js::LazilyConstructed<js::AutoArrayRooter> tvr;
2399 nsresult rv;
2400 rv = ConvertSupportsTojsvals(aArgs, GetNativeGlobal(), &argc,
2401 &argv, poolRelease, tvr);
2402 NS_ENSURE_SUCCESS(rv, rv);
2404 jsval vargs;
2406 // got the arguments, now attach them.
2408 // window.dialogArguments is supposed to be an array if a JS array
2409 // was passed to showModalDialog(), deal with that here.
2410 if (strcmp(aPropName, "dialogArguments") == 0 && argc <= 1) {
2411 vargs = argc ? argv[0] : JSVAL_VOID;
2412 } else {
2413 for (PRUint32 i = 0; i < argc; ++i) {
2414 if (!JS_WrapValue(mContext, &argv[i])) {
2415 return NS_ERROR_FAILURE;
2419 JSObject *args = ::JS_NewArrayObject(mContext, argc, argv);
2420 vargs = OBJECT_TO_JSVAL(args);
2423 // Make sure to use JS_DefineProperty here so that we can override
2424 // readonly XPConnect properties here as well (read dialogArguments).
2425 rv = ::JS_DefineProperty(mContext, reinterpret_cast<JSObject *>(aTarget),
2426 aPropName, vargs, nsnull, nsnull, 0) ?
2427 NS_OK : NS_ERROR_FAILURE;
2429 return rv;
2432 nsresult
2433 nsJSContext::ConvertSupportsTojsvals(nsISupports *aArgs,
2434 void *aScope,
2435 PRUint32 *aArgc,
2436 jsval **aArgv,
2437 js::LazilyConstructed<nsAutoPoolRelease> &aPoolRelease,
2438 js::LazilyConstructed<js::AutoArrayRooter> &aRooter)
2440 nsresult rv = NS_OK;
2442 // If the array implements nsIJSArgArray, just grab the values directly.
2443 nsCOMPtr<nsIJSArgArray> fastArray = do_QueryInterface(aArgs);
2444 if (fastArray != nsnull)
2445 return fastArray->GetArgs(aArgc, reinterpret_cast<void **>(aArgv));
2447 // Take the slower path converting each item.
2448 // Handle only nsIArray and nsIVariant. nsIArray is only needed for
2449 // SetProperty('arguments', ...);
2451 *aArgv = nsnull;
2452 *aArgc = 0;
2454 nsIXPConnect *xpc = nsContentUtils::XPConnect();
2455 NS_ENSURE_TRUE(xpc, NS_ERROR_UNEXPECTED);
2457 if (!aArgs)
2458 return NS_OK;
2459 PRUint32 argCtr, argCount;
2460 // This general purpose function may need to convert an arg array
2461 // (window.arguments, event-handler args) and a generic property.
2462 nsCOMPtr<nsIArray> argsArray(do_QueryInterface(aArgs));
2464 if (argsArray) {
2465 rv = argsArray->GetLength(&argCount);
2466 NS_ENSURE_SUCCESS(rv, rv);
2467 if (argCount == 0)
2468 return NS_OK;
2469 } else {
2470 argCount = 1; // the nsISupports which is not an array
2473 void *mark = JS_ARENA_MARK(&mContext->tempPool);
2474 jsval *argv;
2475 size_t nbytes = argCount * sizeof(jsval);
2476 JS_ARENA_ALLOCATE_CAST(argv, jsval *, &mContext->tempPool, nbytes);
2477 NS_ENSURE_TRUE(argv, NS_ERROR_OUT_OF_MEMORY);
2478 memset(argv, 0, nbytes); /* initialize so GC-able */
2480 // Use the caller's auto guards to release and unroot.
2481 aPoolRelease.construct(&mContext->tempPool, mark);
2482 aRooter.construct(mContext, argCount, argv);
2484 if (argsArray) {
2485 for (argCtr = 0; argCtr < argCount && NS_SUCCEEDED(rv); argCtr++) {
2486 nsCOMPtr<nsISupports> arg;
2487 jsval *thisval = argv + argCtr;
2488 argsArray->QueryElementAt(argCtr, NS_GET_IID(nsISupports),
2489 getter_AddRefs(arg));
2490 if (!arg) {
2491 *thisval = JSVAL_NULL;
2492 continue;
2494 nsCOMPtr<nsIVariant> variant(do_QueryInterface(arg));
2495 if (variant != nsnull) {
2496 rv = xpc->VariantToJS(mContext, (JSObject *)aScope, variant,
2497 thisval);
2498 } else {
2499 // And finally, support the nsISupportsPrimitives supplied
2500 // by the AppShell. It generally will pass only strings, but
2501 // as we have code for handling all, we may as well use it.
2502 rv = AddSupportsPrimitiveTojsvals(arg, thisval);
2503 if (rv == NS_ERROR_NO_INTERFACE) {
2504 // something else - probably an event object or similar -
2505 // just wrap it.
2506 #ifdef NS_DEBUG
2507 // but first, check its not another nsISupportsPrimitive, as
2508 // these are now deprecated for use with script contexts.
2509 nsCOMPtr<nsISupportsPrimitive> prim(do_QueryInterface(arg));
2510 NS_ASSERTION(prim == nsnull,
2511 "Don't pass nsISupportsPrimitives - use nsIVariant!");
2512 #endif
2513 nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
2514 jsval v;
2515 rv = nsContentUtils::WrapNative(mContext, (JSObject *)aScope, arg,
2516 &v, getter_AddRefs(wrapper));
2517 if (NS_SUCCEEDED(rv)) {
2518 *thisval = v;
2523 } else {
2524 nsCOMPtr<nsIVariant> variant(do_QueryInterface(aArgs));
2525 if (variant)
2526 rv = xpc->VariantToJS(mContext, (JSObject *)aScope, variant, argv);
2527 else {
2528 NS_ERROR("Not an array, not an interface?");
2529 rv = NS_ERROR_UNEXPECTED;
2532 if (NS_FAILED(rv))
2533 return rv;
2534 *aArgv = argv;
2535 *aArgc = argCount;
2536 return NS_OK;
2539 // This really should go into xpconnect somewhere...
2540 nsresult
2541 nsJSContext::AddSupportsPrimitiveTojsvals(nsISupports *aArg, jsval *aArgv)
2543 NS_PRECONDITION(aArg, "Empty arg");
2545 nsCOMPtr<nsISupportsPrimitive> argPrimitive(do_QueryInterface(aArg));
2546 if (!argPrimitive)
2547 return NS_ERROR_NO_INTERFACE;
2549 JSContext *cx = mContext;
2550 PRUint16 type;
2551 argPrimitive->GetType(&type);
2553 switch(type) {
2554 case nsISupportsPrimitive::TYPE_CSTRING : {
2555 nsCOMPtr<nsISupportsCString> p(do_QueryInterface(argPrimitive));
2556 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
2558 nsCAutoString data;
2560 p->GetData(data);
2563 JSString *str = ::JS_NewStringCopyN(cx, data.get(), data.Length());
2564 NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY);
2566 *aArgv = STRING_TO_JSVAL(str);
2568 break;
2570 case nsISupportsPrimitive::TYPE_STRING : {
2571 nsCOMPtr<nsISupportsString> p(do_QueryInterface(argPrimitive));
2572 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
2574 nsAutoString data;
2576 p->GetData(data);
2578 // cast is probably safe since wchar_t and jschar are expected
2579 // to be equivalent; both unsigned 16-bit entities
2580 JSString *str =
2581 ::JS_NewUCStringCopyN(cx,
2582 reinterpret_cast<const jschar *>(data.get()),
2583 data.Length());
2584 NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY);
2586 *aArgv = STRING_TO_JSVAL(str);
2587 break;
2589 case nsISupportsPrimitive::TYPE_PRBOOL : {
2590 nsCOMPtr<nsISupportsPRBool> p(do_QueryInterface(argPrimitive));
2591 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
2593 PRBool data;
2595 p->GetData(&data);
2597 *aArgv = BOOLEAN_TO_JSVAL(data);
2599 break;
2601 case nsISupportsPrimitive::TYPE_PRUINT8 : {
2602 nsCOMPtr<nsISupportsPRUint8> p(do_QueryInterface(argPrimitive));
2603 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
2605 PRUint8 data;
2607 p->GetData(&data);
2609 *aArgv = INT_TO_JSVAL(data);
2611 break;
2613 case nsISupportsPrimitive::TYPE_PRUINT16 : {
2614 nsCOMPtr<nsISupportsPRUint16> p(do_QueryInterface(argPrimitive));
2615 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
2617 PRUint16 data;
2619 p->GetData(&data);
2621 *aArgv = INT_TO_JSVAL(data);
2623 break;
2625 case nsISupportsPrimitive::TYPE_PRUINT32 : {
2626 nsCOMPtr<nsISupportsPRUint32> p(do_QueryInterface(argPrimitive));
2627 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
2629 PRUint32 data;
2631 p->GetData(&data);
2633 *aArgv = INT_TO_JSVAL(data);
2635 break;
2637 case nsISupportsPrimitive::TYPE_CHAR : {
2638 nsCOMPtr<nsISupportsChar> p(do_QueryInterface(argPrimitive));
2639 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
2641 char data;
2643 p->GetData(&data);
2645 JSString *str = ::JS_NewStringCopyN(cx, &data, 1);
2646 NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY);
2648 *aArgv = STRING_TO_JSVAL(str);
2650 break;
2652 case nsISupportsPrimitive::TYPE_PRINT16 : {
2653 nsCOMPtr<nsISupportsPRInt16> p(do_QueryInterface(argPrimitive));
2654 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
2656 PRInt16 data;
2658 p->GetData(&data);
2660 *aArgv = INT_TO_JSVAL(data);
2662 break;
2664 case nsISupportsPrimitive::TYPE_PRINT32 : {
2665 nsCOMPtr<nsISupportsPRInt32> p(do_QueryInterface(argPrimitive));
2666 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
2668 PRInt32 data;
2670 p->GetData(&data);
2672 *aArgv = INT_TO_JSVAL(data);
2674 break;
2676 case nsISupportsPrimitive::TYPE_FLOAT : {
2677 nsCOMPtr<nsISupportsFloat> p(do_QueryInterface(argPrimitive));
2678 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
2680 float data;
2682 p->GetData(&data);
2684 JSBool ok = ::JS_NewNumberValue(cx, data, aArgv);
2685 NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
2687 break;
2689 case nsISupportsPrimitive::TYPE_DOUBLE : {
2690 nsCOMPtr<nsISupportsDouble> p(do_QueryInterface(argPrimitive));
2691 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
2693 double data;
2695 p->GetData(&data);
2697 JSBool ok = ::JS_NewNumberValue(cx, data, aArgv);
2698 NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
2700 break;
2702 case nsISupportsPrimitive::TYPE_INTERFACE_POINTER : {
2703 nsCOMPtr<nsISupportsInterfacePointer> p(do_QueryInterface(argPrimitive));
2704 NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
2706 nsCOMPtr<nsISupports> data;
2707 nsIID *iid = nsnull;
2709 p->GetData(getter_AddRefs(data));
2710 p->GetDataIID(&iid);
2711 NS_ENSURE_TRUE(iid, NS_ERROR_UNEXPECTED);
2713 AutoFree iidGuard(iid); // Free iid upon destruction.
2715 nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
2716 jsval v;
2717 nsresult rv = nsContentUtils::WrapNative(cx, ::JS_GetGlobalObject(cx),
2718 data, iid, &v,
2719 getter_AddRefs(wrapper));
2720 NS_ENSURE_SUCCESS(rv, rv);
2722 *aArgv = v;
2724 break;
2726 case nsISupportsPrimitive::TYPE_ID :
2727 case nsISupportsPrimitive::TYPE_PRUINT64 :
2728 case nsISupportsPrimitive::TYPE_PRINT64 :
2729 case nsISupportsPrimitive::TYPE_PRTIME :
2730 case nsISupportsPrimitive::TYPE_VOID : {
2731 NS_WARNING("Unsupported primitive type used");
2732 *aArgv = JSVAL_NULL;
2733 break;
2735 default : {
2736 NS_WARNING("Unknown primitive type used");
2737 *aArgv = JSVAL_NULL;
2738 break;
2741 return NS_OK;
2744 #ifdef NS_TRACE_MALLOC
2746 #include <errno.h> // XXX assume Linux if NS_TRACE_MALLOC
2747 #include <fcntl.h>
2748 #ifdef XP_UNIX
2749 #include <unistd.h>
2750 #endif
2751 #ifdef XP_WIN32
2752 #include <io.h>
2753 #endif
2754 #include "nsTraceMalloc.h"
2756 static JSBool
2757 CheckUniversalXPConnectForTraceMalloc(JSContext *cx)
2759 PRBool hasCap = PR_FALSE;
2760 nsresult rv = nsContentUtils::GetSecurityManager()->
2761 IsCapabilityEnabled("UniversalXPConnect", &hasCap);
2762 if (NS_SUCCEEDED(rv) && hasCap)
2763 return JS_TRUE;
2764 JS_ReportError(cx, "trace-malloc functions require UniversalXPConnect");
2765 return JS_FALSE;
2768 static JSBool
2769 TraceMallocDisable(JSContext *cx, uintN argc, jsval *vp)
2771 if (!CheckUniversalXPConnectForTraceMalloc(cx))
2772 return JS_FALSE;
2774 NS_TraceMallocDisable();
2775 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2776 return JS_TRUE;
2779 static JSBool
2780 TraceMallocEnable(JSContext *cx, uintN argc, jsval *vp)
2782 if (!CheckUniversalXPConnectForTraceMalloc(cx))
2783 return JS_FALSE;
2785 NS_TraceMallocEnable();
2786 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2787 return JS_TRUE;
2790 static JSBool
2791 TraceMallocOpenLogFile(JSContext *cx, uintN argc, jsval *vp)
2793 int fd;
2794 JSString *str;
2796 if (!CheckUniversalXPConnectForTraceMalloc(cx))
2797 return JS_FALSE;
2799 if (argc == 0) {
2800 fd = -1;
2801 } else {
2802 str = JS_ValueToString(cx, JS_ARGV(cx, vp)[0]);
2803 if (!str)
2804 return JS_FALSE;
2805 JSAutoByteString filename(cx, str);
2806 if (!filename)
2807 return JS_FALSE;
2808 fd = open(filename.ptr(), O_CREAT | O_WRONLY | O_TRUNC, 0644);
2809 if (fd < 0) {
2810 JS_ReportError(cx, "can't open %s: %s", filename.ptr(), strerror(errno));
2811 return JS_FALSE;
2814 JS_SET_RVAL(cx, vp, INT_TO_JSVAL(fd));
2815 return JS_TRUE;
2818 static JSBool
2819 TraceMallocChangeLogFD(JSContext *cx, uintN argc, jsval *vp)
2821 int32 fd, oldfd;
2823 if (!CheckUniversalXPConnectForTraceMalloc(cx))
2824 return JS_FALSE;
2826 if (argc == 0) {
2827 oldfd = -1;
2828 } else {
2829 if (!JS_ValueToECMAInt32(cx, JS_ARGV(cx, vp)[0], &fd))
2830 return JS_FALSE;
2831 oldfd = NS_TraceMallocChangeLogFD(fd);
2832 if (oldfd == -2) {
2833 JS_ReportOutOfMemory(cx);
2834 return JS_FALSE;
2837 JS_SET_RVAL(cx, vp, INT_TO_JSVAL(oldfd));
2838 return JS_TRUE;
2841 static JSBool
2842 TraceMallocCloseLogFD(JSContext *cx, uintN argc, jsval *vp)
2844 int32 fd;
2846 if (!CheckUniversalXPConnectForTraceMalloc(cx))
2847 return JS_FALSE;
2849 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2850 if (argc == 0)
2851 return JS_TRUE;
2852 if (!JS_ValueToECMAInt32(cx, JS_ARGV(cx, vp)[0], &fd))
2853 return JS_FALSE;
2854 NS_TraceMallocCloseLogFD((int) fd);
2855 return JS_TRUE;
2858 static JSBool
2859 TraceMallocLogTimestamp(JSContext *cx, uintN argc, jsval *vp)
2861 if (!CheckUniversalXPConnectForTraceMalloc(cx))
2862 return JS_FALSE;
2864 JSString *str = JS_ValueToString(cx, argc ? JS_ARGV(cx, vp)[0] : JSVAL_VOID);
2865 if (!str)
2866 return JS_FALSE;
2867 JSAutoByteString caption(cx, str);
2868 if (!caption)
2869 return JS_FALSE;
2870 NS_TraceMallocLogTimestamp(caption.ptr());
2871 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2872 return JS_TRUE;
2875 static JSBool
2876 TraceMallocDumpAllocations(JSContext *cx, uintN argc, jsval *vp)
2878 if (!CheckUniversalXPConnectForTraceMalloc(cx))
2879 return JS_FALSE;
2881 JSString *str = JS_ValueToString(cx, argc ? JS_ARGV(cx, vp)[0] : JSVAL_VOID);
2882 if (!str)
2883 return JS_FALSE;
2884 JSAutoByteString pathname(cx, str);
2885 if (!pathname)
2886 return JS_FALSE;
2887 if (NS_TraceMallocDumpAllocations(pathname.ptr()) < 0) {
2888 JS_ReportError(cx, "can't dump to %s: %s", pathname.ptr(), strerror(errno));
2889 return JS_FALSE;
2891 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2892 return JS_TRUE;
2895 static JSFunctionSpec TraceMallocFunctions[] = {
2896 {"TraceMallocDisable", TraceMallocDisable, 0, 0},
2897 {"TraceMallocEnable", TraceMallocEnable, 0, 0},
2898 {"TraceMallocOpenLogFile", TraceMallocOpenLogFile, 1, 0},
2899 {"TraceMallocChangeLogFD", TraceMallocChangeLogFD, 1, 0},
2900 {"TraceMallocCloseLogFD", TraceMallocCloseLogFD, 1, 0},
2901 {"TraceMallocLogTimestamp", TraceMallocLogTimestamp, 1, 0},
2902 {"TraceMallocDumpAllocations", TraceMallocDumpAllocations, 1, 0},
2903 {nsnull, nsnull, 0, 0}
2906 #endif /* NS_TRACE_MALLOC */
2908 #ifdef MOZ_JPROF
2910 #include <signal.h>
2912 inline PRBool
2913 IsJProfAction(struct sigaction *action)
2915 return (action->sa_sigaction &&
2916 action->sa_flags == (SA_RESTART | SA_SIGINFO));
2919 void NS_JProfStartProfiling();
2920 void NS_JProfStopProfiling();
2922 static JSBool
2923 JProfStartProfilingJS(JSContext *cx, uintN argc, jsval *vp)
2925 NS_JProfStartProfiling();
2926 return JS_TRUE;
2929 void NS_JProfStartProfiling()
2931 // Figure out whether we're dealing with SIGPROF, SIGALRM, or
2932 // SIGPOLL profiling (SIGALRM for JP_REALTIME, SIGPOLL for
2933 // JP_RTC_HZ)
2934 struct sigaction action;
2936 sigaction(SIGALRM, nsnull, &action);
2937 if (IsJProfAction(&action)) {
2938 printf("Beginning real-time jprof profiling.\n");
2939 raise(SIGALRM);
2940 return;
2943 sigaction(SIGPROF, nsnull, &action);
2944 if (IsJProfAction(&action)) {
2945 printf("Beginning process-time jprof profiling.\n");
2946 raise(SIGPROF);
2947 return;
2950 sigaction(SIGPOLL, nsnull, &action);
2951 if (IsJProfAction(&action)) {
2952 printf("Beginning rtc-based jprof profiling.\n");
2953 raise(SIGPOLL);
2954 return;
2957 printf("Could not start jprof-profiling since JPROF_FLAGS was not set.\n");
2960 static JSBool
2961 JProfStopProfilingJS(JSContext *cx, uintN argc, jsval *vp)
2963 NS_JProfStopProfiling();
2964 return JS_TRUE;
2967 void
2968 NS_JProfStopProfiling()
2970 raise(SIGUSR1);
2971 printf("Stopped jprof profiling.\n");
2974 static JSFunctionSpec JProfFunctions[] = {
2975 {"JProfStartProfiling", JProfStartProfilingJS, 0, 0},
2976 {"JProfStopProfiling", JProfStopProfilingJS, 0, 0},
2977 {nsnull, nsnull, 0, 0}
2980 #endif /* defined(MOZ_JPROF) */
2982 #ifdef MOZ_CALLGRIND
2983 static JSFunctionSpec CallgrindFunctions[] = {
2984 {"startCallgrind", js_StartCallgrind, 0, 0},
2985 {"stopCallgrind", js_StopCallgrind, 0, 0},
2986 {"dumpCallgrind", js_DumpCallgrind, 1, 0},
2987 {nsnull, nsnull, 0, 0}
2989 #endif
2991 #ifdef MOZ_VTUNE
2992 static JSFunctionSpec VtuneFunctions[] = {
2993 {"startVtune", js_StartVtune, 1, 0},
2994 {"stopVtune", js_StopVtune, 0, 0},
2995 {"pauseVtune", js_PauseVtune, 0, 0},
2996 {"resumeVtune", js_ResumeVtune, 0, 0},
2997 {nsnull, nsnull, 0, 0}
2999 #endif
3001 #ifdef MOZ_TRACEVIS
3002 static JSFunctionSpec EthogramFunctions[] = {
3003 {"initEthogram", js_InitEthogram, 0, 0},
3004 {"shutdownEthogram", js_ShutdownEthogram, 0, 0},
3005 {nsnull, nsnull, 0, 0}
3007 #endif
3009 nsresult
3010 nsJSContext::InitClasses(void *aGlobalObj)
3012 nsresult rv = NS_OK;
3014 JSObject *globalObj = static_cast<JSObject *>(aGlobalObj);
3016 rv = InitializeExternalClasses();
3017 NS_ENSURE_SUCCESS(rv, rv);
3019 JSAutoRequest ar(mContext);
3021 ::JS_SetOptions(mContext, mDefaultJSOptions);
3023 // Attempt to initialize profiling functions
3024 ::JS_DefineProfilingFunctions(mContext, globalObj);
3026 #ifdef NS_TRACE_MALLOC
3027 // Attempt to initialize TraceMalloc functions
3028 ::JS_DefineFunctions(mContext, globalObj, TraceMallocFunctions);
3029 #endif
3031 #ifdef MOZ_JPROF
3032 // Attempt to initialize JProf functions
3033 ::JS_DefineFunctions(mContext, globalObj, JProfFunctions);
3034 #endif
3036 #ifdef MOZ_CALLGRIND
3037 // Attempt to initialize Callgrind functions
3038 ::JS_DefineFunctions(mContext, globalObj, CallgrindFunctions);
3039 #endif
3041 #ifdef MOZ_VTUNE
3042 // Attempt to initialize Vtune functions
3043 ::JS_DefineFunctions(mContext, globalObj, VtuneFunctions);
3044 #endif
3046 #ifdef MOZ_TRACEVIS
3047 // Attempt to initialize Ethogram functions
3048 ::JS_DefineFunctions(mContext, globalObj, EthogramFunctions);
3049 #endif
3051 JSOptionChangedCallback(js_options_dot_str, this);
3053 return rv;
3056 void
3057 nsJSContext::ClearScope(void *aGlobalObj, PRBool aClearFromProtoChain)
3059 // Push our JSContext on our thread's context stack.
3060 nsCOMPtr<nsIJSContextStack> stack =
3061 do_GetService("@mozilla.org/js/xpc/ContextStack;1");
3062 if (stack && NS_FAILED(stack->Push(mContext))) {
3063 stack = nsnull;
3066 if (aGlobalObj) {
3067 JSObject *obj = (JSObject *)aGlobalObj;
3068 JSAutoRequest ar(mContext);
3070 JSAutoEnterCompartment ac;
3071 ac.enterAndIgnoreErrors(mContext, obj);
3073 // Grab a reference to the window property, which is the outer
3074 // window, so that we can re-define it once we've cleared
3075 // scope. This is what keeps the outer window alive in cases where
3076 // nothing else does.
3077 jsval window;
3078 if (!JS_GetProperty(mContext, obj, "window", &window)) {
3079 window = JSVAL_VOID;
3081 JS_ClearPendingException(mContext);
3084 JS_ClearScope(mContext, obj);
3086 NS_ABORT_IF_FALSE(!xpc::WrapperFactory::IsXrayWrapper(obj), "unexpected wrapper");
3088 if (window != JSVAL_VOID) {
3089 if (!JS_DefineProperty(mContext, obj, "window", window,
3090 JS_PropertyStub, JS_StrictPropertyStub,
3091 JSPROP_ENUMERATE | JSPROP_READONLY |
3092 JSPROP_PERMANENT)) {
3093 JS_ClearPendingException(mContext);
3097 if (!obj->getParent()) {
3098 JS_ClearRegExpStatics(mContext, obj);
3101 // Always clear watchpoints, to deal with two cases:
3102 // 1. The first document for this window is loading, and a miscreant has
3103 // preset watchpoints on the window object in order to attack the new
3104 // document's privileged information.
3105 // 2. A document loaded and used watchpoints on its own window, leaving
3106 // them set until the next document loads. We must clean up window
3107 // watchpoints here.
3108 // Watchpoints set on document and subordinate objects are all cleared
3109 // when those sub-window objects are finalized, after JS_ClearScope and
3110 // a GC run that finds them to be garbage.
3111 ::JS_ClearWatchPointsForObject(mContext, obj);
3113 // Since the prototype chain is shared between inner and outer (and
3114 // stays with the inner), we don't clear things from the prototype
3115 // chain when we're clearing an outer window whose current inner we
3116 // still want.
3117 if (aClearFromProtoChain) {
3118 nsWindowSH::InvalidateGlobalScopePolluter(mContext, obj);
3120 // Clear up obj's prototype chain, but not Object.prototype.
3121 for (JSObject *o = ::JS_GetPrototype(mContext, obj), *next;
3122 o && (next = ::JS_GetPrototype(mContext, o)); o = next)
3123 ::JS_ClearScope(mContext, o);
3127 if (stack) {
3128 stack->Pop(nsnull);
3132 void
3133 nsJSContext::WillInitializeContext()
3135 mIsInitialized = PR_FALSE;
3138 void
3139 nsJSContext::DidInitializeContext()
3141 mIsInitialized = PR_TRUE;
3144 PRBool
3145 nsJSContext::IsContextInitialized()
3147 return mIsInitialized;
3150 void
3151 nsJSContext::FinalizeContext()
3156 void
3157 nsJSContext::ScriptEvaluated(PRBool aTerminated)
3159 if (aTerminated && mTerminations) {
3160 // Make sure to null out mTerminations before doing anything that
3161 // might cause new termination funcs to be added!
3162 nsJSContext::TerminationFuncClosure* start = mTerminations;
3163 mTerminations = nsnull;
3165 for (nsJSContext::TerminationFuncClosure* cur = start;
3166 cur;
3167 cur = cur->mNext) {
3168 (*(cur->mTerminationFunc))(cur->mTerminationFuncArg);
3170 delete start;
3173 JS_MaybeGC(mContext);
3175 if (aTerminated) {
3176 mOperationCallbackTime = 0;
3177 mModalStateTime = 0;
3181 nsresult
3182 nsJSContext::SetTerminationFunction(nsScriptTerminationFunc aFunc,
3183 nsISupports* aRef)
3185 NS_PRECONDITION(GetExecutingScript(), "should be executing script");
3187 nsJSContext::TerminationFuncClosure* newClosure =
3188 new nsJSContext::TerminationFuncClosure(aFunc, aRef, mTerminations);
3189 if (!newClosure) {
3190 return NS_ERROR_OUT_OF_MEMORY;
3193 mTerminations = newClosure;
3194 return NS_OK;
3197 PRBool
3198 nsJSContext::GetScriptsEnabled()
3200 return mScriptsEnabled;
3203 void
3204 nsJSContext::SetScriptsEnabled(PRBool aEnabled, PRBool aFireTimeouts)
3206 // eeek - this seems the wrong way around - the global should callback
3207 // into each context, so every language is disabled.
3208 mScriptsEnabled = aEnabled;
3210 nsIScriptGlobalObject *global = GetGlobalObject();
3212 if (global) {
3213 global->SetScriptsEnabled(aEnabled, aFireTimeouts);
3218 PRBool
3219 nsJSContext::GetProcessingScriptTag()
3221 return mProcessingScriptTag;
3224 void
3225 nsJSContext::SetProcessingScriptTag(PRBool aFlag)
3227 mProcessingScriptTag = aFlag;
3230 PRBool
3231 nsJSContext::GetExecutingScript()
3233 return JS_IsRunning(mContext) || mExecuteDepth > 0;
3236 void
3237 nsJSContext::SetGCOnDestruction(PRBool aGCOnDestruction)
3239 mGCOnDestruction = aGCOnDestruction;
3242 NS_IMETHODIMP
3243 nsJSContext::ScriptExecuted()
3245 ScriptEvaluated(!::JS_IsRunning(mContext));
3247 return NS_OK;
3250 //static
3251 void
3252 nsJSContext::GarbageCollectNow()
3254 NS_TIME_FUNCTION_MIN(1.0);
3256 KillGCTimer();
3258 // Reset sPendingLoadCount in case the timer that fired was a
3259 // timer we scheduled due to a normal GC timer firing while
3260 // documents were loading. If this happens we're waiting for a
3261 // document that is taking a long time to load, and we effectively
3262 // ignore the fact that the currently loading documents are still
3263 // loading and move on as if they weren't.
3264 sPendingLoadCount = 0;
3265 sLoadingInProgress = PR_FALSE;
3267 nsContentUtils::XPConnect()->GarbageCollect();
3270 //Static
3271 void
3272 nsJSContext::CycleCollectNow(nsICycleCollectorListener *aListener)
3274 if (!NS_IsMainThread()) {
3275 return;
3278 NS_TIME_FUNCTION_MIN(1.0);
3280 KillCCTimer();
3282 PRTime start = PR_Now();
3284 PRUint32 suspected = nsCycleCollector_suspectedCount();
3285 PRUint32 collected = nsCycleCollector_collect(aListener);
3287 // If we collected cycles, poke the GC since more objects might be unreachable now.
3288 if (collected > 0) {
3289 PokeGC();
3292 if (sPostGCEventsToConsole) {
3293 PRTime now = PR_Now();
3294 NS_NAMED_LITERAL_STRING(kFmt,
3295 "CC timestamp: %lld, collected: %lu, suspected: %lu, duration: %llu ms.");
3296 nsString msg;
3297 msg.Adopt(nsTextFormatter::smprintf(kFmt.get(), now,
3298 collected, suspected,
3299 (now - start) / PR_USEC_PER_MSEC));
3300 nsCOMPtr<nsIConsoleService> cs =
3301 do_GetService(NS_CONSOLESERVICE_CONTRACTID);
3302 if (cs) {
3303 cs->LogStringMessage(msg.get());
3308 // static
3309 void
3310 GCTimerFired(nsITimer *aTimer, void *aClosure)
3312 NS_RELEASE(sGCTimer);
3314 nsJSContext::GarbageCollectNow();
3317 // static
3318 void
3319 CCTimerFired(nsITimer *aTimer, void *aClosure)
3321 NS_RELEASE(sCCTimer);
3323 nsJSContext::CycleCollectNow();
3326 // static
3327 void
3328 nsJSContext::LoadStart()
3330 sLoadingInProgress = PR_TRUE;
3331 ++sPendingLoadCount;
3334 // static
3335 void
3336 nsJSContext::LoadEnd()
3338 if (!sLoadingInProgress)
3339 return;
3341 // sPendingLoadCount is not a well managed load counter (and doesn't
3342 // need to be), so make sure we don't make it wrap backwards here.
3343 if (sPendingLoadCount > 0) {
3344 --sPendingLoadCount;
3345 return;
3348 // Its probably a good idea to GC soon since we have finished loading.
3349 sLoadingInProgress = PR_FALSE;
3350 PokeGC();
3353 // static
3354 void
3355 nsJSContext::PokeGC()
3357 if (sGCTimer) {
3358 // There's already a timer for GC'ing, just return
3359 return;
3362 CallCreateInstance("@mozilla.org/timer;1", &sGCTimer);
3364 if (!sGCTimer) {
3365 NS_WARNING("Failed to create timer");
3367 GarbageCollectNow();
3368 return;
3371 static PRBool first = PR_TRUE;
3373 sGCTimer->InitWithFuncCallback(GCTimerFired, nsnull,
3374 first
3375 ? NS_FIRST_GC_DELAY
3376 : NS_GC_DELAY,
3377 nsITimer::TYPE_ONE_SHOT);
3379 first = PR_FALSE;
3382 // static
3383 void
3384 nsJSContext::MaybePokeCC()
3386 if (nsCycleCollector_suspectedCount() > 1000) {
3387 PokeCC();
3391 // static
3392 void
3393 nsJSContext::PokeCC()
3395 if (sCCTimer) {
3396 // There's already a timer for GC'ing, just return
3397 return;
3400 CallCreateInstance("@mozilla.org/timer;1", &sCCTimer);
3402 if (!sCCTimer) {
3403 NS_WARNING("Failed to create timer");
3405 CycleCollectNow();
3406 return;
3409 sCCTimer->InitWithFuncCallback(CCTimerFired, nsnull,
3410 NS_CC_DELAY,
3411 nsITimer::TYPE_ONE_SHOT);
3414 //static
3415 void
3416 nsJSContext::KillGCTimer()
3418 if (sGCTimer) {
3419 sGCTimer->Cancel();
3421 NS_RELEASE(sGCTimer);
3425 //static
3426 void
3427 nsJSContext::KillCCTimer()
3429 if (sCCTimer) {
3430 sCCTimer->Cancel();
3432 NS_RELEASE(sCCTimer);
3436 void
3437 nsJSContext::GC()
3439 PokeGC();
3442 static JSBool
3443 DOMGCCallback(JSContext *cx, JSGCStatus status)
3445 static PRTime start;
3447 if (sPostGCEventsToConsole && NS_IsMainThread()) {
3448 if (status == JSGC_BEGIN) {
3449 start = PR_Now();
3450 } else if (status == JSGC_END) {
3451 PRTime now = PR_Now();
3452 NS_NAMED_LITERAL_STRING(kFmt, "GC timestamp: %lld, duration: %llu ms.");
3453 nsString msg;
3454 msg.Adopt(nsTextFormatter::smprintf(kFmt.get(), now,
3455 (now - start) / PR_USEC_PER_MSEC));
3456 nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
3457 if (cs) {
3458 cs->LogStringMessage(msg.get());
3463 if (status == JSGC_END) {
3464 if (sGCTimer) {
3465 // If we were waiting for a GC to happen, kill the timer.
3466 nsJSContext::KillGCTimer();
3468 // If this is a compartment GC, restart it. We still want
3469 // a full GC to happen. Compartment GCs usually happen as a
3470 // result of last-ditch or MaybeGC. In both cases its
3471 // probably a time of heavy activity and we want to delay
3472 // the full GC, but we do want it to happen eventually.
3473 if (cx->runtime->gcTriggerCompartment) {
3474 nsJSContext::PokeGC();
3476 // We poked the GC, so we can kill any pending CC here.
3477 nsJSContext::KillCCTimer();
3479 } else {
3480 // If this was a full GC, poke the CC to run soon.
3481 if (!cx->runtime->gcTriggerCompartment) {
3482 nsJSContext::PokeCC();
3487 JSBool result = gOldJSGCCallback ? gOldJSGCCallback(cx, status) : JS_TRUE;
3489 if (status == JSGC_BEGIN && !NS_IsMainThread())
3490 return JS_FALSE;
3492 return result;
3495 // Script object mananagement - note duplicate implementation
3496 // in nsJSRuntime below...
3497 nsresult
3498 nsJSContext::HoldScriptObject(void* aScriptObject)
3500 NS_ASSERTION(sIsInitialized, "runtime not initialized");
3501 if (! nsJSRuntime::sRuntime) {
3502 NS_NOTREACHED("couldn't add GC root - no runtime");
3503 return NS_ERROR_FAILURE;
3506 ::JS_LockGCThingRT(nsJSRuntime::sRuntime, aScriptObject);
3507 return NS_OK;
3510 nsresult
3511 nsJSContext::DropScriptObject(void* aScriptObject)
3513 NS_ASSERTION(sIsInitialized, "runtime not initialized");
3514 if (! nsJSRuntime::sRuntime) {
3515 NS_NOTREACHED("couldn't remove GC root");
3516 return NS_ERROR_FAILURE;
3519 ::JS_UnlockGCThingRT(nsJSRuntime::sRuntime, aScriptObject);
3520 return NS_OK;
3523 void
3524 nsJSContext::ReportPendingException()
3526 // set aside the frame chain, since it has nothing to do with the
3527 // exception we're reporting.
3528 if (mIsInitialized && ::JS_IsExceptionPending(mContext)) {
3529 JSStackFrame* frame = JS_SaveFrameChain(mContext);
3530 ::JS_ReportPendingException(mContext);
3531 JS_RestoreFrameChain(mContext, frame);
3535 /**********************************************************************
3536 * nsJSRuntime implementation
3537 *********************************************************************/
3539 // QueryInterface implementation for nsJSRuntime
3540 NS_INTERFACE_MAP_BEGIN(nsJSRuntime)
3541 NS_INTERFACE_MAP_ENTRY(nsIScriptRuntime)
3542 NS_INTERFACE_MAP_END
3545 NS_IMPL_ADDREF(nsJSRuntime)
3546 NS_IMPL_RELEASE(nsJSRuntime)
3548 nsresult
3549 nsJSRuntime::CreateContext(nsIScriptContext **aContext)
3551 nsCOMPtr<nsIScriptContext> scriptContext;
3553 *aContext = new nsJSContext(sRuntime);
3554 NS_ENSURE_TRUE(*aContext, NS_ERROR_OUT_OF_MEMORY);
3555 NS_ADDREF(*aContext);
3556 return NS_OK;
3559 nsresult
3560 nsJSRuntime::ParseVersion(const nsString &aVersionStr, PRUint32 *flags)
3562 NS_PRECONDITION(flags, "Null flags param?");
3563 JSVersion jsVersion = JSVERSION_UNKNOWN;
3564 if (aVersionStr.Length() != 3 || aVersionStr[0] != '1' || aVersionStr[1] != '.')
3565 jsVersion = JSVERSION_UNKNOWN;
3566 else switch (aVersionStr[2]) {
3567 case '0': jsVersion = JSVERSION_1_0; break;
3568 case '1': jsVersion = JSVERSION_1_1; break;
3569 case '2': jsVersion = JSVERSION_1_2; break;
3570 case '3': jsVersion = JSVERSION_1_3; break;
3571 case '4': jsVersion = JSVERSION_1_4; break;
3572 case '5': jsVersion = JSVERSION_1_5; break;
3573 case '6': jsVersion = JSVERSION_1_6; break;
3574 case '7': jsVersion = JSVERSION_1_7; break;
3575 case '8': jsVersion = JSVERSION_1_8; break;
3576 default: jsVersion = JSVERSION_UNKNOWN;
3578 *flags = (PRUint32)jsVersion;
3579 return NS_OK;
3582 //static
3583 void
3584 nsJSRuntime::Startup()
3586 // initialize all our statics, so that we can restart XPCOM
3587 sGCTimer = sCCTimer = nsnull;
3588 sPendingLoadCount = 0;
3589 sLoadingInProgress = PR_FALSE;
3590 sPostGCEventsToConsole = PR_FALSE;
3591 gNameSpaceManager = nsnull;
3592 sRuntimeService = nsnull;
3593 sRuntime = nsnull;
3594 gOldJSGCCallback = nsnull;
3595 sIsInitialized = PR_FALSE;
3596 sDidShutdown = PR_FALSE;
3597 sContextCount = 0;
3598 sSecurityManager = nsnull;
3601 static int
3602 MaxScriptRunTimePrefChangedCallback(const char *aPrefName, void *aClosure)
3604 // Default limit on script run time to 10 seconds. 0 means let
3605 // scripts run forever.
3606 PRBool isChromePref =
3607 strcmp(aPrefName, "dom.max_chrome_script_run_time") == 0;
3608 PRInt32 time = nsContentUtils::GetIntPref(aPrefName, isChromePref ? 20 : 10);
3610 PRTime t;
3611 if (time <= 0) {
3612 // Let scripts run for a really, really long time.
3613 t = LL_INIT(0x40000000, 0);
3614 } else {
3615 t = time * PR_USEC_PER_SEC;
3618 if (isChromePref) {
3619 sMaxChromeScriptRunTime = t;
3620 } else {
3621 sMaxScriptRunTime = t;
3624 return 0;
3627 static int
3628 ReportAllJSExceptionsPrefChangedCallback(const char* aPrefName, void* aClosure)
3630 PRBool reportAll = nsContentUtils::GetBoolPref(aPrefName, PR_FALSE);
3631 nsContentUtils::XPConnect()->SetReportAllJSExceptions(reportAll);
3632 return 0;
3635 static int
3636 SetMemoryHighWaterMarkPrefChangedCallback(const char* aPrefName, void* aClosure)
3638 PRInt32 highwatermark = nsContentUtils::GetIntPref(aPrefName, 128);
3640 JS_SetGCParameter(nsJSRuntime::sRuntime, JSGC_MAX_MALLOC_BYTES,
3641 highwatermark * 1024L * 1024L);
3642 return 0;
3645 static int
3646 SetMemoryMaxPrefChangedCallback(const char* aPrefName, void* aClosure)
3648 PRInt32 pref = nsContentUtils::GetIntPref(aPrefName, -1);
3649 // handle overflow and negative pref values
3650 PRUint32 max = (pref <= 0 || pref >= 0x1000) ? -1 : (PRUint32)pref * 1024 * 1024;
3651 JS_SetGCParameter(nsJSRuntime::sRuntime, JSGC_MAX_BYTES, max);
3652 return 0;
3655 static int
3656 SetMemoryGCFrequencyPrefChangedCallback(const char* aPrefName, void* aClosure)
3658 PRInt32 triggerFactor = nsContentUtils::GetIntPref(aPrefName, 300);
3659 JS_SetGCParameter(nsJSRuntime::sRuntime, JSGC_TRIGGER_FACTOR, triggerFactor);
3660 return 0;
3663 static int
3664 SetMemoryGCModePrefChangedCallback(const char* aPrefName, void* aClosure)
3666 PRBool enableCompartmentGC = nsContentUtils::GetBoolPref(aPrefName);
3667 JS_SetGCParameter(nsJSRuntime::sRuntime, JSGC_MODE, enableCompartmentGC
3668 ? JSGC_MODE_COMPARTMENT
3669 : JSGC_MODE_GLOBAL);
3670 return 0;
3673 static JSPrincipals *
3674 ObjectPrincipalFinder(JSContext *cx, JSObject *obj)
3676 if (!sSecurityManager)
3677 return nsnull;
3679 nsCOMPtr<nsIPrincipal> principal;
3680 nsresult rv =
3681 sSecurityManager->GetObjectPrincipal(cx, obj,
3682 getter_AddRefs(principal));
3684 if (NS_FAILED(rv) || !principal) {
3685 return nsnull;
3688 JSPrincipals *jsPrincipals = nsnull;
3689 principal->GetJSPrincipals(cx, &jsPrincipals);
3691 // nsIPrincipal::GetJSPrincipals() returns a strong reference to the
3692 // JS principals, but the caller of this function expects a weak
3693 // reference. So we need to release here.
3695 JSPRINCIPALS_DROP(cx, jsPrincipals);
3697 return jsPrincipals;
3700 static JSObject*
3701 DOMReadStructuredClone(JSContext* cx,
3702 JSStructuredCloneReader* reader,
3703 uint32 tag,
3704 uint32 data,
3705 void* closure)
3707 // We don't currently support any extensions to structured cloning.
3708 nsDOMClassInfo::ThrowJSException(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
3709 return nsnull;
3712 static JSBool
3713 DOMWriteStructuredClone(JSContext* cx,
3714 JSStructuredCloneWriter* writer,
3715 JSObject* obj,
3716 void *closure)
3718 // We don't currently support any extensions to structured cloning.
3719 nsDOMClassInfo::ThrowJSException(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
3720 return JS_FALSE;
3723 static void
3724 DOMStructuredCloneError(JSContext* cx,
3725 uint32 errorid)
3727 // We don't currently support any extensions to structured cloning.
3728 nsDOMClassInfo::ThrowJSException(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
3731 //static
3732 nsresult
3733 nsJSRuntime::Init()
3735 if (sIsInitialized) {
3736 if (!nsContentUtils::XPConnect())
3737 return NS_ERROR_NOT_AVAILABLE;
3739 return NS_OK;
3742 nsresult rv = CallGetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID,
3743 &sSecurityManager);
3744 NS_ENSURE_SUCCESS(rv, rv);
3746 rv = CallGetService(kJSRuntimeServiceContractID, &sRuntimeService);
3747 // get the JSRuntime from the runtime svc, if possible
3748 NS_ENSURE_SUCCESS(rv, rv);
3750 rv = sRuntimeService->GetRuntime(&sRuntime);
3751 NS_ENSURE_SUCCESS(rv, rv);
3753 // Let's make sure that our main thread is the same as the xpcom main thread.
3754 NS_ASSERTION(NS_IsMainThread(), "bad");
3756 NS_ASSERTION(!gOldJSGCCallback,
3757 "nsJSRuntime initialized more than once");
3759 // Save the old GC callback to chain to it, for GC-observing generality.
3760 gOldJSGCCallback = ::JS_SetGCCallbackRT(sRuntime, DOMGCCallback);
3762 JSSecurityCallbacks *callbacks = JS_GetRuntimeSecurityCallbacks(sRuntime);
3763 NS_ASSERTION(callbacks, "SecMan should have set security callbacks!");
3765 callbacks->findObjectPrincipals = ObjectPrincipalFinder;
3767 // Set up the structured clone callbacks.
3768 static JSStructuredCloneCallbacks cloneCallbacks = {
3769 DOMReadStructuredClone,
3770 DOMWriteStructuredClone,
3771 DOMStructuredCloneError
3773 JS_SetStructuredCloneCallbacks(sRuntime, &cloneCallbacks);
3775 // Set these global xpconnect options...
3776 nsContentUtils::RegisterPrefCallback("dom.max_script_run_time",
3777 MaxScriptRunTimePrefChangedCallback,
3778 nsnull);
3779 MaxScriptRunTimePrefChangedCallback("dom.max_script_run_time", nsnull);
3781 nsContentUtils::RegisterPrefCallback("dom.max_chrome_script_run_time",
3782 MaxScriptRunTimePrefChangedCallback,
3783 nsnull);
3784 MaxScriptRunTimePrefChangedCallback("dom.max_chrome_script_run_time",
3785 nsnull);
3787 nsContentUtils::RegisterPrefCallback("dom.report_all_js_exceptions",
3788 ReportAllJSExceptionsPrefChangedCallback,
3789 nsnull);
3790 ReportAllJSExceptionsPrefChangedCallback("dom.report_all_js_exceptions",
3791 nsnull);
3793 nsContentUtils::RegisterPrefCallback("javascript.options.mem.high_water_mark",
3794 SetMemoryHighWaterMarkPrefChangedCallback,
3795 nsnull);
3796 SetMemoryHighWaterMarkPrefChangedCallback("javascript.options.mem.high_water_mark",
3797 nsnull);
3799 nsContentUtils::RegisterPrefCallback("javascript.options.mem.max",
3800 SetMemoryMaxPrefChangedCallback,
3801 nsnull);
3802 SetMemoryMaxPrefChangedCallback("javascript.options.mem.max",
3803 nsnull);
3805 nsContentUtils::RegisterPrefCallback("javascript.options.mem.gc_frequency",
3806 SetMemoryGCFrequencyPrefChangedCallback,
3807 nsnull);
3808 SetMemoryGCFrequencyPrefChangedCallback("javascript.options.mem.gc_frequency",
3809 nsnull);
3811 nsContentUtils::RegisterPrefCallback("javascript.options.mem.gc_per_compartment",
3812 SetMemoryGCModePrefChangedCallback,
3813 nsnull);
3814 SetMemoryGCModePrefChangedCallback("javascript.options.mem.gc_per_compartment",
3815 nsnull);
3817 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
3818 if (!obs)
3819 return NS_ERROR_FAILURE;
3821 nsIObserver* memPressureObserver = new nsMemoryPressureObserver();
3822 NS_ENSURE_TRUE(memPressureObserver, NS_ERROR_OUT_OF_MEMORY);
3823 obs->AddObserver(memPressureObserver, "memory-pressure", PR_FALSE);
3825 sIsInitialized = PR_TRUE;
3827 return NS_OK;
3830 //static
3831 nsScriptNameSpaceManager*
3832 nsJSRuntime::GetNameSpaceManager()
3834 if (sDidShutdown)
3835 return nsnull;
3837 if (!gNameSpaceManager) {
3838 gNameSpaceManager = new nsScriptNameSpaceManager;
3839 NS_ADDREF(gNameSpaceManager);
3841 nsresult rv = gNameSpaceManager->Init();
3842 NS_ENSURE_SUCCESS(rv, nsnull);
3845 return gNameSpaceManager;
3848 /* static */
3849 void
3850 nsJSRuntime::Shutdown()
3852 nsJSContext::KillGCTimer();
3853 nsJSContext::KillCCTimer();
3855 NS_IF_RELEASE(gNameSpaceManager);
3857 if (!sContextCount) {
3858 // We're being shutdown, and there are no more contexts
3859 // alive, release the JS runtime service and the security manager.
3861 if (sRuntimeService && sSecurityManager) {
3862 JSSecurityCallbacks *callbacks = JS_GetRuntimeSecurityCallbacks(sRuntime);
3863 if (callbacks) {
3864 NS_ASSERTION(callbacks->findObjectPrincipals == ObjectPrincipalFinder,
3865 "Fighting over the findObjectPrincipals callback!");
3866 callbacks->findObjectPrincipals = NULL;
3869 NS_IF_RELEASE(sRuntimeService);
3870 NS_IF_RELEASE(sSecurityManager);
3873 sDidShutdown = PR_TRUE;
3876 // Script object mananagement - note duplicate implementation
3877 // in nsJSContext above...
3878 nsresult
3879 nsJSRuntime::HoldScriptObject(void* aScriptObject)
3881 NS_ASSERTION(sIsInitialized, "runtime not initialized");
3882 if (! sRuntime) {
3883 NS_NOTREACHED("couldn't remove GC root - no runtime");
3884 return NS_ERROR_FAILURE;
3887 ::JS_LockGCThingRT(sRuntime, aScriptObject);
3888 return NS_OK;
3891 nsresult
3892 nsJSRuntime::DropScriptObject(void* aScriptObject)
3894 NS_ASSERTION(sIsInitialized, "runtime not initialized");
3895 if (! sRuntime) {
3896 NS_NOTREACHED("couldn't remove GC root");
3897 return NS_ERROR_FAILURE;
3900 ::JS_UnlockGCThingRT(sRuntime, aScriptObject);
3901 return NS_OK;
3904 // A factory for the runtime.
3905 nsresult NS_CreateJSRuntime(nsIScriptRuntime **aRuntime)
3907 nsresult rv = nsJSRuntime::Init();
3908 NS_ENSURE_SUCCESS(rv, rv);
3910 *aRuntime = new nsJSRuntime();
3911 if (*aRuntime == nsnull)
3912 return NS_ERROR_OUT_OF_MEMORY;
3913 NS_IF_ADDREF(*aRuntime);
3914 return NS_OK;
3917 // A fast-array class for JS. This class supports both nsIJSScriptArray and
3918 // nsIArray. If it is JS itself providing and consuming this class, all work
3919 // can be done via nsIJSScriptArray, and avoid the conversion of elements
3920 // to/from nsISupports.
3921 // When consumed by non-JS (eg, another script language), conversion is done
3922 // on-the-fly.
3923 class nsJSArgArray : public nsIJSArgArray, public nsIArray {
3924 public:
3925 nsJSArgArray(JSContext *aContext, PRUint32 argc, jsval *argv, nsresult *prv);
3926 ~nsJSArgArray();
3927 // nsISupports
3928 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
3929 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsJSArgArray,
3930 nsIJSArgArray)
3932 // nsIArray
3933 NS_DECL_NSIARRAY
3935 // nsIJSArgArray
3936 nsresult GetArgs(PRUint32 *argc, void **argv);
3938 void ReleaseJSObjects();
3940 protected:
3941 JSContext *mContext;
3942 jsval *mArgv;
3943 PRUint32 mArgc;
3946 nsJSArgArray::nsJSArgArray(JSContext *aContext, PRUint32 argc, jsval *argv,
3947 nsresult *prv) :
3948 mContext(aContext),
3949 mArgv(nsnull),
3950 mArgc(argc)
3952 // copy the array - we don't know its lifetime, and ours is tied to xpcom
3953 // refcounting. Alloc zero'd array so cleanup etc is safe.
3954 if (argc) {
3955 mArgv = (jsval *) PR_CALLOC(argc * sizeof(jsval));
3956 if (!mArgv) {
3957 *prv = NS_ERROR_OUT_OF_MEMORY;
3958 return;
3962 // Callers are allowed to pass in a null argv even for argc > 0. They can
3963 // then use GetArgs to initialize the values.
3964 if (argv) {
3965 for (PRUint32 i = 0; i < argc; ++i)
3966 mArgv[i] = argv[i];
3969 *prv = argc > 0 ? NS_HOLD_JS_OBJECTS(this, nsJSArgArray) : NS_OK;
3972 nsJSArgArray::~nsJSArgArray()
3974 ReleaseJSObjects();
3977 void
3978 nsJSArgArray::ReleaseJSObjects()
3980 if (mArgc > 0)
3981 NS_DROP_JS_OBJECTS(this, nsJSArgArray);
3982 if (mArgv) {
3983 PR_DELETE(mArgv);
3985 mArgc = 0;
3988 // QueryInterface implementation for nsJSArgArray
3989 NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSArgArray)
3990 NS_IMPL_CYCLE_COLLECTION_ROOT_BEGIN(nsJSArgArray)
3991 tmp->ReleaseJSObjects();
3992 NS_IMPL_CYCLE_COLLECTION_ROOT_END
3993 NS_IMPL_CYCLE_COLLECTION_UNLINK_0(nsJSArgArray)
3994 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsJSArgArray)
3995 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
3996 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
3998 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSArgArray)
3999 jsval *argv = tmp->mArgv;
4000 if (argv) {
4001 jsval *end;
4002 for (end = argv + tmp->mArgc; argv < end; ++argv) {
4003 if (JSVAL_IS_GCTHING(*argv))
4004 NS_IMPL_CYCLE_COLLECTION_TRACE_CALLBACK(JAVASCRIPT,
4005 JSVAL_TO_GCTHING(*argv))
4008 NS_IMPL_CYCLE_COLLECTION_TRACE_END
4010 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSArgArray)
4011 NS_INTERFACE_MAP_ENTRY(nsIArray)
4012 NS_INTERFACE_MAP_ENTRY(nsIJSArgArray)
4013 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIJSArgArray)
4014 NS_INTERFACE_MAP_END
4016 NS_IMPL_CYCLE_COLLECTING_ADDREF_AMBIGUOUS(nsJSArgArray, nsIJSArgArray)
4017 NS_IMPL_CYCLE_COLLECTING_RELEASE_AMBIGUOUS(nsJSArgArray, nsIJSArgArray)
4019 nsresult
4020 nsJSArgArray::GetArgs(PRUint32 *argc, void **argv)
4022 if (!mArgv) {
4023 NS_WARNING("nsJSArgArray has no argv!");
4024 return NS_ERROR_UNEXPECTED;
4026 *argv = (void *)mArgv;
4027 *argc = mArgc;
4028 return NS_OK;
4031 // nsIArray impl
4032 NS_IMETHODIMP nsJSArgArray::GetLength(PRUint32 *aLength)
4034 *aLength = mArgc;
4035 return NS_OK;
4038 /* void queryElementAt (in unsigned long index, in nsIIDRef uuid, [iid_is (uuid), retval] out nsQIResult result); */
4039 NS_IMETHODIMP nsJSArgArray::QueryElementAt(PRUint32 index, const nsIID & uuid, void * *result)
4041 *result = nsnull;
4042 if (index >= mArgc)
4043 return NS_ERROR_INVALID_ARG;
4045 if (uuid.Equals(NS_GET_IID(nsIVariant)) || uuid.Equals(NS_GET_IID(nsISupports))) {
4046 return nsContentUtils::XPConnect()->JSToVariant(mContext, mArgv[index],
4047 (nsIVariant **)result);
4049 NS_WARNING("nsJSArgArray only handles nsIVariant");
4050 return NS_ERROR_NO_INTERFACE;
4053 /* unsigned long indexOf (in unsigned long startIndex, in nsISupports element); */
4054 NS_IMETHODIMP nsJSArgArray::IndexOf(PRUint32 startIndex, nsISupports *element, PRUint32 *_retval)
4056 return NS_ERROR_NOT_IMPLEMENTED;
4059 /* nsISimpleEnumerator enumerate (); */
4060 NS_IMETHODIMP nsJSArgArray::Enumerate(nsISimpleEnumerator **_retval)
4062 return NS_ERROR_NOT_IMPLEMENTED;
4065 // The factory function
4066 nsresult NS_CreateJSArgv(JSContext *aContext, PRUint32 argc, void *argv,
4067 nsIArray **aArray)
4069 nsresult rv;
4070 nsJSArgArray *ret = new nsJSArgArray(aContext, argc,
4071 static_cast<jsval *>(argv), &rv);
4072 if (ret == nsnull)
4073 return NS_ERROR_OUT_OF_MEMORY;
4074 if (NS_FAILED(rv)) {
4075 delete ret;
4076 return rv;
4078 return ret->QueryInterface(NS_GET_IID(nsIArray), (void **)aArray);