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
5 : *
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/
10 : *
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.
15 : *
16 : * The Original Code is mozilla.org code.
17 : *
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.
22 : *
23 : * Contributor(s):
24 : * Mark Hammond <mhammond@skippinet.com.au>
25 : *
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.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : #include "nsJSEnvironment.h"
41 : #include "nsIScriptGlobalObject.h"
42 : #include "nsIScriptObjectPrincipal.h"
43 : #include "nsIDOMChromeWindow.h"
44 : #include "nsPIDOMWindow.h"
45 : #include "nsIScriptSecurityManager.h"
46 : #include "nsDOMCID.h"
47 : #include "nsIServiceManager.h"
48 : #include "nsIXPConnect.h"
49 : #include "nsIJSContextStack.h"
50 : #include "nsIJSRuntimeService.h"
51 : #include "nsCOMPtr.h"
52 : #include "nsISupportsPrimitives.h"
53 : #include "nsReadableUtils.h"
54 : #include "nsJSUtils.h"
55 : #include "nsIDocShell.h"
56 : #include "nsIDocShellTreeItem.h"
57 : #include "nsPresContext.h"
58 : #include "nsIConsoleService.h"
59 : #include "nsIScriptError.h"
60 : #include "nsIInterfaceRequestor.h"
61 : #include "nsIInterfaceRequestorUtils.h"
62 : #include "nsIPrompt.h"
63 : #include "nsIObserverService.h"
64 : #include "nsGUIEvent.h"
65 : #include "nsThreadUtils.h"
66 : #include "nsITimer.h"
67 : #include "nsIAtom.h"
68 : #include "nsContentUtils.h"
69 : #include "nsEventDispatcher.h"
70 : #include "nsIContent.h"
71 : #include "nsCycleCollector.h"
72 : #include "nsNetUtil.h"
73 : #include "nsXPCOMCIDInternal.h"
74 : #include "nsIXULRuntime.h"
75 :
76 : #include "nsDOMClassInfo.h"
77 : #include "xpcpublic.h"
78 :
79 : #include "jsdbgapi.h" // for JS_ClearWatchPointsForObject
80 : #include "jswrapper.h"
81 : #include "jsxdrapi.h"
82 : #include "nsIArray.h"
83 : #include "nsIObjectInputStream.h"
84 : #include "nsIObjectOutputStream.h"
85 : #include "nsDOMScriptObjectHolder.h"
86 : #include "prmem.h"
87 : #include "WrapperFactory.h"
88 : #include "nsGlobalWindow.h"
89 : #include "nsScriptNameSpaceManager.h"
90 :
91 : #include "nsJSPrincipals.h"
92 :
93 : #ifdef XP_MACOSX
94 : // AssertMacros.h defines 'check' and conflicts with AccessCheck.h
95 : #undef check
96 : #endif
97 : #include "AccessCheck.h"
98 :
99 : #ifdef MOZ_JSDEBUGGER
100 : #include "jsdIDebuggerService.h"
101 : #endif
102 : #ifdef MOZ_LOGGING
103 : // Force PR_LOGGING so we can get JS strict warnings even in release builds
104 : #define FORCE_PR_LOG 1
105 : #endif
106 : #include "prlog.h"
107 : #include "prthread.h"
108 :
109 : #include "mozilla/FunctionTimer.h"
110 : #include "mozilla/Preferences.h"
111 : #include "mozilla/Telemetry.h"
112 :
113 : #include "sampler.h"
114 :
115 : using namespace mozilla;
116 :
117 : const size_t gStackSize = 8192;
118 :
119 : #ifdef PR_LOGGING
120 : static PRLogModuleInfo* gJSDiagnostics;
121 : #endif
122 :
123 : // Thank you Microsoft!
124 : #ifdef CompareString
125 : #undef CompareString
126 : #endif
127 :
128 : #define NS_SHRINK_GC_BUFFERS_DELAY 4000 // ms
129 :
130 : // The amount of time we wait from the first request to GC to actually
131 : // doing the first GC.
132 : #define NS_FIRST_GC_DELAY 10000 // ms
133 :
134 : // Maximum amount of time that should elapse between incremental GC slices
135 : #define NS_INTERSLICE_GC_DELAY 100 // ms
136 :
137 : // The amount of time we wait between a request to CC (after GC ran)
138 : // and doing the actual CC.
139 : #define NS_CC_DELAY 6000 // ms
140 :
141 : #define NS_CC_SKIPPABLE_DELAY 400 // ms
142 :
143 : // Force a CC after this long if there's anything in the purple buffer.
144 : #define NS_CC_FORCED (2 * 60 * PR_USEC_PER_SEC) // 2 min
145 :
146 : // Don't allow an incremental GC to lock out the CC for too long.
147 : #define NS_MAX_CC_LOCKEDOUT_TIME (5 * PR_USEC_PER_SEC) // 5 seconds
148 :
149 : // Trigger a CC if the purple buffer exceeds this size when we check it.
150 : #define NS_CC_PURPLE_LIMIT 250
151 :
152 : #define JAVASCRIPT nsIProgrammingLanguage::JAVASCRIPT
153 :
154 : // if you add statics here, add them to the list in nsJSRuntime::Startup
155 :
156 : static nsITimer *sGCTimer;
157 : static nsITimer *sShrinkGCBuffersTimer;
158 : static nsITimer *sCCTimer;
159 :
160 : static PRTime sLastCCEndTime;
161 :
162 : static bool sCCLockedOut;
163 : static PRTime sCCLockedOutTime;
164 :
165 : static js::GCSliceCallback sPrevGCSliceCallback;
166 :
167 : // The number of currently pending document loads. This count isn't
168 : // guaranteed to always reflect reality and can't easily as we don't
169 : // have an easy place to know when a load ends or is interrupted in
170 : // all cases. This counter also gets reset if we end up GC'ing while
171 : // we're waiting for a slow page to load. IOW, this count may be 0
172 : // even when there are pending loads.
173 : static PRUint32 sPendingLoadCount;
174 : static bool sLoadingInProgress;
175 :
176 : static PRUint32 sCCollectedWaitingForGC;
177 : static bool sPostGCEventsToConsole;
178 : static PRUint32 sCCTimerFireCount = 0;
179 : static PRUint32 sMinForgetSkippableTime = PR_UINT32_MAX;
180 : static PRUint32 sMaxForgetSkippableTime = 0;
181 : static PRUint32 sTotalForgetSkippableTime = 0;
182 : static PRUint32 sRemovedPurples = 0;
183 : static PRUint32 sForgetSkippableBeforeCC = 0;
184 : static PRUint32 sPreviousSuspectedCount = 0;
185 :
186 : static bool sCleanupSinceLastGC = true;
187 : static bool sNeedsFullCC = false;
188 :
189 : nsScriptNameSpaceManager *gNameSpaceManager;
190 :
191 : static nsIJSRuntimeService *sRuntimeService;
192 : JSRuntime *nsJSRuntime::sRuntime;
193 :
194 : static const char kJSRuntimeServiceContractID[] =
195 : "@mozilla.org/js/xpc/RuntimeService;1";
196 :
197 : static PRTime sFirstCollectionTime;
198 :
199 : static bool sIsInitialized;
200 : static bool sDidShutdown;
201 :
202 : static PRInt32 sContextCount;
203 :
204 : static PRTime sMaxScriptRunTime;
205 : static PRTime sMaxChromeScriptRunTime;
206 :
207 : static nsIScriptSecurityManager *sSecurityManager;
208 :
209 : // nsMemoryPressureObserver observes the memory-pressure notifications
210 : // and forces a garbage collection and cycle collection when it happens, if
211 : // the appropriate pref is set.
212 :
213 : static bool sGCOnMemoryPressure;
214 :
215 : class nsMemoryPressureObserver : public nsIObserver
216 50 : {
217 : public:
218 : NS_DECL_ISUPPORTS
219 : NS_DECL_NSIOBSERVER
220 : };
221 :
222 150 : NS_IMPL_ISUPPORTS1(nsMemoryPressureObserver, nsIObserver)
223 :
224 : NS_IMETHODIMP
225 0 : nsMemoryPressureObserver::Observe(nsISupports* aSubject, const char* aTopic,
226 : const PRUnichar* aData)
227 : {
228 0 : if (sGCOnMemoryPressure) {
229 0 : nsJSContext::GarbageCollectNow(js::gcreason::MEM_PRESSURE, nsGCShrinking);
230 0 : nsJSContext::CycleCollectNow();
231 : }
232 0 : return NS_OK;
233 : }
234 :
235 0 : class nsRootedJSValueArray {
236 : public:
237 0 : explicit nsRootedJSValueArray(JSContext *cx) : avr(cx, vals.Length(), vals.Elements()) {}
238 :
239 0 : bool SetCapacity(JSContext *cx, size_t capacity) {
240 0 : bool ok = vals.SetCapacity(capacity);
241 0 : if (!ok)
242 0 : return false;
243 : // Values must be safe for the GC to inspect (they must not contain garbage).
244 0 : memset(vals.Elements(), 0, vals.Capacity() * sizeof(jsval));
245 0 : resetRooter(cx);
246 0 : return true;
247 : }
248 :
249 0 : jsval *Elements() {
250 0 : return vals.Elements();
251 : }
252 :
253 : private:
254 0 : void resetRooter(JSContext *cx) {
255 0 : avr.changeArray(vals.Elements(), vals.Length());
256 0 : }
257 :
258 : nsAutoTArray<jsval, 16> vals;
259 : JS::AutoArrayRooter avr;
260 : };
261 :
262 : /****************************************************************
263 : ************************** AutoFree ****************************
264 : ****************************************************************/
265 :
266 : class AutoFree {
267 : public:
268 0 : AutoFree(void *aPtr) : mPtr(aPtr) {
269 0 : }
270 0 : ~AutoFree() {
271 0 : if (mPtr)
272 0 : nsMemory::Free(mPtr);
273 0 : }
274 : void Invalidate() {
275 : mPtr = 0;
276 : }
277 : private:
278 : void *mPtr;
279 : };
280 :
281 : // A utility function for script languages to call. Although it looks small,
282 : // the use of nsIDocShell and nsPresContext triggers a huge number of
283 : // dependencies that most languages would not otherwise need.
284 : // XXXmarkh - This function is mis-placed!
285 : bool
286 0 : NS_HandleScriptError(nsIScriptGlobalObject *aScriptGlobal,
287 : nsScriptErrorEvent *aErrorEvent,
288 : nsEventStatus *aStatus)
289 : {
290 0 : bool called = false;
291 0 : nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(aScriptGlobal));
292 0 : nsIDocShell *docShell = win ? win->GetDocShell() : nsnull;
293 0 : if (docShell) {
294 0 : nsRefPtr<nsPresContext> presContext;
295 0 : docShell->GetPresContext(getter_AddRefs(presContext));
296 :
297 : static PRInt32 errorDepth; // Recursion prevention
298 0 : ++errorDepth;
299 :
300 0 : if (presContext && errorDepth < 2) {
301 : // Dispatch() must be synchronous for the recursion block
302 : // (errorDepth) to work.
303 : nsEventDispatcher::Dispatch(win, presContext, aErrorEvent, nsnull,
304 0 : aStatus);
305 0 : called = true;
306 : }
307 0 : --errorDepth;
308 : }
309 0 : return called;
310 : }
311 :
312 : class ScriptErrorEvent : public nsRunnable
313 0 : {
314 : public:
315 0 : ScriptErrorEvent(nsIScriptGlobalObject* aScriptGlobal,
316 : nsIPrincipal* aScriptOriginPrincipal,
317 : PRUint32 aLineNr, PRUint32 aColumn, PRUint32 aFlags,
318 : const nsAString& aErrorMsg,
319 : const nsAString& aFileName,
320 : const nsAString& aSourceLine,
321 : bool aDispatchEvent,
322 : PRUint64 aInnerWindowID)
323 : : mScriptGlobal(aScriptGlobal), mOriginPrincipal(aScriptOriginPrincipal),
324 : mLineNr(aLineNr), mColumn(aColumn),
325 : mFlags(aFlags), mErrorMsg(aErrorMsg), mFileName(aFileName),
326 : mSourceLine(aSourceLine), mDispatchEvent(aDispatchEvent),
327 0 : mInnerWindowID(aInnerWindowID)
328 0 : {}
329 :
330 0 : NS_IMETHOD Run()
331 : {
332 0 : nsEventStatus status = nsEventStatus_eIgnore;
333 : // First, notify the DOM that we have a script error.
334 0 : if (mDispatchEvent) {
335 0 : nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(mScriptGlobal));
336 0 : nsIDocShell* docShell = win ? win->GetDocShell() : nsnull;
337 0 : if (docShell &&
338 0 : !JSREPORT_IS_WARNING(mFlags) &&
339 0 : !sHandlingScriptError) {
340 0 : sHandlingScriptError = true; // Recursion prevention
341 :
342 0 : nsRefPtr<nsPresContext> presContext;
343 0 : docShell->GetPresContext(getter_AddRefs(presContext));
344 :
345 0 : if (presContext) {
346 0 : nsScriptErrorEvent errorevent(true, NS_LOAD_ERROR);
347 :
348 0 : errorevent.fileName = mFileName.get();
349 :
350 0 : nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(win));
351 0 : NS_ENSURE_STATE(sop);
352 0 : nsIPrincipal* p = sop->GetPrincipal();
353 0 : NS_ENSURE_STATE(p);
354 :
355 0 : bool sameOrigin = !mOriginPrincipal;
356 :
357 0 : if (p && !sameOrigin) {
358 0 : if (NS_FAILED(p->Subsumes(mOriginPrincipal, &sameOrigin))) {
359 0 : sameOrigin = false;
360 : }
361 : }
362 :
363 0 : NS_NAMED_LITERAL_STRING(xoriginMsg, "Script error.");
364 0 : if (sameOrigin) {
365 0 : errorevent.errorMsg = mErrorMsg.get();
366 0 : errorevent.lineNr = mLineNr;
367 : } else {
368 0 : NS_WARNING("Not same origin error!");
369 0 : errorevent.errorMsg = xoriginMsg.get();
370 0 : errorevent.lineNr = 0;
371 : }
372 :
373 : nsEventDispatcher::Dispatch(win, presContext, &errorevent, nsnull,
374 0 : &status);
375 : }
376 :
377 0 : sHandlingScriptError = false;
378 : }
379 : }
380 :
381 0 : if (status != nsEventStatus_eConsumeNoDefault) {
382 : // Make an nsIScriptError and populate it with information from
383 : // this error.
384 : nsCOMPtr<nsIScriptError> errorObject =
385 0 : do_CreateInstance("@mozilla.org/scripterror;1");
386 :
387 0 : if (errorObject != nsnull) {
388 0 : nsresult rv = NS_ERROR_NOT_AVAILABLE;
389 :
390 : // Set category to chrome or content
391 : nsCOMPtr<nsIScriptObjectPrincipal> scriptPrincipal =
392 0 : do_QueryInterface(mScriptGlobal);
393 0 : NS_ASSERTION(scriptPrincipal, "Global objects must implement "
394 : "nsIScriptObjectPrincipal");
395 0 : nsCOMPtr<nsIPrincipal> systemPrincipal;
396 0 : sSecurityManager->GetSystemPrincipal(getter_AddRefs(systemPrincipal));
397 : const char * category =
398 0 : scriptPrincipal->GetPrincipal() == systemPrincipal
399 : ? "chrome javascript"
400 0 : : "content javascript";
401 :
402 0 : rv = errorObject->InitWithWindowID(mErrorMsg.get(), mFileName.get(),
403 : mSourceLine.get(),
404 : mLineNr, mColumn, mFlags,
405 0 : category, mInnerWindowID);
406 :
407 0 : if (NS_SUCCEEDED(rv)) {
408 : nsCOMPtr<nsIConsoleService> consoleService =
409 0 : do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv);
410 0 : if (NS_SUCCEEDED(rv)) {
411 0 : consoleService->LogMessage(errorObject);
412 : }
413 : }
414 : }
415 : }
416 0 : return NS_OK;
417 : }
418 :
419 :
420 : nsCOMPtr<nsIScriptGlobalObject> mScriptGlobal;
421 : nsCOMPtr<nsIPrincipal> mOriginPrincipal;
422 : PRUint32 mLineNr;
423 : PRUint32 mColumn;
424 : PRUint32 mFlags;
425 : nsString mErrorMsg;
426 : nsString mFileName;
427 : nsString mSourceLine;
428 : bool mDispatchEvent;
429 : PRUint64 mInnerWindowID;
430 :
431 : static bool sHandlingScriptError;
432 : };
433 :
434 : bool ScriptErrorEvent::sHandlingScriptError = false;
435 :
436 : // NOTE: This function could be refactored to use the above. The only reason
437 : // it has not been done is that the code below only fills the error event
438 : // after it has a good nsPresContext - whereas using the above function
439 : // would involve always filling it. Is that a concern?
440 : void
441 0 : NS_ScriptErrorReporter(JSContext *cx,
442 : const char *message,
443 : JSErrorReport *report)
444 : {
445 : // We don't want to report exceptions too eagerly, but warnings in the
446 : // absence of werror are swallowed whole, so report those now.
447 0 : if (!JSREPORT_IS_WARNING(report->flags)) {
448 0 : JSStackFrame * fp = nsnull;
449 0 : while ((fp = JS_FrameIterator(cx, &fp))) {
450 0 : if (JS_IsScriptFrame(cx, fp)) {
451 0 : return;
452 : }
453 : }
454 :
455 0 : nsIXPConnect* xpc = nsContentUtils::XPConnect();
456 0 : if (xpc) {
457 0 : nsAXPCNativeCallContext *cc = nsnull;
458 0 : xpc->GetCurrentNativeCallContext(&cc);
459 0 : if (cc) {
460 0 : nsAXPCNativeCallContext *prev = cc;
461 0 : while (NS_SUCCEEDED(prev->GetPreviousCallContext(&prev)) && prev) {
462 : PRUint16 lang;
463 0 : if (NS_SUCCEEDED(prev->GetLanguage(&lang)) &&
464 : lang == nsAXPCNativeCallContext::LANG_JS) {
465 0 : return;
466 : }
467 : }
468 : }
469 : }
470 : }
471 :
472 : // XXX this means we are not going to get error reports on non DOM contexts
473 0 : nsIScriptContext *context = nsJSUtils::GetDynamicScriptContext(cx);
474 :
475 : // Note: we must do this before running any more code on cx (if cx is the
476 : // dynamic script context).
477 0 : ::JS_ClearPendingException(cx);
478 :
479 0 : if (context) {
480 0 : nsIScriptGlobalObject *globalObject = context->GetGlobalObject();
481 :
482 0 : if (globalObject) {
483 0 : nsAutoString fileName, msg;
484 0 : if (!report->filename) {
485 0 : fileName.SetIsVoid(true);
486 : } else {
487 0 : fileName.AssignWithConversion(report->filename);
488 : }
489 :
490 : const PRUnichar *m = reinterpret_cast<const PRUnichar*>
491 0 : (report->ucmessage);
492 0 : if (m) {
493 0 : msg.Assign(m);
494 : }
495 :
496 0 : if (msg.IsEmpty() && message) {
497 0 : msg.AssignWithConversion(message);
498 : }
499 :
500 :
501 : /* We do not try to report Out Of Memory via a dom
502 : * event because the dom event handler would encounter
503 : * an OOM exception trying to process the event, and
504 : * then we'd need to generate a new OOM event for that
505 : * new OOM instance -- this isn't pretty.
506 : */
507 0 : nsAutoString sourceLine;
508 0 : sourceLine.Assign(reinterpret_cast<const PRUnichar*>(report->uclinebuf));
509 0 : nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(globalObject);
510 0 : PRUint64 innerWindowID = 0;
511 0 : if (win) {
512 0 : nsCOMPtr<nsPIDOMWindow> innerWin = win->GetCurrentInnerWindow();
513 0 : if (innerWin) {
514 0 : innerWindowID = innerWin->WindowID();
515 : }
516 : }
517 : nsContentUtils::AddScriptRunner(
518 : new ScriptErrorEvent(globalObject,
519 0 : nsJSPrincipals::get(report->originPrincipals),
520 : report->lineno,
521 : report->uctokenptr - report->uclinebuf,
522 : report->flags, msg, fileName, sourceLine,
523 : report->errorNumber != JSMSG_OUT_OF_MEMORY,
524 0 : innerWindowID));
525 : }
526 : }
527 :
528 : #ifdef DEBUG
529 : // Print it to stderr as well, for the benefit of those invoking
530 : // mozilla with -console.
531 0 : nsCAutoString error;
532 0 : error.Assign("JavaScript ");
533 0 : if (JSREPORT_IS_STRICT(report->flags))
534 0 : error.Append("strict ");
535 0 : if (JSREPORT_IS_WARNING(report->flags))
536 0 : error.Append("warning: ");
537 : else
538 0 : error.Append("error: ");
539 0 : error.Append(report->filename);
540 0 : error.Append(", line ");
541 0 : error.AppendInt(report->lineno, 10);
542 0 : error.Append(": ");
543 0 : if (report->ucmessage) {
544 : AppendUTF16toUTF8(reinterpret_cast<const PRUnichar*>(report->ucmessage),
545 0 : error);
546 : } else {
547 0 : error.Append(message);
548 : }
549 :
550 0 : fprintf(stderr, "%s\n", error.get());
551 0 : fflush(stderr);
552 : #endif
553 :
554 : #ifdef PR_LOGGING
555 0 : if (!gJSDiagnostics)
556 0 : gJSDiagnostics = PR_NewLogModule("JSDiagnostics");
557 :
558 0 : if (gJSDiagnostics) {
559 0 : PR_LOG(gJSDiagnostics,
560 : JSREPORT_IS_WARNING(report->flags) ? PR_LOG_WARNING : PR_LOG_ERROR,
561 : ("file %s, line %u: %s\n%s%s",
562 : report->filename, report->lineno, message,
563 : report->linebuf ? report->linebuf : "",
564 : (report->linebuf &&
565 : report->linebuf[strlen(report->linebuf)-1] != '\n')
566 : ? "\n"
567 : : ""));
568 : }
569 : #endif
570 : }
571 :
572 : #ifdef DEBUG
573 : // A couple of useful functions to call when you're debugging.
574 : nsGlobalWindow *
575 0 : JSObject2Win(JSContext *cx, JSObject *obj)
576 : {
577 0 : nsIXPConnect *xpc = nsContentUtils::XPConnect();
578 0 : if (!xpc) {
579 0 : return nsnull;
580 : }
581 :
582 0 : nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
583 0 : xpc->GetWrappedNativeOfJSObject(cx, obj, getter_AddRefs(wrapper));
584 0 : if (wrapper) {
585 0 : nsCOMPtr<nsPIDOMWindow> win = do_QueryWrappedNative(wrapper);
586 0 : if (win) {
587 : return static_cast<nsGlobalWindow *>
588 0 : (static_cast<nsPIDOMWindow *>(win));
589 : }
590 : }
591 :
592 0 : return nsnull;
593 : }
594 :
595 : void
596 0 : PrintWinURI(nsGlobalWindow *win)
597 : {
598 0 : if (!win) {
599 0 : printf("No window passed in.\n");
600 0 : return;
601 : }
602 :
603 0 : nsCOMPtr<nsIDocument> doc = do_QueryInterface(win->GetExtantDocument());
604 0 : if (!doc) {
605 0 : printf("No document in the window.\n");
606 : return;
607 : }
608 :
609 0 : nsIURI *uri = doc->GetDocumentURI();
610 0 : if (!uri) {
611 0 : printf("Document doesn't have a URI.\n");
612 : return;
613 : }
614 :
615 0 : nsCAutoString spec;
616 0 : uri->GetSpec(spec);
617 0 : printf("%s\n", spec.get());
618 : }
619 :
620 : void
621 0 : PrintWinCodebase(nsGlobalWindow *win)
622 : {
623 0 : if (!win) {
624 0 : printf("No window passed in.\n");
625 0 : return;
626 : }
627 :
628 0 : nsIPrincipal *prin = win->GetPrincipal();
629 0 : if (!prin) {
630 0 : printf("Window doesn't have principals.\n");
631 0 : return;
632 : }
633 :
634 0 : nsCOMPtr<nsIURI> uri;
635 0 : prin->GetURI(getter_AddRefs(uri));
636 0 : if (!uri) {
637 0 : printf("No URI, maybe the system principal.\n");
638 : return;
639 : }
640 :
641 0 : nsCAutoString spec;
642 0 : uri->GetSpec(spec);
643 0 : printf("%s\n", spec.get());
644 : }
645 :
646 : void
647 0 : DumpString(const nsAString &str)
648 : {
649 0 : printf("%s\n", NS_ConvertUTF16toUTF8(str).get());
650 0 : }
651 : #endif
652 :
653 : static already_AddRefed<nsIPrompt>
654 0 : GetPromptFromContext(nsJSContext* ctx)
655 : {
656 0 : nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(ctx->GetGlobalObject()));
657 0 : NS_ENSURE_TRUE(win, nsnull);
658 :
659 0 : nsIDocShell *docShell = win->GetDocShell();
660 0 : NS_ENSURE_TRUE(docShell, nsnull);
661 :
662 0 : nsCOMPtr<nsIInterfaceRequestor> ireq(do_QueryInterface(docShell));
663 0 : NS_ENSURE_TRUE(ireq, nsnull);
664 :
665 : // Get the nsIPrompt interface from the docshell
666 : nsIPrompt* prompt;
667 0 : ireq->GetInterface(NS_GET_IID(nsIPrompt), (void**)&prompt);
668 0 : return prompt;
669 : }
670 :
671 : JSBool
672 0 : nsJSContext::DOMOperationCallback(JSContext *cx)
673 : {
674 : nsresult rv;
675 :
676 : // Get the native context
677 0 : nsJSContext *ctx = static_cast<nsJSContext *>(::JS_GetContextPrivate(cx));
678 :
679 0 : if (!ctx) {
680 : // Can happen; see bug 355811
681 0 : return JS_TRUE;
682 : }
683 :
684 : // XXX Save the operation callback time so we can restore it after the GC,
685 : // because GCing can cause JS to run on our context, causing our
686 : // ScriptEvaluated to be called, and clearing our operation callback time.
687 : // See bug 302333.
688 0 : PRTime callbackTime = ctx->mOperationCallbackTime;
689 0 : PRTime modalStateTime = ctx->mModalStateTime;
690 :
691 : // Now restore the callback time and count, in case they got reset.
692 0 : ctx->mOperationCallbackTime = callbackTime;
693 0 : ctx->mModalStateTime = modalStateTime;
694 :
695 0 : PRTime now = PR_Now();
696 :
697 0 : if (callbackTime == 0) {
698 : // Initialize mOperationCallbackTime to start timing how long the
699 : // script has run
700 0 : ctx->mOperationCallbackTime = now;
701 0 : return JS_TRUE;
702 : }
703 :
704 0 : if (ctx->mModalStateDepth) {
705 : // We're waiting on a modal dialog, nothing more to do here.
706 0 : return JS_TRUE;
707 : }
708 :
709 0 : PRTime duration = now - callbackTime;
710 :
711 : // Check the amount of time this script has been running, or if the
712 : // dialog is disabled.
713 0 : JSObject* global = ::JS_GetGlobalForScopeChain(cx);
714 : bool isTrackingChromeCodeTime =
715 0 : global && xpc::AccessCheck::isChrome(js::GetObjectCompartment(global));
716 0 : if (duration < (isTrackingChromeCodeTime ?
717 : sMaxChromeScriptRunTime : sMaxScriptRunTime)) {
718 0 : return JS_TRUE;
719 : }
720 :
721 0 : if (!nsContentUtils::IsSafeToRunScript()) {
722 : // If it isn't safe to run script, then it isn't safe to bring up the
723 : // prompt (since that will cause the event loop to spin). In this case
724 : // (which is rare), we just stop the script... But report a warning so
725 : // that developers have some idea of what went wrong.
726 :
727 0 : JS_ReportWarning(cx, "A long running script was terminated");
728 0 : return JS_FALSE;
729 : }
730 :
731 : // If we get here we're most likely executing an infinite loop in JS,
732 : // we'll tell the user about this and we'll give the user the option
733 : // of stopping the execution of the script.
734 0 : nsCOMPtr<nsIPrompt> prompt = GetPromptFromContext(ctx);
735 0 : NS_ENSURE_TRUE(prompt, JS_FALSE);
736 :
737 : // Check if we should offer the option to debug
738 : JSScript *script;
739 : unsigned lineno;
740 0 : JSBool hasFrame = ::JS_DescribeScriptedCaller(cx, &script, &lineno);
741 :
742 0 : bool debugPossible = hasFrame && js::CanCallContextDebugHandler(cx);
743 : #ifdef MOZ_JSDEBUGGER
744 : // Get the debugger service if necessary.
745 0 : if (debugPossible) {
746 0 : bool jsds_IsOn = false;
747 0 : const char jsdServiceCtrID[] = "@mozilla.org/js/jsd/debugger-service;1";
748 0 : nsCOMPtr<jsdIExecutionHook> jsdHook;
749 0 : nsCOMPtr<jsdIDebuggerService> jsds = do_GetService(jsdServiceCtrID, &rv);
750 :
751 : // Check if there's a user for the debugger service that's 'on' for us
752 0 : if (NS_SUCCEEDED(rv)) {
753 0 : jsds->GetDebuggerHook(getter_AddRefs(jsdHook));
754 0 : jsds->GetIsOn(&jsds_IsOn);
755 : }
756 :
757 : // If there is a debug handler registered for this runtime AND
758 : // ((jsd is on AND has a hook) OR (jsd isn't on (something else debugs)))
759 : // then something useful will be done with our request to debug.
760 0 : debugPossible = ((jsds_IsOn && (jsdHook != nsnull)) || !jsds_IsOn);
761 : }
762 : #endif
763 :
764 : // Get localizable strings
765 0 : nsXPIDLString title, msg, stopButton, waitButton, debugButton, neverShowDlg;
766 :
767 : rv = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
768 : "KillScriptTitle",
769 0 : title);
770 :
771 : rv |= nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
772 : "StopScriptButton",
773 0 : stopButton);
774 :
775 : rv |= nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
776 : "WaitForScriptButton",
777 0 : waitButton);
778 :
779 : rv |= nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
780 : "DontAskAgain",
781 0 : neverShowDlg);
782 :
783 :
784 0 : if (debugPossible) {
785 : rv |= nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
786 : "DebugScriptButton",
787 0 : debugButton);
788 :
789 : rv |= nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
790 : "KillScriptWithDebugMessage",
791 0 : msg);
792 : }
793 : else {
794 : rv |= nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
795 : "KillScriptMessage",
796 0 : msg);
797 : }
798 :
799 : //GetStringFromName can return NS_OK and still give NULL string
800 0 : if (NS_FAILED(rv) || !title || !msg || !stopButton || !waitButton ||
801 0 : (!debugButton && debugPossible) || !neverShowDlg) {
802 0 : NS_ERROR("Failed to get localized strings.");
803 0 : return JS_TRUE;
804 : }
805 :
806 : // Append file and line number information, if available
807 0 : if (script) {
808 0 : const char *filename = ::JS_GetScriptFilename(cx, script);
809 0 : if (filename) {
810 0 : nsXPIDLString scriptLocation;
811 0 : NS_ConvertUTF8toUTF16 filenameUTF16(filename);
812 0 : const PRUnichar *formatParams[] = { filenameUTF16.get() };
813 : rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES,
814 : "KillScriptLocation",
815 : formatParams,
816 0 : scriptLocation);
817 :
818 0 : if (NS_SUCCEEDED(rv) && scriptLocation) {
819 0 : msg.AppendLiteral("\n\n");
820 0 : msg.Append(scriptLocation);
821 0 : msg.Append(':');
822 0 : msg.AppendInt(lineno);
823 : }
824 : }
825 : }
826 :
827 0 : PRInt32 buttonPressed = 0; //In case user exits dialog by clicking X
828 0 : bool neverShowDlgChk = false;
829 : PRUint32 buttonFlags = nsIPrompt::BUTTON_POS_1_DEFAULT +
830 : (nsIPrompt::BUTTON_TITLE_IS_STRING *
831 0 : (nsIPrompt::BUTTON_POS_0 + nsIPrompt::BUTTON_POS_1));
832 :
833 : // Add a third button if necessary:
834 0 : if (debugPossible)
835 0 : buttonFlags += nsIPrompt::BUTTON_TITLE_IS_STRING * nsIPrompt::BUTTON_POS_2;
836 :
837 : // Null out the operation callback while we're re-entering JS here.
838 0 : ::JS_SetOperationCallback(cx, nsnull);
839 :
840 : // Open the dialog.
841 0 : rv = prompt->ConfirmEx(title, msg, buttonFlags, waitButton, stopButton,
842 : debugButton, neverShowDlg, &neverShowDlgChk,
843 0 : &buttonPressed);
844 :
845 0 : ::JS_SetOperationCallback(cx, DOMOperationCallback);
846 :
847 0 : if (NS_FAILED(rv) || (buttonPressed == 0)) {
848 : // Allow the script to continue running
849 :
850 0 : if (neverShowDlgChk) {
851 : Preferences::SetInt(isTrackingChromeCodeTime ?
852 0 : "dom.max_chrome_script_run_time" : "dom.max_script_run_time", 0);
853 : }
854 :
855 0 : ctx->mOperationCallbackTime = PR_Now();
856 0 : return JS_TRUE;
857 : }
858 0 : else if ((buttonPressed == 2) && debugPossible) {
859 0 : return js_CallContextDebugHandler(cx);
860 : }
861 :
862 0 : JS_ClearPendingException(cx);
863 0 : return JS_FALSE;
864 : }
865 :
866 : void
867 0 : nsJSContext::EnterModalState()
868 : {
869 0 : if (!mModalStateDepth) {
870 0 : mModalStateTime = mOperationCallbackTime ? PR_Now() : 0;
871 : }
872 0 : ++mModalStateDepth;
873 0 : }
874 :
875 : void
876 0 : nsJSContext::LeaveModalState()
877 : {
878 0 : if (!mModalStateDepth) {
879 0 : NS_ERROR("Uh, mismatched LeaveModalState() call!");
880 :
881 0 : return;
882 : }
883 :
884 0 : --mModalStateDepth;
885 :
886 : // If we're still in a modal dialog, or mOperationCallbackTime is still
887 : // uninitialized, do nothing.
888 0 : if (mModalStateDepth || !mOperationCallbackTime) {
889 0 : return;
890 : }
891 :
892 : // If mOperationCallbackTime was set when we entered the first dialog
893 : // (and mModalStateTime is thus non-zero), adjust mOperationCallbackTime
894 : // to account for time spent in the dialog.
895 : // If mOperationCallbackTime got set while the modal dialog was open,
896 : // simply set mOperationCallbackTime to the closing time of the dialog so
897 : // that we never adjust mOperationCallbackTime to be in the future.
898 0 : if (mModalStateTime) {
899 0 : mOperationCallbackTime += PR_Now() - mModalStateTime;
900 : }
901 : else {
902 0 : mOperationCallbackTime = PR_Now();
903 : }
904 : }
905 :
906 : #define JS_OPTIONS_DOT_STR "javascript.options."
907 :
908 : static const char js_options_dot_str[] = JS_OPTIONS_DOT_STR;
909 : static const char js_strict_option_str[] = JS_OPTIONS_DOT_STR "strict";
910 : #ifdef DEBUG
911 : static const char js_strict_debug_option_str[] = JS_OPTIONS_DOT_STR "strict.debug";
912 : #endif
913 : static const char js_werror_option_str[] = JS_OPTIONS_DOT_STR "werror";
914 : static const char js_relimit_option_str[]= JS_OPTIONS_DOT_STR "relimit";
915 : #ifdef JS_GC_ZEAL
916 : static const char js_zeal_option_str[] = JS_OPTIONS_DOT_STR "gczeal";
917 : static const char js_zeal_frequency_str[] = JS_OPTIONS_DOT_STR "gczeal.frequency";
918 : static const char js_zeal_compartment_str[] = JS_OPTIONS_DOT_STR "gczeal.compartment_gc";
919 : #endif
920 : static const char js_methodjit_content_str[] = JS_OPTIONS_DOT_STR "methodjit.content";
921 : static const char js_methodjit_chrome_str[] = JS_OPTIONS_DOT_STR "methodjit.chrome";
922 : static const char js_methodjit_always_str[] = JS_OPTIONS_DOT_STR "methodjit_always";
923 : static const char js_typeinfer_str[] = JS_OPTIONS_DOT_STR "typeinference";
924 : static const char js_pccounts_content_str[] = JS_OPTIONS_DOT_STR "pccounts.content";
925 : static const char js_pccounts_chrome_str[] = JS_OPTIONS_DOT_STR "pccounts.chrome";
926 : static const char js_jit_hardening_str[] = JS_OPTIONS_DOT_STR "jit_hardening";
927 : static const char js_memlog_option_str[] = JS_OPTIONS_DOT_STR "mem.log";
928 :
929 : int
930 0 : nsJSContext::JSOptionChangedCallback(const char *pref, void *data)
931 : {
932 0 : nsJSContext *context = reinterpret_cast<nsJSContext *>(data);
933 0 : PRUint32 oldDefaultJSOptions = context->mDefaultJSOptions;
934 0 : PRUint32 newDefaultJSOptions = oldDefaultJSOptions;
935 :
936 0 : sPostGCEventsToConsole = Preferences::GetBool(js_memlog_option_str);
937 :
938 0 : bool strict = Preferences::GetBool(js_strict_option_str);
939 0 : if (strict)
940 0 : newDefaultJSOptions |= JSOPTION_STRICT;
941 : else
942 0 : newDefaultJSOptions &= ~JSOPTION_STRICT;
943 :
944 0 : nsIScriptGlobalObject *global = context->GetGlobalObject();
945 : // XXX should we check for sysprin instead of a chrome window, to make
946 : // XXX components be covered by the chrome pref instead of the content one?
947 0 : nsCOMPtr<nsIDOMChromeWindow> chromeWindow(do_QueryInterface(global));
948 :
949 : bool useMethodJIT = Preferences::GetBool(chromeWindow ?
950 : js_methodjit_chrome_str :
951 0 : js_methodjit_content_str);
952 : bool usePCCounts = Preferences::GetBool(chromeWindow ?
953 : js_pccounts_chrome_str :
954 0 : js_pccounts_content_str);
955 0 : bool useMethodJITAlways = Preferences::GetBool(js_methodjit_always_str);
956 0 : bool useTypeInference = !chromeWindow && Preferences::GetBool(js_typeinfer_str);
957 0 : bool useHardening = Preferences::GetBool(js_jit_hardening_str);
958 0 : nsCOMPtr<nsIXULRuntime> xr = do_GetService(XULRUNTIME_SERVICE_CONTRACTID);
959 0 : if (xr) {
960 0 : bool safeMode = false;
961 0 : xr->GetInSafeMode(&safeMode);
962 0 : if (safeMode) {
963 0 : useMethodJIT = false;
964 0 : usePCCounts = false;
965 0 : useTypeInference = false;
966 0 : useMethodJITAlways = true;
967 0 : useHardening = false;
968 : }
969 : }
970 :
971 0 : if (useMethodJIT)
972 0 : newDefaultJSOptions |= JSOPTION_METHODJIT;
973 : else
974 0 : newDefaultJSOptions &= ~JSOPTION_METHODJIT;
975 :
976 0 : if (usePCCounts)
977 0 : newDefaultJSOptions |= JSOPTION_PCCOUNT;
978 : else
979 0 : newDefaultJSOptions &= ~JSOPTION_PCCOUNT;
980 :
981 0 : if (useMethodJITAlways)
982 0 : newDefaultJSOptions |= JSOPTION_METHODJIT_ALWAYS;
983 : else
984 0 : newDefaultJSOptions &= ~JSOPTION_METHODJIT_ALWAYS;
985 :
986 0 : if (useTypeInference)
987 0 : newDefaultJSOptions |= JSOPTION_TYPE_INFERENCE;
988 : else
989 0 : newDefaultJSOptions &= ~JSOPTION_TYPE_INFERENCE;
990 :
991 : #ifdef DEBUG
992 : // In debug builds, warnings are enabled in chrome context if
993 : // javascript.options.strict.debug is true
994 0 : bool strictDebug = Preferences::GetBool(js_strict_debug_option_str);
995 : // Note this callback is also called from context's InitClasses thus we don't
996 : // need to enable this directly from InitContext
997 0 : if (strictDebug && (newDefaultJSOptions & JSOPTION_STRICT) == 0) {
998 0 : if (chromeWindow)
999 0 : newDefaultJSOptions |= JSOPTION_STRICT;
1000 : }
1001 : #endif
1002 :
1003 0 : bool werror = Preferences::GetBool(js_werror_option_str);
1004 0 : if (werror)
1005 0 : newDefaultJSOptions |= JSOPTION_WERROR;
1006 : else
1007 0 : newDefaultJSOptions &= ~JSOPTION_WERROR;
1008 :
1009 0 : bool relimit = Preferences::GetBool(js_relimit_option_str);
1010 0 : if (relimit)
1011 0 : newDefaultJSOptions |= JSOPTION_RELIMIT;
1012 : else
1013 0 : newDefaultJSOptions &= ~JSOPTION_RELIMIT;
1014 :
1015 0 : ::JS_SetOptions(context->mContext, newDefaultJSOptions & JSRUNOPTION_MASK);
1016 :
1017 : // Save the new defaults for the next page load (InitContext).
1018 0 : context->mDefaultJSOptions = newDefaultJSOptions;
1019 :
1020 0 : JSRuntime *rt = JS_GetRuntime(context->mContext);
1021 0 : JS_SetJitHardening(rt, useHardening);
1022 :
1023 : #ifdef JS_GC_ZEAL
1024 0 : PRInt32 zeal = Preferences::GetInt(js_zeal_option_str, -1);
1025 0 : PRInt32 frequency = Preferences::GetInt(js_zeal_frequency_str, JS_DEFAULT_ZEAL_FREQ);
1026 0 : bool compartment = Preferences::GetBool(js_zeal_compartment_str, false);
1027 0 : if (zeal >= 0)
1028 0 : ::JS_SetGCZeal(context->mContext, (PRUint8)zeal, frequency, compartment);
1029 : #endif
1030 :
1031 0 : return 0;
1032 : }
1033 :
1034 0 : nsJSContext::nsJSContext(JSRuntime *aRuntime)
1035 : : mGCOnDestruction(true),
1036 0 : mExecuteDepth(0)
1037 : {
1038 :
1039 0 : ++sContextCount;
1040 :
1041 0 : mDefaultJSOptions = JSOPTION_PRIVATE_IS_NSISUPPORTS;
1042 :
1043 0 : mContext = ::JS_NewContext(aRuntime, gStackSize);
1044 0 : if (mContext) {
1045 0 : ::JS_SetContextPrivate(mContext, static_cast<nsIScriptContext *>(this));
1046 :
1047 : // Preserve any flags the context callback might have set.
1048 0 : mDefaultJSOptions |= ::JS_GetOptions(mContext);
1049 :
1050 : // Make sure the new context gets the default context options
1051 0 : ::JS_SetOptions(mContext, mDefaultJSOptions);
1052 :
1053 : // Watch for the JS boolean options
1054 : Preferences::RegisterCallback(JSOptionChangedCallback,
1055 0 : js_options_dot_str, this);
1056 :
1057 0 : ::JS_SetOperationCallback(mContext, DOMOperationCallback);
1058 :
1059 0 : xpc_LocalizeContext(mContext);
1060 : }
1061 0 : mIsInitialized = false;
1062 0 : mTerminations = nsnull;
1063 0 : mScriptsEnabled = true;
1064 0 : mOperationCallbackTime = 0;
1065 0 : mModalStateTime = 0;
1066 0 : mModalStateDepth = 0;
1067 0 : mProcessingScriptTag = false;
1068 0 : }
1069 :
1070 0 : nsJSContext::~nsJSContext()
1071 : {
1072 : #ifdef DEBUG
1073 0 : nsCycleCollector_DEBUG_wasFreed(static_cast<nsIScriptContext*>(this));
1074 : #endif
1075 :
1076 : // We may still have pending termination functions if the context is destroyed
1077 : // before they could be executed. In this case, free the references to their
1078 : // parameters, but don't execute the functions (see bug 622326).
1079 0 : delete mTerminations;
1080 :
1081 0 : mGlobalObjectRef = nsnull;
1082 :
1083 0 : DestroyJSContext();
1084 :
1085 0 : --sContextCount;
1086 :
1087 0 : if (!sContextCount && sDidShutdown) {
1088 : // The last context is being deleted, and we're already in the
1089 : // process of shutting down, release the JS runtime service, and
1090 : // the security manager.
1091 :
1092 0 : NS_IF_RELEASE(sRuntimeService);
1093 0 : NS_IF_RELEASE(sSecurityManager);
1094 : }
1095 0 : }
1096 :
1097 : void
1098 0 : nsJSContext::DestroyJSContext()
1099 : {
1100 0 : if (!mContext) {
1101 0 : return;
1102 : }
1103 :
1104 : // Clear our entry in the JSContext, bugzilla bug 66413
1105 0 : ::JS_SetContextPrivate(mContext, nsnull);
1106 :
1107 : // Unregister our "javascript.options.*" pref-changed callback.
1108 : Preferences::UnregisterCallback(JSOptionChangedCallback,
1109 0 : js_options_dot_str, this);
1110 :
1111 0 : if (mGCOnDestruction) {
1112 0 : PokeGC(js::gcreason::NSJSCONTEXT_DESTROY);
1113 : }
1114 :
1115 : // Let xpconnect destroy the JSContext when it thinks the time is right.
1116 0 : nsIXPConnect *xpc = nsContentUtils::XPConnect();
1117 0 : if (xpc) {
1118 0 : xpc->ReleaseJSContext(mContext, true);
1119 : } else {
1120 0 : ::JS_DestroyContextNoGC(mContext);
1121 : }
1122 0 : mContext = nsnull;
1123 : }
1124 :
1125 : // QueryInterface implementation for nsJSContext
1126 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSContext)
1127 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSContext)
1128 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_END
1129 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSContext)
1130 0 : NS_ASSERTION(!tmp->mContext || js::GetContextOutstandingRequests(tmp->mContext) == 0,
1131 : "Trying to unlink a context with outstanding requests.");
1132 0 : tmp->mIsInitialized = false;
1133 0 : tmp->mGCOnDestruction = false;
1134 0 : tmp->DestroyJSContext();
1135 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mGlobalObjectRef)
1136 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1137 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsJSContext)
1138 0 : NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsJSContext, tmp->GetCCRefcnt())
1139 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mGlobalObjectRef)
1140 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mContext");
1141 0 : nsContentUtils::XPConnect()->NoteJSContext(tmp->mContext, cb);
1142 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1143 :
1144 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSContext)
1145 0 : NS_INTERFACE_MAP_ENTRY(nsIScriptContext)
1146 0 : NS_INTERFACE_MAP_ENTRY(nsIScriptContextPrincipal)
1147 0 : NS_INTERFACE_MAP_ENTRY(nsIXPCScriptNotify)
1148 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIScriptContext)
1149 0 : NS_INTERFACE_MAP_END
1150 :
1151 :
1152 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsJSContext)
1153 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsJSContext)
1154 :
1155 : nsrefcnt
1156 0 : nsJSContext::GetCCRefcnt()
1157 : {
1158 0 : nsrefcnt refcnt = mRefCnt.get();
1159 0 : if (NS_LIKELY(mContext))
1160 0 : refcnt += js::GetContextOutstandingRequests(mContext);
1161 0 : return refcnt;
1162 : }
1163 :
1164 : nsresult
1165 0 : nsJSContext::EvaluateStringWithValue(const nsAString& aScript,
1166 : JSObject* aScopeObject,
1167 : nsIPrincipal *aPrincipal,
1168 : const char *aURL,
1169 : PRUint32 aLineNo,
1170 : PRUint32 aVersion,
1171 : JS::Value* aRetValue,
1172 : bool* aIsUndefined)
1173 : {
1174 : NS_TIME_FUNCTION_MIN_FMT(1.0, "%s (line %d) (url: %s, line: %d)", MOZ_FUNCTION_NAME,
1175 : __LINE__, aURL, aLineNo);
1176 :
1177 0 : SAMPLE_LABEL("JS", "EvaluateStringWithValue");
1178 0 : NS_ABORT_IF_FALSE(aScopeObject,
1179 : "Shouldn't call EvaluateStringWithValue with null scope object.");
1180 :
1181 0 : NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED);
1182 :
1183 0 : if (!mScriptsEnabled) {
1184 0 : if (aIsUndefined) {
1185 0 : *aIsUndefined = true;
1186 : }
1187 :
1188 0 : return NS_OK;
1189 : }
1190 :
1191 : // Safety first: get an object representing the script's principals, i.e.,
1192 : // the entities who signed this script, or the fully-qualified-domain-name
1193 : // or "codebase" from which it was loaded.
1194 0 : nsCOMPtr<nsIPrincipal> principal = aPrincipal;
1195 : nsresult rv;
1196 0 : if (!aPrincipal) {
1197 0 : nsIScriptGlobalObject *global = GetGlobalObject();
1198 0 : if (!global)
1199 0 : return NS_ERROR_FAILURE;
1200 : nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal =
1201 0 : do_QueryInterface(global, &rv);
1202 0 : if (NS_FAILED(rv))
1203 0 : return NS_ERROR_FAILURE;
1204 0 : principal = objPrincipal->GetPrincipal();
1205 0 : if (!principal)
1206 0 : return NS_ERROR_FAILURE;
1207 : }
1208 :
1209 0 : bool ok = false;
1210 :
1211 0 : rv = sSecurityManager->CanExecuteScripts(mContext, principal, &ok);
1212 0 : if (NS_FAILED(rv)) {
1213 0 : return NS_ERROR_FAILURE;
1214 : }
1215 :
1216 : // Push our JSContext on the current thread's context stack so JS called
1217 : // from native code via XPConnect uses the right context. Do this whether
1218 : // or not the SecurityManager said "ok", in order to simplify control flow
1219 : // below where we pop before returning.
1220 : nsCOMPtr<nsIJSContextStack> stack =
1221 0 : do_GetService("@mozilla.org/js/xpc/ContextStack;1", &rv);
1222 0 : if (NS_FAILED(rv) || NS_FAILED(stack->Push(mContext))) {
1223 0 : return NS_ERROR_FAILURE;
1224 : }
1225 :
1226 : jsval val;
1227 :
1228 0 : rv = sSecurityManager->PushContextPrincipal(mContext, nsnull, principal);
1229 0 : NS_ENSURE_SUCCESS(rv, rv);
1230 :
1231 0 : nsJSContext::TerminationFuncHolder holder(this);
1232 :
1233 : // SecurityManager said "ok", but don't compile if aVersion is unknown.
1234 : // Since the caller is responsible for parsing the version strings, we just
1235 : // check it isn't JSVERSION_UNKNOWN.
1236 0 : if (ok && ((JSVersion)aVersion) != JSVERSION_UNKNOWN) {
1237 :
1238 0 : JSAutoRequest ar(mContext);
1239 :
1240 0 : JSAutoEnterCompartment ac;
1241 0 : if (!ac.enter(mContext, aScopeObject)) {
1242 0 : stack->Pop(nsnull);
1243 0 : return NS_ERROR_FAILURE;
1244 : }
1245 :
1246 0 : ++mExecuteDepth;
1247 :
1248 : ok = ::JS_EvaluateUCScriptForPrincipalsVersion(mContext,
1249 : aScopeObject,
1250 0 : nsJSPrincipals::get(principal),
1251 0 : static_cast<const jschar*>(PromiseFlatString(aScript).get()),
1252 : aScript.Length(),
1253 : aURL,
1254 : aLineNo,
1255 : &val,
1256 0 : JSVersion(aVersion));
1257 :
1258 0 : --mExecuteDepth;
1259 :
1260 0 : if (!ok) {
1261 : // Tell XPConnect about any pending exceptions. This is needed
1262 : // to avoid dropping JS exceptions in case we got here through
1263 : // nested calls through XPConnect.
1264 :
1265 0 : ReportPendingException();
1266 : }
1267 : }
1268 :
1269 : // If all went well, convert val to a string (XXXbe unless undefined?).
1270 0 : if (ok) {
1271 0 : if (aIsUndefined) {
1272 0 : *aIsUndefined = JSVAL_IS_VOID(val);
1273 : }
1274 :
1275 0 : *aRetValue = val;
1276 : // XXX - nsScriptObjectHolder should be used once this method moves to
1277 : // the new world order. However, use of 'jsval' appears to make this
1278 : // tricky...
1279 : }
1280 : else {
1281 0 : if (aIsUndefined) {
1282 0 : *aIsUndefined = true;
1283 : }
1284 : }
1285 :
1286 0 : sSecurityManager->PopContextPrincipal(mContext);
1287 :
1288 : // Pop here, after JS_ValueToString and any other possible evaluation.
1289 0 : if (NS_FAILED(stack->Pop(nsnull)))
1290 0 : rv = NS_ERROR_FAILURE;
1291 :
1292 : // ScriptEvaluated needs to come after we pop the stack
1293 0 : ScriptEvaluated(true);
1294 :
1295 0 : return rv;
1296 :
1297 : }
1298 :
1299 : // Helper function to convert a jsval to an nsAString, and set
1300 : // exception flags if the conversion fails.
1301 : static nsresult
1302 0 : JSValueToAString(JSContext *cx, jsval val, nsAString *result,
1303 : bool *isUndefined)
1304 : {
1305 0 : if (isUndefined) {
1306 0 : *isUndefined = JSVAL_IS_VOID(val);
1307 : }
1308 :
1309 0 : if (!result) {
1310 0 : return NS_OK;
1311 : }
1312 :
1313 0 : JSString* jsstring = ::JS_ValueToString(cx, val);
1314 0 : if (!jsstring) {
1315 0 : goto error;
1316 : }
1317 :
1318 : size_t length;
1319 : const jschar *chars;
1320 0 : chars = ::JS_GetStringCharsAndLength(cx, jsstring, &length);
1321 0 : if (!chars) {
1322 0 : goto error;
1323 : }
1324 :
1325 0 : result->Assign(chars, length);
1326 0 : return NS_OK;
1327 :
1328 : error:
1329 : // We failed to convert val to a string. We're either OOM, or the
1330 : // security manager denied access to .toString(), or somesuch, on
1331 : // an object. Treat this case as if the result were undefined.
1332 :
1333 0 : result->Truncate();
1334 :
1335 0 : if (isUndefined) {
1336 0 : *isUndefined = true;
1337 : }
1338 :
1339 0 : if (!::JS_IsExceptionPending(cx)) {
1340 : // JS_ValueToString()/JS_GetStringCharsAndLength returned null w/o an
1341 : // exception pending. That means we're OOM.
1342 :
1343 0 : return NS_ERROR_OUT_OF_MEMORY;
1344 : }
1345 :
1346 0 : return NS_OK;
1347 : }
1348 :
1349 : nsIScriptObjectPrincipal*
1350 0 : nsJSContext::GetObjectPrincipal()
1351 : {
1352 0 : nsCOMPtr<nsIScriptObjectPrincipal> prin = do_QueryInterface(GetGlobalObject());
1353 0 : return prin;
1354 : }
1355 :
1356 : nsresult
1357 0 : nsJSContext::EvaluateString(const nsAString& aScript,
1358 : JSObject* aScopeObject,
1359 : nsIPrincipal *aPrincipal,
1360 : nsIPrincipal *aOriginPrincipal,
1361 : const char *aURL,
1362 : PRUint32 aLineNo,
1363 : PRUint32 aVersion,
1364 : nsAString *aRetValue,
1365 : bool* aIsUndefined)
1366 : {
1367 : NS_TIME_FUNCTION_MIN_FMT(1.0, "%s (line %d) (url: %s, line: %d)", MOZ_FUNCTION_NAME,
1368 : __LINE__, aURL, aLineNo);
1369 :
1370 0 : SAMPLE_LABEL("JS", "EvaluateString");
1371 0 : NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED);
1372 :
1373 0 : if (!mScriptsEnabled) {
1374 0 : if (aIsUndefined) {
1375 0 : *aIsUndefined = true;
1376 : }
1377 :
1378 0 : if (aRetValue) {
1379 0 : aRetValue->Truncate();
1380 : }
1381 :
1382 0 : return NS_OK;
1383 : }
1384 :
1385 0 : if (!aScopeObject) {
1386 0 : aScopeObject = JS_GetGlobalObject(mContext);
1387 : }
1388 :
1389 : // Safety first: get an object representing the script's principals, i.e.,
1390 : // the entities who signed this script, or the fully-qualified-domain-name
1391 : // or "codebase" from which it was loaded.
1392 0 : nsCOMPtr<nsIPrincipal> principal = aPrincipal;
1393 0 : if (!aPrincipal) {
1394 : nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal =
1395 0 : do_QueryInterface(GetGlobalObject());
1396 0 : if (!objPrincipal)
1397 0 : return NS_ERROR_FAILURE;
1398 0 : principal = objPrincipal->GetPrincipal();
1399 0 : if (!principal)
1400 0 : return NS_ERROR_FAILURE;
1401 : }
1402 :
1403 0 : bool ok = false;
1404 :
1405 0 : nsresult rv = sSecurityManager->CanExecuteScripts(mContext, principal, &ok);
1406 0 : if (NS_FAILED(rv)) {
1407 0 : return NS_ERROR_FAILURE;
1408 : }
1409 :
1410 : // Push our JSContext on the current thread's context stack so JS called
1411 : // from native code via XPConnect uses the right context. Do this whether
1412 : // or not the SecurityManager said "ok", in order to simplify control flow
1413 : // below where we pop before returning.
1414 : nsCOMPtr<nsIJSContextStack> stack =
1415 0 : do_GetService("@mozilla.org/js/xpc/ContextStack;1", &rv);
1416 0 : if (NS_FAILED(rv) || NS_FAILED(stack->Push(mContext))) {
1417 0 : return NS_ERROR_FAILURE;
1418 : }
1419 :
1420 : // The result of evaluation, used only if there were no errors. This need
1421 : // not be a GC root currently, provided we run the GC only from the
1422 : // operation callback or from ScriptEvaluated.
1423 0 : jsval val = JSVAL_VOID;
1424 0 : jsval* vp = aRetValue ? &val : NULL;
1425 :
1426 0 : rv = sSecurityManager->PushContextPrincipal(mContext, nsnull, principal);
1427 0 : NS_ENSURE_SUCCESS(rv, rv);
1428 :
1429 0 : nsJSContext::TerminationFuncHolder holder(this);
1430 :
1431 0 : ++mExecuteDepth;
1432 :
1433 : // SecurityManager said "ok", but don't compile if aVersion is unknown.
1434 : // Since the caller is responsible for parsing the version strings, we just
1435 : // check it isn't JSVERSION_UNKNOWN.
1436 0 : if (ok && JSVersion(aVersion) != JSVERSION_UNKNOWN) {
1437 0 : JSAutoRequest ar(mContext);
1438 0 : JSAutoEnterCompartment ac;
1439 0 : if (!ac.enter(mContext, aScopeObject)) {
1440 0 : stack->Pop(nsnull);
1441 0 : return NS_ERROR_FAILURE;
1442 : }
1443 :
1444 : ok = JS_EvaluateUCScriptForPrincipalsVersionOrigin(
1445 : mContext, aScopeObject,
1446 0 : nsJSPrincipals::get(principal), nsJSPrincipals::get(aOriginPrincipal),
1447 0 : static_cast<const jschar*>(PromiseFlatString(aScript).get()),
1448 0 : aScript.Length(), aURL, aLineNo, vp, JSVersion(aVersion));
1449 :
1450 0 : if (!ok) {
1451 : // Tell XPConnect about any pending exceptions. This is needed
1452 : // to avoid dropping JS exceptions in case we got here through
1453 : // nested calls through XPConnect.
1454 :
1455 0 : ReportPendingException();
1456 : }
1457 : }
1458 :
1459 : // If all went well, convert val to a string if one is wanted.
1460 0 : if (ok) {
1461 0 : JSAutoRequest ar(mContext);
1462 0 : JSAutoEnterCompartment ac;
1463 0 : if (!ac.enter(mContext, aScopeObject)) {
1464 0 : stack->Pop(nsnull);
1465 : }
1466 0 : rv = JSValueToAString(mContext, val, aRetValue, aIsUndefined);
1467 : }
1468 : else {
1469 0 : if (aIsUndefined) {
1470 0 : *aIsUndefined = true;
1471 : }
1472 :
1473 0 : if (aRetValue) {
1474 0 : aRetValue->Truncate();
1475 : }
1476 : }
1477 :
1478 0 : --mExecuteDepth;
1479 :
1480 0 : sSecurityManager->PopContextPrincipal(mContext);
1481 :
1482 : // Pop here, after JS_ValueToString and any other possible evaluation.
1483 0 : if (NS_FAILED(stack->Pop(nsnull)))
1484 0 : rv = NS_ERROR_FAILURE;
1485 :
1486 : // ScriptEvaluated needs to come after we pop the stack
1487 0 : ScriptEvaluated(true);
1488 :
1489 0 : return rv;
1490 : }
1491 :
1492 : nsresult
1493 0 : nsJSContext::CompileScript(const PRUnichar* aText,
1494 : PRInt32 aTextLength,
1495 : nsIPrincipal *aPrincipal,
1496 : const char *aURL,
1497 : PRUint32 aLineNo,
1498 : PRUint32 aVersion,
1499 : nsScriptObjectHolder<JSScript>& aScriptObject)
1500 : {
1501 0 : NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED);
1502 :
1503 0 : NS_ENSURE_ARG_POINTER(aPrincipal);
1504 :
1505 0 : JSObject* scopeObject = ::JS_GetGlobalObject(mContext);
1506 :
1507 0 : bool ok = false;
1508 :
1509 0 : nsresult rv = sSecurityManager->CanExecuteScripts(mContext, aPrincipal, &ok);
1510 0 : if (NS_FAILED(rv)) {
1511 0 : return NS_ERROR_FAILURE;
1512 : }
1513 :
1514 0 : aScriptObject.drop(); // ensure old object not used on failure...
1515 :
1516 : // Don't compile if SecurityManager said "not ok" or aVersion is unknown.
1517 : // Since the caller is responsible for parsing the version strings, we just
1518 : // check it isn't JSVERSION_UNKNOWN.
1519 0 : if (!ok || JSVersion(aVersion) == JSVERSION_UNKNOWN)
1520 0 : return NS_OK;
1521 :
1522 0 : JSAutoRequest ar(mContext);
1523 :
1524 : JSScript* script =
1525 : ::JS_CompileUCScriptForPrincipalsVersion(mContext,
1526 : scopeObject,
1527 0 : nsJSPrincipals::get(aPrincipal),
1528 : static_cast<const jschar*>(aText),
1529 : aTextLength,
1530 : aURL,
1531 : aLineNo,
1532 0 : JSVersion(aVersion));
1533 0 : if (!script) {
1534 0 : return NS_ERROR_OUT_OF_MEMORY;
1535 : }
1536 0 : NS_ASSERTION(aScriptObject.getScriptTypeID()==JAVASCRIPT,
1537 : "Expecting JS script object holder");
1538 0 : return aScriptObject.set(script);
1539 : }
1540 :
1541 : nsresult
1542 0 : nsJSContext::ExecuteScript(JSScript* aScriptObject,
1543 : JSObject* aScopeObject,
1544 : nsAString* aRetValue,
1545 : bool* aIsUndefined)
1546 : {
1547 0 : NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED);
1548 :
1549 0 : if (!mScriptsEnabled) {
1550 0 : if (aIsUndefined) {
1551 0 : *aIsUndefined = true;
1552 : }
1553 :
1554 0 : if (aRetValue) {
1555 0 : aRetValue->Truncate();
1556 : }
1557 :
1558 0 : return NS_OK;
1559 : }
1560 :
1561 0 : if (!aScopeObject) {
1562 0 : aScopeObject = JS_GetGlobalObject(mContext);
1563 : }
1564 :
1565 : // Push our JSContext on our thread's context stack, in case native code
1566 : // called from JS calls back into JS via XPConnect.
1567 : nsresult rv;
1568 : nsCOMPtr<nsIJSContextStack> stack =
1569 0 : do_GetService("@mozilla.org/js/xpc/ContextStack;1", &rv);
1570 0 : if (NS_FAILED(rv) || NS_FAILED(stack->Push(mContext))) {
1571 0 : return NS_ERROR_FAILURE;
1572 : }
1573 :
1574 0 : nsCOMPtr<nsIPrincipal> principal;
1575 : rv = sSecurityManager->GetObjectPrincipal(mContext,
1576 : JS_GetGlobalFromScript(aScriptObject),
1577 0 : getter_AddRefs(principal));
1578 0 : NS_ENSURE_SUCCESS(rv, rv);
1579 :
1580 0 : rv = sSecurityManager->PushContextPrincipal(mContext, nsnull, principal);
1581 0 : NS_ENSURE_SUCCESS(rv, rv);
1582 :
1583 0 : nsJSContext::TerminationFuncHolder holder(this);
1584 0 : JSAutoRequest ar(mContext);
1585 0 : ++mExecuteDepth;
1586 :
1587 : // The result of evaluation, used only if there were no errors. This need
1588 : // not be a GC root currently, provided we run the GC only from the
1589 : // operation callback or from ScriptEvaluated.
1590 : jsval val;
1591 0 : bool ok = JS_ExecuteScript(mContext, aScopeObject, aScriptObject, &val);
1592 0 : if (ok) {
1593 : // If all went well, convert val to a string (XXXbe unless undefined?).
1594 0 : rv = JSValueToAString(mContext, val, aRetValue, aIsUndefined);
1595 : } else {
1596 0 : ReportPendingException();
1597 :
1598 0 : if (aIsUndefined) {
1599 0 : *aIsUndefined = true;
1600 : }
1601 :
1602 0 : if (aRetValue) {
1603 0 : aRetValue->Truncate();
1604 : }
1605 : }
1606 :
1607 0 : --mExecuteDepth;
1608 :
1609 0 : sSecurityManager->PopContextPrincipal(mContext);
1610 :
1611 : // Pop here, after JS_ValueToString and any other possible evaluation.
1612 0 : if (NS_FAILED(stack->Pop(nsnull)))
1613 0 : rv = NS_ERROR_FAILURE;
1614 :
1615 : // ScriptEvaluated needs to come after we pop the stack
1616 0 : ScriptEvaluated(true);
1617 :
1618 0 : return rv;
1619 : }
1620 :
1621 :
1622 : #ifdef DEBUG
1623 : bool
1624 0 : AtomIsEventHandlerName(nsIAtom *aName)
1625 : {
1626 0 : const PRUnichar *name = aName->GetUTF16String();
1627 :
1628 : const PRUnichar *cp;
1629 : PRUnichar c;
1630 0 : for (cp = name; *cp != '\0'; ++cp)
1631 : {
1632 0 : c = *cp;
1633 0 : if ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z'))
1634 0 : return false;
1635 : }
1636 :
1637 0 : return true;
1638 : }
1639 : #endif
1640 :
1641 : // Helper function to find the JSObject associated with a (presumably DOM)
1642 : // interface.
1643 : nsresult
1644 0 : nsJSContext::JSObjectFromInterface(nsISupports* aTarget, JSObject* aScope, JSObject** aRet)
1645 : {
1646 : // It is legal to specify a null target.
1647 0 : if (!aTarget) {
1648 0 : *aRet = nsnull;
1649 0 : return NS_OK;
1650 : }
1651 :
1652 : // Get the jsobject associated with this target
1653 : // We don't wrap here because we trust the JS engine to wrap the target
1654 : // later.
1655 : jsval v;
1656 0 : nsresult rv = nsContentUtils::WrapNative(mContext, aScope, aTarget, &v);
1657 0 : NS_ENSURE_SUCCESS(rv, rv);
1658 :
1659 : #ifdef NS_DEBUG
1660 0 : nsCOMPtr<nsISupports> targetSupp = do_QueryInterface(aTarget);
1661 : nsCOMPtr<nsISupports> native =
1662 0 : nsContentUtils::XPConnect()->GetNativeOfWrapper(mContext,
1663 0 : JSVAL_TO_OBJECT(v));
1664 0 : NS_ASSERTION(native == targetSupp, "Native should be the target!");
1665 : #endif
1666 :
1667 0 : *aRet = JSVAL_TO_OBJECT(v);
1668 :
1669 0 : return NS_OK;
1670 : }
1671 :
1672 :
1673 : nsresult
1674 0 : nsJSContext::CompileEventHandler(nsIAtom *aName,
1675 : PRUint32 aArgCount,
1676 : const char** aArgNames,
1677 : const nsAString& aBody,
1678 : const char *aURL, PRUint32 aLineNo,
1679 : PRUint32 aVersion,
1680 : nsScriptObjectHolder<JSObject>& aHandler)
1681 : {
1682 : NS_TIME_FUNCTION_MIN_FMT(1.0, "%s (line %d) (url: %s, line: %d)", MOZ_FUNCTION_NAME,
1683 : __LINE__, aURL, aLineNo);
1684 :
1685 0 : NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED);
1686 :
1687 0 : NS_PRECONDITION(AtomIsEventHandlerName(aName), "Bad event name");
1688 0 : NS_PRECONDITION(!::JS_IsExceptionPending(mContext),
1689 : "Why are we being called with a pending exception?");
1690 :
1691 0 : if (!sSecurityManager) {
1692 : NS_ERROR("Huh, we need a script security manager to compile "
1693 0 : "an event handler!");
1694 :
1695 0 : return NS_ERROR_UNEXPECTED;
1696 : }
1697 :
1698 : // Don't compile if aVersion is unknown. Since the caller is responsible for
1699 : // parsing the version strings, we just check it isn't JSVERSION_UNKNOWN.
1700 0 : if ((JSVersion)aVersion == JSVERSION_UNKNOWN) {
1701 0 : return NS_ERROR_ILLEGAL_VALUE;
1702 : }
1703 :
1704 : #ifdef DEBUG
1705 0 : JSContext* top = nsContentUtils::GetCurrentJSContext();
1706 0 : NS_ASSERTION(mContext == top, "Context not properly pushed!");
1707 : #endif
1708 :
1709 : // Event handlers are always shared, and must be bound before use.
1710 : // Therefore we never bother compiling with principals.
1711 : // (that probably means we should avoid JS_CompileUCFunctionForPrincipals!)
1712 0 : JSAutoRequest ar(mContext);
1713 :
1714 : JSFunction* fun =
1715 : ::JS_CompileUCFunctionForPrincipalsVersion(mContext,
1716 : nsnull, nsnull,
1717 0 : nsAtomCString(aName).get(), aArgCount, aArgNames,
1718 0 : (jschar*)PromiseFlatString(aBody).get(),
1719 : aBody.Length(),
1720 0 : aURL, aLineNo, JSVersion(aVersion));
1721 :
1722 0 : if (!fun) {
1723 0 : ReportPendingException();
1724 0 : return NS_ERROR_ILLEGAL_VALUE;
1725 : }
1726 :
1727 0 : JSObject *handler = ::JS_GetFunctionObject(fun);
1728 0 : NS_ASSERTION(aHandler.getScriptTypeID()==JAVASCRIPT,
1729 : "Expecting JS script object holder");
1730 0 : return aHandler.set(handler);
1731 : }
1732 :
1733 : // XXX - note that CompileFunction doesn't yet play the nsScriptObjectHolder
1734 : // game - caller must still ensure JS GC root.
1735 : nsresult
1736 0 : nsJSContext::CompileFunction(JSObject* aTarget,
1737 : const nsACString& aName,
1738 : PRUint32 aArgCount,
1739 : const char** aArgArray,
1740 : const nsAString& aBody,
1741 : const char* aURL,
1742 : PRUint32 aLineNo,
1743 : PRUint32 aVersion,
1744 : bool aShared,
1745 : JSObject** aFunctionObject)
1746 : {
1747 : NS_TIME_FUNCTION_FMT(1.0, "%s (line %d) (function: %s, url: %s, line: %d)", MOZ_FUNCTION_NAME,
1748 : __LINE__, aName.BeginReading(), aURL, aLineNo);
1749 :
1750 0 : NS_ABORT_IF_FALSE(aFunctionObject,
1751 : "Shouldn't call CompileFunction with null return value.");
1752 :
1753 0 : NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED);
1754 :
1755 : // Don't compile if aVersion is unknown. Since the caller is responsible for
1756 : // parsing the version strings, we just check it isn't JSVERSION_UNKNOWN.
1757 0 : if ((JSVersion)aVersion == JSVERSION_UNKNOWN) {
1758 0 : return NS_ERROR_ILLEGAL_VALUE;
1759 : }
1760 :
1761 0 : nsIScriptGlobalObject *global = GetGlobalObject();
1762 0 : nsCOMPtr<nsIPrincipal> principal;
1763 0 : if (global) {
1764 : // XXXbe why the two-step QI? speed up via a new GetGlobalObjectData func?
1765 0 : nsCOMPtr<nsIScriptObjectPrincipal> globalData = do_QueryInterface(global);
1766 0 : if (globalData) {
1767 0 : principal = globalData->GetPrincipal();
1768 0 : if (!principal)
1769 0 : return NS_ERROR_FAILURE;
1770 : }
1771 : }
1772 :
1773 0 : JSObject *target = aTarget;
1774 :
1775 0 : JSAutoRequest ar(mContext);
1776 :
1777 : JSFunction* fun =
1778 : ::JS_CompileUCFunctionForPrincipalsVersion(mContext,
1779 : aShared ? nsnull : target,
1780 0 : nsJSPrincipals::get(principal),
1781 0 : PromiseFlatCString(aName).get(),
1782 : aArgCount, aArgArray,
1783 0 : static_cast<const jschar*>(PromiseFlatString(aBody).get()),
1784 : aBody.Length(),
1785 : aURL, aLineNo,
1786 0 : JSVersion(aVersion));
1787 :
1788 0 : if (!fun)
1789 0 : return NS_ERROR_FAILURE;
1790 :
1791 0 : *aFunctionObject = JS_GetFunctionObject(fun);
1792 0 : return NS_OK;
1793 : }
1794 :
1795 : nsresult
1796 0 : nsJSContext::CallEventHandler(nsISupports* aTarget, JSObject* aScope,
1797 : JSObject* aHandler, nsIArray* aargv,
1798 : nsIVariant** arv)
1799 : {
1800 0 : NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED);
1801 :
1802 0 : if (!mScriptsEnabled) {
1803 0 : return NS_OK;
1804 : }
1805 :
1806 : #ifdef NS_FUNCTION_TIMER
1807 : {
1808 : JSObject *obj = aHandler;
1809 : if (js::IsFunctionProxy(obj))
1810 : obj = js::UnwrapObject(obj);
1811 : JSString *id = JS_GetFunctionId(static_cast<JSFunction *>(JS_GetPrivate(obj)));
1812 : JSAutoByteString bytes;
1813 : const char *name = !id ? "anonymous" : bytes.encode(mContext, id) ? bytes.ptr() : "<error>";
1814 : NS_TIME_FUNCTION_FMT(1.0, "%s (line %d) (function: %s)", MOZ_FUNCTION_NAME, __LINE__, name);
1815 : }
1816 : #endif
1817 0 : SAMPLE_LABEL("JS", "CallEventHandler");
1818 :
1819 0 : JSAutoRequest ar(mContext);
1820 0 : JSObject* target = nsnull;
1821 0 : nsresult rv = JSObjectFromInterface(aTarget, aScope, &target);
1822 0 : NS_ENSURE_SUCCESS(rv, rv);
1823 :
1824 0 : JS::AutoObjectRooter targetVal(mContext, target);
1825 0 : jsval rval = JSVAL_VOID;
1826 :
1827 : // This one's a lot easier than EvaluateString because we don't have to
1828 : // hassle with principals: they're already compiled into the JS function.
1829 : // xxxmarkh - this comment is no longer true - principals are not used at
1830 : // all now, and never were in some cases.
1831 :
1832 0 : nsCxPusher pusher;
1833 0 : if (!pusher.Push(mContext, true))
1834 0 : return NS_ERROR_FAILURE;
1835 :
1836 : // check if the event handler can be run on the object in question
1837 0 : rv = sSecurityManager->CheckFunctionAccess(mContext, aHandler, target);
1838 :
1839 0 : nsJSContext::TerminationFuncHolder holder(this);
1840 :
1841 0 : if (NS_SUCCEEDED(rv)) {
1842 : // Convert args to jsvals.
1843 0 : PRUint32 argc = 0;
1844 0 : jsval *argv = nsnull;
1845 :
1846 0 : JSObject *funobj = aHandler;
1847 0 : nsCOMPtr<nsIPrincipal> principal;
1848 : rv = sSecurityManager->GetObjectPrincipal(mContext, funobj,
1849 0 : getter_AddRefs(principal));
1850 0 : NS_ENSURE_SUCCESS(rv, rv);
1851 :
1852 0 : JSStackFrame *currentfp = nsnull;
1853 : rv = sSecurityManager->PushContextPrincipal(mContext,
1854 : JS_FrameIterator(mContext, ¤tfp),
1855 0 : principal);
1856 0 : NS_ENSURE_SUCCESS(rv, rv);
1857 :
1858 0 : jsval funval = OBJECT_TO_JSVAL(funobj);
1859 0 : JSAutoEnterCompartment ac;
1860 0 : js::ForceFrame ff(mContext, funobj);
1861 0 : if (!ac.enter(mContext, funobj) || !ff.enter() ||
1862 0 : !JS_WrapObject(mContext, &target)) {
1863 0 : ReportPendingException();
1864 0 : sSecurityManager->PopContextPrincipal(mContext);
1865 0 : return NS_ERROR_FAILURE;
1866 : }
1867 :
1868 0 : Maybe<nsRootedJSValueArray> tempStorage;
1869 :
1870 : // Use |target| as the scope for wrapping the arguments, since aScope is
1871 : // the safe scope in many cases, which isn't very useful. Wrapping aTarget
1872 : // was OK because those typically have PreCreate methods that give them the
1873 : // right scope anyway, and we want to make sure that the arguments end up
1874 : // in the same scope as aTarget.
1875 0 : rv = ConvertSupportsTojsvals(aargv, target, &argc, &argv, tempStorage);
1876 0 : NS_ENSURE_SUCCESS(rv, rv);
1877 :
1878 0 : ++mExecuteDepth;
1879 : bool ok = ::JS_CallFunctionValue(mContext, target,
1880 0 : funval, argc, argv, &rval);
1881 0 : --mExecuteDepth;
1882 :
1883 0 : if (!ok) {
1884 : // Don't pass back results from failed calls.
1885 0 : rval = JSVAL_VOID;
1886 :
1887 : // Tell the caller that the handler threw an error.
1888 0 : rv = NS_ERROR_FAILURE;
1889 0 : } else if (rval == JSVAL_NULL) {
1890 0 : *arv = nsnull;
1891 0 : } else if (!JS_WrapValue(mContext, &rval)) {
1892 0 : rv = NS_ERROR_FAILURE;
1893 : } else {
1894 0 : rv = nsContentUtils::XPConnect()->JSToVariant(mContext, rval, arv);
1895 : }
1896 :
1897 : // Tell XPConnect about any pending exceptions. This is needed
1898 : // to avoid dropping JS exceptions in case we got here through
1899 : // nested calls through XPConnect.
1900 0 : if (NS_FAILED(rv))
1901 0 : ReportPendingException();
1902 :
1903 0 : sSecurityManager->PopContextPrincipal(mContext);
1904 : }
1905 :
1906 0 : pusher.Pop();
1907 :
1908 : // ScriptEvaluated needs to come after we pop the stack
1909 0 : ScriptEvaluated(true);
1910 :
1911 0 : return rv;
1912 : }
1913 :
1914 : nsresult
1915 0 : nsJSContext::BindCompiledEventHandler(nsISupports* aTarget, JSObject* aScope,
1916 : JSObject* aHandler,
1917 : nsScriptObjectHolder<JSObject>& aBoundHandler)
1918 : {
1919 0 : NS_ENSURE_ARG(aHandler);
1920 0 : NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED);
1921 0 : NS_PRECONDITION(!aBoundHandler, "Shouldn't already have a bound handler!");
1922 :
1923 0 : JSAutoRequest ar(mContext);
1924 :
1925 : // Get the jsobject associated with this target
1926 0 : JSObject *target = nsnull;
1927 0 : nsresult rv = JSObjectFromInterface(aTarget, aScope, &target);
1928 0 : NS_ENSURE_SUCCESS(rv, rv);
1929 :
1930 : #ifdef DEBUG
1931 : {
1932 0 : JSAutoEnterCompartment ac;
1933 0 : if (!ac.enter(mContext, aHandler)) {
1934 0 : return NS_ERROR_FAILURE;
1935 : }
1936 :
1937 0 : NS_ASSERTION(JS_TypeOfValue(mContext,
1938 : OBJECT_TO_JSVAL(aHandler)) == JSTYPE_FUNCTION,
1939 : "Event handler object not a function");
1940 : }
1941 : #endif
1942 :
1943 0 : JSAutoEnterCompartment ac;
1944 0 : if (!ac.enter(mContext, target)) {
1945 0 : return NS_ERROR_FAILURE;
1946 : }
1947 :
1948 : JSObject* funobj;
1949 : // Make sure the handler function is parented by its event target object
1950 0 : if (aHandler) {
1951 0 : funobj = JS_CloneFunctionObject(mContext, aHandler, target);
1952 0 : if (!funobj) {
1953 0 : rv = NS_ERROR_OUT_OF_MEMORY;
1954 : }
1955 : } else {
1956 0 : funobj = NULL;
1957 : }
1958 :
1959 0 : aBoundHandler.set(funobj);
1960 :
1961 0 : return rv;
1962 : }
1963 :
1964 : // serialization
1965 : nsresult
1966 0 : nsJSContext::Serialize(nsIObjectOutputStream* aStream, JSScript* aScriptObject)
1967 : {
1968 0 : if (!aScriptObject)
1969 0 : return NS_ERROR_FAILURE;
1970 :
1971 : nsresult rv;
1972 :
1973 0 : JSContext* cx = mContext;
1974 0 : JSXDRState *xdr = ::JS_XDRNewMem(cx, JSXDR_ENCODE);
1975 0 : if (! xdr)
1976 0 : return NS_ERROR_OUT_OF_MEMORY;
1977 0 : xdr->userdata = (void*) aStream;
1978 :
1979 0 : JSAutoRequest ar(cx);
1980 0 : if (! ::JS_XDRScript(xdr, &aScriptObject)) {
1981 0 : rv = NS_ERROR_FAILURE; // likely to be a principals serialization error
1982 : } else {
1983 : // Get the encoded JSXDRState data and write it. The JSXDRState owns
1984 : // this buffer memory and will free it beneath ::JS_XDRDestroy.
1985 : //
1986 : // If an XPCOM object needs to be written in the midst of the JS XDR
1987 : // encoding process, the C++ code called back from the JS engine (e.g.,
1988 : // nsEncodeJSPrincipals in caps/src/nsJSPrincipals.cpp) will flush data
1989 : // from the JSXDRState to aStream, then write the object, then return
1990 : // to JS XDR code with xdr reset so new JS data is encoded at the front
1991 : // of the xdr's data buffer.
1992 : //
1993 : // However many XPCOM objects are interleaved with JS XDR data in the
1994 : // stream, when control returns here from ::JS_XDRScript, we'll have
1995 : // one last buffer of data to write to aStream.
1996 :
1997 : uint32_t size;
1998 : const char* data = reinterpret_cast<const char*>
1999 0 : (::JS_XDRMemGetData(xdr, &size));
2000 0 : NS_ASSERTION(data, "no decoded JSXDRState data!");
2001 :
2002 0 : rv = aStream->Write32(size);
2003 0 : if (NS_SUCCEEDED(rv))
2004 0 : rv = aStream->WriteBytes(data, size);
2005 : }
2006 :
2007 0 : ::JS_XDRDestroy(xdr);
2008 0 : if (NS_FAILED(rv)) return rv;
2009 :
2010 0 : return rv;
2011 : }
2012 :
2013 : nsresult
2014 0 : nsJSContext::Deserialize(nsIObjectInputStream* aStream,
2015 : nsScriptObjectHolder<JSScript>& aResult)
2016 : {
2017 : NS_TIME_FUNCTION_MIN(1.0);
2018 :
2019 : PRUint32 size;
2020 0 : nsresult rv = aStream->Read32(&size);
2021 0 : if (NS_FAILED(rv)) return rv;
2022 :
2023 : char* data;
2024 0 : rv = aStream->ReadBytes(size, &data);
2025 0 : if (NS_FAILED(rv)) return rv;
2026 :
2027 0 : JSContext* cx = mContext;
2028 :
2029 0 : JSXDRState *xdr = ::JS_XDRNewMem(cx, JSXDR_DECODE);
2030 0 : JSScript *result = nsnull;
2031 0 : if (! xdr) {
2032 0 : rv = NS_ERROR_OUT_OF_MEMORY;
2033 : } else {
2034 0 : xdr->userdata = (void*) aStream;
2035 0 : JSAutoRequest ar(cx);
2036 0 : ::JS_XDRMemSetData(xdr, data, size);
2037 :
2038 0 : if (! ::JS_XDRScript(xdr, &result)) {
2039 0 : rv = NS_ERROR_FAILURE; // principals deserialization error?
2040 : }
2041 :
2042 : // Update data in case ::JS_XDRScript called back into C++ code to
2043 : // read an XPCOM object.
2044 : //
2045 : // In that case, the serialization process must have flushed a run
2046 : // of counted bytes containing JS data at the point where the XPCOM
2047 : // object starts, after which an encoding C++ callback from the JS
2048 : // XDR code must have written the XPCOM object directly into the
2049 : // nsIObjectOutputStream.
2050 : //
2051 : // The deserialization process will XDR-decode counted bytes up to
2052 : // but not including the XPCOM object, then call back into C++ to
2053 : // read the object, then read more counted bytes and hand them off
2054 : // to the JSXDRState, so more JS data can be decoded.
2055 : //
2056 : // This interleaving of JS XDR data and XPCOM object data may occur
2057 : // several times beneath the call to ::JS_XDRScript, above. At the
2058 : // end of the day, we need to free (via nsMemory) the data owned by
2059 : // the JSXDRState. So we steal it back, nulling xdr's buffer so it
2060 : // doesn't get passed to ::JS_free by ::JS_XDRDestroy.
2061 :
2062 : uint32_t junk;
2063 0 : data = (char*) ::JS_XDRMemGetData(xdr, &junk);
2064 0 : if (data)
2065 0 : ::JS_XDRMemSetData(xdr, NULL, 0);
2066 0 : ::JS_XDRDestroy(xdr);
2067 : }
2068 :
2069 : // If data is null now, it must have been freed while deserializing an
2070 : // XPCOM object (e.g., a principal) beneath ::JS_XDRScript.
2071 0 : if (data)
2072 0 : nsMemory::Free(data);
2073 0 : NS_ASSERTION(aResult.getScriptTypeID()==JAVASCRIPT,
2074 : "Expecting JS script object holder");
2075 :
2076 : // Now that we've cleaned up, handle the case when rv is a failure
2077 : // code, which could happen for all sorts of reasons above.
2078 0 : NS_ENSURE_SUCCESS(rv, rv);
2079 :
2080 0 : return aResult.set(result);
2081 : }
2082 :
2083 : nsIScriptGlobalObject *
2084 0 : nsJSContext::GetGlobalObject()
2085 : {
2086 0 : JSObject *global = ::JS_GetGlobalObject(mContext);
2087 :
2088 0 : if (!global) {
2089 0 : return nsnull;
2090 : }
2091 :
2092 0 : if (mGlobalObjectRef)
2093 0 : return mGlobalObjectRef;
2094 :
2095 : #ifdef DEBUG
2096 : {
2097 0 : JSObject *inner = JS_ObjectToInnerObject(mContext, global);
2098 :
2099 : // If this assertion hits then it means that we have a window object as
2100 : // our global, but we never called CreateOuterObject.
2101 0 : NS_ASSERTION(inner == global, "Shouldn't be able to innerize here");
2102 : }
2103 : #endif
2104 :
2105 0 : JSClass *c = JS_GetClass(global);
2106 :
2107 0 : if (!c || ((~c->flags) & (JSCLASS_HAS_PRIVATE |
2108 : JSCLASS_PRIVATE_IS_NSISUPPORTS))) {
2109 0 : return nsnull;
2110 : }
2111 :
2112 0 : nsISupports *priv = (nsISupports *)js::GetObjectPrivate(global);
2113 :
2114 : nsCOMPtr<nsIXPConnectWrappedNative> wrapped_native =
2115 0 : do_QueryInterface(priv);
2116 :
2117 0 : nsCOMPtr<nsIScriptGlobalObject> sgo;
2118 0 : if (wrapped_native) {
2119 : // The global object is a XPConnect wrapped native, the native in
2120 : // the wrapper might be the nsIScriptGlobalObject
2121 :
2122 0 : sgo = do_QueryWrappedNative(wrapped_native);
2123 : } else {
2124 0 : sgo = do_QueryInterface(priv);
2125 : }
2126 :
2127 : // This'll return a pointer to something we're about to release, but
2128 : // that's ok, the JS object will hold it alive long enough.
2129 0 : return sgo;
2130 : }
2131 :
2132 : JSObject*
2133 0 : nsJSContext::GetNativeGlobal()
2134 : {
2135 0 : return JS_GetGlobalObject(mContext);
2136 : }
2137 :
2138 : nsresult
2139 0 : nsJSContext::CreateNativeGlobalForInner(
2140 : nsIScriptGlobalObject *aNewInner,
2141 : bool aIsChrome,
2142 : nsIPrincipal *aPrincipal,
2143 : JSObject** aNativeGlobal, nsISupports **aHolder)
2144 : {
2145 0 : nsIXPConnect *xpc = nsContentUtils::XPConnect();
2146 0 : PRUint32 flags = aIsChrome? nsIXPConnect::FLAG_SYSTEM_GLOBAL_OBJECT : 0;
2147 :
2148 0 : nsCOMPtr<nsIPrincipal> systemPrincipal;
2149 0 : if (aIsChrome) {
2150 0 : nsIScriptSecurityManager *ssm = nsContentUtils::GetSecurityManager();
2151 0 : ssm->GetSystemPrincipal(getter_AddRefs(systemPrincipal));
2152 : }
2153 :
2154 0 : nsRefPtr<nsIXPConnectJSObjectHolder> jsholder;
2155 : nsresult rv = xpc->
2156 : InitClassesWithNewWrappedGlobal(mContext, aNewInner,
2157 : aIsChrome ? systemPrincipal.get() : aPrincipal,
2158 0 : flags, getter_AddRefs(jsholder));
2159 0 : if (NS_FAILED(rv)) {
2160 0 : return rv;
2161 : }
2162 0 : jsholder->GetJSObject(aNativeGlobal);
2163 0 : jsholder.forget(aHolder);
2164 0 : return NS_OK;
2165 : }
2166 :
2167 : nsresult
2168 0 : nsJSContext::ConnectToInner(nsIScriptGlobalObject *aNewInner, JSObject *aOuterGlobal)
2169 : {
2170 0 : NS_ENSURE_ARG(aNewInner);
2171 : #ifdef DEBUG
2172 0 : JSObject *newInnerJSObject = aNewInner->GetGlobalJSObject();
2173 : #endif
2174 :
2175 : // Now that we're connecting the outer global to the inner one,
2176 : // we must have transplanted it. The JS engine tries to maintain
2177 : // the global object's compartment as its default compartment,
2178 : // so update that now since it might have changed.
2179 0 : JS_SetGlobalObject(mContext, aOuterGlobal);
2180 0 : NS_ASSERTION(JS_GetPrototype(aOuterGlobal) ==
2181 : JS_GetPrototype(newInnerJSObject),
2182 : "outer and inner globals should have the same prototype");
2183 :
2184 0 : return NS_OK;
2185 : }
2186 :
2187 : JSContext*
2188 0 : nsJSContext::GetNativeContext()
2189 : {
2190 0 : return mContext;
2191 : }
2192 :
2193 : nsresult
2194 0 : nsJSContext::InitContext()
2195 : {
2196 : // Make sure callers of this use
2197 : // WillInitializeContext/DidInitializeContext around this call.
2198 0 : NS_ENSURE_TRUE(!mIsInitialized, NS_ERROR_ALREADY_INITIALIZED);
2199 :
2200 0 : if (!mContext)
2201 0 : return NS_ERROR_OUT_OF_MEMORY;
2202 :
2203 0 : ::JS_SetErrorReporter(mContext, NS_ScriptErrorReporter);
2204 :
2205 0 : return NS_OK;
2206 : }
2207 :
2208 : nsresult
2209 0 : nsJSContext::CreateOuterObject(nsIScriptGlobalObject *aGlobalObject,
2210 : nsIScriptGlobalObject *aCurrentInner)
2211 : {
2212 0 : mGlobalObjectRef = aGlobalObject;
2213 :
2214 0 : nsCOMPtr<nsIDOMChromeWindow> chromeWindow(do_QueryInterface(aGlobalObject));
2215 :
2216 0 : if (chromeWindow) {
2217 : // Always enable E4X for XUL and other chrome content -- there is no
2218 : // need to preserve the <!-- script hiding hack from JS-in-HTML daze
2219 : // (introduced in 1995 for graceful script degradation in Netscape 1,
2220 : // Mosaic, and other pre-JS browsers).
2221 0 : JS_SetOptions(mContext, JS_GetOptions(mContext) | JSOPTION_XML);
2222 : }
2223 :
2224 : JSObject *outer =
2225 0 : NS_NewOuterWindowProxy(mContext, aCurrentInner->GetGlobalJSObject());
2226 0 : if (!outer) {
2227 0 : return NS_ERROR_FAILURE;
2228 : }
2229 :
2230 0 : js::SetProxyExtra(outer, 0, js::PrivateValue(aGlobalObject));
2231 :
2232 0 : return SetOuterObject(outer);
2233 : }
2234 :
2235 : nsresult
2236 0 : nsJSContext::SetOuterObject(JSObject* aOuterObject)
2237 : {
2238 : // Force our context's global object to be the outer.
2239 : // NB: JS_SetGlobalObject sets mContext->compartment.
2240 0 : JS_SetGlobalObject(mContext, aOuterObject);
2241 :
2242 : // Set up the prototype for the outer object.
2243 0 : JSObject *inner = JS_GetParent(aOuterObject);
2244 0 : JS_SetPrototype(mContext, aOuterObject, JS_GetPrototype(inner));
2245 :
2246 0 : return NS_OK;
2247 : }
2248 :
2249 : nsresult
2250 0 : nsJSContext::InitOuterWindow()
2251 : {
2252 0 : JSObject *global = JS_ObjectToInnerObject(mContext, JS_GetGlobalObject(mContext));
2253 :
2254 0 : nsresult rv = InitClasses(global); // this will complete global object initialization
2255 0 : NS_ENSURE_SUCCESS(rv, rv);
2256 :
2257 0 : return NS_OK;
2258 : }
2259 :
2260 : nsresult
2261 0 : nsJSContext::InitializeExternalClasses()
2262 : {
2263 0 : nsScriptNameSpaceManager *nameSpaceManager = nsJSRuntime::GetNameSpaceManager();
2264 0 : NS_ENSURE_TRUE(nameSpaceManager, NS_ERROR_NOT_INITIALIZED);
2265 :
2266 0 : return nameSpaceManager->InitForContext(this);
2267 : }
2268 :
2269 : nsresult
2270 0 : nsJSContext::SetProperty(JSObject* aTarget, const char* aPropName, nsISupports* aArgs)
2271 : {
2272 : PRUint32 argc;
2273 0 : jsval *argv = nsnull;
2274 :
2275 0 : JSAutoRequest ar(mContext);
2276 :
2277 0 : Maybe<nsRootedJSValueArray> tempStorage;
2278 :
2279 : nsresult rv =
2280 0 : ConvertSupportsTojsvals(aArgs, GetNativeGlobal(), &argc, &argv, tempStorage);
2281 0 : NS_ENSURE_SUCCESS(rv, rv);
2282 :
2283 : jsval vargs;
2284 :
2285 : // got the arguments, now attach them.
2286 :
2287 : // window.dialogArguments is supposed to be an array if a JS array
2288 : // was passed to showModalDialog(), deal with that here.
2289 0 : if (strcmp(aPropName, "dialogArguments") == 0 && argc <= 1) {
2290 0 : vargs = argc ? argv[0] : JSVAL_VOID;
2291 : } else {
2292 0 : for (PRUint32 i = 0; i < argc; ++i) {
2293 0 : if (!JS_WrapValue(mContext, &argv[i])) {
2294 0 : return NS_ERROR_FAILURE;
2295 : }
2296 : }
2297 :
2298 0 : JSObject *args = ::JS_NewArrayObject(mContext, argc, argv);
2299 0 : vargs = OBJECT_TO_JSVAL(args);
2300 : }
2301 :
2302 : // Make sure to use JS_DefineProperty here so that we can override
2303 : // readonly XPConnect properties here as well (read dialogArguments).
2304 0 : return JS_DefineProperty(mContext, aTarget, aPropName, vargs, NULL, NULL, 0)
2305 : ? NS_OK
2306 0 : : NS_ERROR_FAILURE;
2307 : }
2308 :
2309 : nsresult
2310 0 : nsJSContext::ConvertSupportsTojsvals(nsISupports *aArgs,
2311 : JSObject *aScope,
2312 : PRUint32 *aArgc,
2313 : jsval **aArgv,
2314 : Maybe<nsRootedJSValueArray> &aTempStorage)
2315 : {
2316 0 : nsresult rv = NS_OK;
2317 :
2318 : // If the array implements nsIJSArgArray, just grab the values directly.
2319 0 : nsCOMPtr<nsIJSArgArray> fastArray = do_QueryInterface(aArgs);
2320 0 : if (fastArray != nsnull)
2321 0 : return fastArray->GetArgs(aArgc, reinterpret_cast<void **>(aArgv));
2322 :
2323 : // Take the slower path converting each item.
2324 : // Handle only nsIArray and nsIVariant. nsIArray is only needed for
2325 : // SetProperty('arguments', ...);
2326 :
2327 0 : *aArgv = nsnull;
2328 0 : *aArgc = 0;
2329 :
2330 0 : nsIXPConnect *xpc = nsContentUtils::XPConnect();
2331 0 : NS_ENSURE_TRUE(xpc, NS_ERROR_UNEXPECTED);
2332 :
2333 0 : if (!aArgs)
2334 0 : return NS_OK;
2335 : PRUint32 argCount;
2336 : // This general purpose function may need to convert an arg array
2337 : // (window.arguments, event-handler args) and a generic property.
2338 0 : nsCOMPtr<nsIArray> argsArray(do_QueryInterface(aArgs));
2339 :
2340 0 : if (argsArray) {
2341 0 : rv = argsArray->GetLength(&argCount);
2342 0 : NS_ENSURE_SUCCESS(rv, rv);
2343 0 : if (argCount == 0)
2344 0 : return NS_OK;
2345 : } else {
2346 0 : argCount = 1; // the nsISupports which is not an array
2347 : }
2348 :
2349 : // Use the caller's auto guards to release and unroot.
2350 0 : aTempStorage.construct(mContext);
2351 0 : bool ok = aTempStorage.ref().SetCapacity(mContext, argCount);
2352 0 : NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
2353 0 : jsval *argv = aTempStorage.ref().Elements();
2354 :
2355 0 : if (argsArray) {
2356 0 : for (PRUint32 argCtr = 0; argCtr < argCount && NS_SUCCEEDED(rv); argCtr++) {
2357 0 : nsCOMPtr<nsISupports> arg;
2358 0 : jsval *thisval = argv + argCtr;
2359 0 : argsArray->QueryElementAt(argCtr, NS_GET_IID(nsISupports),
2360 0 : getter_AddRefs(arg));
2361 0 : if (!arg) {
2362 0 : *thisval = JSVAL_NULL;
2363 0 : continue;
2364 : }
2365 0 : nsCOMPtr<nsIVariant> variant(do_QueryInterface(arg));
2366 0 : if (variant != nsnull) {
2367 0 : rv = xpc->VariantToJS(mContext, aScope, variant, thisval);
2368 : } else {
2369 : // And finally, support the nsISupportsPrimitives supplied
2370 : // by the AppShell. It generally will pass only strings, but
2371 : // as we have code for handling all, we may as well use it.
2372 0 : rv = AddSupportsPrimitiveTojsvals(arg, thisval);
2373 0 : if (rv == NS_ERROR_NO_INTERFACE) {
2374 : // something else - probably an event object or similar -
2375 : // just wrap it.
2376 : #ifdef NS_DEBUG
2377 : // but first, check its not another nsISupportsPrimitive, as
2378 : // these are now deprecated for use with script contexts.
2379 0 : nsCOMPtr<nsISupportsPrimitive> prim(do_QueryInterface(arg));
2380 0 : NS_ASSERTION(prim == nsnull,
2381 : "Don't pass nsISupportsPrimitives - use nsIVariant!");
2382 : #endif
2383 0 : nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
2384 : jsval v;
2385 : rv = nsContentUtils::WrapNative(mContext, aScope, arg, &v,
2386 0 : getter_AddRefs(wrapper));
2387 0 : if (NS_SUCCEEDED(rv)) {
2388 0 : *thisval = v;
2389 : }
2390 : }
2391 : }
2392 : }
2393 : } else {
2394 0 : nsCOMPtr<nsIVariant> variant = do_QueryInterface(aArgs);
2395 0 : if (variant) {
2396 0 : rv = xpc->VariantToJS(mContext, aScope, variant, argv);
2397 : } else {
2398 0 : NS_ERROR("Not an array, not an interface?");
2399 0 : rv = NS_ERROR_UNEXPECTED;
2400 : }
2401 : }
2402 0 : if (NS_FAILED(rv))
2403 0 : return rv;
2404 0 : *aArgv = argv;
2405 0 : *aArgc = argCount;
2406 0 : return NS_OK;
2407 : }
2408 :
2409 : // This really should go into xpconnect somewhere...
2410 : nsresult
2411 0 : nsJSContext::AddSupportsPrimitiveTojsvals(nsISupports *aArg, jsval *aArgv)
2412 : {
2413 0 : NS_PRECONDITION(aArg, "Empty arg");
2414 :
2415 0 : nsCOMPtr<nsISupportsPrimitive> argPrimitive(do_QueryInterface(aArg));
2416 0 : if (!argPrimitive)
2417 0 : return NS_ERROR_NO_INTERFACE;
2418 :
2419 0 : JSContext *cx = mContext;
2420 : PRUint16 type;
2421 0 : argPrimitive->GetType(&type);
2422 :
2423 0 : switch(type) {
2424 : case nsISupportsPrimitive::TYPE_CSTRING : {
2425 0 : nsCOMPtr<nsISupportsCString> p(do_QueryInterface(argPrimitive));
2426 0 : NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
2427 :
2428 0 : nsCAutoString data;
2429 :
2430 0 : p->GetData(data);
2431 :
2432 :
2433 0 : JSString *str = ::JS_NewStringCopyN(cx, data.get(), data.Length());
2434 0 : NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY);
2435 :
2436 0 : *aArgv = STRING_TO_JSVAL(str);
2437 :
2438 0 : break;
2439 : }
2440 : case nsISupportsPrimitive::TYPE_STRING : {
2441 0 : nsCOMPtr<nsISupportsString> p(do_QueryInterface(argPrimitive));
2442 0 : NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
2443 :
2444 0 : nsAutoString data;
2445 :
2446 0 : p->GetData(data);
2447 :
2448 : // cast is probably safe since wchar_t and jschar are expected
2449 : // to be equivalent; both unsigned 16-bit entities
2450 : JSString *str =
2451 : ::JS_NewUCStringCopyN(cx,
2452 0 : reinterpret_cast<const jschar *>(data.get()),
2453 0 : data.Length());
2454 0 : NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY);
2455 :
2456 0 : *aArgv = STRING_TO_JSVAL(str);
2457 0 : break;
2458 : }
2459 : case nsISupportsPrimitive::TYPE_PRBOOL : {
2460 0 : nsCOMPtr<nsISupportsPRBool> p(do_QueryInterface(argPrimitive));
2461 0 : NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
2462 :
2463 : bool data;
2464 :
2465 0 : p->GetData(&data);
2466 :
2467 0 : *aArgv = BOOLEAN_TO_JSVAL(data);
2468 :
2469 0 : break;
2470 : }
2471 : case nsISupportsPrimitive::TYPE_PRUINT8 : {
2472 0 : nsCOMPtr<nsISupportsPRUint8> p(do_QueryInterface(argPrimitive));
2473 0 : NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
2474 :
2475 : PRUint8 data;
2476 :
2477 0 : p->GetData(&data);
2478 :
2479 0 : *aArgv = INT_TO_JSVAL(data);
2480 :
2481 0 : break;
2482 : }
2483 : case nsISupportsPrimitive::TYPE_PRUINT16 : {
2484 0 : nsCOMPtr<nsISupportsPRUint16> p(do_QueryInterface(argPrimitive));
2485 0 : NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
2486 :
2487 : PRUint16 data;
2488 :
2489 0 : p->GetData(&data);
2490 :
2491 0 : *aArgv = INT_TO_JSVAL(data);
2492 :
2493 0 : break;
2494 : }
2495 : case nsISupportsPrimitive::TYPE_PRUINT32 : {
2496 0 : nsCOMPtr<nsISupportsPRUint32> p(do_QueryInterface(argPrimitive));
2497 0 : NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
2498 :
2499 : PRUint32 data;
2500 :
2501 0 : p->GetData(&data);
2502 :
2503 0 : *aArgv = INT_TO_JSVAL(data);
2504 :
2505 0 : break;
2506 : }
2507 : case nsISupportsPrimitive::TYPE_CHAR : {
2508 0 : nsCOMPtr<nsISupportsChar> p(do_QueryInterface(argPrimitive));
2509 0 : NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
2510 :
2511 : char data;
2512 :
2513 0 : p->GetData(&data);
2514 :
2515 0 : JSString *str = ::JS_NewStringCopyN(cx, &data, 1);
2516 0 : NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY);
2517 :
2518 0 : *aArgv = STRING_TO_JSVAL(str);
2519 :
2520 0 : break;
2521 : }
2522 : case nsISupportsPrimitive::TYPE_PRINT16 : {
2523 0 : nsCOMPtr<nsISupportsPRInt16> p(do_QueryInterface(argPrimitive));
2524 0 : NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
2525 :
2526 : PRInt16 data;
2527 :
2528 0 : p->GetData(&data);
2529 :
2530 0 : *aArgv = INT_TO_JSVAL(data);
2531 :
2532 0 : break;
2533 : }
2534 : case nsISupportsPrimitive::TYPE_PRINT32 : {
2535 0 : nsCOMPtr<nsISupportsPRInt32> p(do_QueryInterface(argPrimitive));
2536 0 : NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
2537 :
2538 : PRInt32 data;
2539 :
2540 0 : p->GetData(&data);
2541 :
2542 0 : *aArgv = INT_TO_JSVAL(data);
2543 :
2544 0 : break;
2545 : }
2546 : case nsISupportsPrimitive::TYPE_FLOAT : {
2547 0 : nsCOMPtr<nsISupportsFloat> p(do_QueryInterface(argPrimitive));
2548 0 : NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
2549 :
2550 : float data;
2551 :
2552 0 : p->GetData(&data);
2553 :
2554 0 : JSBool ok = ::JS_NewNumberValue(cx, data, aArgv);
2555 0 : NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
2556 :
2557 0 : break;
2558 : }
2559 : case nsISupportsPrimitive::TYPE_DOUBLE : {
2560 0 : nsCOMPtr<nsISupportsDouble> p(do_QueryInterface(argPrimitive));
2561 0 : NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
2562 :
2563 : double data;
2564 :
2565 0 : p->GetData(&data);
2566 :
2567 0 : JSBool ok = ::JS_NewNumberValue(cx, data, aArgv);
2568 0 : NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
2569 :
2570 0 : break;
2571 : }
2572 : case nsISupportsPrimitive::TYPE_INTERFACE_POINTER : {
2573 0 : nsCOMPtr<nsISupportsInterfacePointer> p(do_QueryInterface(argPrimitive));
2574 0 : NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
2575 :
2576 0 : nsCOMPtr<nsISupports> data;
2577 0 : nsIID *iid = nsnull;
2578 :
2579 0 : p->GetData(getter_AddRefs(data));
2580 0 : p->GetDataIID(&iid);
2581 0 : NS_ENSURE_TRUE(iid, NS_ERROR_UNEXPECTED);
2582 :
2583 0 : AutoFree iidGuard(iid); // Free iid upon destruction.
2584 :
2585 0 : nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
2586 : jsval v;
2587 : nsresult rv = nsContentUtils::WrapNative(cx, ::JS_GetGlobalObject(cx),
2588 : data, iid, &v,
2589 0 : getter_AddRefs(wrapper));
2590 0 : NS_ENSURE_SUCCESS(rv, rv);
2591 :
2592 0 : *aArgv = v;
2593 :
2594 0 : break;
2595 : }
2596 : case nsISupportsPrimitive::TYPE_ID :
2597 : case nsISupportsPrimitive::TYPE_PRUINT64 :
2598 : case nsISupportsPrimitive::TYPE_PRINT64 :
2599 : case nsISupportsPrimitive::TYPE_PRTIME :
2600 : case nsISupportsPrimitive::TYPE_VOID : {
2601 0 : NS_WARNING("Unsupported primitive type used");
2602 0 : *aArgv = JSVAL_NULL;
2603 0 : break;
2604 : }
2605 : default : {
2606 0 : NS_WARNING("Unknown primitive type used");
2607 0 : *aArgv = JSVAL_NULL;
2608 0 : break;
2609 : }
2610 : }
2611 0 : return NS_OK;
2612 : }
2613 :
2614 : #ifdef NS_TRACE_MALLOC
2615 :
2616 : #include <errno.h> // XXX assume Linux if NS_TRACE_MALLOC
2617 : #include <fcntl.h>
2618 : #ifdef XP_UNIX
2619 : #include <unistd.h>
2620 : #endif
2621 : #ifdef XP_WIN32
2622 : #include <io.h>
2623 : #endif
2624 : #include "nsTraceMalloc.h"
2625 :
2626 : static JSBool
2627 : CheckUniversalXPConnectForTraceMalloc(JSContext *cx)
2628 : {
2629 : bool hasCap = false;
2630 : nsresult rv = nsContentUtils::GetSecurityManager()->
2631 : IsCapabilityEnabled("UniversalXPConnect", &hasCap);
2632 : if (NS_SUCCEEDED(rv) && hasCap)
2633 : return JS_TRUE;
2634 : JS_ReportError(cx, "trace-malloc functions require UniversalXPConnect");
2635 : return JS_FALSE;
2636 : }
2637 :
2638 : static JSBool
2639 : TraceMallocDisable(JSContext *cx, unsigned argc, jsval *vp)
2640 : {
2641 : if (!CheckUniversalXPConnectForTraceMalloc(cx))
2642 : return JS_FALSE;
2643 :
2644 : NS_TraceMallocDisable();
2645 : JS_SET_RVAL(cx, vp, JSVAL_VOID);
2646 : return JS_TRUE;
2647 : }
2648 :
2649 : static JSBool
2650 : TraceMallocEnable(JSContext *cx, unsigned argc, jsval *vp)
2651 : {
2652 : if (!CheckUniversalXPConnectForTraceMalloc(cx))
2653 : return JS_FALSE;
2654 :
2655 : NS_TraceMallocEnable();
2656 : JS_SET_RVAL(cx, vp, JSVAL_VOID);
2657 : return JS_TRUE;
2658 : }
2659 :
2660 : static JSBool
2661 : TraceMallocOpenLogFile(JSContext *cx, unsigned argc, jsval *vp)
2662 : {
2663 : int fd;
2664 : JSString *str;
2665 :
2666 : if (!CheckUniversalXPConnectForTraceMalloc(cx))
2667 : return JS_FALSE;
2668 :
2669 : if (argc == 0) {
2670 : fd = -1;
2671 : } else {
2672 : str = JS_ValueToString(cx, JS_ARGV(cx, vp)[0]);
2673 : if (!str)
2674 : return JS_FALSE;
2675 : JSAutoByteString filename(cx, str);
2676 : if (!filename)
2677 : return JS_FALSE;
2678 : fd = open(filename.ptr(), O_CREAT | O_WRONLY | O_TRUNC, 0644);
2679 : if (fd < 0) {
2680 : JS_ReportError(cx, "can't open %s: %s", filename.ptr(), strerror(errno));
2681 : return JS_FALSE;
2682 : }
2683 : }
2684 : JS_SET_RVAL(cx, vp, INT_TO_JSVAL(fd));
2685 : return JS_TRUE;
2686 : }
2687 :
2688 : static JSBool
2689 : TraceMallocChangeLogFD(JSContext *cx, unsigned argc, jsval *vp)
2690 : {
2691 : if (!CheckUniversalXPConnectForTraceMalloc(cx))
2692 : return JS_FALSE;
2693 :
2694 : int32_t fd, oldfd;
2695 : if (argc == 0) {
2696 : oldfd = -1;
2697 : } else {
2698 : if (!JS_ValueToECMAInt32(cx, JS_ARGV(cx, vp)[0], &fd))
2699 : return JS_FALSE;
2700 : oldfd = NS_TraceMallocChangeLogFD(fd);
2701 : if (oldfd == -2) {
2702 : JS_ReportOutOfMemory(cx);
2703 : return JS_FALSE;
2704 : }
2705 : }
2706 : JS_SET_RVAL(cx, vp, INT_TO_JSVAL(oldfd));
2707 : return JS_TRUE;
2708 : }
2709 :
2710 : static JSBool
2711 : TraceMallocCloseLogFD(JSContext *cx, unsigned argc, jsval *vp)
2712 : {
2713 : if (!CheckUniversalXPConnectForTraceMalloc(cx))
2714 : return JS_FALSE;
2715 :
2716 : int32_t fd;
2717 : JS_SET_RVAL(cx, vp, JSVAL_VOID);
2718 : if (argc == 0)
2719 : return JS_TRUE;
2720 : if (!JS_ValueToECMAInt32(cx, JS_ARGV(cx, vp)[0], &fd))
2721 : return JS_FALSE;
2722 : NS_TraceMallocCloseLogFD((int) fd);
2723 : return JS_TRUE;
2724 : }
2725 :
2726 : static JSBool
2727 : TraceMallocLogTimestamp(JSContext *cx, unsigned argc, jsval *vp)
2728 : {
2729 : if (!CheckUniversalXPConnectForTraceMalloc(cx))
2730 : return JS_FALSE;
2731 :
2732 : JSString *str = JS_ValueToString(cx, argc ? JS_ARGV(cx, vp)[0] : JSVAL_VOID);
2733 : if (!str)
2734 : return JS_FALSE;
2735 : JSAutoByteString caption(cx, str);
2736 : if (!caption)
2737 : return JS_FALSE;
2738 : NS_TraceMallocLogTimestamp(caption.ptr());
2739 : JS_SET_RVAL(cx, vp, JSVAL_VOID);
2740 : return JS_TRUE;
2741 : }
2742 :
2743 : static JSBool
2744 : TraceMallocDumpAllocations(JSContext *cx, unsigned argc, jsval *vp)
2745 : {
2746 : if (!CheckUniversalXPConnectForTraceMalloc(cx))
2747 : return JS_FALSE;
2748 :
2749 : JSString *str = JS_ValueToString(cx, argc ? JS_ARGV(cx, vp)[0] : JSVAL_VOID);
2750 : if (!str)
2751 : return JS_FALSE;
2752 : JSAutoByteString pathname(cx, str);
2753 : if (!pathname)
2754 : return JS_FALSE;
2755 : if (NS_TraceMallocDumpAllocations(pathname.ptr()) < 0) {
2756 : JS_ReportError(cx, "can't dump to %s: %s", pathname.ptr(), strerror(errno));
2757 : return JS_FALSE;
2758 : }
2759 : JS_SET_RVAL(cx, vp, JSVAL_VOID);
2760 : return JS_TRUE;
2761 : }
2762 :
2763 : static JSFunctionSpec TraceMallocFunctions[] = {
2764 : {"TraceMallocDisable", TraceMallocDisable, 0, 0},
2765 : {"TraceMallocEnable", TraceMallocEnable, 0, 0},
2766 : {"TraceMallocOpenLogFile", TraceMallocOpenLogFile, 1, 0},
2767 : {"TraceMallocChangeLogFD", TraceMallocChangeLogFD, 1, 0},
2768 : {"TraceMallocCloseLogFD", TraceMallocCloseLogFD, 1, 0},
2769 : {"TraceMallocLogTimestamp", TraceMallocLogTimestamp, 1, 0},
2770 : {"TraceMallocDumpAllocations", TraceMallocDumpAllocations, 1, 0},
2771 : {nsnull, nsnull, 0, 0}
2772 : };
2773 :
2774 : #endif /* NS_TRACE_MALLOC */
2775 :
2776 : #ifdef MOZ_JPROF
2777 :
2778 : #include <signal.h>
2779 :
2780 : inline bool
2781 : IsJProfAction(struct sigaction *action)
2782 : {
2783 : return (action->sa_sigaction &&
2784 : (action->sa_flags & (SA_RESTART | SA_SIGINFO)) == (SA_RESTART | SA_SIGINFO));
2785 : }
2786 :
2787 : void NS_JProfStartProfiling();
2788 : void NS_JProfStopProfiling();
2789 : void NS_JProfClearCircular();
2790 :
2791 : static JSBool
2792 : JProfStartProfilingJS(JSContext *cx, unsigned argc, jsval *vp)
2793 : {
2794 : NS_JProfStartProfiling();
2795 : return JS_TRUE;
2796 : }
2797 :
2798 : void NS_JProfStartProfiling()
2799 : {
2800 : // Figure out whether we're dealing with SIGPROF, SIGALRM, or
2801 : // SIGPOLL profiling (SIGALRM for JP_REALTIME, SIGPOLL for
2802 : // JP_RTC_HZ)
2803 : struct sigaction action;
2804 :
2805 : // Must check ALRM before PROF since both are enabled for real-time
2806 : sigaction(SIGALRM, nsnull, &action);
2807 : //printf("SIGALRM: %p, flags = %x\n",action.sa_sigaction,action.sa_flags);
2808 : if (IsJProfAction(&action)) {
2809 : //printf("Beginning real-time jprof profiling.\n");
2810 : raise(SIGALRM);
2811 : return;
2812 : }
2813 :
2814 : sigaction(SIGPROF, nsnull, &action);
2815 : //printf("SIGPROF: %p, flags = %x\n",action.sa_sigaction,action.sa_flags);
2816 : if (IsJProfAction(&action)) {
2817 : //printf("Beginning process-time jprof profiling.\n");
2818 : raise(SIGPROF);
2819 : return;
2820 : }
2821 :
2822 : sigaction(SIGPOLL, nsnull, &action);
2823 : //printf("SIGPOLL: %p, flags = %x\n",action.sa_sigaction,action.sa_flags);
2824 : if (IsJProfAction(&action)) {
2825 : //printf("Beginning rtc-based jprof profiling.\n");
2826 : raise(SIGPOLL);
2827 : return;
2828 : }
2829 :
2830 : printf("Could not start jprof-profiling since JPROF_FLAGS was not set.\n");
2831 : }
2832 :
2833 : static JSBool
2834 : JProfStopProfilingJS(JSContext *cx, unsigned argc, jsval *vp)
2835 : {
2836 : NS_JProfStopProfiling();
2837 : return JS_TRUE;
2838 : }
2839 :
2840 : void
2841 : NS_JProfStopProfiling()
2842 : {
2843 : raise(SIGUSR1);
2844 : //printf("Stopped jprof profiling.\n");
2845 : }
2846 :
2847 : static JSBool
2848 : JProfClearCircularJS(JSContext *cx, unsigned argc, jsval *vp)
2849 : {
2850 : NS_JProfClearCircular();
2851 : return JS_TRUE;
2852 : }
2853 :
2854 : void
2855 : NS_JProfClearCircular()
2856 : {
2857 : raise(SIGUSR2);
2858 : //printf("cleared jprof buffer\n");
2859 : }
2860 :
2861 : static JSBool
2862 : JProfSaveCircularJS(JSContext *cx, unsigned argc, jsval *vp)
2863 : {
2864 : // Not ideal...
2865 : NS_JProfStopProfiling();
2866 : NS_JProfStartProfiling();
2867 : return JS_TRUE;
2868 : }
2869 :
2870 : static JSFunctionSpec JProfFunctions[] = {
2871 : {"JProfStartProfiling", JProfStartProfilingJS, 0, 0},
2872 : {"JProfStopProfiling", JProfStopProfilingJS, 0, 0},
2873 : {"JProfClearCircular", JProfClearCircularJS, 0, 0},
2874 : {"JProfSaveCircular", JProfSaveCircularJS, 0, 0},
2875 : {nsnull, nsnull, 0, 0}
2876 : };
2877 :
2878 : #endif /* defined(MOZ_JPROF) */
2879 :
2880 : #ifdef MOZ_DMD
2881 :
2882 : // See https://wiki.mozilla.org/Performance/MemShrink/DMD for instructions on
2883 : // how to use DMD.
2884 :
2885 : static JSBool
2886 : DMDCheckJS(JSContext *cx, unsigned argc, jsval *vp)
2887 : {
2888 : mozilla::DMDCheckAndDump();
2889 : return JS_TRUE;
2890 : }
2891 :
2892 : static JSFunctionSpec DMDFunctions[] = {
2893 : {"DMD", DMDCheckJS, 0, 0},
2894 : {nsnull, nsnull, 0, 0}
2895 : };
2896 :
2897 : #endif /* defined(MOZ_DMD) */
2898 :
2899 : nsresult
2900 0 : nsJSContext::InitClasses(JSObject* aGlobalObj)
2901 : {
2902 0 : nsresult rv = InitializeExternalClasses();
2903 0 : NS_ENSURE_SUCCESS(rv, rv);
2904 :
2905 0 : JSAutoRequest ar(mContext);
2906 :
2907 0 : ::JS_SetOptions(mContext, mDefaultJSOptions);
2908 :
2909 : // Attempt to initialize profiling functions
2910 0 : ::JS_DefineProfilingFunctions(mContext, aGlobalObj);
2911 :
2912 : #ifdef NS_TRACE_MALLOC
2913 : // Attempt to initialize TraceMalloc functions
2914 : ::JS_DefineFunctions(mContext, aGlobalObj, TraceMallocFunctions);
2915 : #endif
2916 :
2917 : #ifdef MOZ_JPROF
2918 : // Attempt to initialize JProf functions
2919 : ::JS_DefineFunctions(mContext, aGlobalObj, JProfFunctions);
2920 : #endif
2921 :
2922 : #ifdef MOZ_DMD
2923 : // Attempt to initialize DMD functions
2924 : ::JS_DefineFunctions(mContext, aGlobalObj, DMDFunctions);
2925 : #endif
2926 :
2927 0 : JSOptionChangedCallback(js_options_dot_str, this);
2928 :
2929 0 : return rv;
2930 : }
2931 :
2932 : void
2933 0 : nsJSContext::WillInitializeContext()
2934 : {
2935 0 : mIsInitialized = false;
2936 0 : }
2937 :
2938 : void
2939 0 : nsJSContext::DidInitializeContext()
2940 : {
2941 0 : mIsInitialized = true;
2942 0 : }
2943 :
2944 : bool
2945 0 : nsJSContext::IsContextInitialized()
2946 : {
2947 0 : return mIsInitialized;
2948 : }
2949 :
2950 : void
2951 0 : nsJSContext::ScriptEvaluated(bool aTerminated)
2952 : {
2953 0 : if (aTerminated && mTerminations) {
2954 : // Make sure to null out mTerminations before doing anything that
2955 : // might cause new termination funcs to be added!
2956 0 : nsJSContext::TerminationFuncClosure* start = mTerminations;
2957 0 : mTerminations = nsnull;
2958 :
2959 0 : for (nsJSContext::TerminationFuncClosure* cur = start;
2960 : cur;
2961 : cur = cur->mNext) {
2962 0 : (*(cur->mTerminationFunc))(cur->mTerminationFuncArg);
2963 : }
2964 0 : delete start;
2965 : }
2966 :
2967 0 : JS_MaybeGC(mContext);
2968 :
2969 0 : if (aTerminated) {
2970 0 : mOperationCallbackTime = 0;
2971 0 : mModalStateTime = 0;
2972 : }
2973 0 : }
2974 :
2975 : void
2976 0 : nsJSContext::SetTerminationFunction(nsScriptTerminationFunc aFunc,
2977 : nsIDOMWindow* aRef)
2978 : {
2979 0 : NS_PRECONDITION(GetExecutingScript(), "should be executing script");
2980 :
2981 : nsJSContext::TerminationFuncClosure* newClosure =
2982 0 : new nsJSContext::TerminationFuncClosure(aFunc, aRef, mTerminations);
2983 0 : mTerminations = newClosure;
2984 0 : }
2985 :
2986 : bool
2987 0 : nsJSContext::GetScriptsEnabled()
2988 : {
2989 0 : return mScriptsEnabled;
2990 : }
2991 :
2992 : void
2993 0 : nsJSContext::SetScriptsEnabled(bool aEnabled, bool aFireTimeouts)
2994 : {
2995 : // eeek - this seems the wrong way around - the global should callback
2996 : // into each context, so every language is disabled.
2997 0 : mScriptsEnabled = aEnabled;
2998 :
2999 0 : nsIScriptGlobalObject *global = GetGlobalObject();
3000 :
3001 0 : if (global) {
3002 0 : global->SetScriptsEnabled(aEnabled, aFireTimeouts);
3003 : }
3004 0 : }
3005 :
3006 :
3007 : bool
3008 0 : nsJSContext::GetProcessingScriptTag()
3009 : {
3010 0 : return mProcessingScriptTag;
3011 : }
3012 :
3013 : void
3014 0 : nsJSContext::SetProcessingScriptTag(bool aFlag)
3015 : {
3016 0 : mProcessingScriptTag = aFlag;
3017 0 : }
3018 :
3019 : bool
3020 0 : nsJSContext::GetExecutingScript()
3021 : {
3022 0 : return JS_IsRunning(mContext) || mExecuteDepth > 0;
3023 : }
3024 :
3025 : void
3026 0 : nsJSContext::SetGCOnDestruction(bool aGCOnDestruction)
3027 : {
3028 0 : mGCOnDestruction = aGCOnDestruction;
3029 0 : }
3030 :
3031 : NS_IMETHODIMP
3032 0 : nsJSContext::ScriptExecuted()
3033 : {
3034 0 : ScriptEvaluated(!::JS_IsRunning(mContext));
3035 :
3036 0 : return NS_OK;
3037 : }
3038 :
3039 : //static
3040 : void
3041 4 : nsJSContext::GarbageCollectNow(js::gcreason::Reason reason, PRUint32 gckind)
3042 : {
3043 : NS_TIME_FUNCTION_MIN(1.0);
3044 8 : SAMPLE_LABEL("GC", "GarbageCollectNow");
3045 :
3046 4 : KillGCTimer();
3047 4 : KillShrinkGCBuffersTimer();
3048 :
3049 : // Reset sPendingLoadCount in case the timer that fired was a
3050 : // timer we scheduled due to a normal GC timer firing while
3051 : // documents were loading. If this happens we're waiting for a
3052 : // document that is taking a long time to load, and we effectively
3053 : // ignore the fact that the currently loading documents are still
3054 : // loading and move on as if they weren't.
3055 4 : sPendingLoadCount = 0;
3056 4 : sLoadingInProgress = false;
3057 :
3058 4 : if (nsContentUtils::XPConnect()) {
3059 4 : nsContentUtils::XPConnect()->GarbageCollect(reason, gckind);
3060 : }
3061 4 : }
3062 :
3063 : //static
3064 : void
3065 0 : nsJSContext::ShrinkGCBuffersNow()
3066 : {
3067 : NS_TIME_FUNCTION_MIN(1.0);
3068 0 : SAMPLE_LABEL("GC", "ShrinkGCBuffersNow");
3069 :
3070 0 : KillShrinkGCBuffersTimer();
3071 :
3072 0 : JS_ShrinkGCBuffers(nsJSRuntime::sRuntime);
3073 0 : }
3074 :
3075 : //Static
3076 : void
3077 48 : nsJSContext::CycleCollectNow(nsICycleCollectorListener *aListener,
3078 : PRInt32 aExtraForgetSkippableCalls)
3079 : {
3080 48 : if (!NS_IsMainThread()) {
3081 0 : return;
3082 : }
3083 :
3084 48 : if (sCCLockedOut) {
3085 : // We're in the middle of an incremental GC; finish it first
3086 0 : nsJSContext::GarbageCollectNow(js::gcreason::CC_FORCED, nsGCNormal);
3087 : }
3088 :
3089 96 : SAMPLE_LABEL("GC", "CycleCollectNow");
3090 : NS_TIME_FUNCTION_MIN(1.0);
3091 :
3092 48 : KillCCTimer();
3093 :
3094 48 : PRTime start = PR_Now();
3095 :
3096 48 : PRUint32 suspected = nsCycleCollector_suspectedCount();
3097 :
3098 48 : for (PRInt32 i = 0; i < aExtraForgetSkippableCalls; ++i) {
3099 0 : nsCycleCollector_forgetSkippable();
3100 : }
3101 :
3102 : // nsCycleCollector_forgetSkippable may mark some gray js to black.
3103 48 : if (!sCleanupSinceLastGC && aExtraForgetSkippableCalls >= 0) {
3104 0 : nsCycleCollector_forgetSkippable();
3105 : }
3106 48 : nsCycleCollectorResults ccResults;
3107 48 : nsCycleCollector_collect(&ccResults, aListener);
3108 48 : sCCollectedWaitingForGC += ccResults.mFreedRefCounted + ccResults.mFreedGCed;
3109 :
3110 : // If we collected a substantial amount of cycles, poke the GC since more objects
3111 : // might be unreachable now.
3112 48 : if (sCCollectedWaitingForGC > 250) {
3113 16 : PokeGC(js::gcreason::CC_WAITING);
3114 : }
3115 :
3116 48 : PRTime now = PR_Now();
3117 :
3118 48 : if (sLastCCEndTime) {
3119 8 : PRUint32 timeBetween = (PRUint32)(start - sLastCCEndTime) / PR_USEC_PER_SEC;
3120 8 : Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_TIME_BETWEEN, timeBetween);
3121 : }
3122 48 : sLastCCEndTime = now;
3123 :
3124 : Telemetry::Accumulate(Telemetry::FORGET_SKIPPABLE_MAX,
3125 48 : sMaxForgetSkippableTime / PR_USEC_PER_MSEC);
3126 :
3127 48 : if (sPostGCEventsToConsole) {
3128 0 : PRTime delta = 0;
3129 0 : if (sFirstCollectionTime) {
3130 0 : delta = now - sFirstCollectionTime;
3131 : } else {
3132 0 : sFirstCollectionTime = now;
3133 : }
3134 :
3135 0 : nsString gcmsg;
3136 0 : if (ccResults.mForcedGC) {
3137 0 : gcmsg.AssignLiteral(", forced a GC");
3138 : }
3139 :
3140 0 : NS_NAMED_MULTILINE_LITERAL_STRING(kFmt,
3141 : NS_LL("CC(T+%.1f) duration: %llums, suspected: %lu, visited: %lu RCed and %lu GCed, collected: %lu RCed and %lu GCed (%lu waiting for GC)%s\n")
3142 : NS_LL("ForgetSkippable %lu times before CC, min: %lu ms, max: %lu ms, avg: %lu ms, total: %lu ms, removed: %lu"));
3143 0 : nsString msg;
3144 0 : PRUint32 cleanups = sForgetSkippableBeforeCC ? sForgetSkippableBeforeCC : 1;
3145 : sMinForgetSkippableTime = (sMinForgetSkippableTime == PR_UINT32_MAX)
3146 0 : ? 0 : sMinForgetSkippableTime;
3147 : msg.Adopt(nsTextFormatter::smprintf(kFmt.get(), double(delta) / PR_USEC_PER_SEC,
3148 : (now - start) / PR_USEC_PER_MSEC, suspected,
3149 : ccResults.mVisitedRefCounted, ccResults.mVisitedGCed,
3150 : ccResults.mFreedRefCounted, ccResults.mFreedGCed,
3151 : sCCollectedWaitingForGC, gcmsg.get(),
3152 : sForgetSkippableBeforeCC,
3153 : sMinForgetSkippableTime / PR_USEC_PER_MSEC,
3154 : sMaxForgetSkippableTime / PR_USEC_PER_MSEC,
3155 : (sTotalForgetSkippableTime / cleanups) /
3156 : PR_USEC_PER_MSEC,
3157 : sTotalForgetSkippableTime / PR_USEC_PER_MSEC,
3158 0 : sRemovedPurples));
3159 : nsCOMPtr<nsIConsoleService> cs =
3160 0 : do_GetService(NS_CONSOLESERVICE_CONTRACTID);
3161 0 : if (cs) {
3162 0 : cs->LogStringMessage(msg.get());
3163 : }
3164 : }
3165 48 : sMinForgetSkippableTime = PR_UINT32_MAX;
3166 48 : sMaxForgetSkippableTime = 0;
3167 48 : sTotalForgetSkippableTime = 0;
3168 48 : sRemovedPurples = 0;
3169 48 : sForgetSkippableBeforeCC = 0;
3170 48 : sCleanupSinceLastGC = true;
3171 48 : sNeedsFullCC = false;
3172 : }
3173 :
3174 : // static
3175 : void
3176 4 : GCTimerFired(nsITimer *aTimer, void *aClosure)
3177 : {
3178 4 : NS_RELEASE(sGCTimer);
3179 :
3180 4 : uintptr_t reason = reinterpret_cast<uintptr_t>(aClosure);
3181 4 : nsJSContext::GarbageCollectNow(static_cast<js::gcreason::Reason>(reason), nsGCIncremental);
3182 4 : }
3183 :
3184 : void
3185 0 : ShrinkGCBuffersTimerFired(nsITimer *aTimer, void *aClosure)
3186 : {
3187 0 : NS_RELEASE(sShrinkGCBuffersTimer);
3188 :
3189 0 : nsJSContext::ShrinkGCBuffersNow();
3190 0 : }
3191 :
3192 : static bool
3193 146 : ShouldTriggerCC(PRUint32 aSuspected)
3194 : {
3195 : return sNeedsFullCC ||
3196 : aSuspected > NS_CC_PURPLE_LIMIT ||
3197 146 : sLastCCEndTime + NS_CC_FORCED < PR_Now();
3198 : }
3199 :
3200 : static void
3201 276 : TimerFireForgetSkippable(PRUint32 aSuspected, bool aRemoveChildless)
3202 : {
3203 276 : PRTime startTime = PR_Now();
3204 276 : nsCycleCollector_forgetSkippable(aRemoveChildless);
3205 276 : sPreviousSuspectedCount = nsCycleCollector_suspectedCount();
3206 276 : sCleanupSinceLastGC = true;
3207 276 : PRTime delta = PR_Now() - startTime;
3208 276 : if (sMinForgetSkippableTime > delta) {
3209 153 : sMinForgetSkippableTime = delta;
3210 : }
3211 276 : if (sMaxForgetSkippableTime < delta) {
3212 167 : sMaxForgetSkippableTime = delta;
3213 : }
3214 276 : sTotalForgetSkippableTime += delta;
3215 276 : sRemovedPurples += (aSuspected - sPreviousSuspectedCount);
3216 276 : ++sForgetSkippableBeforeCC;
3217 276 : }
3218 :
3219 : static void
3220 1740 : CCTimerFired(nsITimer *aTimer, void *aClosure)
3221 : {
3222 1740 : if (sDidShutdown) {
3223 0 : return;
3224 : }
3225 :
3226 1740 : if (sCCLockedOut) {
3227 0 : PRTime now = PR_Now();
3228 0 : if (sCCLockedOutTime == 0) {
3229 0 : sCCLockedOutTime = now;
3230 0 : return;
3231 : }
3232 0 : if (now - sCCLockedOutTime < NS_MAX_CC_LOCKEDOUT_TIME) {
3233 0 : return;
3234 : }
3235 :
3236 : // Finish the current incremental GC
3237 0 : nsJSContext::GarbageCollectNow(js::gcreason::CC_FORCED, nsGCNormal);
3238 : }
3239 :
3240 1740 : ++sCCTimerFireCount;
3241 :
3242 : // During early timer fires, we only run forgetSkippable. During the first
3243 : // late timer fire, we decide if we are going to have a second and final
3244 : // late timer fire, where we may run the CC.
3245 1740 : const PRUint32 numEarlyTimerFires = NS_CC_DELAY / NS_CC_SKIPPABLE_DELAY - 2;
3246 1740 : bool isLateTimerFire = sCCTimerFireCount > numEarlyTimerFires;
3247 1740 : PRUint32 suspected = nsCycleCollector_suspectedCount();
3248 1740 : if (isLateTimerFire && ShouldTriggerCC(suspected)) {
3249 97 : if (sCCTimerFireCount == numEarlyTimerFires + 1) {
3250 49 : TimerFireForgetSkippable(suspected, true);
3251 49 : if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
3252 : // Our efforts to avoid a CC have failed, so we return to let the
3253 : // timer fire once more to trigger a CC.
3254 49 : return;
3255 : }
3256 : } else {
3257 : // We are in the final timer fire and still meet the conditions for
3258 : // triggering a CC.
3259 48 : nsJSContext::CycleCollectNow();
3260 : }
3261 1643 : } else if ((sPreviousSuspectedCount + 100) <= suspected) {
3262 : // Only do a forget skippable if there are more than a few new objects.
3263 227 : TimerFireForgetSkippable(suspected, false);
3264 : }
3265 :
3266 1691 : if (isLateTimerFire) {
3267 : // We have either just run the CC or decided we don't want to run the CC
3268 : // next time, so kill the timer.
3269 48 : sPreviousSuspectedCount = 0;
3270 48 : nsJSContext::KillCCTimer();
3271 : }
3272 : }
3273 :
3274 : // static
3275 : bool
3276 323 : nsJSContext::CleanupSinceLastGC()
3277 : {
3278 323 : return sCleanupSinceLastGC;
3279 : }
3280 :
3281 : // static
3282 : void
3283 0 : nsJSContext::LoadStart()
3284 : {
3285 0 : sLoadingInProgress = true;
3286 0 : ++sPendingLoadCount;
3287 0 : }
3288 :
3289 : // static
3290 : void
3291 0 : nsJSContext::LoadEnd()
3292 : {
3293 0 : if (!sLoadingInProgress)
3294 0 : return;
3295 :
3296 : // sPendingLoadCount is not a well managed load counter (and doesn't
3297 : // need to be), so make sure we don't make it wrap backwards here.
3298 0 : if (sPendingLoadCount > 0) {
3299 0 : --sPendingLoadCount;
3300 0 : return;
3301 : }
3302 :
3303 : // Its probably a good idea to GC soon since we have finished loading.
3304 0 : sLoadingInProgress = false;
3305 0 : PokeGC(js::gcreason::LOAD_END);
3306 : }
3307 :
3308 : // static
3309 : void
3310 16 : nsJSContext::PokeGC(js::gcreason::Reason aReason, int aDelay)
3311 : {
3312 16 : if (sGCTimer) {
3313 : // There's already a timer for GC'ing, just return
3314 6 : return;
3315 : }
3316 :
3317 10 : CallCreateInstance("@mozilla.org/timer;1", &sGCTimer);
3318 :
3319 10 : if (!sGCTimer) {
3320 : // Failed to create timer (probably because we're in XPCOM shutdown)
3321 0 : return;
3322 : }
3323 :
3324 : static bool first = true;
3325 :
3326 : sGCTimer->InitWithFuncCallback(GCTimerFired, reinterpret_cast<void *>(aReason),
3327 : aDelay
3328 : ? aDelay
3329 : : (first
3330 : ? NS_FIRST_GC_DELAY
3331 : : NS_GC_DELAY),
3332 10 : nsITimer::TYPE_ONE_SHOT);
3333 :
3334 10 : first = false;
3335 : }
3336 :
3337 : // static
3338 : void
3339 398 : nsJSContext::PokeShrinkGCBuffers()
3340 : {
3341 398 : if (sShrinkGCBuffersTimer) {
3342 0 : return;
3343 : }
3344 :
3345 398 : CallCreateInstance("@mozilla.org/timer;1", &sShrinkGCBuffersTimer);
3346 :
3347 398 : if (!sShrinkGCBuffersTimer) {
3348 : // Failed to create timer (probably because we're in XPCOM shutdown)
3349 248 : return;
3350 : }
3351 :
3352 : sShrinkGCBuffersTimer->InitWithFuncCallback(ShrinkGCBuffersTimerFired, nsnull,
3353 : NS_SHRINK_GC_BUFFERS_DELAY,
3354 150 : nsITimer::TYPE_ONE_SHOT);
3355 : }
3356 :
3357 : // static
3358 : void
3359 174926 : nsJSContext::MaybePokeCC()
3360 : {
3361 174926 : if (sCCTimer || sDidShutdown) {
3362 153788 : return;
3363 : }
3364 :
3365 63366 : if (sNeedsFullCC ||
3366 21138 : nsCycleCollector_suspectedCount() > 100 ||
3367 21090 : sLastCCEndTime + NS_CC_FORCED < PR_Now()) {
3368 1041 : sCCTimerFireCount = 0;
3369 1041 : CallCreateInstance("@mozilla.org/timer;1", &sCCTimer);
3370 1041 : if (!sCCTimer) {
3371 0 : return;
3372 : }
3373 : sCCTimer->InitWithFuncCallback(CCTimerFired, nsnull,
3374 : NS_CC_SKIPPABLE_DELAY,
3375 1041 : nsITimer::TYPE_REPEATING_SLACK);
3376 : }
3377 : }
3378 :
3379 : //static
3380 : void
3381 1805 : nsJSContext::KillGCTimer()
3382 : {
3383 1805 : if (sGCTimer) {
3384 6 : sGCTimer->Cancel();
3385 :
3386 6 : NS_RELEASE(sGCTimer);
3387 : }
3388 1805 : }
3389 :
3390 : //static
3391 : void
3392 1805 : nsJSContext::KillShrinkGCBuffersTimer()
3393 : {
3394 1805 : if (sShrinkGCBuffersTimer) {
3395 150 : sShrinkGCBuffersTimer->Cancel();
3396 :
3397 150 : NS_RELEASE(sShrinkGCBuffersTimer);
3398 : }
3399 1805 : }
3400 :
3401 : //static
3402 : void
3403 1499 : nsJSContext::KillCCTimer()
3404 : {
3405 1499 : sCCLockedOutTime = 0;
3406 :
3407 1499 : if (sCCTimer) {
3408 1040 : sCCTimer->Cancel();
3409 :
3410 1040 : NS_RELEASE(sCCTimer);
3411 : }
3412 1499 : }
3413 :
3414 : void
3415 0 : nsJSContext::GC(js::gcreason::Reason aReason)
3416 : {
3417 0 : PokeGC(aReason);
3418 0 : }
3419 :
3420 : static void
3421 796 : DOMGCSliceCallback(JSRuntime *aRt, js::GCProgress aProgress, const js::GCDescription &aDesc)
3422 : {
3423 796 : NS_ASSERTION(NS_IsMainThread(), "GCs must run on the main thread");
3424 :
3425 796 : if (aDesc.logMessage && sPostGCEventsToConsole) {
3426 0 : PRTime now = PR_Now();
3427 0 : PRTime delta = 0;
3428 0 : if (sFirstCollectionTime) {
3429 0 : delta = now - sFirstCollectionTime;
3430 : } else {
3431 0 : sFirstCollectionTime = now;
3432 : }
3433 :
3434 0 : NS_NAMED_LITERAL_STRING(kFmt, "GC(T+%.1f) %s");
3435 0 : nsString msg;
3436 : msg.Adopt(nsTextFormatter::smprintf(kFmt.get(),
3437 : double(delta) / PR_USEC_PER_SEC,
3438 0 : aDesc.logMessage));
3439 0 : nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
3440 0 : if (cs) {
3441 0 : cs->LogStringMessage(msg.get());
3442 : }
3443 : }
3444 :
3445 : // Prevent cycle collections and shrinking during incremental GC.
3446 796 : if (aProgress == js::GC_CYCLE_BEGIN) {
3447 398 : sCCLockedOut = true;
3448 398 : nsJSContext::KillShrinkGCBuffersTimer();
3449 398 : } else if (aProgress == js::GC_CYCLE_END) {
3450 398 : sCCLockedOut = false;
3451 : }
3452 :
3453 : // The GC has more work to do, so schedule another GC slice.
3454 796 : if (aProgress == js::GC_SLICE_END) {
3455 0 : nsJSContext::KillGCTimer();
3456 0 : nsJSContext::PokeGC(js::gcreason::INTER_SLICE_GC, NS_INTERSLICE_GC_DELAY);
3457 : }
3458 :
3459 796 : if (aProgress == js::GC_CYCLE_END) {
3460 : // May need to kill the inter-slice GC timer
3461 398 : nsJSContext::KillGCTimer();
3462 :
3463 398 : sCCollectedWaitingForGC = 0;
3464 398 : sCleanupSinceLastGC = false;
3465 :
3466 398 : if (aDesc.isCompartment) {
3467 : // If this is a compartment GC, restart it. We still want
3468 : // a full GC to happen. Compartment GCs usually happen as a
3469 : // result of last-ditch or MaybeGC. In both cases it is
3470 : // probably a time of heavy activity and we want to delay
3471 : // the full GC, but we do want it to happen eventually.
3472 0 : nsJSContext::PokeGC(js::gcreason::POST_COMPARTMENT);
3473 : }
3474 :
3475 398 : sNeedsFullCC = true;
3476 398 : nsJSContext::MaybePokeCC();
3477 :
3478 398 : if (!aDesc.isCompartment) {
3479 : // Avoid shrinking during heavy activity, which is suggested by
3480 : // compartment GC.
3481 398 : nsJSContext::PokeShrinkGCBuffers();
3482 : }
3483 : }
3484 :
3485 796 : if (sPrevGCSliceCallback)
3486 0 : (*sPrevGCSliceCallback)(aRt, aProgress, aDesc);
3487 796 : }
3488 :
3489 : // Script object mananagement - note duplicate implementation
3490 : // in nsJSRuntime below...
3491 : nsresult
3492 0 : nsJSContext::HoldScriptObject(void* aScriptObject)
3493 : {
3494 0 : NS_ASSERTION(sIsInitialized, "runtime not initialized");
3495 0 : if (! nsJSRuntime::sRuntime) {
3496 0 : NS_NOTREACHED("couldn't add GC root - no runtime");
3497 0 : return NS_ERROR_FAILURE;
3498 : }
3499 :
3500 0 : ::JS_LockGCThingRT(nsJSRuntime::sRuntime, aScriptObject);
3501 0 : return NS_OK;
3502 : }
3503 :
3504 : nsresult
3505 0 : nsJSContext::DropScriptObject(void* aScriptObject)
3506 : {
3507 0 : NS_ASSERTION(sIsInitialized, "runtime not initialized");
3508 0 : if (! nsJSRuntime::sRuntime) {
3509 0 : NS_NOTREACHED("couldn't remove GC root");
3510 0 : return NS_ERROR_FAILURE;
3511 : }
3512 :
3513 0 : ::JS_UnlockGCThingRT(nsJSRuntime::sRuntime, aScriptObject);
3514 0 : return NS_OK;
3515 : }
3516 :
3517 : void
3518 0 : nsJSContext::ReportPendingException()
3519 : {
3520 : // set aside the frame chain, since it has nothing to do with the
3521 : // exception we're reporting.
3522 0 : if (mIsInitialized && ::JS_IsExceptionPending(mContext)) {
3523 0 : bool saved = ::JS_SaveFrameChain(mContext);
3524 0 : ::JS_ReportPendingException(mContext);
3525 0 : if (saved)
3526 0 : ::JS_RestoreFrameChain(mContext);
3527 : }
3528 0 : }
3529 :
3530 : /**********************************************************************
3531 : * nsJSRuntime implementation
3532 : *********************************************************************/
3533 :
3534 : // QueryInterface implementation for nsJSRuntime
3535 50 : NS_INTERFACE_MAP_BEGIN(nsJSRuntime)
3536 50 : NS_INTERFACE_MAP_ENTRY(nsIScriptRuntime)
3537 0 : NS_INTERFACE_MAP_END
3538 :
3539 :
3540 100 : NS_IMPL_ADDREF(nsJSRuntime)
3541 150 : NS_IMPL_RELEASE(nsJSRuntime)
3542 :
3543 : already_AddRefed<nsIScriptContext>
3544 0 : nsJSRuntime::CreateContext()
3545 : {
3546 0 : nsCOMPtr<nsIScriptContext> scriptContext = new nsJSContext(sRuntime);
3547 0 : return scriptContext.forget();
3548 : }
3549 :
3550 : nsresult
3551 0 : nsJSRuntime::ParseVersion(const nsString &aVersionStr, PRUint32 *flags)
3552 : {
3553 0 : NS_PRECONDITION(flags, "Null flags param?");
3554 0 : JSVersion jsVersion = JSVERSION_UNKNOWN;
3555 0 : if (aVersionStr.Length() != 3 || aVersionStr[0] != '1' || aVersionStr[1] != '.')
3556 0 : jsVersion = JSVERSION_UNKNOWN;
3557 0 : else switch (aVersionStr[2]) {
3558 0 : case '0': jsVersion = JSVERSION_1_0; break;
3559 0 : case '1': jsVersion = JSVERSION_1_1; break;
3560 0 : case '2': jsVersion = JSVERSION_1_2; break;
3561 0 : case '3': jsVersion = JSVERSION_1_3; break;
3562 0 : case '4': jsVersion = JSVERSION_1_4; break;
3563 0 : case '5': jsVersion = JSVERSION_1_5; break;
3564 0 : case '6': jsVersion = JSVERSION_1_6; break;
3565 0 : case '7': jsVersion = JSVERSION_1_7; break;
3566 0 : case '8': jsVersion = JSVERSION_1_8; break;
3567 0 : default: jsVersion = JSVERSION_UNKNOWN;
3568 : }
3569 0 : *flags = (PRUint32)jsVersion;
3570 0 : return NS_OK;
3571 : }
3572 :
3573 : //static
3574 : void
3575 1404 : nsJSRuntime::Startup()
3576 : {
3577 : // initialize all our statics, so that we can restart XPCOM
3578 1404 : sGCTimer = sCCTimer = nsnull;
3579 1404 : sCCLockedOut = false;
3580 1404 : sCCLockedOutTime = 0;
3581 1404 : sLastCCEndTime = 0;
3582 1404 : sPendingLoadCount = 0;
3583 1404 : sLoadingInProgress = false;
3584 1404 : sCCollectedWaitingForGC = 0;
3585 1404 : sPostGCEventsToConsole = false;
3586 1404 : sNeedsFullCC = false;
3587 1404 : gNameSpaceManager = nsnull;
3588 1404 : sRuntimeService = nsnull;
3589 1404 : sRuntime = nsnull;
3590 1404 : sIsInitialized = false;
3591 1404 : sDidShutdown = false;
3592 1404 : sContextCount = 0;
3593 1404 : sSecurityManager = nsnull;
3594 1404 : }
3595 :
3596 : static int
3597 100 : MaxScriptRunTimePrefChangedCallback(const char *aPrefName, void *aClosure)
3598 : {
3599 : // Default limit on script run time to 10 seconds. 0 means let
3600 : // scripts run forever.
3601 : bool isChromePref =
3602 100 : strcmp(aPrefName, "dom.max_chrome_script_run_time") == 0;
3603 100 : PRInt32 time = Preferences::GetInt(aPrefName, isChromePref ? 20 : 10);
3604 :
3605 : PRTime t;
3606 100 : if (time <= 0) {
3607 : // Let scripts run for a really, really long time.
3608 0 : t = LL_INIT(0x40000000, 0);
3609 : } else {
3610 100 : t = time * PR_USEC_PER_SEC;
3611 : }
3612 :
3613 100 : if (isChromePref) {
3614 50 : sMaxChromeScriptRunTime = t;
3615 : } else {
3616 50 : sMaxScriptRunTime = t;
3617 : }
3618 :
3619 100 : return 0;
3620 : }
3621 :
3622 : static int
3623 50 : ReportAllJSExceptionsPrefChangedCallback(const char* aPrefName, void* aClosure)
3624 : {
3625 50 : bool reportAll = Preferences::GetBool(aPrefName, false);
3626 50 : nsContentUtils::XPConnect()->SetReportAllJSExceptions(reportAll);
3627 50 : return 0;
3628 : }
3629 :
3630 : static int
3631 50 : SetMemoryHighWaterMarkPrefChangedCallback(const char* aPrefName, void* aClosure)
3632 : {
3633 50 : PRInt32 highwatermark = Preferences::GetInt(aPrefName, 128);
3634 :
3635 : JS_SetGCParameter(nsJSRuntime::sRuntime, JSGC_MAX_MALLOC_BYTES,
3636 50 : highwatermark * 1024L * 1024L);
3637 50 : return 0;
3638 : }
3639 :
3640 : static int
3641 50 : SetMemoryMaxPrefChangedCallback(const char* aPrefName, void* aClosure)
3642 : {
3643 50 : PRInt32 pref = Preferences::GetInt(aPrefName, -1);
3644 : // handle overflow and negative pref values
3645 50 : PRUint32 max = (pref <= 0 || pref >= 0x1000) ? -1 : (PRUint32)pref * 1024 * 1024;
3646 50 : JS_SetGCParameter(nsJSRuntime::sRuntime, JSGC_MAX_BYTES, max);
3647 50 : return 0;
3648 : }
3649 :
3650 : static int
3651 100 : SetMemoryGCModePrefChangedCallback(const char* aPrefName, void* aClosure)
3652 : {
3653 100 : PRBool enableCompartmentGC = Preferences::GetBool("javascript.options.mem.gc_per_compartment");
3654 100 : PRBool enableIncrementalGC = Preferences::GetBool("javascript.options.mem.gc_incremental");
3655 : JSGCMode mode;
3656 100 : if (enableIncrementalGC) {
3657 0 : mode = JSGC_MODE_INCREMENTAL;
3658 100 : } else if (enableCompartmentGC) {
3659 100 : mode = JSGC_MODE_COMPARTMENT;
3660 : } else {
3661 0 : mode = JSGC_MODE_GLOBAL;
3662 : }
3663 100 : JS_SetGCParameter(nsJSRuntime::sRuntime, JSGC_MODE, mode);
3664 100 : return 0;
3665 : }
3666 :
3667 : static int
3668 50 : SetMemoryGCSliceTimePrefChangedCallback(const char* aPrefName, void* aClosure)
3669 : {
3670 50 : PRInt32 pref = Preferences::GetInt(aPrefName, -1);
3671 : // handle overflow and negative pref values
3672 50 : if (pref > 0 && pref < 100000)
3673 50 : JS_SetGCParameter(nsJSRuntime::sRuntime, JSGC_SLICE_TIME_BUDGET, pref);
3674 50 : return 0;
3675 : }
3676 :
3677 : JSObject*
3678 0 : NS_DOMReadStructuredClone(JSContext* cx,
3679 : JSStructuredCloneReader* reader,
3680 : uint32_t tag,
3681 : uint32_t data,
3682 : void* closure)
3683 : {
3684 : // We don't currently support any extensions to structured cloning.
3685 0 : nsDOMClassInfo::ThrowJSException(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
3686 0 : return nsnull;
3687 : }
3688 :
3689 : JSBool
3690 0 : NS_DOMWriteStructuredClone(JSContext* cx,
3691 : JSStructuredCloneWriter* writer,
3692 : JSObject* obj,
3693 : void *closure)
3694 : {
3695 : // We don't currently support any extensions to structured cloning.
3696 0 : nsDOMClassInfo::ThrowJSException(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
3697 0 : return JS_FALSE;
3698 : }
3699 :
3700 : void
3701 0 : NS_DOMStructuredCloneError(JSContext* cx,
3702 : uint32_t errorid)
3703 : {
3704 : // We don't currently support any extensions to structured cloning.
3705 0 : nsDOMClassInfo::ThrowJSException(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
3706 0 : }
3707 :
3708 : //static
3709 : nsresult
3710 50 : nsJSRuntime::Init()
3711 : {
3712 50 : if (sIsInitialized) {
3713 0 : if (!nsContentUtils::XPConnect())
3714 0 : return NS_ERROR_NOT_AVAILABLE;
3715 :
3716 0 : return NS_OK;
3717 : }
3718 :
3719 : nsresult rv = CallGetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID,
3720 50 : &sSecurityManager);
3721 50 : NS_ENSURE_SUCCESS(rv, rv);
3722 :
3723 50 : rv = CallGetService(kJSRuntimeServiceContractID, &sRuntimeService);
3724 : // get the JSRuntime from the runtime svc, if possible
3725 50 : NS_ENSURE_SUCCESS(rv, rv);
3726 :
3727 50 : rv = sRuntimeService->GetRuntime(&sRuntime);
3728 50 : NS_ENSURE_SUCCESS(rv, rv);
3729 :
3730 : // Let's make sure that our main thread is the same as the xpcom main thread.
3731 50 : NS_ASSERTION(NS_IsMainThread(), "bad");
3732 :
3733 50 : sPrevGCSliceCallback = js::SetGCSliceCallback(sRuntime, DOMGCSliceCallback);
3734 :
3735 : // Set up the structured clone callbacks.
3736 : static JSStructuredCloneCallbacks cloneCallbacks = {
3737 : NS_DOMReadStructuredClone,
3738 : NS_DOMWriteStructuredClone,
3739 : NS_DOMStructuredCloneError
3740 : };
3741 50 : JS_SetStructuredCloneCallbacks(sRuntime, &cloneCallbacks);
3742 :
3743 : // Set these global xpconnect options...
3744 : Preferences::RegisterCallback(MaxScriptRunTimePrefChangedCallback,
3745 50 : "dom.max_script_run_time");
3746 50 : MaxScriptRunTimePrefChangedCallback("dom.max_script_run_time", nsnull);
3747 :
3748 : Preferences::RegisterCallback(MaxScriptRunTimePrefChangedCallback,
3749 50 : "dom.max_chrome_script_run_time");
3750 : MaxScriptRunTimePrefChangedCallback("dom.max_chrome_script_run_time",
3751 50 : nsnull);
3752 :
3753 : Preferences::RegisterCallback(ReportAllJSExceptionsPrefChangedCallback,
3754 50 : "dom.report_all_js_exceptions");
3755 : ReportAllJSExceptionsPrefChangedCallback("dom.report_all_js_exceptions",
3756 50 : nsnull);
3757 :
3758 : Preferences::RegisterCallback(SetMemoryHighWaterMarkPrefChangedCallback,
3759 50 : "javascript.options.mem.high_water_mark");
3760 : SetMemoryHighWaterMarkPrefChangedCallback("javascript.options.mem.high_water_mark",
3761 50 : nsnull);
3762 :
3763 : Preferences::RegisterCallback(SetMemoryMaxPrefChangedCallback,
3764 50 : "javascript.options.mem.max");
3765 : SetMemoryMaxPrefChangedCallback("javascript.options.mem.max",
3766 50 : nsnull);
3767 :
3768 : Preferences::RegisterCallback(SetMemoryGCModePrefChangedCallback,
3769 50 : "javascript.options.mem.gc_per_compartment");
3770 : SetMemoryGCModePrefChangedCallback("javascript.options.mem.gc_per_compartment",
3771 50 : nsnull);
3772 :
3773 : Preferences::RegisterCallback(SetMemoryGCModePrefChangedCallback,
3774 50 : "javascript.options.mem.gc_incremental");
3775 : SetMemoryGCModePrefChangedCallback("javascript.options.mem.gc_incremental",
3776 50 : nsnull);
3777 :
3778 : Preferences::RegisterCallback(SetMemoryGCSliceTimePrefChangedCallback,
3779 50 : "javascript.options.mem.gc_incremental_slice_ms");
3780 : SetMemoryGCSliceTimePrefChangedCallback("javascript.options.mem.gc_incremental_slice_ms",
3781 50 : nsnull);
3782 :
3783 100 : nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
3784 50 : if (!obs)
3785 0 : return NS_ERROR_FAILURE;
3786 :
3787 : Preferences::AddBoolVarCache(&sGCOnMemoryPressure,
3788 : "javascript.options.gc_on_memory_pressure",
3789 50 : true);
3790 :
3791 50 : nsIObserver* memPressureObserver = new nsMemoryPressureObserver();
3792 50 : NS_ENSURE_TRUE(memPressureObserver, NS_ERROR_OUT_OF_MEMORY);
3793 50 : obs->AddObserver(memPressureObserver, "memory-pressure", false);
3794 :
3795 50 : sIsInitialized = true;
3796 :
3797 50 : return NS_OK;
3798 : }
3799 :
3800 : //static
3801 : nsScriptNameSpaceManager*
3802 231336 : nsJSRuntime::GetNameSpaceManager()
3803 : {
3804 231336 : if (sDidShutdown)
3805 0 : return nsnull;
3806 :
3807 231336 : if (!gNameSpaceManager) {
3808 306 : gNameSpaceManager = new nsScriptNameSpaceManager;
3809 306 : NS_ADDREF(gNameSpaceManager);
3810 :
3811 306 : nsresult rv = gNameSpaceManager->Init();
3812 306 : NS_ENSURE_SUCCESS(rv, nsnull);
3813 : }
3814 :
3815 231336 : return gNameSpaceManager;
3816 : }
3817 :
3818 : /* static */
3819 : void
3820 1403 : nsJSRuntime::Shutdown()
3821 : {
3822 1403 : nsJSContext::KillGCTimer();
3823 1403 : nsJSContext::KillShrinkGCBuffersTimer();
3824 1403 : nsJSContext::KillCCTimer();
3825 :
3826 1403 : NS_IF_RELEASE(gNameSpaceManager);
3827 :
3828 1403 : if (!sContextCount) {
3829 : // We're being shutdown, and there are no more contexts
3830 : // alive, release the JS runtime service and the security manager.
3831 :
3832 1403 : NS_IF_RELEASE(sRuntimeService);
3833 1403 : NS_IF_RELEASE(sSecurityManager);
3834 : }
3835 :
3836 1403 : sDidShutdown = true;
3837 1403 : }
3838 :
3839 : // Script object mananagement - note duplicate implementation
3840 : // in nsJSContext above...
3841 : nsresult
3842 0 : nsJSRuntime::HoldScriptObject(void* aScriptObject)
3843 : {
3844 0 : NS_ASSERTION(sIsInitialized, "runtime not initialized");
3845 0 : if (! sRuntime) {
3846 0 : NS_NOTREACHED("couldn't remove GC root - no runtime");
3847 0 : return NS_ERROR_FAILURE;
3848 : }
3849 :
3850 0 : ::JS_LockGCThingRT(sRuntime, aScriptObject);
3851 0 : return NS_OK;
3852 : }
3853 :
3854 : nsresult
3855 0 : nsJSRuntime::DropScriptObject(void* aScriptObject)
3856 : {
3857 0 : NS_ASSERTION(sIsInitialized, "runtime not initialized");
3858 0 : if (! sRuntime) {
3859 0 : NS_NOTREACHED("couldn't remove GC root");
3860 0 : return NS_ERROR_FAILURE;
3861 : }
3862 :
3863 0 : ::JS_UnlockGCThingRT(sRuntime, aScriptObject);
3864 0 : return NS_OK;
3865 : }
3866 :
3867 : // A factory for the runtime.
3868 50 : nsresult NS_CreateJSRuntime(nsIScriptRuntime **aRuntime)
3869 : {
3870 50 : nsresult rv = nsJSRuntime::Init();
3871 50 : NS_ENSURE_SUCCESS(rv, rv);
3872 :
3873 50 : *aRuntime = new nsJSRuntime();
3874 50 : if (*aRuntime == nsnull)
3875 0 : return NS_ERROR_OUT_OF_MEMORY;
3876 50 : NS_IF_ADDREF(*aRuntime);
3877 50 : return NS_OK;
3878 : }
3879 :
3880 : // A fast-array class for JS. This class supports both nsIJSScriptArray and
3881 : // nsIArray. If it is JS itself providing and consuming this class, all work
3882 : // can be done via nsIJSScriptArray, and avoid the conversion of elements
3883 : // to/from nsISupports.
3884 : // When consumed by non-JS (eg, another script language), conversion is done
3885 : // on-the-fly.
3886 : class nsJSArgArray : public nsIJSArgArray {
3887 : public:
3888 : nsJSArgArray(JSContext *aContext, PRUint32 argc, jsval *argv, nsresult *prv);
3889 : ~nsJSArgArray();
3890 : // nsISupports
3891 0 : NS_DECL_CYCLE_COLLECTING_ISUPPORTS
3892 1464 : NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsJSArgArray,
3893 : nsIJSArgArray)
3894 :
3895 : // nsIArray
3896 : NS_DECL_NSIARRAY
3897 :
3898 : // nsIJSArgArray
3899 : nsresult GetArgs(PRUint32 *argc, void **argv);
3900 :
3901 : void ReleaseJSObjects();
3902 :
3903 : protected:
3904 : JSContext *mContext;
3905 : jsval *mArgv;
3906 : PRUint32 mArgc;
3907 : };
3908 :
3909 0 : nsJSArgArray::nsJSArgArray(JSContext *aContext, PRUint32 argc, jsval *argv,
3910 : nsresult *prv) :
3911 : mContext(aContext),
3912 : mArgv(nsnull),
3913 0 : mArgc(argc)
3914 : {
3915 : // copy the array - we don't know its lifetime, and ours is tied to xpcom
3916 : // refcounting. Alloc zero'd array so cleanup etc is safe.
3917 0 : if (argc) {
3918 0 : mArgv = (jsval *) PR_CALLOC(argc * sizeof(jsval));
3919 0 : if (!mArgv) {
3920 0 : *prv = NS_ERROR_OUT_OF_MEMORY;
3921 0 : return;
3922 : }
3923 : }
3924 :
3925 : // Callers are allowed to pass in a null argv even for argc > 0. They can
3926 : // then use GetArgs to initialize the values.
3927 0 : if (argv) {
3928 0 : for (PRUint32 i = 0; i < argc; ++i)
3929 0 : mArgv[i] = argv[i];
3930 : }
3931 :
3932 0 : *prv = argc > 0 ? NS_HOLD_JS_OBJECTS(this, nsJSArgArray) : NS_OK;
3933 : }
3934 :
3935 0 : nsJSArgArray::~nsJSArgArray()
3936 : {
3937 0 : ReleaseJSObjects();
3938 0 : }
3939 :
3940 : void
3941 0 : nsJSArgArray::ReleaseJSObjects()
3942 : {
3943 0 : if (mArgc > 0)
3944 0 : NS_DROP_JS_OBJECTS(this, nsJSArgArray);
3945 0 : if (mArgv) {
3946 0 : PR_DELETE(mArgv);
3947 : }
3948 0 : mArgc = 0;
3949 0 : }
3950 :
3951 : // QueryInterface implementation for nsJSArgArray
3952 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSArgArray)
3953 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSArgArray)
3954 0 : tmp->ReleaseJSObjects();
3955 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
3956 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsJSArgArray)
3957 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
3958 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
3959 :
3960 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSArgArray)
3961 0 : jsval *argv = tmp->mArgv;
3962 0 : if (argv) {
3963 : jsval *end;
3964 0 : for (end = argv + tmp->mArgc; argv < end; ++argv) {
3965 0 : if (JSVAL_IS_GCTHING(*argv))
3966 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_CALLBACK(JAVASCRIPT,
3967 : JSVAL_TO_GCTHING(*argv),
3968 : "mArgv[i]")
3969 : }
3970 : }
3971 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_END
3972 :
3973 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSArgArray)
3974 0 : NS_INTERFACE_MAP_ENTRY(nsIArray)
3975 0 : NS_INTERFACE_MAP_ENTRY(nsIJSArgArray)
3976 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIJSArgArray)
3977 0 : NS_INTERFACE_MAP_END
3978 :
3979 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsJSArgArray)
3980 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsJSArgArray)
3981 :
3982 : nsresult
3983 0 : nsJSArgArray::GetArgs(PRUint32 *argc, void **argv)
3984 : {
3985 0 : *argv = (void *)mArgv;
3986 0 : *argc = mArgc;
3987 0 : return NS_OK;
3988 : }
3989 :
3990 : // nsIArray impl
3991 0 : NS_IMETHODIMP nsJSArgArray::GetLength(PRUint32 *aLength)
3992 : {
3993 0 : *aLength = mArgc;
3994 0 : return NS_OK;
3995 : }
3996 :
3997 : /* void queryElementAt (in unsigned long index, in nsIIDRef uuid, [iid_is (uuid), retval] out nsQIResult result); */
3998 0 : NS_IMETHODIMP nsJSArgArray::QueryElementAt(PRUint32 index, const nsIID & uuid, void * *result)
3999 : {
4000 0 : *result = nsnull;
4001 0 : if (index >= mArgc)
4002 0 : return NS_ERROR_INVALID_ARG;
4003 :
4004 0 : if (uuid.Equals(NS_GET_IID(nsIVariant)) || uuid.Equals(NS_GET_IID(nsISupports))) {
4005 0 : return nsContentUtils::XPConnect()->JSToVariant(mContext, mArgv[index],
4006 0 : (nsIVariant **)result);
4007 : }
4008 0 : NS_WARNING("nsJSArgArray only handles nsIVariant");
4009 0 : return NS_ERROR_NO_INTERFACE;
4010 : }
4011 :
4012 : /* unsigned long indexOf (in unsigned long startIndex, in nsISupports element); */
4013 0 : NS_IMETHODIMP nsJSArgArray::IndexOf(PRUint32 startIndex, nsISupports *element, PRUint32 *_retval)
4014 : {
4015 0 : return NS_ERROR_NOT_IMPLEMENTED;
4016 : }
4017 :
4018 : /* nsISimpleEnumerator enumerate (); */
4019 0 : NS_IMETHODIMP nsJSArgArray::Enumerate(nsISimpleEnumerator **_retval)
4020 : {
4021 0 : return NS_ERROR_NOT_IMPLEMENTED;
4022 : }
4023 :
4024 : // The factory function
4025 0 : nsresult NS_CreateJSArgv(JSContext *aContext, PRUint32 argc, void *argv,
4026 : nsIJSArgArray **aArray)
4027 : {
4028 : nsresult rv;
4029 : nsJSArgArray *ret = new nsJSArgArray(aContext, argc,
4030 0 : static_cast<jsval *>(argv), &rv);
4031 0 : if (ret == nsnull)
4032 0 : return NS_ERROR_OUT_OF_MEMORY;
4033 0 : if (NS_FAILED(rv)) {
4034 0 : delete ret;
4035 0 : return rv;
4036 : }
4037 0 : return ret->QueryInterface(NS_GET_IID(nsIArray), (void **)aArray);
4038 4392 : }
|