Imported Upstream version 1.0.0
[platform/upstream/js.git] / js / jsd / jsd_xpc.cpp
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  *
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  *   Robert Ginda, <rginda@netscape.com>
25  *
26  * Alternatively, the contents of this file may be used under the terms of
27  * either the GNU General Public License Version 2 or later (the "GPL"), or
28  * 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 "jsdbgapi.h"
41 #include "jslock.h"
42 #include "jsd_xpc.h"
43
44 #include "nsIXPConnect.h"
45 #include "mozilla/ModuleUtils.h"
46 #include "nsIServiceManager.h"
47 #include "nsIScriptGlobalObject.h"
48 #include "nsIObserver.h"
49 #include "nsIObserverService.h"
50 #include "nsICategoryManager.h"
51 #include "nsIJSRuntimeService.h"
52 #include "nsIThreadInternal.h"
53 #include "nsThreadUtils.h"
54 #include "nsMemory.h"
55 #include "jsdebug.h"
56 #include "nsReadableUtils.h"
57 #include "nsCRT.h"
58
59 /* XXX DOM dependency */
60 #include "nsIScriptContext.h"
61 #include "nsIJSContextStack.h"
62
63 /* XXX private JS headers. */
64 #include "jscompartment.h"
65
66 /*
67  * defining CAUTIOUS_SCRIPTHOOK makes jsds disable GC while calling out to the
68  * script hook.  This was a hack to avoid some js engine problems that should
69  * be fixed now (see Mozilla bug 77636).
70  */
71 #undef CAUTIOUS_SCRIPTHOOK
72
73 #ifdef DEBUG_verbose
74 #   define DEBUG_COUNT(name, count)                                             \
75         { if ((count % 10) == 0) printf (name ": %i\n", count); }
76 #   define DEBUG_CREATE(name, count) {count++; DEBUG_COUNT ("+++++ "name,count)}
77 #   define DEBUG_DESTROY(name, count) {count--; DEBUG_COUNT ("----- "name,count)}
78 #else
79 #   define DEBUG_CREATE(name, count) 
80 #   define DEBUG_DESTROY(name, count)
81 #endif
82
83 #define ASSERT_VALID_CONTEXT   { if (!mCx) return NS_ERROR_NOT_AVAILABLE; }
84 #define ASSERT_VALID_EPHEMERAL { if (!mValid) return NS_ERROR_NOT_AVAILABLE; }
85
86 #define JSDSERVICE_CID                               \
87 { /* f1299dc2-1dd1-11b2-a347-ee6b7660e048 */         \
88      0xf1299dc2,                                     \
89      0x1dd1,                                         \
90      0x11b2,                                         \
91     {0xa3, 0x47, 0xee, 0x6b, 0x76, 0x60, 0xe0, 0x48} \
92 }
93
94 #define JSDASO_CID                                   \
95 { /* 2fd6b7f6-eb8c-4f32-ad26-113f2c02d0fe */         \
96      0x2fd6b7f6,                                     \
97      0xeb8c,                                         \
98      0x4f32,                                         \
99     {0xad, 0x26, 0x11, 0x3f, 0x2c, 0x02, 0xd0, 0xfe} \
100 }
101
102 #define JSDS_MAJOR_VERSION 1
103 #define JSDS_MINOR_VERSION 2
104
105 #define NS_CATMAN_CTRID   "@mozilla.org/categorymanager;1"
106 #define NS_JSRT_CTRID     "@mozilla.org/js/xpc/RuntimeService;1"
107
108 #define AUTOREG_CATEGORY  "xpcom-autoregistration"
109 #define APPSTART_CATEGORY "app-startup"
110 #define JSD_AUTOREG_ENTRY "JSDebugger Startup Observer"
111 #define JSD_STARTUP_ENTRY "JSDebugger Startup Observer"
112
113 static JSBool
114 jsds_GCCallbackProc (JSContext *cx, JSGCStatus status);
115
116 /*******************************************************************************
117  * global vars
118  ******************************************************************************/
119
120 const char implementationString[] = "Mozilla JavaScript Debugger Service";
121
122 const char jsdServiceCtrID[] = "@mozilla.org/js/jsd/debugger-service;1";
123 const char jsdARObserverCtrID[] = "@mozilla.org/js/jsd/app-start-observer;2";
124 const char jsdASObserverCtrID[] = "service,@mozilla.org/js/jsd/app-start-observer;2";
125
126 #ifdef DEBUG_verbose
127 PRUint32 gScriptCount   = 0;
128 PRUint32 gValueCount    = 0;
129 PRUint32 gPropertyCount = 0;
130 PRUint32 gContextCount  = 0;
131 PRUint32 gFrameCount  = 0;
132 #endif
133
134 static jsdService   *gJsds       = 0;
135 static JSGCCallback  gLastGCProc = jsds_GCCallbackProc;
136 static JSGCStatus    gGCStatus   = JSGC_END;
137
138 static struct DeadScript {
139     PRCList     links;
140     JSDContext *jsdc;
141     jsdIScript *script;
142 } *gDeadScripts = nsnull;
143
144 enum PatternType {
145     ptIgnore     = 0U,
146     ptStartsWith = 1U,
147     ptEndsWith   = 2U,
148     ptContains   = 3U,
149     ptEquals     = 4U
150 };
151
152 static struct FilterRecord {
153     PRCList      links;
154     jsdIFilter  *filterObject;
155     void        *glob;
156     nsCString    urlPattern;
157     PatternType  patternType;
158     PRUint32     startLine;
159     PRUint32     endLine;
160 } *gFilters = nsnull;
161
162 static struct LiveEphemeral *gLiveValues      = nsnull;
163 static struct LiveEphemeral *gLiveProperties  = nsnull;
164 static struct LiveEphemeral *gLiveContexts    = nsnull;
165 static struct LiveEphemeral *gLiveStackFrames = nsnull;
166
167 /*******************************************************************************
168  * utility functions for ephemeral lists
169  *******************************************************************************/
170 already_AddRefed<jsdIEphemeral>
171 jsds_FindEphemeral (LiveEphemeral **listHead, void *key)
172 {
173     if (!*listHead)
174         return nsnull;
175     
176     LiveEphemeral *lv_record = 
177         reinterpret_cast<LiveEphemeral *>
178                         (PR_NEXT_LINK(&(*listHead)->links));
179     do
180     {
181         if (lv_record->key == key)
182         {
183             NS_IF_ADDREF(lv_record->value);
184             return lv_record->value;
185         }
186         lv_record = reinterpret_cast<LiveEphemeral *>
187                                     (PR_NEXT_LINK(&lv_record->links));
188     }
189     while (lv_record != *listHead);
190
191     return nsnull;
192 }
193
194 void
195 jsds_InvalidateAllEphemerals (LiveEphemeral **listHead)
196 {
197     LiveEphemeral *lv_record = 
198         reinterpret_cast<LiveEphemeral *>
199                         (PR_NEXT_LINK(&(*listHead)->links));
200     do
201     {
202         LiveEphemeral *next =
203             reinterpret_cast<LiveEphemeral *>
204                             (PR_NEXT_LINK(&lv_record->links));
205         lv_record->value->Invalidate();
206         lv_record = next;
207     }
208     while (*listHead);
209 }
210
211 void
212 jsds_InsertEphemeral (LiveEphemeral **listHead, LiveEphemeral *item)
213 {
214     if (*listHead) {
215         /* if the list exists, add to it */
216         PR_APPEND_LINK(&item->links, &(*listHead)->links);
217     } else {
218         /* otherwise create the list */
219         PR_INIT_CLIST(&item->links);
220         *listHead = item;
221     }
222 }
223
224 void
225 jsds_RemoveEphemeral (LiveEphemeral **listHead, LiveEphemeral *item)
226 {
227     LiveEphemeral *next = reinterpret_cast<LiveEphemeral *>
228                                           (PR_NEXT_LINK(&item->links));
229
230     if (next == item)
231     {
232         /* if the current item is also the next item, we're the only element,
233          * null out the list head */
234         NS_ASSERTION (*listHead == item,
235                       "How could we not be the head of a one item list?");
236         *listHead = nsnull;
237     }
238     else if (item == *listHead)
239     {
240         /* otherwise, if we're currently the list head, change it */
241         *listHead = next;
242     }
243     
244     PR_REMOVE_AND_INIT_LINK(&item->links);
245 }
246
247 /*******************************************************************************
248  * utility functions for filters
249  *******************************************************************************/
250 void
251 jsds_FreeFilter (FilterRecord *rec)
252 {
253     NS_IF_RELEASE (rec->filterObject);
254     PR_Free (rec);
255 }
256
257 /* copies appropriate |filter| attributes into |rec|.
258  * False return indicates failure, the contents of |rec| will not be changed.
259  */
260 PRBool
261 jsds_SyncFilter (FilterRecord *rec, jsdIFilter *filter)
262 {
263     NS_ASSERTION (rec, "jsds_SyncFilter without rec");
264     NS_ASSERTION (filter, "jsds_SyncFilter without filter");
265     
266     JSObject *glob_proper = nsnull;
267     nsCOMPtr<nsISupports> glob;
268     nsresult rv = filter->GetGlobalObject(getter_AddRefs(glob));
269     if (NS_FAILED(rv))
270         return PR_FALSE;
271     if (glob) {
272         nsCOMPtr<nsIScriptGlobalObject> nsiglob = do_QueryInterface(glob);
273         if (nsiglob)
274             glob_proper = nsiglob->GetGlobalJSObject();
275     }
276     
277     PRUint32 startLine;
278     rv = filter->GetStartLine(&startLine);
279     if (NS_FAILED(rv))
280         return PR_FALSE;
281
282     PRUint32 endLine;
283     rv = filter->GetStartLine(&endLine);
284     if (NS_FAILED(rv))
285         return PR_FALSE;    
286
287     nsCAutoString urlPattern;
288     rv = filter->GetUrlPattern (urlPattern);
289     if (NS_FAILED(rv))
290         return PR_FALSE;
291     
292     PRUint32 len = urlPattern.Length();
293     if (len) {
294         if (urlPattern[0] == '*') {
295             /* pattern starts with a *, shift all chars once to the left,
296              * including the trailing null. */
297             urlPattern = Substring(urlPattern, 1, len);
298
299             if (urlPattern[len - 2] == '*') {
300                 /* pattern is in the format "*foo*", overwrite the final * with
301                  * a null. */
302                 urlPattern.Truncate(len - 2);
303                 rec->patternType = ptContains;
304             } else {
305                 /* pattern is in the format "*foo", just make a note of the
306                  * new length. */
307                 rec->patternType = ptEndsWith;
308             }
309         } else if (urlPattern[len - 1] == '*') {
310             /* pattern is in the format "foo*", overwrite the final * with a 
311              * null. */
312             urlPattern.Truncate(len - 1);
313             rec->patternType = ptStartsWith;
314         } else {
315             /* pattern is in the format "foo". */
316             rec->patternType = ptEquals;
317         }
318     } else {
319         rec->patternType = ptIgnore;
320     }
321
322     /* we got everything we need without failing, now copy it into rec. */
323
324     if (rec->filterObject != filter) {
325         NS_IF_RELEASE(rec->filterObject);
326         NS_ADDREF(filter);
327         rec->filterObject = filter;
328     }
329     
330     rec->glob = glob_proper;
331     
332     rec->startLine     = startLine;
333     rec->endLine       = endLine;
334     
335     rec->urlPattern = urlPattern;
336
337     return PR_TRUE;
338             
339 }
340
341 FilterRecord *
342 jsds_FindFilter (jsdIFilter *filter)
343 {
344     if (!gFilters)
345         return nsnull;
346     
347     FilterRecord *current = gFilters;
348     
349     do {
350         if (current->filterObject == filter)
351             return current;
352         current = reinterpret_cast<FilterRecord *>
353                                   (PR_NEXT_LINK(&current->links));
354     } while (current != gFilters);
355     
356     return nsnull;
357 }
358
359 /* returns true if the hook should be executed. */
360 PRBool
361 jsds_FilterHook (JSDContext *jsdc, JSDThreadState *state)
362 {
363     JSContext *cx = JSD_GetJSContext (jsdc, state);
364     void *glob = static_cast<void *>(JS_GetGlobalObject (cx));
365
366     if (!glob) {
367         NS_WARNING("No global in threadstate");
368         return PR_FALSE;
369     }
370     
371     JSDStackFrameInfo *frame = JSD_GetStackFrame (jsdc, state);
372
373     if (!frame) {
374         NS_WARNING("No frame in threadstate");
375         return PR_FALSE;
376     }
377
378     JSDScript *script = JSD_GetScriptForStackFrame (jsdc, state, frame);
379     if (!script)
380         return PR_TRUE;
381
382     jsuword pc = JSD_GetPCForStackFrame (jsdc, state, frame);
383
384     nsDependentCString url(JSD_GetScriptFilename (jsdc, script));
385     if (url.IsEmpty()) {
386         NS_WARNING ("Script with no filename");
387         return PR_FALSE;
388     }
389
390     if (!gFilters)
391         return PR_TRUE;    
392
393     PRUint32 currentLine = JSD_GetClosestLine (jsdc, script, pc);
394     PRUint32 len = 0;
395     FilterRecord *currentFilter = gFilters;
396     do {
397         PRUint32 flags = 0;
398         nsresult rv = currentFilter->filterObject->GetFlags(&flags);
399         NS_ASSERTION(NS_SUCCEEDED(rv), "Error getting flags for filter");
400         if (flags & jsdIFilter::FLAG_ENABLED) {
401             /* if there is no glob, or the globs match */
402             if ((!currentFilter->glob || currentFilter->glob == glob) &&
403                 /* and there is no start line, or the start line is before 
404                  * or equal to the current */
405                 (!currentFilter->startLine || 
406                  currentFilter->startLine <= currentLine) &&
407                 /* and there is no end line, or the end line is after
408                  * or equal to the current */
409                 (!currentFilter->endLine ||
410                  currentFilter->endLine >= currentLine)) {
411                 /* then we're going to have to compare the url. */
412                 if (currentFilter->patternType == ptIgnore)
413                     return !!(flags & jsdIFilter::FLAG_PASS);
414
415                 if (!len)
416                     len = url.Length();
417                 nsCString urlPattern = currentFilter->urlPattern;
418                 PRUint32 patternLength = urlPattern.Length();
419                 if (len >= patternLength) {
420                     switch (currentFilter->patternType) {
421                         case ptEquals:
422                             if (urlPattern.Equals(url))
423                                 return !!(flags & jsdIFilter::FLAG_PASS);
424                             break;
425                         case ptStartsWith:
426                             if (urlPattern.Equals(Substring(url, 0, patternLength)))
427                                 return !!(flags & jsdIFilter::FLAG_PASS);
428                             break;
429                         case ptEndsWith:
430                             if (urlPattern.Equals(Substring(url, len - patternLength)))
431                                 return !!(flags & jsdIFilter::FLAG_PASS);
432                             break;
433                         case ptContains:
434                             {
435                                 nsACString::const_iterator start, end;
436                                 url.BeginReading(start);
437                                 url.EndReading(end);
438                                 if (FindInReadable(currentFilter->urlPattern, start, end))
439                                     return !!(flags & jsdIFilter::FLAG_PASS);
440                             }
441                             break;
442                         default:
443                             NS_ERROR("Invalid pattern type");
444                     }
445                 }                
446             }
447         }
448         currentFilter = reinterpret_cast<FilterRecord *>
449                                         (PR_NEXT_LINK(&currentFilter->links));
450     } while (currentFilter != gFilters);
451
452     return PR_TRUE;
453     
454 }
455
456 /*******************************************************************************
457  * c callbacks
458  *******************************************************************************/
459
460 static void
461 jsds_NotifyPendingDeadScripts (JSContext *cx)
462 {
463 #ifdef CAUTIOUS_SCRIPTHOOK
464     JSRuntime *rt = JS_GetRuntime(cx);
465 #endif
466     jsdService *jsds = gJsds;
467
468     nsCOMPtr<jsdIScriptHook> hook;
469     if (jsds) {
470         NS_ADDREF(jsds);
471         jsds->GetScriptHook (getter_AddRefs(hook));
472         jsds->Pause(nsnull);
473     }
474
475     DeadScript *deadScripts = gDeadScripts;
476     gDeadScripts = nsnull;
477     while (deadScripts) {
478         DeadScript *ds = deadScripts;
479         /* get next deleted script */
480         deadScripts = reinterpret_cast<DeadScript *>
481                                        (PR_NEXT_LINK(&ds->links));
482         if (deadScripts == ds)
483             deadScripts = nsnull;
484
485         if (hook)
486         {
487             /* tell the user this script has been destroyed */
488 #ifdef CAUTIOUS_SCRIPTHOOK
489             JS_UNKEEP_ATOMS(rt);
490 #endif
491             hook->OnScriptDestroyed (ds->script);
492 #ifdef CAUTIOUS_SCRIPTHOOK
493             JS_KEEP_ATOMS(rt);
494 #endif
495         }
496
497         /* take it out of the circular list */
498         PR_REMOVE_LINK(&ds->links);
499
500         /* addref came from the FromPtr call in jsds_ScriptHookProc */
501         NS_RELEASE(ds->script);
502         /* free the struct! */
503         PR_Free(ds);
504     }
505
506     if (jsds) {
507         jsds->UnPause(nsnull);
508         NS_RELEASE(jsds);
509     }
510 }
511
512 static JSBool
513 jsds_GCCallbackProc (JSContext *cx, JSGCStatus status)
514 {
515 #ifdef DEBUG_verbose
516     printf ("new gc status is %i\n", status);
517 #endif
518     if (status == JSGC_END) {
519         /* just to guard against reentering. */
520         gGCStatus = JSGC_BEGIN;
521         while (gDeadScripts)
522             jsds_NotifyPendingDeadScripts (cx);
523     }
524
525     gGCStatus = status;
526     if (gLastGCProc && !gLastGCProc (cx, status)) {
527         /*
528          * If gLastGCProc returns false, then the GC will abort without making
529          * another callback with status=JSGC_END, so set the status to JSGC_END
530          * here.
531          */
532         gGCStatus = JSGC_END;
533         return JS_FALSE;
534     }
535     
536     return JS_TRUE;
537 }
538
539 static uintN
540 jsds_ErrorHookProc (JSDContext *jsdc, JSContext *cx, const char *message,
541                     JSErrorReport *report, void *callerdata)
542 {
543     static PRBool running = PR_FALSE;
544
545     nsCOMPtr<jsdIErrorHook> hook;
546     gJsds->GetErrorHook(getter_AddRefs(hook));
547     if (!hook)
548         return JSD_ERROR_REPORTER_PASS_ALONG;
549
550     if (running)
551         return JSD_ERROR_REPORTER_PASS_ALONG;
552     
553     running = PR_TRUE;
554     
555     nsCOMPtr<jsdIValue> val;
556     if (JS_IsExceptionPending(cx)) {
557         jsval jv;
558         JS_GetPendingException(cx, &jv);
559         JSDValue *jsdv = JSD_NewValue (jsdc, jv);
560         val = getter_AddRefs(jsdValue::FromPtr(jsdc, jsdv));
561     }
562     
563     nsCAutoString fileName;
564     PRUint32    line;
565     PRUint32    pos;
566     PRUint32    flags;
567     PRUint32    errnum;
568     PRBool      rval;
569     if (report) {
570         fileName.Assign(report->filename);
571         line = report->lineno;
572         pos = report->tokenptr - report->linebuf;
573         flags = report->flags;
574         errnum = report->errorNumber;
575     }
576     else
577     {
578         line     = 0;
579         pos      = 0;
580         flags    = 0;
581         errnum   = 0;
582     }
583     
584     gJsds->Pause(nsnull);
585     hook->OnError (nsDependentCString(message), fileName, line, pos, flags, errnum, val, &rval);
586     gJsds->UnPause(nsnull);
587     
588     running = PR_FALSE;
589     if (!rval)
590         return JSD_ERROR_REPORTER_DEBUG;
591     
592     return JSD_ERROR_REPORTER_PASS_ALONG;
593 }
594
595 static JSBool
596 jsds_CallHookProc (JSDContext* jsdc, JSDThreadState* jsdthreadstate,
597                    uintN type, void* callerdata)
598 {
599     nsCOMPtr<jsdICallHook> hook;
600
601     switch (type)
602     {
603         case JSD_HOOK_TOPLEVEL_START:
604         case JSD_HOOK_TOPLEVEL_END:
605             gJsds->GetTopLevelHook(getter_AddRefs(hook));
606             break;
607             
608         case JSD_HOOK_FUNCTION_CALL:
609         case JSD_HOOK_FUNCTION_RETURN:
610             gJsds->GetFunctionHook(getter_AddRefs(hook));
611             break;
612
613         default:
614             NS_ASSERTION (0, "Unknown hook type.");
615     }
616     
617     if (!hook)
618         return JS_TRUE;
619
620     if (!jsds_FilterHook (jsdc, jsdthreadstate))
621         return JS_FALSE;
622
623     JSDStackFrameInfo *native_frame = JSD_GetStackFrame (jsdc, jsdthreadstate);
624     nsCOMPtr<jsdIStackFrame> frame =
625         getter_AddRefs(jsdStackFrame::FromPtr(jsdc, jsdthreadstate,
626                                               native_frame));
627     gJsds->Pause(nsnull);
628     hook->OnCall(frame, type);    
629     gJsds->UnPause(nsnull);
630     jsdStackFrame::InvalidateAll();
631
632     return JS_TRUE;
633 }
634
635 static PRUint32
636 jsds_ExecutionHookProc (JSDContext* jsdc, JSDThreadState* jsdthreadstate,
637                         uintN type, void* callerdata, jsval* rval)
638 {
639     nsCOMPtr<jsdIExecutionHook> hook(0);
640     PRUint32 hook_rv = JSD_HOOK_RETURN_CONTINUE;
641     nsCOMPtr<jsdIValue> js_rv;
642
643     switch (type)
644     {
645         case JSD_HOOK_INTERRUPTED:
646             gJsds->GetInterruptHook(getter_AddRefs(hook));
647             break;
648         case JSD_HOOK_DEBUG_REQUESTED:
649             gJsds->GetDebugHook(getter_AddRefs(hook));
650             break;
651         case JSD_HOOK_DEBUGGER_KEYWORD:
652             gJsds->GetDebuggerHook(getter_AddRefs(hook));
653             break;
654         case JSD_HOOK_BREAKPOINT:
655             {
656                 /* we can't pause breakpoints the way we pause the other
657                  * execution hooks (at least, not easily.)  Instead we bail
658                  * here if the service is paused. */
659                 PRUint32 level;
660                 gJsds->GetPauseDepth(&level);
661                 if (!level)
662                     gJsds->GetBreakpointHook(getter_AddRefs(hook));
663             }
664             break;
665         case JSD_HOOK_THROW:
666         {
667             hook_rv = JSD_HOOK_RETURN_CONTINUE_THROW;
668             gJsds->GetThrowHook(getter_AddRefs(hook));
669             if (hook) {
670                 JSDValue *jsdv = JSD_GetException (jsdc, jsdthreadstate);
671                 js_rv = getter_AddRefs(jsdValue::FromPtr (jsdc, jsdv));
672             }
673             break;
674         }
675         default:
676             NS_ASSERTION (0, "Unknown hook type.");
677     }
678
679     if (!hook)
680         return hook_rv;
681     
682     if (!jsds_FilterHook (jsdc, jsdthreadstate))
683         return JSD_HOOK_RETURN_CONTINUE;
684     
685     JSDStackFrameInfo *native_frame = JSD_GetStackFrame (jsdc, jsdthreadstate);
686     nsCOMPtr<jsdIStackFrame> frame =
687         getter_AddRefs(jsdStackFrame::FromPtr(jsdc, jsdthreadstate,
688                                               native_frame));
689     gJsds->Pause(nsnull);
690     jsdIValue *inout_rv = js_rv;
691     NS_IF_ADDREF(inout_rv);
692     hook->OnExecute (frame, type, &inout_rv, &hook_rv);
693     js_rv = inout_rv;
694     NS_IF_RELEASE(inout_rv);
695     gJsds->UnPause(nsnull);
696     jsdStackFrame::InvalidateAll();
697         
698     if (hook_rv == JSD_HOOK_RETURN_RET_WITH_VAL ||
699         hook_rv == JSD_HOOK_RETURN_THROW_WITH_VAL) {
700         *rval = JSVAL_VOID;
701         if (js_rv) {
702             JSDValue *jsdv;
703             if (NS_SUCCEEDED(js_rv->GetJSDValue (&jsdv)))
704                 *rval = JSD_GetValueWrappedJSVal(jsdc, jsdv);
705         }
706     }
707     
708     return hook_rv;
709 }
710
711 static void
712 jsds_ScriptHookProc (JSDContext* jsdc, JSDScript* jsdscript, JSBool creating,
713                      void* callerdata)
714 {
715 #ifdef CAUTIOUS_SCRIPTHOOK
716     JSContext *cx = JSD_GetDefaultJSContext(jsdc);
717     JSRuntime *rt = JS_GetRuntime(cx);
718 #endif
719
720     if (creating) {
721         nsCOMPtr<jsdIScriptHook> hook;
722         gJsds->GetScriptHook(getter_AddRefs(hook));
723
724         /* a script is being created */
725         if (!hook) {
726             /* nobody cares, just exit */
727             return;
728         }
729             
730         nsCOMPtr<jsdIScript> script = 
731             getter_AddRefs(jsdScript::FromPtr(jsdc, jsdscript));
732 #ifdef CAUTIOUS_SCRIPTHOOK
733         JS_UNKEEP_ATOMS(rt);
734 #endif
735         gJsds->Pause(nsnull);
736         hook->OnScriptCreated (script);
737         gJsds->UnPause(nsnull);
738 #ifdef CAUTIOUS_SCRIPTHOOK
739         JS_KEEP_ATOMS(rt);
740 #endif
741     } else {
742         /* a script is being destroyed.  even if there is no registered hook
743          * we'll still need to invalidate the jsdIScript record, in order
744          * to remove the reference held in the JSDScript private data. */
745         nsCOMPtr<jsdIScript> jsdis = 
746             static_cast<jsdIScript *>(JSD_GetScriptPrivate(jsdscript));
747         if (!jsdis)
748             return;
749
750         jsdis->Invalidate();
751
752         if (gGCStatus == JSGC_END) {
753             nsCOMPtr<jsdIScriptHook> hook;
754             gJsds->GetScriptHook(getter_AddRefs(hook));
755             if (!hook)
756                 return;
757
758             /* if GC *isn't* running, we can tell the user about the script
759              * delete now. */
760 #ifdef CAUTIOUS_SCRIPTHOOK
761             JS_UNKEEP_ATOMS(rt);
762 #endif
763                 
764             gJsds->Pause(nsnull);
765             hook->OnScriptDestroyed (jsdis);
766             gJsds->UnPause(nsnull);
767 #ifdef CAUTIOUS_SCRIPTHOOK
768             JS_KEEP_ATOMS(rt);
769 #endif
770         } else {
771             /* if a GC *is* running, we've got to wait until it's done before
772              * we can execute any JS, so we queue the notification in a PRCList
773              * until GC tells us it's done. See jsds_GCCallbackProc(). */
774             DeadScript *ds = PR_NEW(DeadScript);
775             if (!ds)
776                 return; /* NS_ERROR_OUT_OF_MEMORY */
777         
778             ds->jsdc = jsdc;
779             ds->script = jsdis;
780             NS_ADDREF(ds->script);
781             if (gDeadScripts)
782                 /* if the queue exists, add to it */
783                 PR_APPEND_LINK(&ds->links, &gDeadScripts->links);
784             else {
785                 /* otherwise create the queue */
786                 PR_INIT_CLIST(&ds->links);
787                 gDeadScripts = ds;
788             }
789         }
790     }            
791 }
792
793 /*******************************************************************************
794  * reflected jsd data structures
795  *******************************************************************************/
796
797 /* Contexts */
798 /*
799 NS_IMPL_THREADSAFE_ISUPPORTS2(jsdContext, jsdIContext, jsdIEphemeral);
800
801 NS_IMETHODIMP
802 jsdContext::GetJSDContext(JSDContext **_rval)
803 {
804     *_rval = mCx;
805     return NS_OK;
806 }
807 */
808
809 /* Objects */
810 NS_IMPL_THREADSAFE_ISUPPORTS1(jsdObject, jsdIObject)
811
812 NS_IMETHODIMP
813 jsdObject::GetJSDContext(JSDContext **_rval)
814 {
815     *_rval = mCx;
816     return NS_OK;
817 }
818
819 NS_IMETHODIMP
820 jsdObject::GetJSDObject(JSDObject **_rval)
821 {
822     *_rval = mObject;
823     return NS_OK;
824 }
825
826 NS_IMETHODIMP
827 jsdObject::GetCreatorURL(nsACString &_rval)
828 {
829     _rval.Assign(JSD_GetObjectNewURL(mCx, mObject));
830     return NS_OK;
831 }
832
833 NS_IMETHODIMP
834 jsdObject::GetCreatorLine(PRUint32 *_rval)
835 {
836     *_rval = JSD_GetObjectNewLineNumber(mCx, mObject);
837     return NS_OK;
838 }
839
840 NS_IMETHODIMP
841 jsdObject::GetConstructorURL(nsACString &_rval)
842 {
843     _rval.Assign(JSD_GetObjectConstructorURL(mCx, mObject));
844     return NS_OK;
845 }
846
847 NS_IMETHODIMP
848 jsdObject::GetConstructorLine(PRUint32 *_rval)
849 {
850     *_rval = JSD_GetObjectConstructorLineNumber(mCx, mObject);
851     return NS_OK;
852 }
853
854 NS_IMETHODIMP
855 jsdObject::GetValue(jsdIValue **_rval)
856 {
857     JSDValue *jsdv = JSD_GetValueForObject (mCx, mObject);
858     
859     *_rval = jsdValue::FromPtr (mCx, jsdv);
860     return NS_OK;
861 }
862
863 /* Properties */
864 NS_IMPL_THREADSAFE_ISUPPORTS2(jsdProperty, jsdIProperty, jsdIEphemeral)
865
866 jsdProperty::jsdProperty (JSDContext *aCx, JSDProperty *aProperty) :
867     mCx(aCx), mProperty(aProperty)
868 {
869     DEBUG_CREATE ("jsdProperty", gPropertyCount);
870     mValid = (aCx && aProperty);
871     mLiveListEntry.value = this;
872     jsds_InsertEphemeral (&gLiveProperties, &mLiveListEntry);
873 }
874
875 jsdProperty::~jsdProperty () 
876 {
877     DEBUG_DESTROY ("jsdProperty", gPropertyCount);
878     if (mValid)
879         Invalidate();
880 }
881
882 NS_IMETHODIMP
883 jsdProperty::Invalidate()
884 {
885     ASSERT_VALID_EPHEMERAL;
886     mValid = PR_FALSE;
887     jsds_RemoveEphemeral (&gLiveProperties, &mLiveListEntry);
888     JSD_DropProperty (mCx, mProperty);
889     return NS_OK;
890 }
891
892 void
893 jsdProperty::InvalidateAll()
894 {
895     if (gLiveProperties)
896         jsds_InvalidateAllEphemerals (&gLiveProperties);
897 }
898
899 NS_IMETHODIMP
900 jsdProperty::GetJSDContext(JSDContext **_rval)
901 {
902     *_rval = mCx;
903     return NS_OK;
904 }
905
906 NS_IMETHODIMP
907 jsdProperty::GetJSDProperty(JSDProperty **_rval)
908 {
909     *_rval = mProperty;
910     return NS_OK;
911 }
912
913 NS_IMETHODIMP
914 jsdProperty::GetIsValid(PRBool *_rval)
915 {
916     *_rval = mValid;
917     return NS_OK;
918 }
919
920 NS_IMETHODIMP
921 jsdProperty::GetAlias(jsdIValue **_rval)
922 {
923     JSDValue *jsdv = JSD_GetPropertyValue (mCx, mProperty);
924     
925     *_rval = jsdValue::FromPtr (mCx, jsdv);
926     return NS_OK;
927 }
928
929 NS_IMETHODIMP
930 jsdProperty::GetFlags(PRUint32 *_rval)
931 {
932     *_rval = JSD_GetPropertyFlags (mCx, mProperty);
933     return NS_OK;
934 }
935
936 NS_IMETHODIMP
937 jsdProperty::GetName(jsdIValue **_rval)
938 {
939     JSDValue *jsdv = JSD_GetPropertyName (mCx, mProperty);
940     
941     *_rval = jsdValue::FromPtr (mCx, jsdv);
942     return NS_OK;
943 }
944
945 NS_IMETHODIMP
946 jsdProperty::GetValue(jsdIValue **_rval)
947 {
948     JSDValue *jsdv = JSD_GetPropertyValue (mCx, mProperty);
949     
950     *_rval = jsdValue::FromPtr (mCx, jsdv);
951     return NS_OK;
952 }
953
954 NS_IMETHODIMP
955 jsdProperty::GetVarArgSlot(PRUint32 *_rval)
956 {
957     *_rval = JSD_GetPropertyVarArgSlot (mCx, mProperty);
958     return NS_OK;
959 }
960
961 /* Scripts */
962 NS_IMPL_THREADSAFE_ISUPPORTS2(jsdScript, jsdIScript, jsdIEphemeral)
963
964 static NS_IMETHODIMP
965 AssignToJSString(nsACString *x, JSString *str)
966 {
967     if (!str) {
968         x->SetLength(0);
969         return NS_OK;
970     }
971     size_t length = JS_GetStringEncodingLength(NULL, str);
972     if (length == size_t(-1))
973         return NS_ERROR_FAILURE;
974     x->SetLength(PRUint32(length));
975     if (x->Length() != PRUint32(length))
976         return NS_ERROR_OUT_OF_MEMORY;
977     JS_EncodeStringToBuffer(str, x->BeginWriting(), length);
978     return NS_OK;
979 }
980
981 jsdScript::jsdScript (JSDContext *aCx, JSDScript *aScript) : mValid(PR_FALSE),
982                                                              mTag(0),
983                                                              mCx(aCx),
984                                                              mScript(aScript),
985                                                              mFileName(0), 
986                                                              mFunctionName(0),
987                                                              mBaseLineNumber(0),
988                                                              mLineExtent(0),
989                                                              mPPLineMap(0),
990                                                              mFirstPC(0)
991 {
992     DEBUG_CREATE ("jsdScript", gScriptCount);
993
994     if (mScript) {
995         /* copy the script's information now, so we have it later, when it
996          * gets destroyed. */
997         JSD_LockScriptSubsystem(mCx);
998         mFileName = new nsCString(JSD_GetScriptFilename(mCx, mScript));
999         mFunctionName = new nsCString();
1000         if (mFunctionName) {
1001             JSString *str = JSD_GetScriptFunctionId(mCx, mScript);
1002             if (str)
1003                 AssignToJSString(mFunctionName, str);
1004         }
1005         mBaseLineNumber = JSD_GetScriptBaseLineNumber(mCx, mScript);
1006         mLineExtent = JSD_GetScriptLineExtent(mCx, mScript);
1007         mFirstPC = JSD_GetClosestPC(mCx, mScript, 0);
1008         JSD_UnlockScriptSubsystem(mCx);
1009         
1010         mValid = PR_TRUE;
1011     }
1012 }
1013
1014 jsdScript::~jsdScript () 
1015 {
1016     DEBUG_DESTROY ("jsdScript", gScriptCount);
1017     if (mFileName)
1018         delete mFileName;
1019     if (mFunctionName)
1020         delete mFunctionName;
1021
1022     if (mPPLineMap)
1023         PR_Free(mPPLineMap);
1024
1025     /* Invalidate() needs to be called to release an owning reference to
1026      * ourselves, so if we got here without being invalidated, something
1027      * has gone wrong with our ref count. */
1028     NS_ASSERTION (!mValid, "Script destroyed without being invalidated.");
1029 }
1030
1031 /*
1032  * This method populates a line <-> pc map for a pretty printed version of this
1033  * script.  It does this by decompiling, and then recompiling the script.  The
1034  * resulting script is scanned for the line map, and then left as GC fodder.
1035  */
1036 PCMapEntry *
1037 jsdScript::CreatePPLineMap()
1038 {    
1039     JSContext  *cx  = JSD_GetDefaultJSContext (mCx);
1040     JSAutoRequest ar(cx);
1041     JSObject   *obj = JS_NewObject(cx, NULL, NULL, NULL);
1042     JSFunction *fun = JSD_GetJSFunction (mCx, mScript);
1043     JSScript   *script; /* In JSD compartment */
1044     PRUint32    baseLine;
1045     JSObject   *scriptObj = NULL;
1046     JSString   *jsstr;
1047     size_t      length;
1048     const jschar *chars;
1049     
1050     if (fun) {
1051         uintN nargs;
1052
1053         {
1054             JSAutoEnterCompartment ac;
1055             if (!ac.enter(cx, JS_GetFunctionObject(fun)))
1056                 return nsnull;
1057
1058             nargs = JS_GetFunctionArgumentCount(cx, fun);
1059             if (nargs > 12)
1060                 return nsnull;
1061             jsstr = JS_DecompileFunctionBody (cx, fun, 4);
1062             if (!jsstr)
1063                 return nsnull;
1064
1065             if (!(chars = JS_GetStringCharsAndLength(cx, jsstr, &length)))
1066                 return nsnull;
1067         }
1068
1069         JS::Anchor<JSString *> kungFuDeathGrip(jsstr);
1070         const char *argnames[] = {"arg1", "arg2", "arg3", "arg4", 
1071                                   "arg5", "arg6", "arg7", "arg8",
1072                                   "arg9", "arg10", "arg11", "arg12" };
1073         fun = JS_CompileUCFunction (cx, obj, "ppfun", nargs, argnames, chars,
1074                                     length, "x-jsd:ppbuffer?type=function", 3);
1075         if (!fun || !(script = JS_GetFunctionScript(cx, fun)))
1076             return nsnull;
1077         baseLine = 3;
1078     } else {
1079         script = JSD_GetJSScript(mCx, mScript);
1080         JSString *jsstr;
1081
1082         {
1083             JS::AutoEnterScriptCompartment ac;
1084             if (!ac.enter(cx, script))
1085                 return nsnull;
1086
1087             jsstr = JS_DecompileScript (cx, script, "ppscript", 4);
1088             if (!jsstr)
1089                 return nsnull;
1090
1091             if (!(chars = JS_GetStringCharsAndLength(cx, jsstr, &length)))
1092                 return nsnull;
1093         }
1094
1095         JS::Anchor<JSString *> kungFuDeathGrip(jsstr);
1096         scriptObj = JS_CompileUCScript (cx, obj, chars, length, "x-jsd:ppbuffer?type=script", 1);
1097         if (!scriptObj)
1098             return nsnull;
1099         script = JS_GetScriptFromObject(scriptObj);
1100         baseLine = 1;
1101     }
1102
1103     /* Make sure that a non-function script is rooted via scriptObj until the
1104      * end of script usage. */
1105     JS::Anchor<JSObject *> scriptAnchor(scriptObj);
1106
1107     PRUint32 scriptExtent = JS_GetScriptLineExtent (cx, script);
1108     jsbytecode* firstPC = JS_LineNumberToPC (cx, script, 0);
1109     /* allocate worst case size of map (number of lines in script + 1
1110      * for our 0 record), we'll shrink it with a realloc later. */
1111     PCMapEntry *lineMap =
1112         static_cast<PCMapEntry *>
1113                    (PR_Malloc((scriptExtent + 1) * sizeof (PCMapEntry)));
1114     PRUint32 lineMapSize = 0;
1115
1116     if (lineMap) {
1117         for (PRUint32 line = baseLine; line < scriptExtent + baseLine; ++line) {
1118             jsbytecode* pc = JS_LineNumberToPC (cx, script, line);
1119             if (line == JS_PCToLineNumber (cx, script, pc)) {
1120                 lineMap[lineMapSize].line = line;
1121                 lineMap[lineMapSize].pc = pc - firstPC;
1122                 ++lineMapSize;
1123             }
1124         }
1125         if (scriptExtent != lineMapSize) {
1126             lineMap =
1127                 static_cast<PCMapEntry *>
1128                            (PR_Realloc(mPPLineMap = lineMap,
1129                                        lineMapSize * sizeof(PCMapEntry)));
1130             if (!lineMap) {
1131                 PR_Free(mPPLineMap);
1132                 lineMapSize = 0;
1133             }
1134         }
1135     }
1136
1137     mPCMapSize = lineMapSize;
1138     return mPPLineMap = lineMap;
1139 }
1140
1141 PRUint32
1142 jsdScript::PPPcToLine (PRUint32 aPC)
1143 {
1144     if (!mPPLineMap && !CreatePPLineMap())
1145         return 0;
1146     PRUint32 i;
1147     for (i = 1; i < mPCMapSize; ++i) {
1148         if (mPPLineMap[i].pc > aPC)
1149             return mPPLineMap[i - 1].line;            
1150     }
1151
1152     return mPPLineMap[mPCMapSize - 1].line;
1153 }
1154
1155 PRUint32
1156 jsdScript::PPLineToPc (PRUint32 aLine)
1157 {
1158     if (!mPPLineMap && !CreatePPLineMap())
1159         return 0;
1160     PRUint32 i;
1161     for (i = 1; i < mPCMapSize; ++i) {
1162         if (mPPLineMap[i].line > aLine)
1163             return mPPLineMap[i - 1].pc;
1164     }
1165
1166     return mPPLineMap[mPCMapSize - 1].pc;
1167 }
1168
1169 NS_IMETHODIMP
1170 jsdScript::GetJSDContext(JSDContext **_rval)
1171 {
1172     ASSERT_VALID_EPHEMERAL;
1173     *_rval = mCx;
1174     return NS_OK;
1175 }
1176
1177 NS_IMETHODIMP
1178 jsdScript::GetJSDScript(JSDScript **_rval)
1179 {
1180     ASSERT_VALID_EPHEMERAL;
1181     *_rval = mScript;
1182     return NS_OK;
1183 }
1184
1185 NS_IMETHODIMP
1186 jsdScript::GetVersion (PRInt32 *_rval)
1187 {
1188     ASSERT_VALID_EPHEMERAL;
1189     JSContext *cx = JSD_GetDefaultJSContext (mCx);
1190     JSScript *script = JSD_GetJSScript(mCx, mScript);
1191     JS::AutoEnterScriptCompartment ac;
1192     if (!ac.enter(cx, script))
1193         return NS_ERROR_FAILURE;
1194     *_rval = static_cast<PRInt32>(JS_GetScriptVersion(cx, script));
1195     return NS_OK;
1196 }
1197
1198 NS_IMETHODIMP
1199 jsdScript::GetTag(PRUint32 *_rval)
1200 {
1201     if (!mTag)
1202         mTag = ++jsdScript::LastTag;
1203     
1204     *_rval = mTag;
1205     return NS_OK;
1206 }
1207
1208 NS_IMETHODIMP
1209 jsdScript::Invalidate()
1210 {
1211     ASSERT_VALID_EPHEMERAL;
1212     mValid = PR_FALSE;
1213     
1214     /* release the addref we do in FromPtr */
1215     jsdIScript *script = static_cast<jsdIScript *>
1216                                     (JSD_GetScriptPrivate(mScript));
1217     NS_ASSERTION (script == this, "That's not my script!");
1218     NS_RELEASE(script);
1219     JSD_SetScriptPrivate(mScript, NULL);
1220     return NS_OK;
1221 }
1222
1223 void
1224 jsdScript::InvalidateAll ()
1225 {
1226     JSDContext *cx;
1227     if (NS_FAILED(gJsds->GetJSDContext (&cx)))
1228         return;
1229
1230     JSDScript *script;
1231     JSDScript *iter = NULL;
1232     
1233     JSD_LockScriptSubsystem(cx);
1234     while((script = JSD_IterateScripts(cx, &iter)) != NULL) {
1235         nsCOMPtr<jsdIScript> jsdis = 
1236             static_cast<jsdIScript *>(JSD_GetScriptPrivate(script));
1237         if (jsdis)
1238             jsdis->Invalidate();
1239     }
1240     JSD_UnlockScriptSubsystem(cx);
1241 }
1242
1243 NS_IMETHODIMP
1244 jsdScript::GetIsValid(PRBool *_rval)
1245 {
1246     *_rval = mValid;
1247     return NS_OK;
1248 }
1249
1250 NS_IMETHODIMP
1251 jsdScript::SetFlags(PRUint32 flags)
1252 {
1253     ASSERT_VALID_EPHEMERAL;
1254     JSD_SetScriptFlags(mCx, mScript, flags);
1255     return NS_OK;
1256 }
1257
1258 NS_IMETHODIMP
1259 jsdScript::GetFlags(PRUint32 *_rval)
1260 {
1261     ASSERT_VALID_EPHEMERAL;
1262     *_rval = JSD_GetScriptFlags(mCx, mScript);
1263     return NS_OK;
1264 }
1265
1266 NS_IMETHODIMP
1267 jsdScript::GetFileName(nsACString &_rval)
1268 {
1269     _rval.Assign(*mFileName);
1270     return NS_OK;
1271 }
1272
1273 NS_IMETHODIMP
1274 jsdScript::GetFunctionName(nsACString &_rval)
1275 {
1276     _rval.Assign(*mFunctionName);
1277     return NS_OK;
1278 }
1279
1280 NS_IMETHODIMP
1281 jsdScript::GetParameterNames(PRUint32* count, PRUnichar*** paramNames)
1282 {
1283     ASSERT_VALID_EPHEMERAL;
1284     JSContext *cx = JSD_GetDefaultJSContext (mCx);
1285     if (!cx) {
1286         NS_WARNING("No default context !?");
1287         return NS_ERROR_FAILURE;
1288     }
1289     JSFunction *fun = JSD_GetJSFunction (mCx, mScript);
1290     if (!fun) {
1291         *count = 0;
1292         *paramNames = nsnull;
1293         return NS_OK;
1294     }
1295
1296     JSAutoRequest ar(cx);
1297     JSAutoEnterCompartment ac;
1298     if (!ac.enter(cx, JS_GetFunctionObject(fun)))
1299         return NS_ERROR_FAILURE;
1300
1301     uintN nargs;
1302     if (!JS_FunctionHasLocalNames(cx, fun) ||
1303         (nargs = JS_GetFunctionArgumentCount(cx, fun)) == 0) {
1304         *count = 0;
1305         *paramNames = nsnull;
1306         return NS_OK;
1307     }
1308
1309     PRUnichar **ret =
1310         static_cast<PRUnichar**>(NS_Alloc(nargs * sizeof(PRUnichar*)));
1311     if (!ret)
1312         return NS_ERROR_OUT_OF_MEMORY;
1313
1314     void *mark;
1315     jsuword *names = JS_GetFunctionLocalNameArray(cx, fun, &mark);
1316     if (!names) {
1317         NS_Free(ret);
1318         return NS_ERROR_OUT_OF_MEMORY;
1319     }
1320
1321     nsresult rv = NS_OK;
1322     for (uintN i = 0; i < nargs; ++i) {
1323         JSAtom *atom = JS_LocalNameToAtom(names[i]);
1324         if (!atom) {
1325             ret[i] = 0;
1326         } else {
1327             JSString *str = JS_AtomKey(atom);
1328             ret[i] = NS_strndup(JS_GetInternedStringChars(str), JS_GetStringLength(str));
1329             if (!ret[i]) {
1330                 NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(i, ret);
1331                 rv = NS_ERROR_OUT_OF_MEMORY;
1332                 break;
1333             }
1334         }
1335     }
1336     JS_ReleaseFunctionLocalNameArray(cx, mark);
1337     if (NS_FAILED(rv))
1338         return rv;
1339     *count = nargs;
1340     *paramNames = ret;
1341     return NS_OK;
1342 }
1343
1344 NS_IMETHODIMP
1345 jsdScript::GetFunctionObject(jsdIValue **_rval)
1346 {
1347     JSFunction *fun = JSD_GetJSFunction(mCx, mScript);
1348     if (!fun)
1349         return NS_ERROR_NOT_AVAILABLE;
1350     
1351     JSObject *obj = JS_GetFunctionObject(fun);
1352     if (!obj)
1353         return NS_ERROR_FAILURE;
1354
1355     JSDContext *cx;
1356     if (NS_FAILED(gJsds->GetJSDContext (&cx)))
1357         return NS_ERROR_NOT_INITIALIZED;
1358
1359     JSDValue *jsdv = JSD_NewValue(cx, OBJECT_TO_JSVAL(obj));
1360     if (!jsdv)
1361         return NS_ERROR_OUT_OF_MEMORY;
1362
1363     *_rval = jsdValue::FromPtr(cx, jsdv);
1364     if (!*_rval) {
1365         JSD_DropValue(cx, jsdv);
1366         return NS_ERROR_OUT_OF_MEMORY;
1367     }
1368
1369     return NS_OK;
1370 }
1371
1372 NS_IMETHODIMP
1373 jsdScript::GetFunctionSource(nsAString & aFunctionSource)
1374 {
1375     ASSERT_VALID_EPHEMERAL;
1376     JSContext *cx = JSD_GetDefaultJSContext (mCx);
1377     if (!cx) {
1378         NS_WARNING("No default context !?");
1379         return NS_ERROR_FAILURE;
1380     }
1381     JSFunction *fun = JSD_GetJSFunction (mCx, mScript);
1382
1383     JSAutoRequest ar(cx);
1384
1385     JSString *jsstr;
1386     JSAutoEnterCompartment ac;
1387     JS::AutoEnterScriptCompartment asc;
1388     if (fun) {
1389         if (!ac.enter(cx, JS_GetFunctionObject(fun)))
1390             return NS_ERROR_FAILURE;
1391         jsstr = JS_DecompileFunction (cx, fun, 4);
1392     } else {
1393         JSScript *script = JSD_GetJSScript (mCx, mScript);
1394         if (!asc.enter(cx, script))
1395             return NS_ERROR_FAILURE;
1396         jsstr = JS_DecompileScript (cx, script, "ppscript", 4);
1397     }
1398     if (!jsstr)
1399         return NS_ERROR_FAILURE;
1400
1401     size_t length;
1402     const jschar *chars = JS_GetStringCharsZAndLength(cx, jsstr, &length);
1403     if (!chars)
1404         return NS_ERROR_FAILURE;
1405
1406     aFunctionSource = nsDependentString(chars, length);
1407     return NS_OK;
1408 }
1409
1410 NS_IMETHODIMP
1411 jsdScript::GetBaseLineNumber(PRUint32 *_rval)
1412 {
1413     *_rval = mBaseLineNumber;
1414     return NS_OK;
1415 }
1416
1417 NS_IMETHODIMP
1418 jsdScript::GetLineExtent(PRUint32 *_rval)
1419 {
1420     *_rval = mLineExtent;
1421     return NS_OK;
1422 }
1423
1424 NS_IMETHODIMP
1425 jsdScript::GetCallCount(PRUint32 *_rval)
1426 {
1427     ASSERT_VALID_EPHEMERAL;
1428     *_rval = JSD_GetScriptCallCount (mCx, mScript);
1429     return NS_OK;
1430 }
1431
1432 NS_IMETHODIMP
1433 jsdScript::GetMaxRecurseDepth(PRUint32 *_rval)
1434 {
1435     ASSERT_VALID_EPHEMERAL;
1436     *_rval = JSD_GetScriptMaxRecurseDepth (mCx, mScript);
1437     return NS_OK;
1438 }
1439
1440 NS_IMETHODIMP
1441 jsdScript::GetMinExecutionTime(double *_rval)
1442 {
1443     ASSERT_VALID_EPHEMERAL;
1444     *_rval = JSD_GetScriptMinExecutionTime (mCx, mScript);
1445     return NS_OK;
1446 }
1447
1448 NS_IMETHODIMP
1449 jsdScript::GetMaxExecutionTime(double *_rval)
1450 {
1451     ASSERT_VALID_EPHEMERAL;
1452     *_rval = JSD_GetScriptMaxExecutionTime (mCx, mScript);
1453     return NS_OK;
1454 }
1455
1456 NS_IMETHODIMP
1457 jsdScript::GetTotalExecutionTime(double *_rval)
1458 {
1459     ASSERT_VALID_EPHEMERAL;
1460     *_rval = JSD_GetScriptTotalExecutionTime (mCx, mScript);
1461     return NS_OK;
1462 }
1463
1464 NS_IMETHODIMP
1465 jsdScript::GetMinOwnExecutionTime(double *_rval)
1466 {
1467     ASSERT_VALID_EPHEMERAL;
1468     *_rval = JSD_GetScriptMinOwnExecutionTime (mCx, mScript);
1469     return NS_OK;
1470 }
1471
1472 NS_IMETHODIMP
1473 jsdScript::GetMaxOwnExecutionTime(double *_rval)
1474 {
1475     ASSERT_VALID_EPHEMERAL;
1476     *_rval = JSD_GetScriptMaxOwnExecutionTime (mCx, mScript);
1477     return NS_OK;
1478 }
1479
1480 NS_IMETHODIMP
1481 jsdScript::GetTotalOwnExecutionTime(double *_rval)
1482 {
1483     ASSERT_VALID_EPHEMERAL;
1484     *_rval = JSD_GetScriptTotalOwnExecutionTime (mCx, mScript);
1485     return NS_OK;
1486 }
1487
1488 NS_IMETHODIMP
1489 jsdScript::ClearProfileData()
1490 {
1491     ASSERT_VALID_EPHEMERAL;
1492     JSD_ClearScriptProfileData(mCx, mScript);
1493     return NS_OK;
1494 }
1495
1496 NS_IMETHODIMP
1497 jsdScript::PcToLine(PRUint32 aPC, PRUint32 aPcmap, PRUint32 *_rval)
1498 {
1499     ASSERT_VALID_EPHEMERAL;
1500     if (aPcmap == PCMAP_SOURCETEXT) {
1501         *_rval = JSD_GetClosestLine (mCx, mScript, mFirstPC + aPC);
1502     } else if (aPcmap == PCMAP_PRETTYPRINT) {
1503         *_rval = PPPcToLine(aPC);
1504     } else {
1505         return NS_ERROR_INVALID_ARG;
1506     }
1507     
1508     return NS_OK;
1509 }
1510
1511 NS_IMETHODIMP
1512 jsdScript::LineToPc(PRUint32 aLine, PRUint32 aPcmap, PRUint32 *_rval)
1513 {
1514     ASSERT_VALID_EPHEMERAL;
1515     if (aPcmap == PCMAP_SOURCETEXT) {
1516         jsuword pc = JSD_GetClosestPC (mCx, mScript, aLine);
1517         *_rval = pc - mFirstPC;
1518     } else if (aPcmap == PCMAP_PRETTYPRINT) {
1519         *_rval = PPLineToPc(aLine);
1520     } else {
1521         return NS_ERROR_INVALID_ARG;
1522     }
1523
1524     return NS_OK;
1525 }
1526
1527 NS_IMETHODIMP
1528 jsdScript::EnableSingleStepInterrupts(PRBool enable)
1529 {
1530     ASSERT_VALID_EPHEMERAL;
1531
1532     /* Must have set interrupt hook before enabling */
1533     if (enable && !jsdService::GetService()->CheckInterruptHook())
1534         return NS_ERROR_NOT_INITIALIZED;
1535
1536     return (JSD_EnableSingleStepInterrupts(mCx, mScript, enable) ? NS_OK : NS_ERROR_FAILURE);
1537 }
1538
1539 NS_IMETHODIMP
1540 jsdScript::IsLineExecutable(PRUint32 aLine, PRUint32 aPcmap, PRBool *_rval)
1541 {
1542     ASSERT_VALID_EPHEMERAL;
1543     if (aPcmap == PCMAP_SOURCETEXT) {    
1544         jsuword pc = JSD_GetClosestPC (mCx, mScript, aLine);
1545         *_rval = (aLine == JSD_GetClosestLine (mCx, mScript, pc));
1546     } else if (aPcmap == PCMAP_PRETTYPRINT) {
1547         if (!mPPLineMap && !CreatePPLineMap())
1548             return NS_ERROR_OUT_OF_MEMORY;
1549         *_rval = PR_FALSE;
1550         for (PRUint32 i = 0; i < mPCMapSize; ++i) {
1551             if (mPPLineMap[i].line >= aLine) {
1552                 *_rval = (mPPLineMap[i].line == aLine);
1553                 break;
1554             }
1555         }
1556     } else {
1557         return NS_ERROR_INVALID_ARG;
1558     }
1559     
1560     return NS_OK;
1561 }
1562
1563 NS_IMETHODIMP
1564 jsdScript::SetBreakpoint(PRUint32 aPC)
1565 {
1566     ASSERT_VALID_EPHEMERAL;
1567     jsuword pc = mFirstPC + aPC;
1568     JSD_SetExecutionHook (mCx, mScript, pc, jsds_ExecutionHookProc, NULL);
1569     return NS_OK;
1570 }
1571
1572 NS_IMETHODIMP
1573 jsdScript::ClearBreakpoint(PRUint32 aPC)
1574 {
1575     ASSERT_VALID_EPHEMERAL;    
1576     jsuword pc = mFirstPC + aPC;
1577     JSD_ClearExecutionHook (mCx, mScript, pc);
1578     return NS_OK;
1579 }
1580
1581 NS_IMETHODIMP
1582 jsdScript::ClearAllBreakpoints()
1583 {
1584     ASSERT_VALID_EPHEMERAL;
1585     JSD_LockScriptSubsystem(mCx);
1586     JSD_ClearAllExecutionHooksForScript (mCx, mScript);
1587     JSD_UnlockScriptSubsystem(mCx);
1588     return NS_OK;
1589 }
1590
1591 /* Contexts */
1592 NS_IMPL_THREADSAFE_ISUPPORTS2(jsdContext, jsdIContext, jsdIEphemeral)
1593
1594 jsdIContext *
1595 jsdContext::FromPtr (JSDContext *aJSDCx, JSContext *aJSCx)
1596 {
1597     if (!aJSDCx || !aJSCx)
1598         return nsnull;
1599
1600     nsCOMPtr<jsdIContext> jsdicx;
1601     nsCOMPtr<jsdIEphemeral> eph = 
1602         jsds_FindEphemeral (&gLiveContexts, static_cast<void *>(aJSCx));
1603     if (eph)
1604     {
1605         jsdicx = do_QueryInterface(eph);
1606     }
1607     else
1608     {
1609         nsCOMPtr<nsISupports> iscx;
1610         if (JS_GetOptions(aJSCx) & JSOPTION_PRIVATE_IS_NSISUPPORTS)
1611             iscx = static_cast<nsISupports *>(JS_GetContextPrivate(aJSCx));
1612         jsdicx = new jsdContext (aJSDCx, aJSCx, iscx);
1613     }
1614
1615     jsdIContext *ctx = nsnull;
1616     jsdicx.swap(ctx);
1617     return ctx;
1618 }
1619
1620 jsdContext::jsdContext (JSDContext *aJSDCx, JSContext *aJSCx,
1621                         nsISupports *aISCx) : mValid(PR_TRUE), mTag(0),
1622                                               mJSDCx(aJSDCx),
1623                                               mJSCx(aJSCx), mISCx(aISCx)
1624 {
1625     DEBUG_CREATE ("jsdContext", gContextCount);
1626     mLiveListEntry.value = this;
1627     mLiveListEntry.key   = static_cast<void *>(aJSCx);
1628     jsds_InsertEphemeral (&gLiveContexts, &mLiveListEntry);
1629 }
1630
1631 jsdContext::~jsdContext() 
1632 {
1633     DEBUG_DESTROY ("jsdContext", gContextCount);
1634     if (mValid)
1635     {
1636         /* call Invalidate() to take ourselves out of the live list */
1637         Invalidate();
1638     }
1639 }
1640
1641 NS_IMETHODIMP
1642 jsdContext::GetIsValid(PRBool *_rval)
1643 {
1644     *_rval = mValid;
1645     return NS_OK;
1646 }
1647
1648 NS_IMETHODIMP
1649 jsdContext::Invalidate()
1650 {
1651     ASSERT_VALID_EPHEMERAL;
1652     mValid = PR_FALSE;
1653     jsds_RemoveEphemeral (&gLiveContexts, &mLiveListEntry);
1654     return NS_OK;
1655 }
1656
1657 void
1658 jsdContext::InvalidateAll()
1659 {
1660     if (gLiveContexts)
1661         jsds_InvalidateAllEphemerals (&gLiveContexts);
1662 }
1663
1664 NS_IMETHODIMP
1665 jsdContext::GetJSContext(JSContext **_rval)
1666 {
1667     ASSERT_VALID_EPHEMERAL;
1668     *_rval = mJSCx;
1669     return NS_OK;
1670 }
1671
1672 NS_IMETHODIMP
1673 jsdContext::GetOptions(PRUint32 *_rval)
1674 {
1675     ASSERT_VALID_EPHEMERAL;
1676     *_rval = JS_GetOptions(mJSCx);
1677     return NS_OK;
1678 }
1679
1680 NS_IMETHODIMP
1681 jsdContext::SetOptions(PRUint32 options)
1682 {
1683     ASSERT_VALID_EPHEMERAL;
1684     PRUint32 lastOptions = JS_GetOptions(mJSCx);
1685
1686     /* don't let users change this option, they'd just be shooting themselves
1687      * in the foot. */
1688     if ((options ^ lastOptions) & JSOPTION_PRIVATE_IS_NSISUPPORTS)
1689         return NS_ERROR_ILLEGAL_VALUE;
1690
1691     JS_SetOptions(mJSCx, options);
1692     return NS_OK;
1693 }
1694
1695 NS_IMETHODIMP
1696 jsdContext::GetPrivateData(nsISupports **_rval)
1697 {
1698     ASSERT_VALID_EPHEMERAL;
1699     PRUint32 options = JS_GetOptions(mJSCx);
1700     if (options & JSOPTION_PRIVATE_IS_NSISUPPORTS)
1701     {
1702         *_rval = static_cast<nsISupports*>(JS_GetContextPrivate(mJSCx));
1703         NS_IF_ADDREF(*_rval);
1704     }
1705     else
1706     {
1707         *_rval = nsnull;
1708     }
1709     
1710     return NS_OK;
1711 }
1712         
1713 NS_IMETHODIMP
1714 jsdContext::GetWrappedContext(nsISupports **_rval)
1715 {
1716     ASSERT_VALID_EPHEMERAL;
1717     NS_IF_ADDREF(*_rval = mISCx);
1718     return NS_OK;
1719 }
1720
1721 NS_IMETHODIMP
1722 jsdContext::GetTag(PRUint32 *_rval)
1723 {
1724     ASSERT_VALID_EPHEMERAL;
1725     if (!mTag)
1726         mTag = ++jsdContext::LastTag;
1727     
1728     *_rval = mTag;
1729     return NS_OK;
1730 }
1731
1732 NS_IMETHODIMP
1733 jsdContext::GetVersion (PRInt32 *_rval)
1734 {
1735     ASSERT_VALID_EPHEMERAL;
1736     *_rval = static_cast<PRInt32>(JS_GetVersion(mJSCx));
1737     return NS_OK;
1738 }
1739
1740 NS_IMETHODIMP
1741 jsdContext::SetVersion (PRInt32 id)
1742 {
1743     ASSERT_VALID_EPHEMERAL;
1744     JSVersion ver = static_cast<JSVersion>(id);
1745     JS_SetVersion(mJSCx, ver);
1746     return NS_OK;
1747 }
1748
1749 NS_IMETHODIMP
1750 jsdContext::GetGlobalObject (jsdIValue **_rval)
1751 {
1752     ASSERT_VALID_EPHEMERAL;
1753     JSObject *glob = JS_GetGlobalObject(mJSCx);
1754     JSDValue *jsdv = JSD_NewValue (mJSDCx, OBJECT_TO_JSVAL(glob));
1755     if (!jsdv)
1756         return NS_ERROR_FAILURE;
1757     *_rval = jsdValue::FromPtr (mJSDCx, jsdv);
1758     if (!*_rval)
1759         return NS_ERROR_FAILURE;
1760     return NS_OK;
1761 }
1762
1763 NS_IMETHODIMP
1764 jsdContext::GetScriptsEnabled (PRBool *_rval)
1765 {
1766     ASSERT_VALID_EPHEMERAL;
1767     if (!mISCx) {
1768         *_rval = PR_TRUE;
1769         return NS_OK;
1770     }
1771
1772     nsCOMPtr<nsIScriptContext> context = do_QueryInterface(mISCx);
1773     if (!context)
1774         return NS_ERROR_NO_INTERFACE;
1775
1776     *_rval = context->GetScriptsEnabled();
1777
1778     return NS_OK;
1779 }
1780
1781 NS_IMETHODIMP
1782 jsdContext::SetScriptsEnabled (PRBool _rval)
1783 {
1784     ASSERT_VALID_EPHEMERAL;
1785     if (!mISCx) {
1786         if (_rval)
1787             return NS_OK;
1788         return NS_ERROR_NO_INTERFACE;
1789     }
1790
1791     nsCOMPtr<nsIScriptContext> context = do_QueryInterface(mISCx);
1792     if (!context)
1793         return NS_ERROR_NO_INTERFACE;
1794
1795     context->SetScriptsEnabled(_rval, PR_TRUE);
1796
1797     return NS_OK;
1798 }
1799
1800 /* Stack Frames */
1801 NS_IMPL_THREADSAFE_ISUPPORTS2(jsdStackFrame, jsdIStackFrame, jsdIEphemeral)
1802
1803 jsdStackFrame::jsdStackFrame (JSDContext *aCx, JSDThreadState *aThreadState,
1804                               JSDStackFrameInfo *aStackFrameInfo) :
1805     mCx(aCx), mThreadState(aThreadState), mStackFrameInfo(aStackFrameInfo)
1806 {
1807     DEBUG_CREATE ("jsdStackFrame", gFrameCount);
1808     mValid = (aCx && aThreadState && aStackFrameInfo);
1809     if (mValid) {
1810         mLiveListEntry.key = aStackFrameInfo;
1811         mLiveListEntry.value = this;
1812         jsds_InsertEphemeral (&gLiveStackFrames, &mLiveListEntry);
1813     }
1814 }
1815
1816 jsdStackFrame::~jsdStackFrame() 
1817 {
1818     DEBUG_DESTROY ("jsdStackFrame", gFrameCount);
1819     if (mValid)
1820     {
1821         /* call Invalidate() to take ourselves out of the live list */
1822         Invalidate();
1823     }
1824 }
1825
1826 jsdIStackFrame *
1827 jsdStackFrame::FromPtr (JSDContext *aCx, JSDThreadState *aThreadState,
1828                         JSDStackFrameInfo *aStackFrameInfo)
1829 {
1830     if (!aStackFrameInfo)
1831         return nsnull;
1832
1833     jsdIStackFrame *rv;
1834     nsCOMPtr<jsdIStackFrame> frame;
1835
1836     nsCOMPtr<jsdIEphemeral> eph =
1837         jsds_FindEphemeral (&gLiveStackFrames,
1838                             reinterpret_cast<void *>(aStackFrameInfo));
1839
1840     if (eph)
1841     {
1842         frame = do_QueryInterface(eph);
1843         rv = frame;
1844     }
1845     else
1846     {
1847         rv = new jsdStackFrame (aCx, aThreadState, aStackFrameInfo);
1848     }
1849
1850     NS_IF_ADDREF(rv);
1851     return rv;
1852 }
1853
1854 NS_IMETHODIMP
1855 jsdStackFrame::Invalidate()
1856 {
1857     ASSERT_VALID_EPHEMERAL;
1858     mValid = PR_FALSE;
1859     jsds_RemoveEphemeral (&gLiveStackFrames, &mLiveListEntry);
1860     return NS_OK;
1861 }
1862
1863 void
1864 jsdStackFrame::InvalidateAll()
1865 {
1866     if (gLiveStackFrames)
1867         jsds_InvalidateAllEphemerals (&gLiveStackFrames);
1868 }
1869
1870 NS_IMETHODIMP
1871 jsdStackFrame::GetJSDContext(JSDContext **_rval)
1872 {
1873     ASSERT_VALID_EPHEMERAL;
1874     *_rval = mCx;
1875     return NS_OK;
1876 }
1877
1878 NS_IMETHODIMP
1879 jsdStackFrame::GetJSDThreadState(JSDThreadState **_rval)
1880 {
1881     ASSERT_VALID_EPHEMERAL;
1882     *_rval = mThreadState;
1883     return NS_OK;
1884 }
1885
1886 NS_IMETHODIMP
1887 jsdStackFrame::GetJSDStackFrameInfo(JSDStackFrameInfo **_rval)
1888 {
1889     ASSERT_VALID_EPHEMERAL;
1890     *_rval = mStackFrameInfo;
1891     return NS_OK;
1892 }
1893
1894 NS_IMETHODIMP
1895 jsdStackFrame::GetIsValid(PRBool *_rval)
1896 {
1897     *_rval = mValid;
1898     return NS_OK;
1899 }
1900
1901 NS_IMETHODIMP
1902 jsdStackFrame::GetCallingFrame(jsdIStackFrame **_rval)
1903 {
1904     ASSERT_VALID_EPHEMERAL;
1905     JSDStackFrameInfo *sfi = JSD_GetCallingStackFrame (mCx, mThreadState,
1906                                                        mStackFrameInfo);
1907     *_rval = jsdStackFrame::FromPtr (mCx, mThreadState, sfi);
1908     return NS_OK;
1909 }
1910
1911 NS_IMETHODIMP
1912 jsdStackFrame::GetExecutionContext(jsdIContext **_rval)
1913 {
1914     ASSERT_VALID_EPHEMERAL;
1915     JSContext *cx = JSD_GetJSContext (mCx, mThreadState);
1916     *_rval = jsdContext::FromPtr (mCx, cx);
1917     return NS_OK;
1918 }
1919
1920 NS_IMETHODIMP
1921 jsdStackFrame::GetFunctionName(nsACString &_rval)
1922 {
1923     ASSERT_VALID_EPHEMERAL;
1924     JSString *str = JSD_GetIdForStackFrame(mCx, mThreadState, mStackFrameInfo);
1925     if (str)
1926         return AssignToJSString(&_rval, str);
1927     
1928     _rval.Assign("anonymous");
1929     return NS_OK;
1930 }
1931
1932 NS_IMETHODIMP
1933 jsdStackFrame::GetIsDebugger(PRBool *_rval)
1934 {
1935     ASSERT_VALID_EPHEMERAL;
1936     *_rval = JSD_IsStackFrameDebugger (mCx, mThreadState, mStackFrameInfo);
1937     return NS_OK;
1938 }
1939
1940 NS_IMETHODIMP
1941 jsdStackFrame::GetIsConstructing(PRBool *_rval)
1942 {
1943     ASSERT_VALID_EPHEMERAL;
1944     *_rval = JSD_IsStackFrameConstructing (mCx, mThreadState, mStackFrameInfo);
1945     return NS_OK;
1946 }
1947
1948 NS_IMETHODIMP
1949 jsdStackFrame::GetScript(jsdIScript **_rval)
1950 {
1951     ASSERT_VALID_EPHEMERAL;
1952     JSDScript *script = JSD_GetScriptForStackFrame (mCx, mThreadState,
1953                                                     mStackFrameInfo);
1954     *_rval = jsdScript::FromPtr (mCx, script);
1955     return NS_OK;
1956 }
1957
1958 NS_IMETHODIMP
1959 jsdStackFrame::GetPc(PRUint32 *_rval)
1960 {
1961     ASSERT_VALID_EPHEMERAL;
1962     JSDScript *script = JSD_GetScriptForStackFrame (mCx, mThreadState,
1963                                                     mStackFrameInfo);
1964     if (!script)
1965         return NS_ERROR_FAILURE;
1966     jsuword pcbase = JSD_GetClosestPC(mCx, script, 0);
1967     
1968     jsuword pc = JSD_GetPCForStackFrame (mCx, mThreadState, mStackFrameInfo);
1969     if (pc)
1970         *_rval = pc - pcbase;
1971     else
1972         *_rval = pcbase;
1973     return NS_OK;
1974 }
1975
1976 NS_IMETHODIMP
1977 jsdStackFrame::GetLine(PRUint32 *_rval)
1978 {
1979     ASSERT_VALID_EPHEMERAL;
1980     JSDScript *script = JSD_GetScriptForStackFrame (mCx, mThreadState,
1981                                                     mStackFrameInfo);
1982     if (script) {
1983         jsuword pc = JSD_GetPCForStackFrame (mCx, mThreadState, mStackFrameInfo);
1984         *_rval = JSD_GetClosestLine (mCx, script, pc);
1985     } else {
1986         return NS_ERROR_FAILURE;
1987     }
1988     return NS_OK;
1989 }
1990
1991 NS_IMETHODIMP
1992 jsdStackFrame::GetCallee(jsdIValue **_rval)
1993 {
1994     ASSERT_VALID_EPHEMERAL;
1995     JSDValue *jsdv = JSD_GetCallObjectForStackFrame (mCx, mThreadState,
1996                                                      mStackFrameInfo);
1997     
1998     *_rval = jsdValue::FromPtr (mCx, jsdv);
1999     return NS_OK;
2000 }
2001
2002 NS_IMETHODIMP
2003 jsdStackFrame::GetScope(jsdIValue **_rval)
2004 {
2005     ASSERT_VALID_EPHEMERAL;
2006     JSDValue *jsdv = JSD_GetScopeChainForStackFrame (mCx, mThreadState,
2007                                                      mStackFrameInfo);
2008     
2009     *_rval = jsdValue::FromPtr (mCx, jsdv);
2010     return NS_OK;
2011 }
2012
2013 NS_IMETHODIMP
2014 jsdStackFrame::GetThisValue(jsdIValue **_rval)
2015 {
2016     ASSERT_VALID_EPHEMERAL;
2017     JSDValue *jsdv = JSD_GetThisForStackFrame (mCx, mThreadState,
2018                                                mStackFrameInfo);
2019     
2020     *_rval = jsdValue::FromPtr (mCx, jsdv);
2021     return NS_OK;
2022 }
2023
2024
2025 NS_IMETHODIMP
2026 jsdStackFrame::Eval (const nsAString &bytes, const nsACString &fileName,
2027                      PRUint32 line, jsdIValue **result, PRBool *_rval)
2028 {
2029     ASSERT_VALID_EPHEMERAL;
2030
2031     if (bytes.IsEmpty())
2032         return NS_ERROR_INVALID_ARG;
2033
2034     // get pointer to buffer contained in |bytes|
2035     nsAString::const_iterator h;
2036     bytes.BeginReading(h);
2037     const jschar *char_bytes = reinterpret_cast<const jschar *>(h.get());
2038
2039     JSExceptionState *estate = 0;
2040     jsval jv;
2041
2042     JSContext *cx = JSD_GetJSContext (mCx, mThreadState);
2043
2044     JSAutoRequest ar(cx);
2045
2046     estate = JS_SaveExceptionState (cx);
2047     JS_ClearPendingException (cx);
2048
2049     nsresult rv;
2050     nsCOMPtr<nsIJSContextStack> stack = do_GetService("@mozilla.org/js/xpc/ContextStack;1", &rv);
2051     if (NS_SUCCEEDED(rv))
2052         rv = stack->Push(cx);
2053     if (NS_FAILED(rv)) {
2054         JS_RestoreExceptionState (cx, estate);
2055         return rv;
2056     }
2057
2058     *_rval = JSD_AttemptUCScriptInStackFrame (mCx, mThreadState,
2059                                               mStackFrameInfo,
2060                                               char_bytes, bytes.Length(),
2061                                               PromiseFlatCString(fileName).get(),
2062                                               line, &jv);
2063     if (!*_rval) {
2064         if (JS_IsExceptionPending(cx))
2065             JS_GetPendingException (cx, &jv);
2066         else
2067             jv = JSVAL_NULL;
2068     }
2069
2070     JS_RestoreExceptionState (cx, estate);
2071
2072 #ifdef DEBUG
2073     JSContext* poppedCX;
2074     rv = stack->Pop(&poppedCX);
2075     NS_ASSERTION(NS_SUCCEEDED(rv) && poppedCX == cx, "bad pop");
2076 #else
2077     (void) stack->Pop(nsnull);
2078 #endif
2079
2080     JSDValue *jsdv = JSD_NewValue (mCx, jv);
2081     if (!jsdv)
2082         return NS_ERROR_FAILURE;
2083     *result = jsdValue::FromPtr (mCx, jsdv);
2084     if (!*result)
2085         return NS_ERROR_FAILURE;
2086     
2087     return NS_OK;
2088 }        
2089
2090 /* Values */
2091 NS_IMPL_THREADSAFE_ISUPPORTS2(jsdValue, jsdIValue, jsdIEphemeral)
2092 jsdIValue *
2093 jsdValue::FromPtr (JSDContext *aCx, JSDValue *aValue)
2094 {
2095     /* value will be dropped by te jsdValue destructor. */
2096
2097     if (!aValue)
2098         return nsnull;
2099     
2100     jsdIValue *rv = new jsdValue (aCx, aValue);
2101     NS_IF_ADDREF(rv);
2102     return rv;
2103 }
2104
2105 jsdValue::jsdValue (JSDContext *aCx, JSDValue *aValue) : mValid(PR_TRUE),
2106                                                          mCx(aCx), 
2107                                                          mValue(aValue)
2108 {
2109     DEBUG_CREATE ("jsdValue", gValueCount);
2110     mLiveListEntry.value = this;
2111     jsds_InsertEphemeral (&gLiveValues, &mLiveListEntry);
2112 }
2113
2114 jsdValue::~jsdValue() 
2115 {
2116     DEBUG_DESTROY ("jsdValue", gValueCount);
2117     if (mValid)
2118         /* call Invalidate() to take ourselves out of the live list */
2119         Invalidate();
2120 }   
2121
2122 NS_IMETHODIMP
2123 jsdValue::GetIsValid(PRBool *_rval)
2124 {
2125     *_rval = mValid;
2126     return NS_OK;
2127 }
2128
2129 NS_IMETHODIMP
2130 jsdValue::Invalidate()
2131 {
2132     ASSERT_VALID_EPHEMERAL;
2133     mValid = PR_FALSE;
2134     jsds_RemoveEphemeral (&gLiveValues, &mLiveListEntry);
2135     JSD_DropValue (mCx, mValue);
2136     return NS_OK;
2137 }
2138
2139 void
2140 jsdValue::InvalidateAll()
2141 {
2142     if (gLiveValues)
2143         jsds_InvalidateAllEphemerals (&gLiveValues);
2144 }
2145
2146 NS_IMETHODIMP
2147 jsdValue::GetJSDContext(JSDContext **_rval)
2148 {
2149     ASSERT_VALID_EPHEMERAL;
2150     *_rval = mCx;
2151     return NS_OK;
2152 }
2153
2154 NS_IMETHODIMP
2155 jsdValue::GetJSDValue (JSDValue **_rval)
2156 {
2157     ASSERT_VALID_EPHEMERAL;
2158     *_rval = mValue;
2159     return NS_OK;
2160 }
2161
2162 NS_IMETHODIMP
2163 jsdValue::GetIsNative (PRBool *_rval)
2164 {
2165     ASSERT_VALID_EPHEMERAL;
2166     *_rval = JSD_IsValueNative (mCx, mValue);
2167     return NS_OK;
2168 }
2169
2170 NS_IMETHODIMP
2171 jsdValue::GetIsNumber (PRBool *_rval)
2172 {
2173     ASSERT_VALID_EPHEMERAL;
2174     *_rval = JSD_IsValueNumber (mCx, mValue);
2175     return NS_OK;
2176 }
2177
2178 NS_IMETHODIMP
2179 jsdValue::GetIsPrimitive (PRBool *_rval)
2180 {
2181     ASSERT_VALID_EPHEMERAL;
2182     *_rval = JSD_IsValuePrimitive (mCx, mValue);
2183     return NS_OK;
2184 }
2185
2186 NS_IMETHODIMP
2187 jsdValue::GetJsType (PRUint32 *_rval)
2188 {
2189     ASSERT_VALID_EPHEMERAL;
2190     jsval val;
2191
2192     val = JSD_GetValueWrappedJSVal (mCx, mValue);
2193     
2194     if (JSVAL_IS_NULL(val))
2195         *_rval = TYPE_NULL;
2196     else if (JSVAL_IS_BOOLEAN(val))
2197         *_rval = TYPE_BOOLEAN;
2198     else if (JSVAL_IS_DOUBLE(val))
2199         *_rval = TYPE_DOUBLE;
2200     else if (JSVAL_IS_INT(val))
2201         *_rval = TYPE_INT;
2202     else if (JSVAL_IS_STRING(val))
2203         *_rval = TYPE_STRING;
2204     else if (JSVAL_IS_VOID(val))
2205         *_rval = TYPE_VOID;
2206     else if (JSD_IsValueFunction (mCx, mValue))
2207         *_rval = TYPE_FUNCTION;
2208     else if (JSVAL_IS_OBJECT(val))
2209         *_rval = TYPE_OBJECT;
2210     else
2211         NS_ASSERTION (0, "Value has no discernible type.");
2212
2213     return NS_OK;
2214 }
2215
2216 NS_IMETHODIMP
2217 jsdValue::GetJsPrototype (jsdIValue **_rval)
2218 {
2219     ASSERT_VALID_EPHEMERAL;
2220     JSDValue *jsdv = JSD_GetValuePrototype (mCx, mValue);
2221     *_rval = jsdValue::FromPtr (mCx, jsdv);
2222     return NS_OK;
2223 }
2224
2225 NS_IMETHODIMP
2226 jsdValue::GetJsParent (jsdIValue **_rval)
2227 {
2228     ASSERT_VALID_EPHEMERAL;
2229     JSDValue *jsdv = JSD_GetValueParent (mCx, mValue);
2230     *_rval = jsdValue::FromPtr (mCx, jsdv);
2231     return NS_OK;
2232 }
2233
2234 NS_IMETHODIMP
2235 jsdValue::GetJsClassName(nsACString &_rval)
2236 {
2237     ASSERT_VALID_EPHEMERAL;
2238     _rval.Assign(JSD_GetValueClassName(mCx, mValue));
2239     
2240     return NS_OK;
2241 }
2242
2243 NS_IMETHODIMP
2244 jsdValue::GetJsConstructor (jsdIValue **_rval)
2245 {
2246     ASSERT_VALID_EPHEMERAL;
2247     JSDValue *jsdv = JSD_GetValueConstructor (mCx, mValue);
2248     *_rval = jsdValue::FromPtr (mCx, jsdv);
2249     return NS_OK;
2250 }
2251
2252 NS_IMETHODIMP
2253 jsdValue::GetJsFunctionName(nsACString &_rval)
2254 {
2255     ASSERT_VALID_EPHEMERAL;
2256     return AssignToJSString(&_rval, JSD_GetValueFunctionId(mCx, mValue));
2257 }
2258
2259 NS_IMETHODIMP
2260 jsdValue::GetBooleanValue(PRBool *_rval)
2261 {
2262     ASSERT_VALID_EPHEMERAL;
2263     *_rval = JSD_GetValueBoolean (mCx, mValue);
2264     return NS_OK;
2265 }
2266
2267 NS_IMETHODIMP
2268 jsdValue::GetDoubleValue(double *_rval)
2269 {
2270     ASSERT_VALID_EPHEMERAL;
2271     *_rval = JSD_GetValueDouble (mCx, mValue);
2272     return NS_OK;
2273 }
2274
2275 NS_IMETHODIMP
2276 jsdValue::GetIntValue(PRInt32 *_rval)
2277 {
2278     ASSERT_VALID_EPHEMERAL;
2279     *_rval = JSD_GetValueInt (mCx, mValue);
2280     return NS_OK;
2281 }
2282
2283 NS_IMETHODIMP
2284 jsdValue::GetObjectValue(jsdIObject **_rval)
2285 {
2286     ASSERT_VALID_EPHEMERAL;
2287     JSDObject *obj;
2288     obj = JSD_GetObjectForValue (mCx, mValue);
2289     *_rval = jsdObject::FromPtr (mCx, obj);
2290     if (!*_rval)
2291         return NS_ERROR_FAILURE;
2292     return NS_OK;
2293 }
2294     
2295 NS_IMETHODIMP
2296 jsdValue::GetStringValue(nsACString &_rval)
2297 {
2298     ASSERT_VALID_EPHEMERAL;
2299     JSContext *cx = JSD_GetDefaultJSContext (mCx);
2300     if (!cx) {
2301         NS_WARNING("No default context !?");
2302         return NS_ERROR_FAILURE;
2303     }
2304     JSString *jstr_val = JSD_GetValueString(mCx, mValue);
2305     if (jstr_val) {
2306         size_t length;
2307         const jschar *chars = JS_GetStringCharsZAndLength(cx, jstr_val, &length);
2308         if (!chars)
2309             return NS_ERROR_FAILURE;
2310         nsDependentString depStr(chars, length);
2311         CopyUTF16toUTF8(depStr, _rval);
2312     } else {
2313         _rval.Truncate();
2314     }
2315     return NS_OK;
2316 }
2317
2318 NS_IMETHODIMP
2319 jsdValue::GetPropertyCount (PRInt32 *_rval)
2320 {
2321     ASSERT_VALID_EPHEMERAL;
2322     if (JSD_IsValueObject(mCx, mValue))
2323         *_rval = JSD_GetCountOfProperties (mCx, mValue);
2324     else
2325         *_rval = -1;
2326     return NS_OK;
2327 }
2328
2329 NS_IMETHODIMP
2330 jsdValue::GetProperties (jsdIProperty ***propArray, PRUint32 *length)
2331 {
2332     ASSERT_VALID_EPHEMERAL;
2333     *propArray = nsnull;
2334     if (length)
2335         *length = 0;
2336
2337     PRUint32 prop_count = JSD_IsValueObject(mCx, mValue)
2338         ? JSD_GetCountOfProperties (mCx, mValue)
2339         : 0;
2340     NS_ENSURE_TRUE(prop_count, NS_OK);
2341
2342     jsdIProperty **pa_temp =
2343         static_cast<jsdIProperty **>
2344                    (nsMemory::Alloc(sizeof (jsdIProperty *) * 
2345                                        prop_count));
2346     NS_ENSURE_TRUE(pa_temp, NS_ERROR_OUT_OF_MEMORY);
2347
2348     PRUint32     i    = 0;
2349     JSDProperty *iter = NULL;
2350     JSDProperty *prop;
2351     while ((prop = JSD_IterateProperties (mCx, mValue, &iter))) {
2352         pa_temp[i] = jsdProperty::FromPtr (mCx, prop);
2353         ++i;
2354     }
2355     
2356     NS_ASSERTION (prop_count == i, "property count mismatch");    
2357
2358     /* if caller doesn't care about length, don't bother telling them */
2359     *propArray = pa_temp;
2360     if (length)
2361         *length = prop_count;
2362     
2363     return NS_OK;
2364 }
2365
2366 NS_IMETHODIMP
2367 jsdValue::GetProperty (const nsACString &name, jsdIProperty **_rval)
2368 {
2369     ASSERT_VALID_EPHEMERAL;
2370     JSContext *cx = JSD_GetDefaultJSContext (mCx);
2371
2372     JSAutoRequest ar(cx);
2373
2374     /* not rooting this */
2375     JSString *jstr_name = JS_NewStringCopyZ(cx, PromiseFlatCString(name).get());
2376     if (!jstr_name)
2377         return NS_ERROR_OUT_OF_MEMORY;
2378
2379     JSDProperty *prop = JSD_GetValueProperty (mCx, mValue, jstr_name);
2380     
2381     *_rval = jsdProperty::FromPtr (mCx, prop);
2382     return NS_OK;
2383 }
2384
2385 NS_IMETHODIMP
2386 jsdValue::Refresh()
2387 {
2388     ASSERT_VALID_EPHEMERAL;
2389     JSD_RefreshValue (mCx, mValue);
2390     return NS_OK;
2391 }
2392
2393 NS_IMETHODIMP
2394 jsdValue::GetWrappedValue()
2395 {
2396     ASSERT_VALID_EPHEMERAL;
2397     nsresult rv;
2398     nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID(), &rv);
2399     if (NS_FAILED(rv))
2400         return rv;
2401
2402     nsAXPCNativeCallContext *cc = nsnull;
2403     rv = xpc->GetCurrentNativeCallContext(&cc);
2404     if (NS_FAILED(rv))
2405         return rv;
2406
2407     jsval *result;
2408     rv = cc->GetRetValPtr(&result);
2409     if (NS_FAILED(rv))
2410         return rv;
2411
2412     if (result)
2413     {
2414         JSContext *cx;
2415         rv = cc->GetJSContext(&cx);
2416         if (NS_FAILED(rv))
2417             return rv;
2418         *result = JSD_GetValueWrappedJSVal (mCx, mValue);
2419         if (!JS_WrapValue(cx, result))
2420             return NS_ERROR_FAILURE;
2421         cc->SetReturnValueWasSet(PR_TRUE);
2422     }
2423
2424     return NS_OK;
2425 }
2426
2427 NS_IMETHODIMP
2428 jsdValue::GetScript(jsdIScript **_rval)
2429 {
2430     ASSERT_VALID_EPHEMERAL;
2431     JSDScript *script = JSD_GetScriptForValue(mCx, mValue);
2432     *_rval = jsdScript::FromPtr(mCx, script);
2433     return NS_OK;
2434 }
2435
2436 /******************************************************************************
2437  * debugger service implementation
2438  ******************************************************************************/
2439 NS_IMPL_THREADSAFE_ISUPPORTS1(jsdService, jsdIDebuggerService)
2440
2441 NS_IMETHODIMP
2442 jsdService::GetJSDContext(JSDContext **_rval)
2443 {
2444     *_rval = mCx;
2445     return NS_OK;
2446 }
2447
2448 NS_IMETHODIMP
2449 jsdService::GetFlags (PRUint32 *_rval)
2450 {
2451     ASSERT_VALID_CONTEXT;
2452     *_rval = JSD_GetContextFlags (mCx);
2453     return NS_OK;
2454 }
2455
2456 NS_IMETHODIMP
2457 jsdService::SetFlags (PRUint32 flags)
2458 {
2459     ASSERT_VALID_CONTEXT;
2460     JSD_SetContextFlags (mCx, flags);
2461     return NS_OK;
2462 }
2463
2464 NS_IMETHODIMP
2465 jsdService::GetImplementationString(nsACString &aImplementationString)
2466 {
2467     aImplementationString.AssignLiteral(implementationString);
2468     return NS_OK;
2469 }
2470
2471 NS_IMETHODIMP
2472 jsdService::GetImplementationMajor(PRUint32 *_rval)
2473 {
2474     *_rval = JSDS_MAJOR_VERSION;
2475     return NS_OK;
2476 }
2477
2478 NS_IMETHODIMP
2479 jsdService::GetImplementationMinor(PRUint32 *_rval)
2480 {
2481     *_rval = JSDS_MINOR_VERSION;
2482     return NS_OK;
2483 }
2484
2485 NS_IMETHODIMP
2486 jsdService::GetIsOn (PRBool *_rval)
2487 {
2488     *_rval = mOn;
2489     return NS_OK;
2490 }
2491
2492 NS_IMETHODIMP
2493 jsdService::On (void)
2494 {
2495     return NS_ERROR_NOT_IMPLEMENTED;
2496 }
2497
2498 NS_IMETHODIMP
2499 jsdService::AsyncOn (jsdIActivationCallback *activationCallback)
2500 {
2501     nsresult  rv;
2502
2503     /* get JS things from the CallContext */
2504     nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID(), &rv);
2505     if (NS_FAILED(rv)) return rv;
2506
2507     nsAXPCNativeCallContext *cc = nsnull;
2508     rv = xpc->GetCurrentNativeCallContext(&cc);
2509     if (NS_FAILED(rv)) return rv;
2510
2511     JSContext *cx;
2512     rv = cc->GetJSContext (&cx);
2513     if (NS_FAILED(rv)) return rv;
2514
2515     mActivationCallback = activationCallback;
2516     
2517     return xpc->SetDebugModeWhenPossible(PR_TRUE);
2518 }
2519
2520 NS_IMETHODIMP
2521 jsdService::RecompileForDebugMode (JSContext *cx, JSCompartment *comp, JSBool mode) {
2522   NS_ASSERTION(NS_IsMainThread(), "wrong thread");
2523   /* XPConnect now does this work itself, so this IDL entry point is no longer used. */
2524   return NS_ERROR_NOT_IMPLEMENTED;
2525 }
2526
2527 NS_IMETHODIMP
2528 jsdService::DeactivateDebugger ()
2529 {
2530     if (!mCx)
2531         return NS_OK;
2532
2533     jsdContext::InvalidateAll();
2534     jsdScript::InvalidateAll();
2535     jsdValue::InvalidateAll();
2536     jsdProperty::InvalidateAll();
2537     jsdStackFrame::InvalidateAll();
2538     ClearAllBreakpoints();
2539
2540     JSD_SetErrorReporter (mCx, NULL, NULL);
2541     JSD_SetScriptHook (mCx, NULL, NULL);
2542     JSD_ClearThrowHook (mCx);
2543     JSD_ClearInterruptHook (mCx);
2544     JSD_ClearDebuggerHook (mCx);
2545     JSD_ClearDebugBreakHook (mCx);
2546     JSD_ClearTopLevelHook (mCx);
2547     JSD_ClearFunctionHook (mCx);
2548     
2549     JSD_DebuggerOff (mCx);
2550
2551     mCx = nsnull;
2552     mRuntime = nsnull;
2553     mOn = PR_FALSE;
2554
2555     return NS_OK;
2556 }
2557
2558
2559 NS_IMETHODIMP
2560 jsdService::ActivateDebugger (JSRuntime *rt)
2561 {
2562     if (mOn)
2563         return (rt == mRuntime) ? NS_OK : NS_ERROR_ALREADY_INITIALIZED;
2564
2565     mRuntime = rt;
2566
2567     if (gLastGCProc == jsds_GCCallbackProc)
2568         /* condition indicates that the callback proc has not been set yet */
2569         gLastGCProc = JS_SetGCCallbackRT (rt, jsds_GCCallbackProc);
2570
2571     mCx = JSD_DebuggerOnForUser (rt, NULL, NULL);
2572     if (!mCx)
2573         return NS_ERROR_FAILURE;
2574
2575     JSContext *cx   = JSD_GetDefaultJSContext (mCx);
2576     JSObject  *glob = JS_GetGlobalObject (cx);
2577
2578     /* init xpconnect on the debugger's context in case xpconnect tries to
2579      * use it for stuff. */
2580     nsresult rv;
2581     nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID(), &rv);
2582     if (NS_FAILED(rv))
2583         return rv;
2584     
2585     xpc->InitClasses (cx, glob);
2586
2587     /* Start watching for script creation/destruction and manage jsdScript
2588      * objects accordingly
2589      */
2590     JSD_SetScriptHook (mCx, jsds_ScriptHookProc, NULL);
2591
2592     /* If any of these mFooHook objects are installed, do the required JSD
2593      * hookup now.   See also, jsdService::SetFooHook().
2594      */
2595     if (mErrorHook)
2596         JSD_SetErrorReporter (mCx, jsds_ErrorHookProc, NULL);
2597     if (mThrowHook)
2598         JSD_SetThrowHook (mCx, jsds_ExecutionHookProc, NULL);
2599     /* can't ignore script callbacks, as we need to |Release| the wrapper 
2600      * stored in private data when a script is deleted. */
2601     if (mInterruptHook)
2602         JSD_SetInterruptHook (mCx, jsds_ExecutionHookProc, NULL);
2603     if (mDebuggerHook)
2604         JSD_SetDebuggerHook (mCx, jsds_ExecutionHookProc, NULL);
2605     if (mDebugHook)
2606         JSD_SetDebugBreakHook (mCx, jsds_ExecutionHookProc, NULL);
2607     if (mTopLevelHook)
2608         JSD_SetTopLevelHook (mCx, jsds_CallHookProc, NULL);
2609     else
2610         JSD_ClearTopLevelHook (mCx);
2611     if (mFunctionHook)
2612         JSD_SetFunctionHook (mCx, jsds_CallHookProc, NULL);
2613     else
2614         JSD_ClearFunctionHook (mCx);
2615     mOn = PR_TRUE;
2616
2617 #ifdef DEBUG
2618     printf ("+++ JavaScript debugging hooks installed.\n");
2619 #endif
2620
2621     if (mActivationCallback)
2622         return mActivationCallback->OnDebuggerActivated();
2623
2624     return NS_OK;
2625 }
2626
2627 NS_IMETHODIMP
2628 jsdService::Off (void)
2629 {
2630     if (!mOn)
2631         return NS_OK;
2632     
2633     if (!mCx || !mRuntime)
2634         return NS_ERROR_NOT_INITIALIZED;
2635     
2636     if (gDeadScripts) {
2637         if (gGCStatus != JSGC_END)
2638             return NS_ERROR_NOT_AVAILABLE;
2639
2640         JSContext *cx = JSD_GetDefaultJSContext(mCx);
2641         while (gDeadScripts)
2642             jsds_NotifyPendingDeadScripts (cx);
2643     }
2644
2645     /*
2646     if (gLastGCProc != jsds_GCCallbackProc)
2647         JS_SetGCCallbackRT (mRuntime, gLastGCProc);
2648     */
2649
2650     DeactivateDebugger();
2651
2652 #ifdef DEBUG
2653     printf ("+++ JavaScript debugging hooks removed.\n");
2654 #endif
2655
2656     nsresult rv;
2657     nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID(), &rv);
2658     if (NS_FAILED(rv))
2659         return rv;
2660
2661     xpc->SetDebugModeWhenPossible(PR_FALSE);
2662
2663     return NS_OK;
2664 }
2665
2666 NS_IMETHODIMP
2667 jsdService::GetPauseDepth(PRUint32 *_rval)
2668 {
2669     NS_ENSURE_ARG_POINTER(_rval);
2670     *_rval = mPauseLevel;
2671     return NS_OK;
2672 }
2673     
2674 NS_IMETHODIMP
2675 jsdService::Pause(PRUint32 *_rval)
2676 {
2677     if (!mCx)
2678         return NS_ERROR_NOT_INITIALIZED;
2679
2680     if (++mPauseLevel == 1) {
2681         JSD_SetErrorReporter (mCx, NULL, NULL);
2682         JSD_ClearThrowHook (mCx);
2683         JSD_ClearInterruptHook (mCx);
2684         JSD_ClearDebuggerHook (mCx);
2685         JSD_ClearDebugBreakHook (mCx);
2686         JSD_ClearTopLevelHook (mCx);
2687         JSD_ClearFunctionHook (mCx);
2688         JSD_DebuggerPause (mCx);
2689     }
2690
2691     if (_rval)
2692         *_rval = mPauseLevel;
2693
2694     return NS_OK;
2695 }
2696
2697 NS_IMETHODIMP
2698 jsdService::UnPause(PRUint32 *_rval)
2699 {
2700     if (!mCx)
2701         return NS_ERROR_NOT_INITIALIZED;
2702
2703     if (mPauseLevel == 0)
2704         return NS_ERROR_NOT_AVAILABLE;
2705
2706     /* check mOn before we muck with this stuff, it's possible the debugger
2707      * was turned off while we were paused.
2708      */
2709     if (--mPauseLevel == 0 && mOn) {
2710         JSD_DebuggerUnpause (mCx);
2711         if (mErrorHook)
2712             JSD_SetErrorReporter (mCx, jsds_ErrorHookProc, NULL);
2713         if (mThrowHook)
2714             JSD_SetThrowHook (mCx, jsds_ExecutionHookProc, NULL);
2715         if (mInterruptHook)
2716             JSD_SetInterruptHook (mCx, jsds_ExecutionHookProc, NULL);
2717         if (mDebuggerHook)
2718             JSD_SetDebuggerHook (mCx, jsds_ExecutionHookProc, NULL);
2719         if (mDebugHook)
2720             JSD_SetDebugBreakHook (mCx, jsds_ExecutionHookProc, NULL);
2721         if (mTopLevelHook)
2722             JSD_SetTopLevelHook (mCx, jsds_CallHookProc, NULL);
2723         else
2724             JSD_ClearTopLevelHook (mCx);
2725         if (mFunctionHook)
2726             JSD_SetFunctionHook (mCx, jsds_CallHookProc, NULL);
2727         else
2728             JSD_ClearFunctionHook (mCx);
2729     }
2730     
2731     if (_rval)
2732         *_rval = mPauseLevel;
2733
2734     return NS_OK;
2735 }
2736
2737 NS_IMETHODIMP
2738 jsdService::EnumerateContexts (jsdIContextEnumerator *enumerator)
2739 {
2740     ASSERT_VALID_CONTEXT;
2741     
2742     if (!enumerator)
2743         return NS_OK;
2744     
2745     JSContext *iter = NULL;
2746     JSContext *cx;
2747
2748     while ((cx = JS_ContextIterator (mRuntime, &iter)))
2749     {
2750         nsCOMPtr<jsdIContext> jsdicx = 
2751             getter_AddRefs(jsdContext::FromPtr(mCx, cx));
2752         if (jsdicx)
2753         {
2754             if (NS_FAILED(enumerator->EnumerateContext(jsdicx)))
2755                 break;
2756         }
2757     }
2758
2759     return NS_OK;
2760 }
2761
2762 NS_IMETHODIMP
2763 jsdService::EnumerateScripts (jsdIScriptEnumerator *enumerator)
2764 {
2765     ASSERT_VALID_CONTEXT;
2766     
2767     JSDScript *script;
2768     JSDScript *iter = NULL;
2769     nsresult rv = NS_OK;
2770     
2771     JSD_LockScriptSubsystem(mCx);
2772     while((script = JSD_IterateScripts(mCx, &iter))) {
2773         nsCOMPtr<jsdIScript> jsdis =
2774             getter_AddRefs(jsdScript::FromPtr(mCx, script));
2775         rv = enumerator->EnumerateScript (jsdis);
2776         if (NS_FAILED(rv))
2777             break;
2778     }
2779     JSD_UnlockScriptSubsystem(mCx);
2780
2781     return rv;
2782 }
2783
2784 NS_IMETHODIMP
2785 jsdService::GC (void)
2786 {
2787     ASSERT_VALID_CONTEXT;
2788     JSContext *cx = JSD_GetDefaultJSContext (mCx);
2789     JS_GC(cx);
2790     return NS_OK;
2791 }
2792     
2793 NS_IMETHODIMP
2794 jsdService::DumpHeap(const nsACString &fileName)
2795 {
2796     ASSERT_VALID_CONTEXT;
2797 #ifndef DEBUG
2798     return NS_ERROR_NOT_IMPLEMENTED;
2799 #else
2800     nsresult rv = NS_OK;
2801     FILE *file = !fileName.IsEmpty() ? fopen(PromiseFlatCString(fileName).get(), "w") : stdout;
2802     if (!file) {
2803         rv = NS_ERROR_FAILURE;
2804     } else {
2805         JSContext *cx = JSD_GetDefaultJSContext (mCx);
2806         if (!JS_DumpHeap(cx, file, NULL, 0, NULL, (size_t)-1, NULL))
2807             rv = NS_ERROR_FAILURE;
2808         if (file != stdout)
2809             fclose(file);
2810     }
2811     return rv;
2812 #endif
2813 }
2814
2815 NS_IMETHODIMP
2816 jsdService::ClearProfileData ()
2817 {
2818     ASSERT_VALID_CONTEXT;
2819     JSD_ClearAllProfileData (mCx);
2820     return NS_OK;
2821 }
2822
2823 NS_IMETHODIMP
2824 jsdService::InsertFilter (jsdIFilter *filter, jsdIFilter *after)
2825 {
2826     NS_ENSURE_ARG_POINTER (filter);
2827     if (jsds_FindFilter (filter))
2828         return NS_ERROR_INVALID_ARG;
2829
2830     FilterRecord *rec = PR_NEWZAP (FilterRecord);
2831     if (!rec)
2832         return NS_ERROR_OUT_OF_MEMORY;
2833
2834     if (!jsds_SyncFilter (rec, filter)) {
2835         PR_Free (rec);
2836         return NS_ERROR_FAILURE;
2837     }
2838     
2839     if (gFilters) {
2840         if (!after) {
2841             /* insert at head of list */
2842             PR_INSERT_LINK(&rec->links, &gFilters->links);
2843             gFilters = rec;
2844         } else {
2845             /* insert somewhere in the list */
2846             FilterRecord *afterRecord = jsds_FindFilter (after);
2847             if (!afterRecord) {
2848                 jsds_FreeFilter(rec);
2849                 return NS_ERROR_INVALID_ARG;
2850             }
2851             PR_INSERT_AFTER(&rec->links, &afterRecord->links);
2852         }
2853     } else {
2854         if (after) {
2855             /* user asked to insert into the middle of an empty list, bail. */
2856             jsds_FreeFilter(rec);
2857             return NS_ERROR_NOT_INITIALIZED;
2858         }
2859         PR_INIT_CLIST(&rec->links);
2860         gFilters = rec;
2861     }
2862     
2863     return NS_OK;
2864 }
2865
2866 NS_IMETHODIMP
2867 jsdService::AppendFilter (jsdIFilter *filter)
2868 {
2869     NS_ENSURE_ARG_POINTER (filter);
2870     if (jsds_FindFilter (filter))
2871         return NS_ERROR_INVALID_ARG;
2872     FilterRecord *rec = PR_NEWZAP (FilterRecord);
2873
2874     if (!jsds_SyncFilter (rec, filter)) {
2875         PR_Free (rec);
2876         return NS_ERROR_FAILURE;
2877     }
2878     
2879     if (gFilters) {
2880         PR_INSERT_BEFORE(&rec->links, &gFilters->links);
2881     } else {
2882         PR_INIT_CLIST(&rec->links);
2883         gFilters = rec;
2884     }
2885     
2886     return NS_OK;
2887 }
2888
2889 NS_IMETHODIMP
2890 jsdService::RemoveFilter (jsdIFilter *filter)
2891 {
2892     NS_ENSURE_ARG_POINTER(filter);
2893     FilterRecord *rec = jsds_FindFilter (filter);
2894     if (!rec)
2895         return NS_ERROR_INVALID_ARG;
2896     
2897     if (gFilters == rec) {
2898         gFilters = reinterpret_cast<FilterRecord *>
2899                                    (PR_NEXT_LINK(&rec->links));
2900         /* If we're the only filter left, null out the list head. */
2901         if (gFilters == rec)
2902             gFilters = nsnull;
2903     }
2904
2905     
2906     PR_REMOVE_LINK(&rec->links);
2907     jsds_FreeFilter (rec);
2908     
2909     return NS_OK;
2910 }
2911
2912 NS_IMETHODIMP
2913 jsdService::SwapFilters (jsdIFilter *filter_a, jsdIFilter *filter_b)
2914 {
2915     NS_ENSURE_ARG_POINTER(filter_a);
2916     NS_ENSURE_ARG_POINTER(filter_b);
2917     
2918     FilterRecord *rec_a = jsds_FindFilter (filter_a);
2919     if (!rec_a)
2920         return NS_ERROR_INVALID_ARG;
2921     
2922     if (filter_a == filter_b) {
2923         /* just a refresh */
2924         if (!jsds_SyncFilter (rec_a, filter_a))
2925             return NS_ERROR_FAILURE;
2926         return NS_OK;
2927     }
2928     
2929     FilterRecord *rec_b = jsds_FindFilter (filter_b);
2930     if (!rec_b) {
2931         /* filter_b is not in the list, replace filter_a with filter_b. */
2932         if (!jsds_SyncFilter (rec_a, filter_b))
2933             return NS_ERROR_FAILURE;
2934     } else {
2935         /* both filters are in the list, swap. */
2936         if (!jsds_SyncFilter (rec_a, filter_b))
2937             return NS_ERROR_FAILURE;
2938         if (!jsds_SyncFilter (rec_b, filter_a))
2939             return NS_ERROR_FAILURE;
2940     }
2941     
2942     return NS_OK;
2943 }
2944
2945 NS_IMETHODIMP
2946 jsdService::EnumerateFilters (jsdIFilterEnumerator *enumerator) 
2947 {
2948     if (!gFilters)
2949         return NS_OK;
2950     
2951     FilterRecord *current = gFilters;
2952     do {
2953         jsds_SyncFilter (current, current->filterObject);
2954         /* SyncFilter failure would be bad, but what would we do about it? */
2955         if (enumerator) {
2956             nsresult rv = enumerator->EnumerateFilter (current->filterObject);
2957             if (NS_FAILED(rv))
2958                 return rv;
2959         }
2960         current = reinterpret_cast<FilterRecord *>
2961                                   (PR_NEXT_LINK (&current->links));
2962     } while (current != gFilters);
2963     
2964     return NS_OK;
2965 }
2966
2967 NS_IMETHODIMP
2968 jsdService::RefreshFilters ()
2969 {
2970     return EnumerateFilters(nsnull);
2971 }
2972
2973 NS_IMETHODIMP
2974 jsdService::ClearFilters ()
2975 {
2976     if (!gFilters)
2977         return NS_OK;
2978
2979     FilterRecord *current = reinterpret_cast<FilterRecord *>
2980                                             (PR_NEXT_LINK (&gFilters->links));
2981     do {
2982         FilterRecord *next = reinterpret_cast<FilterRecord *>
2983                                              (PR_NEXT_LINK (&current->links));
2984         PR_REMOVE_AND_INIT_LINK(&current->links);
2985         jsds_FreeFilter(current);
2986         current = next;
2987     } while (current != gFilters);
2988     
2989     jsds_FreeFilter(current);
2990     gFilters = nsnull;
2991     
2992     return NS_OK;
2993 }
2994         
2995 NS_IMETHODIMP
2996 jsdService::ClearAllBreakpoints (void)
2997 {
2998     ASSERT_VALID_CONTEXT;
2999
3000     JSD_LockScriptSubsystem(mCx);
3001     JSD_ClearAllExecutionHooks (mCx);
3002     JSD_UnlockScriptSubsystem(mCx);
3003     return NS_OK;
3004 }
3005
3006 NS_IMETHODIMP
3007 jsdService::WrapValue(jsdIValue **_rval)
3008 {
3009     ASSERT_VALID_CONTEXT;
3010
3011     nsresult rv;
3012     nsCOMPtr<nsIXPConnect> xpc = do_GetService (nsIXPConnect::GetCID(), &rv);
3013     if (NS_FAILED(rv))
3014         return rv;
3015
3016     nsAXPCNativeCallContext *cc = nsnull;
3017     rv = xpc->GetCurrentNativeCallContext (&cc);
3018     if (NS_FAILED(rv))
3019         return rv;
3020
3021     PRUint32 argc;
3022     rv = cc->GetArgc (&argc);
3023     if (NS_FAILED(rv))
3024         return rv;
3025     if (argc < 1)
3026         return NS_ERROR_INVALID_ARG;
3027     
3028     jsval    *argv;
3029     rv = cc->GetArgvPtr (&argv);
3030     if (NS_FAILED(rv))
3031         return rv;
3032
3033     return WrapJSValue(argv[0], _rval);
3034 }
3035
3036 NS_IMETHODIMP
3037 jsdService::WrapJSValue(const jsval &value, jsdIValue** _rval)
3038 {
3039     JSDValue *jsdv = JSD_NewValue(mCx, value);
3040     if (!jsdv)
3041         return NS_ERROR_FAILURE;
3042     
3043     *_rval = jsdValue::FromPtr (mCx, jsdv);
3044     return NS_OK;
3045 }
3046
3047
3048 NS_IMETHODIMP
3049 jsdService::EnterNestedEventLoop (jsdINestCallback *callback, PRUint32 *_rval)
3050 {
3051     // Nesting event queues is a thing of the past.  Now, we just spin the
3052     // current event loop.
3053  
3054     nsresult rv;
3055     nsCOMPtr<nsIJSContextStack> 
3056         stack(do_GetService("@mozilla.org/js/xpc/ContextStack;1", &rv));
3057     if (NS_FAILED(rv))
3058         return rv;
3059     PRUint32 nestLevel = ++mNestedLoopLevel;
3060     
3061     nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
3062
3063     if (NS_SUCCEEDED(stack->Push(nsnull))) {
3064         if (callback) {
3065             Pause(nsnull);
3066             rv = callback->OnNest();
3067             UnPause(nsnull);
3068         }
3069         
3070         while (NS_SUCCEEDED(rv) && mNestedLoopLevel >= nestLevel) {
3071             if (!NS_ProcessNextEvent(thread))
3072                 rv = NS_ERROR_UNEXPECTED;
3073         }
3074
3075         JSContext* cx;
3076         stack->Pop(&cx);
3077         NS_ASSERTION(cx == nsnull, "JSContextStack mismatch");
3078     }
3079     else
3080         rv = NS_ERROR_FAILURE;
3081     
3082     NS_ASSERTION (mNestedLoopLevel <= nestLevel,
3083                   "nested event didn't unwind properly");
3084     if (mNestedLoopLevel == nestLevel)
3085         --mNestedLoopLevel;
3086
3087     *_rval = mNestedLoopLevel;
3088     return rv;
3089 }
3090
3091 NS_IMETHODIMP
3092 jsdService::ExitNestedEventLoop (PRUint32 *_rval)
3093 {
3094     if (mNestedLoopLevel > 0)
3095         --mNestedLoopLevel;
3096     else
3097         return NS_ERROR_FAILURE;
3098
3099     *_rval = mNestedLoopLevel;    
3100     return NS_OK;
3101 }    
3102
3103 /* hook attribute get/set functions */
3104
3105 NS_IMETHODIMP
3106 jsdService::SetErrorHook (jsdIErrorHook *aHook)
3107 {
3108     mErrorHook = aHook;
3109
3110     /* if the debugger isn't initialized, that's all we can do for now.  The
3111      * ActivateDebugger() method will do the rest when the coast is clear.
3112      */
3113     if (!mCx || mPauseLevel)
3114         return NS_OK;
3115
3116     if (aHook)
3117         JSD_SetErrorReporter (mCx, jsds_ErrorHookProc, NULL);
3118     else
3119         JSD_SetErrorReporter (mCx, NULL, NULL);
3120
3121     return NS_OK;
3122 }
3123
3124 NS_IMETHODIMP
3125 jsdService::GetErrorHook (jsdIErrorHook **aHook)
3126 {
3127     *aHook = mErrorHook;
3128     NS_IF_ADDREF(*aHook);
3129     
3130     return NS_OK;
3131 }
3132
3133 NS_IMETHODIMP
3134 jsdService::SetBreakpointHook (jsdIExecutionHook *aHook)
3135 {    
3136     mBreakpointHook = aHook;
3137     return NS_OK;
3138 }
3139
3140 NS_IMETHODIMP
3141 jsdService::GetBreakpointHook (jsdIExecutionHook **aHook)
3142 {   
3143     *aHook = mBreakpointHook;
3144     NS_IF_ADDREF(*aHook);
3145     
3146     return NS_OK;
3147 }
3148
3149 NS_IMETHODIMP
3150 jsdService::SetDebugHook (jsdIExecutionHook *aHook)
3151 {    
3152     mDebugHook = aHook;
3153
3154     /* if the debugger isn't initialized, that's all we can do for now.  The
3155      * ActivateDebugger() method will do the rest when the coast is clear.
3156      */
3157     if (!mCx || mPauseLevel)
3158         return NS_OK;
3159
3160     if (aHook)
3161         JSD_SetDebugBreakHook (mCx, jsds_ExecutionHookProc, NULL);
3162     else
3163         JSD_ClearDebugBreakHook (mCx);
3164     
3165     return NS_OK;
3166 }
3167
3168 NS_IMETHODIMP
3169 jsdService::GetDebugHook (jsdIExecutionHook **aHook)
3170 {   
3171     *aHook = mDebugHook;
3172     NS_IF_ADDREF(*aHook);
3173     
3174     return NS_OK;
3175 }
3176
3177 NS_IMETHODIMP
3178 jsdService::SetDebuggerHook (jsdIExecutionHook *aHook)
3179 {    
3180     mDebuggerHook = aHook;
3181
3182     /* if the debugger isn't initialized, that's all we can do for now.  The
3183      * ActivateDebugger() method will do the rest when the coast is clear.
3184      */
3185     if (!mCx || mPauseLevel)
3186         return NS_OK;
3187
3188     if (aHook)
3189         JSD_SetDebuggerHook (mCx, jsds_ExecutionHookProc, NULL);
3190     else
3191         JSD_ClearDebuggerHook (mCx);
3192     
3193     return NS_OK;
3194 }
3195
3196 NS_IMETHODIMP
3197 jsdService::GetDebuggerHook (jsdIExecutionHook **aHook)
3198 {   
3199     *aHook = mDebuggerHook;
3200     NS_IF_ADDREF(*aHook);
3201     
3202     return NS_OK;
3203 }
3204
3205 NS_IMETHODIMP
3206 jsdService::SetInterruptHook (jsdIExecutionHook *aHook)
3207 {    
3208     mInterruptHook = aHook;
3209
3210     /* if the debugger isn't initialized, that's all we can do for now.  The
3211      * ActivateDebugger() method will do the rest when the coast is clear.
3212      */
3213     if (!mCx || mPauseLevel)
3214         return NS_OK;
3215
3216     if (aHook)
3217         JSD_SetInterruptHook (mCx, jsds_ExecutionHookProc, NULL);
3218     else
3219         JSD_ClearInterruptHook (mCx);
3220     
3221     return NS_OK;
3222 }
3223
3224 NS_IMETHODIMP
3225 jsdService::GetInterruptHook (jsdIExecutionHook **aHook)
3226 {   
3227     *aHook = mInterruptHook;
3228     NS_IF_ADDREF(*aHook);
3229     
3230     return NS_OK;
3231 }
3232
3233 NS_IMETHODIMP
3234 jsdService::SetScriptHook (jsdIScriptHook *aHook)
3235 {    
3236     mScriptHook = aHook;
3237
3238     /* if the debugger isn't initialized, that's all we can do for now.  The
3239      * ActivateDebugger() method will do the rest when the coast is clear.
3240      */
3241     if (!mCx || mPauseLevel)
3242         return NS_OK;
3243     
3244     if (aHook)
3245         JSD_SetScriptHook (mCx, jsds_ScriptHookProc, NULL);
3246     /* we can't unset it if !aHook, because we still need to see script
3247      * deletes in order to Release the jsdIScripts held in JSDScript
3248      * private data. */
3249     return NS_OK;
3250 }
3251
3252 NS_IMETHODIMP
3253 jsdService::GetScriptHook (jsdIScriptHook **aHook)
3254 {   
3255     *aHook = mScriptHook;
3256     NS_IF_ADDREF(*aHook);
3257     
3258     return NS_OK;
3259 }
3260
3261 NS_IMETHODIMP
3262 jsdService::SetThrowHook (jsdIExecutionHook *aHook)
3263 {    
3264     mThrowHook = aHook;
3265
3266     /* if the debugger isn't initialized, that's all we can do for now.  The
3267      * ActivateDebugger() method will do the rest when the coast is clear.
3268      */
3269     if (!mCx || mPauseLevel)
3270         return NS_OK;
3271
3272     if (aHook)
3273         JSD_SetThrowHook (mCx, jsds_ExecutionHookProc, NULL);
3274     else
3275         JSD_ClearThrowHook (mCx);
3276     
3277     return NS_OK;
3278 }
3279
3280 NS_IMETHODIMP
3281 jsdService::GetThrowHook (jsdIExecutionHook **aHook)
3282 {   
3283     *aHook = mThrowHook;
3284     NS_IF_ADDREF(*aHook);
3285     
3286     return NS_OK;
3287 }
3288
3289 NS_IMETHODIMP
3290 jsdService::SetTopLevelHook (jsdICallHook *aHook)
3291 {    
3292     mTopLevelHook = aHook;
3293
3294     /* if the debugger isn't initialized, that's all we can do for now.  The
3295      * ActivateDebugger() method will do the rest when the coast is clear.
3296      */
3297     if (!mCx || mPauseLevel)
3298         return NS_OK;
3299
3300     if (aHook)
3301         JSD_SetTopLevelHook (mCx, jsds_CallHookProc, NULL);
3302     else
3303         JSD_ClearTopLevelHook (mCx);
3304     
3305     return NS_OK;
3306 }
3307
3308 NS_IMETHODIMP
3309 jsdService::GetTopLevelHook (jsdICallHook **aHook)
3310 {   
3311     *aHook = mTopLevelHook;
3312     NS_IF_ADDREF(*aHook);
3313     
3314     return NS_OK;
3315 }
3316
3317 NS_IMETHODIMP
3318 jsdService::SetFunctionHook (jsdICallHook *aHook)
3319 {    
3320     mFunctionHook = aHook;
3321
3322     /* if the debugger isn't initialized, that's all we can do for now.  The
3323      * ActivateDebugger() method will do the rest when the coast is clear.
3324      */
3325     if (!mCx || mPauseLevel)
3326         return NS_OK;
3327
3328     if (aHook)
3329         JSD_SetFunctionHook (mCx, jsds_CallHookProc, NULL);
3330     else
3331         JSD_ClearFunctionHook (mCx);
3332     
3333     return NS_OK;
3334 }
3335
3336 NS_IMETHODIMP
3337 jsdService::GetFunctionHook (jsdICallHook **aHook)
3338 {   
3339     *aHook = mFunctionHook;
3340     NS_IF_ADDREF(*aHook);
3341     
3342     return NS_OK;
3343 }
3344
3345 /* virtual */
3346 jsdService::~jsdService()
3347 {
3348     ClearFilters();
3349     mErrorHook = nsnull;
3350     mBreakpointHook = nsnull;
3351     mDebugHook = nsnull;
3352     mDebuggerHook = nsnull;
3353     mInterruptHook = nsnull;
3354     mScriptHook = nsnull;
3355     mThrowHook = nsnull;
3356     mTopLevelHook = nsnull;
3357     mFunctionHook = nsnull;
3358     gGCStatus = JSGC_END;
3359     Off();
3360     gJsds = nsnull;
3361 }
3362
3363 jsdService *
3364 jsdService::GetService ()
3365 {
3366     if (!gJsds)
3367         gJsds = new jsdService();
3368         
3369     NS_IF_ADDREF(gJsds);
3370     return gJsds;
3371 }
3372
3373 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(jsdService, jsdService::GetService)
3374
3375 /* app-start observer.  turns on the debugger at app-start.  this is inserted
3376  * and/or removed from the app-start category by the jsdService::initAtStartup
3377  * property.
3378  */
3379 class jsdASObserver : public nsIObserver 
3380 {
3381   public:
3382     NS_DECL_ISUPPORTS
3383     NS_DECL_NSIOBSERVER
3384
3385     jsdASObserver () {}    
3386 };
3387
3388 NS_IMPL_THREADSAFE_ISUPPORTS1(jsdASObserver, nsIObserver)
3389
3390 NS_IMETHODIMP
3391 jsdASObserver::Observe (nsISupports *aSubject, const char *aTopic,
3392                         const PRUnichar *aData)
3393 {
3394     nsresult rv;
3395
3396     // Hmm.  Why is the app-startup observer called multiple times?
3397     //NS_ASSERTION(!gJsds, "app startup observer called twice");
3398     nsCOMPtr<jsdIDebuggerService> jsds = do_GetService(jsdServiceCtrID, &rv);
3399     if (NS_FAILED(rv))
3400         return rv;
3401
3402     PRBool on;
3403     rv = jsds->GetIsOn(&on);
3404     if (NS_FAILED(rv) || on)
3405         return rv;
3406     
3407     nsCOMPtr<nsIJSRuntimeService> rts = do_GetService(NS_JSRT_CTRID, &rv);
3408     if (NS_FAILED(rv))
3409         return rv;    
3410
3411     JSRuntime *rt;
3412     rts->GetRuntime (&rt);
3413     if (NS_FAILED(rv))
3414         return rv;
3415
3416     rv = jsds->ActivateDebugger(rt);
3417     if (NS_FAILED(rv))
3418         return rv;
3419     
3420     return NS_OK;
3421 }
3422
3423 NS_GENERIC_FACTORY_CONSTRUCTOR(jsdASObserver)
3424 NS_DEFINE_NAMED_CID(JSDSERVICE_CID);
3425 NS_DEFINE_NAMED_CID(JSDASO_CID);
3426
3427 static const mozilla::Module::CIDEntry kJSDCIDs[] = {
3428     { &kJSDSERVICE_CID, false, NULL, jsdServiceConstructor },
3429     { &kJSDASO_CID, false, NULL, jsdASObserverConstructor },
3430     { NULL }
3431 };
3432
3433 static const mozilla::Module::ContractIDEntry kJSDContracts[] = {
3434     { jsdServiceCtrID, &kJSDSERVICE_CID },
3435     { jsdARObserverCtrID, &kJSDASO_CID },
3436     { NULL }
3437 };
3438
3439 static const mozilla::Module kJSDModule = {
3440     mozilla::Module::kVersion,
3441     kJSDCIDs,
3442     kJSDContracts
3443 };
3444
3445 NSMODULE_DEFN(JavaScript_Debugger) = &kJSDModule;
3446
3447 /********************************************************************************
3448  ********************************************************************************
3449  * graveyard
3450  */
3451
3452 #if 0
3453 /* Thread States */
3454 NS_IMPL_THREADSAFE_ISUPPORTS1(jsdThreadState, jsdIThreadState); 
3455
3456 NS_IMETHODIMP
3457 jsdThreadState::GetJSDContext(JSDContext **_rval)
3458 {
3459     *_rval = mCx;
3460     return NS_OK;
3461 }
3462
3463 NS_IMETHODIMP
3464 jsdThreadState::GetJSDThreadState(JSDThreadState **_rval)
3465 {
3466     *_rval = mThreadState;
3467     return NS_OK;
3468 }
3469
3470 NS_IMETHODIMP
3471 jsdThreadState::GetFrameCount (PRUint32 *_rval)
3472 {
3473     *_rval = JSD_GetCountOfStackFrames (mCx, mThreadState);
3474     return NS_OK;
3475 }
3476
3477 NS_IMETHODIMP
3478 jsdThreadState::GetTopFrame (jsdIStackFrame **_rval)
3479 {
3480     JSDStackFrameInfo *sfi = JSD_GetStackFrame (mCx, mThreadState);
3481     
3482     *_rval = jsdStackFrame::FromPtr (mCx, mThreadState, sfi);
3483     return NS_OK;
3484 }
3485
3486 NS_IMETHODIMP
3487 jsdThreadState::GetPendingException(jsdIValue **_rval)
3488 {
3489     JSDValue *jsdv = JSD_GetException (mCx, mThreadState);
3490     
3491     *_rval = jsdValue::FromPtr (mCx, jsdv);
3492     return NS_OK;
3493 }
3494
3495 NS_IMETHODIMP
3496 jsdThreadState::SetPendingException(jsdIValue *aException)
3497 {
3498     JSDValue *jsdv;
3499     
3500     nsresult rv = aException->GetJSDValue (&jsdv);
3501     if (NS_FAILED(rv))
3502         return NS_ERROR_FAILURE;
3503     
3504     if (!JSD_SetException (mCx, mThreadState, jsdv))
3505         return NS_ERROR_FAILURE;
3506
3507     return NS_OK;
3508 }
3509
3510 #endif