Import from 1.9a8 tarball
[mozilla-extra.git] / extensions / venkman / resources / content / venkman-debugger.js
blob9a6adf9745f24e305c5e5abe20baed7776550ba8
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 The JavaScript Debugger.
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>, original author
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 ***** */
40 const JSD_CTRID           = "@mozilla.org/js/jsd/debugger-service;1";
41 const jsdIDebuggerService = Components.interfaces.jsdIDebuggerService;
42 const jsdIExecutionHook   = Components.interfaces.jsdIExecutionHook;
43 const jsdIErrorHook       = Components.interfaces.jsdIErrorHook;
44 const jsdICallHook        = Components.interfaces.jsdICallHook;
45 const jsdIValue           = Components.interfaces.jsdIValue;
46 const jsdIProperty        = Components.interfaces.jsdIProperty;
47 const jsdIScript          = Components.interfaces.jsdIScript;
48 const jsdIStackFrame      = Components.interfaces.jsdIStackFrame;
50 const TYPE_VOID     = jsdIValue.TYPE_VOID;
51 const TYPE_NULL     = jsdIValue.TYPE_NULL;
52 const TYPE_BOOLEAN  = jsdIValue.TYPE_BOOLEAN;
53 const TYPE_INT      = jsdIValue.TYPE_INT;
54 const TYPE_DOUBLE   = jsdIValue.TYPE_DOUBLE;
55 const TYPE_STRING   = jsdIValue.TYPE_STRING;
56 const TYPE_FUNCTION = jsdIValue.TYPE_FUNCTION;
57 const TYPE_OBJECT   = jsdIValue.TYPE_OBJECT;
59 const PROP_ENUMERATE = jsdIProperty.FLAG_ENUMERATE;
60 const PROP_READONLY  = jsdIProperty.FLAG_READONLY;
61 const PROP_PERMANENT = jsdIProperty.FLAG_PERMANENT;
62 const PROP_ALIAS     = jsdIProperty.FLAG_ALIAS;
63 const PROP_ARGUMENT  = jsdIProperty.FLAG_ARGUMENT;
64 const PROP_VARIABLE  = jsdIProperty.FLAG_VARIABLE;
65 const PROP_EXCEPTION = jsdIProperty.FLAG_EXCEPTION;
66 const PROP_ERROR     = jsdIProperty.FLAG_ERROR;
67 const PROP_HINTED    = jsdIProperty.FLAG_HINTED;
69 const SCRIPT_NODEBUG   = jsdIScript.FLAG_DEBUG;
70 const SCRIPT_NOPROFILE = jsdIScript.FLAG_PROFILE;
72 const COLLECT_PROFILE_DATA  = jsdIDebuggerService.COLLECT_PROFILE_DATA;
74 const PCMAP_SOURCETEXT    = jsdIScript.PCMAP_SOURCETEXT;
75 const PCMAP_PRETTYPRINT   = jsdIScript.PCMAP_PRETTYPRINT;
77 const RETURN_CONTINUE   = jsdIExecutionHook.RETURN_CONTINUE;
78 const RETURN_CONT_THROW = jsdIExecutionHook.RETURN_CONTINUE_THROW;
79 const RETURN_VALUE      = jsdIExecutionHook.RETURN_RET_WITH_VAL;
80 const RETURN_THROW      = jsdIExecutionHook.RETURN_THROW_WITH_VAL;
82 const FTYPE_STD     = 0;
83 const FTYPE_SUMMARY = 1;
84 const FTYPE_ARRAY   = 2;
86 const BREAKPOINT_STOPNEVER   = 0;
87 const BREAKPOINT_STOPALWAYS  = 1;
88 const BREAKPOINT_STOPTRUE    = 2;
89 const BREAKPOINT_EARLYRETURN = 3;
91 var $ = new Array(); /* array to store results from evals in debug frames */
93 function compareVersion(maj, min)
95     if (console.jsds.implementationMajor < maj)
96         return -1;
97     
98     if (console.jsds.implementationMajor > maj)
99         return 1;
101     if (console.jsds.implementationMinor < min)
102         return -1;
103     
104     if (console.jsds.implementationMinor > min)
105         return 1;
107     return 0;
110 function initDebugger()
111 {   
112     dd ("initDebugger {");
114     console.instanceSequence = 0;
115     console._continueCodeStack = new Array(); /* top of stack is the default  */
116                                               /* return code for the most     */
117                                               /* recent debugTrap().          */
118     console.scriptWrappers = new Object();
119     console.scriptManagers = new Object();
120     console.breaks  = new Object();
121     console.fbreaks = new Object();
122     console.sbreaks = new Object();
124     /* create the debugger instance */
125     if (!(JSD_CTRID in Components.classes))
126         throw new BadMojo (ERR_NO_DEBUGGER);
127     
128     console.jsds = 
129         Components.classes[JSD_CTRID].getService(jsdIDebuggerService);
130     console.jsds.on();
132     if (compareVersion(1, 2) >= 0)
133         console.jsds.flags = jsdIDebuggerService.DISABLE_OBJECT_TRACE;
135     console.executionHook = { onExecute: jsdExecutionHook };
136     console.errorHook     = { onError: jsdErrorHook };
137     console.callHook      = { onCall: jsdCallHook };
139     console.jsdConsole = console.jsds.wrapValue(console);
141     dispatch ("tmode", {mode: console.prefs["lastThrowMode"]});
142     dispatch ("emode", {mode: console.prefs["lastErrorMode"]});
144     console.enumeratingScripts = true;
145     var enumer = { enumerateScript: console.scriptHook.onScriptCreated };
146     console.jsds.scriptHook = console.scriptHook;
147     console.jsds.enumerateScripts(enumer);
148     delete console.enumeratingScripts;
150     console.jsds.breakpointHook = console.executionHook;
151     console.jsds.debuggerHook   = console.executionHook;
152     console.jsds.debugHook      = console.executionHook;
153     console.jsds.errorHook      = console.errorHook;
155     console.jsds.flags = jsdIDebuggerService.ENABLE_NATIVE_FRAMES;
157     dd ("} initDebugger");
160 function detachDebugger()
162     if ("frames" in console)
163         console.jsds.exitNestedEventLoop();
165     var b;
166     for (b in console.breaks)
167         console.breaks[b].clearBreakpoint();
168     for (b in console.fbreaks)
169         console.fbreaks[b].clearFutureBreakpoint();
171     console.jsds.topLevelHook = null;
172     console.jsds.functionHook = null;
173     console.jsds.breakpointHook = null;
174     console.jsds.debuggerHook = null;
175     console.jsds.debugHook = null;
176     console.jsds.errorHook = null;
177     console.jsds.scriptHook = null;
178     console.jsds.interruptHook = null;
179     console.jsds.throwHook = null;
180     console.jsds.clearAllBreakpoints();
182     console.jsds.GC();
184     if (!console.jsds.initAtStartup)
185         console.jsds.off();
188 console.scriptHook = new Object();
190 console.scriptHook.onScriptCreated =
191 function sh_created (jsdScript)
193     try
194     {
195         jsdScriptCreated(jsdScript);
196     }
197     catch (ex)
198     {
199         dd ("caught " + dumpObjectTree(ex) + " while creating script.");
200     }
203 console.scriptHook.onScriptDestroyed =
204 function sh_destroyed (jsdScript)
206     try
207     {
208         jsdScriptDestroyed(jsdScript);
209     }
210     catch (ex)
211     {
212         dd ("caught " + dumpObjectTree(ex) + " while destroying script.");
213     }
215     
216 function jsdScriptCreated (jsdScript)
218     var url = jsdScript.fileName;
219     var manager;
220     
221     if (!(url in console.scriptManagers))
222     {
223         manager = console.scriptManagers[url] = new ScriptManager(url);
224         //dispatchCommand (console.coManagerCreated, { scriptManager: manager });
225     }
226     else
227     {
228         manager = console.scriptManagers[url];
229     }
230     
231     manager.onScriptCreated(jsdScript);
234 function jsdScriptDestroyed (jsdScript)
236     if (!(jsdScript.tag in console.scriptWrappers))
237         return;
238     
239     var scriptWrapper = console.scriptWrappers[jsdScript.tag];
240     scriptWrapper.scriptManager.onScriptInvalidated(scriptWrapper);
241     
242     if (scriptWrapper.scriptManager.instances.length == 0 && 
243         scriptWrapper.scriptManager.transientCount == 0)
244     {
245         delete console.scriptManagers[scriptWrapper.scriptManager.url];
246         //dispatchCommand (console.coManagerDestroyed,
247         //                 { scriptManager: scriptWrapper.scriptManager });
248     }
251 function jsdExecutionHook (frame, type, rv)
253     dd ("execution hook: " + formatFrame(frame));
254     
255     var hookReturn = jsdIExecutionHook.RETURN_CONTINUE;
257     if (!console.initialized)
258         return hookReturn;
259     
261     if (!ASSERT(!("frames" in console),
262                 "Execution hook called while stopped") ||
263         frame.isNative ||
264         !ASSERT(frame.script, "Execution hook called with no script") ||
265         frame.script.fileName == MSG_VAL_CONSOLE ||
266         !ASSERT(!(frame.script.flags & SCRIPT_NODEBUG),
267                 "Stopped in a script marked as don't debug") ||
268         !ASSERT(isURLVenkman(frame.script.fileName) ||
269                 !isURLFiltered(frame.script.fileName),
270                 "stopped in a filtered URL"))
271     {
272         return hookReturn;
273     }
275     var frames = new Array();
276     var prevFrame = frame;
277     var hasDisabledFrame = false;
278     
279     while (prevFrame)
280     {
281         frames.push(prevFrame);
282         prevFrame = prevFrame.callingFrame;
283     }
285     var targetWindow = null;
286     var wasModal = false;
287     var cx;
289     try
290     {
291         cx = frame.executionContext;
292     }
293     catch (ex)
294     {
295         dd ("no context");
296         cx = null;
297     }
298     
299     var targetWasEnabled = true;
300     var debuggerWasEnabled = console.baseWindow.enabled;
301     console.baseWindow.enabled = true;
302     
303     if (!ASSERT(cx, "no cx in execution hook"))
304         return hookReturn;
305     
306     var glob = cx.globalObject;
307     if (!ASSERT(glob, "no glob in execution hook"))
308         return hookReturn;
309     
310     console.targetWindow = getBaseWindowFromWindow(glob.getWrappedValue());
311     targetWasEnabled = console.targetWindow.enabled;
312     if (console.targetWindow != console.baseWindow)
313     {
314         cx.scriptsEnabled = false;
315         console.targetWindow.enabled = false;
316     }
318     try
319     {
320         //dd ("debug trap " + formatFrame(frame));
321         hookReturn = debugTrap(frames, type, rv);
322         //dd ("debug trap returned " + hookReturn);
323     }
324     catch (ex)
325     {
326         display (MSG_ERR_INTERNAL_BPT, MT_ERROR);
327         display (formatException(ex), MT_ERROR);
328     }
329     
331     
332     if (console.targetWindow && console.targetWindow != console.baseWindow)
333     {
334         console.targetWindow.enabled = targetWasEnabled;
335         cx.scriptsEnabled = true;
336     }
337     
338     console.baseWindow.enabled = debuggerWasEnabled;
339     delete console.frames;
340     delete console.targetWindow;
341     if ("__exitAfterContinue__" in console)
342         window.close();
344     return hookReturn;
347 function jsdCallHook (frame, type)
349     if (!console.initialized)
350         return;
351     
352     if (type == jsdICallHook.TYPE_FUNCTION_CALL)
353     {
354         setStopState(false);
355         //dd ("Calling: " + frame.functionName);
356     }
357     else if (type == jsdICallHook.TYPE_FUNCTION_RETURN)
358     {
359         // we're called *before* the returning frame is popped from the
360         // stack, so we want our depth calculation to be off by one.
361         var depth = -1;
362         var prevFrame = frame;
363         
364         while (prevFrame)
365         {
366             depth++;
367             prevFrame = prevFrame.callingFrame;
368         }
370         
371         //dd ("Returning: " + frame.functionName +
372         //    ", target depth: " + console._stepOverDepth +
373         //    ", current depth: " + depth);
374         
375         if (depth <= console._stepOverDepth)
376         {
377             //dd ("step over at target depth of " + depth);
378             setStopState(true);
379             console.jsds.functionHook = null;
380             delete console._stepOverDepth;
381         }
382     }
385 function jsdErrorHook (message, fileName, line, pos, flags, exception)
387     if (!console.initialized || isURLFiltered (fileName))
388         return true;
389     
390     try
391     {
392         var flagstr;
393         flagstr  = (flags & jsdIErrorHook.REPORT_EXCEPTION) ? "x" : "-";
394         flagstr += (flags & jsdIErrorHook.REPORT_STRICT) ? "s" : "-";
395         
396         //dd ("===\n" + message + "\n" + fileName + "@" + 
397         //    line + ":" + pos + "; " + flagstr);
398         var msn = (flags & jsdIErrorHook.REPORT_WARNING) ?
399             MSN_ERPT_WARN : MSN_ERPT_ERROR;
401         if (console.errorMode != EMODE_IGNORE)
402             display (getMsg(msn, [message, flagstr, fileName,
403                                   line, pos]), MT_ETRACE);
404         
405         if (console.errorMode == EMODE_BREAK)
406             return false;
407         
408         return true;
409     }
410     catch (ex)
411     {
412         dd ("error in error hook: " + ex);
413     }
414     return true;
417 function ScriptManager (url)
419     this.url = url;
420     this.instances = new Array();
421     this.transients = new Object();
422     this.transientCount = 0;
423     this.disableTransients = isURLFiltered(url);
426 ScriptManager.prototype.onScriptCreated =
427 function smgr_created (jsdScript)
429     var instance;
431     if (!ASSERT(jsdScript.isValid, "invalid script created!"))
432         return;
433     
434     if (this.instances.length != 0)
435         instance = this.instances[this.instances.length - 1];
437     if (!instance || (instance.isSealed && jsdScript.functionName))
438     {
439         //dd ("instance created for " + jsdScript.fileName);
440         instance = new ScriptInstance(this);
441         instance.sequence = console.instanceSequence++;
442         this.instances.push(instance);
443         dispatchCommand (console.coInstanceCreated,
444                          { scriptInstance: instance });
445     }
447     if ("_lastScriptWrapper" in console)
448     {
449         if ((console._lastScriptWrapper.scriptManager != this ||
450              console._lastScriptWrapper.scriptInstance != instance) &&
451             console._lastScriptWrapper.scriptInstance.scriptCount &&
452             !console._lastScriptWrapper.scriptInstance.isSealed)
453         {
454             console._lastScriptWrapper.scriptInstance.seal();
455         }
456     }
457             
458     var scriptWrapper = new ScriptWrapper(jsdScript);
459     console._lastScriptWrapper = scriptWrapper;
460     scriptWrapper.scriptManager = this;
461     console.scriptWrappers[jsdScript.tag] = scriptWrapper;
462     scriptWrapper.scriptInstance = instance;
464     if (!instance.isSealed)
465     {
466         //dd ("function created " + formatScript(jsdScript));
467         instance.onScriptCreated (scriptWrapper);
468     }
469     else
470     {
471         //dd ("transient created " + formatScript(jsdScript));
472         ++this.transientCount;
473         if (this.disableTransients)
474             jsdScript.flags |= SCRIPT_NODEBUG | SCRIPT_NOPROFILE;
476         this.transients[jsdScript.tag] = scriptWrapper;
477         scriptWrapper.functionName = MSG_VAL_EVSCRIPT;
478         //dispatch ("hook-transient-script", { scriptWrapper: scriptWrapper });
479     }    
482 ScriptManager.prototype.onScriptInvalidated =
483 function smgr_invalidated (scriptWrapper)
485     //dd ("script invalidated");
486     
487     delete console.scriptWrappers[scriptWrapper.tag];
488     if (scriptWrapper.tag in this.transients)
489     {
490         //dd ("transient destroyed " + formatScript(scriptWrapper.jsdScript));
491         --this.transientCount;
492         delete this.transients[scriptWrapper.tag];
493         //dispatch ("hook-script-invalidated", { scriptWrapper: scriptWrapper });
494     }
495     else
496     {
497         //dd ("function destroyed " + formatScript(scriptWrapper.jsdScript));
498         scriptWrapper.scriptInstance.onScriptInvalidated(scriptWrapper);
499         //dispatch ("hook-script-invalidated", { scriptWrapper: scriptWrapper });
501         if (scriptWrapper.scriptInstance.scriptCount == 0)
502         {
503             var i = arrayIndexOf(this.instances, scriptWrapper.scriptInstance);
504             arrayRemoveAt(this.instances, i);
505             dispatchCommand (console.coInstanceDestroyed,
506                              { scriptInstance: scriptWrapper.scriptInstance });
507         }
508     }
509 }    
511 ScriptManager.prototype.__defineGetter__ ("sourceText", smgr_sourcetext);
512 function smgr_sourcetext()
514     return this.instances[this.instances.length - 1].sourceText;
517 ScriptManager.prototype.__defineGetter__ ("lineMap", smgr_linemap);
518 function smgr_linemap()
520     return this.instances[this.instances.length - 1].lineMap;
523 ScriptManager.prototype.getInstanceBySequence =
524 function smgr_bysequence (seq)
526     for (var i = 0; i < this.instances.length; ++i)
527     {
528         if (this.instances[i].sequence == seq)
529             return this.instances[i];
530     }
531     
532     return null;
535 ScriptManager.prototype.isLineExecutable =
536 function smgr_isexe (line)
538     for (var i in this.instances)
539     {
540         if (this.instances[i].isLineExecutable(line))
541             return true;
542     }
544     return false;
547 ScriptManager.prototype.hasBreakpoint =
548 function smgr_hasbp (line)
550     for (var i in this.instances)
551     {
552         if (this.instances[i].hasBreakpoint(line))
553             return true;
554     }
555     
556     return false;
559 ScriptManager.prototype.setBreakpoint =
560 function smgr_break (line, parentBP, props)
562     var found = false;
563     
564     for (var i in this.instances)
565         found |= this.instances[i].setBreakpoint(line, parentBP, props);
567     return found;
570 ScriptManager.prototype.clearBreakpoint =
571 function smgr_break (line)
573     var found = false;
575     for (var i in this.instances)
576         found |= this.instances[i].clearBreakpoint(line);
578     return found;
581 ScriptManager.prototype.hasFutureBreakpoint =
582 function smgr_hasbp (line)
584     var key = this.url + "#" + line;
585     return (key in console.fbreaks);
588 ScriptManager.prototype.getFutureBreakpoint =
589 function smgr_getfbp (line)
591     return getFutureBreakpoint (this.url, line);
594 ScriptManager.prototype.noteFutureBreakpoint =
595 function smgr_fbreak (line, state)
597     for (var i in this.instances)
598     {
599         if (this.instances[i]._lineMapInited)
600         {
601             if (state)
602             {
603                 arrayOrFlag (this.instances[i]._lineMap, line - 1, LINE_FBREAK);
604             }
605             else
606             {
607                 arrayAndFlag (this.instances[i]._lineMap, line - 1,
608                               ~LINE_FBREAK);
609             }
610         }
611     }
614 function ScriptInstance (manager)
616     this.scriptManager = manager;
617     this.url = manager.url;
618     this.creationDate = new Date();
619     this.topLevel = null;
620     this.functions = new Object();
621     this.nestLevel = 0;
622     this.isSealed = false;
623     this.scriptCount = 0;
624     this.breakpointCount = 0;
625     this.disabledScripts = 0;
626     this._lineMap = new Array();
627     this._lineMapInited = false;
630 ScriptInstance.prototype.scanForMetaComments =
631 function si_scan (start)
633     const CHUNK_SIZE = 500;
634     const CHUNK_DELAY = 100;
635     
636     var scriptInstance = this;
637     var sourceText = this.sourceText;
638     
639     function onSourceLoaded(result)
640     {
641         if (result == Components.results.NS_OK)
642             scriptInstance.scanForMetaComments();
643     };
644     
645     if (!sourceText.isLoaded)
646     {
647         sourceText.loadSource(onSourceLoaded);
648         return;
649     }
651     if (typeof start == "undefined")
652         start = 0;
653     
654     var end = Math.min (sourceText.lines.length, start + CHUNK_SIZE);
655     var obj = new Object();
656     
657     for (var i = start; i < end; ++i)
658     {
659         var ary = sourceText.lines[i].match (/\/\/@(\S+)(.*)/);
660         if (ary && ary[1] in console.metaDirectives && !(ary[1] in obj))
661         {
662             try
663             {
664                 console.metaDirectives[ary[1]](scriptInstance, i + 1, ary);
665             }
666             catch (ex)
667             {
668                 display (getMsg(MSN_ERR_META_FAILED, [ary[1], this.url, i + 1]),
669                          MT_ERROR);
670                 display (formatException (ex), MT_ERROR);
671             }
672         }
673     }
675     if (i != sourceText.lines.length)
676         setTimeout (this.scanForMetaComments, CHUNK_DELAY, i);
679 ScriptInstance.prototype.seal =
680 function si_seal ()
682     this.sealDate = new Date();
683     this.isSealed = true;
685     if (isURLFiltered(this.url))
686     {
687         this.disabledScripts = 1;
688         var nada = SCRIPT_NODEBUG | SCRIPT_NOPROFILE;
689         if (this.topLevel && this.topLevel.isValid)
690             this.topLevel.jsdScript.flags |= nada;
692         for (var f in this.functions)
693         {
694             if (this.functions[f].jsdScript.isValid)
695                 this.functions[f].jsdScript.flags |= nada;
696             ++this.disabledScripts;
697         }
698     }
700     dispatch ("hook-script-instance-sealed", { scriptInstance: this });
703 ScriptInstance.prototype.onScriptCreated =
704 function si_created (scriptWrapper)
706     var tag = scriptWrapper.jsdScript.tag;
707     
708     if (scriptWrapper.functionName)
709     {
710         this.functions[tag] = scriptWrapper;
711     }
712     else
713     {
714         this.topLevel = scriptWrapper;
715         scriptWrapper.functionName = MSG_VAL_TLSCRIPT;
716         scriptWrapper.addToLineMap(this._lineMap);
717         //var dummy = scriptWrapper.sourceText;
718         this.seal();
719     }
721     ++this.scriptCount;
724 ScriptInstance.prototype.onScriptInvalidated =
725 function si_invalidated (scriptWrapper)
727     //dd ("script invalidated");
728     scriptWrapper.clearBreakpoints();
729     --this.scriptCount;
732 ScriptInstance.prototype.__defineGetter__ ("sourceText", si_gettext);
733 function si_gettext ()
735     if (!("_sourceText" in this))
736         this._sourceText = new SourceText (this);
738     return this._sourceText;
741 ScriptInstance.prototype.__defineGetter__ ("lineMap", si_linemap);
742 function si_linemap()
744     if (!this._lineMapInited)
745     {
746         if (this.topLevel && this.topLevel.jsdScript.isValid)
747             this.topLevel.addToLineMap(this._lineMap);
748         
749         for (var i in this.functions)
750         {
751             if (this.functions[i].jsdScript.isValid)
752                 this.functions[i].addToLineMap(this._lineMap);
753         }
754         
755         for (var fbp in console.fbreaks)
756         {
757             var fbreak = console.fbreaks[fbp];
758             if (fbreak.url == this.url)
759                 arrayOrFlag (this._lineMap, fbreak.lineNumber - 1, LINE_FBREAK);
760         }
761         
762         this._lineMapInited = true;
763     }
765     return this._lineMap;            
768 ScriptInstance.prototype.isLineExecutable =
769 function si_isexe (line)
771     if (this.topLevel && this.topLevel.jsdScript.isValid &&
772         this.topLevel.jsdScript.isLineExecutable (line, PCMAP_SOURCETEXT))
773     {
774         return true;
775     }
776     
777     for (var f in this.functions)
778     {
779         var jsdScript = this.functions[f].jsdScript;
780         if (line >= jsdScript.baseLineNumber &&
781             line <= jsdScript.baseLineNumber + jsdScript.lineExtent &&
782             jsdScript.isLineExecutable (line, PCMAP_SOURCETEXT))
783         {
784             return true;
785         }
786     }
788     return false;
791 ScriptInstance.prototype.hasBreakpoint =
792 function si_hasbp (line)
794     return Boolean (this.getBreakpoint(line));
797 ScriptInstance.prototype.getBreakpoint =
798 function si_getbp (line)
800     for (var b in console.breaks)
801     {
802         if (console.breaks[b].scriptWrapper.scriptInstance == this)
803         {
804             if (typeof line == "undefined")
805                 return true;
806             
807             var jsdScript = console.breaks[b].scriptWrapper.jsdScript;
808             if (jsdScript.pcToLine(console.breaks[b].pc, PCMAP_SOURCETEXT) ==
809                 line)
810             {
811                 return console.breaks[b];
812             }
813         }
814     }
816     return false;
819 ScriptInstance.prototype.setBreakpoint =
820 function si_setbp (line, parentBP, props)
822     function setBP (scriptWrapper)
823     {
824         if (!scriptWrapper.jsdScript.isValid)
825             return false;
827         var jsdScript = scriptWrapper.jsdScript;
829         if (line >= jsdScript.baseLineNumber &&
830             line <= jsdScript.baseLineNumber + jsdScript.lineExtent &&
831             (jsdScript.isLineExecutable (line, PCMAP_SOURCETEXT) ||
832              jsdScript.baseLineNumber == line))
833         {
834             var pc = jsdScript.lineToPc(line, PCMAP_SOURCETEXT);
835             scriptWrapper.setBreakpoint(pc, parentBP, props);
836             return true;
837         }
838         return false;
839     };
841     var found;
842     
843     if (this.topLevel)
844         found = setBP (this.topLevel);
845     for (var f in this.functions)
846         found |= setBP (this.functions[f]);
848     if (this._lineMapInited && found)
849         arrayOrFlag(this._lineMap, line - 1, LINE_BREAK);
851     return found;
854 ScriptInstance.prototype.clearBreakpoint =
855 function si_setbp (line)
857     var found = false;
858     
859     function clearBP (scriptWrapper)
860     {
861         var jsdScript = scriptWrapper.jsdScript;
862         if (!jsdScript.isValid)
863             return;
864         
865         var pc = jsdScript.lineToPc(line, PCMAP_SOURCETEXT);
866         if (line >= jsdScript.baseLineNumber &&
867             line <= jsdScript.baseLineNumber + jsdScript.lineExtent &&
868             scriptWrapper.hasBreakpoint(pc))
869         {
870             found |= scriptWrapper.clearBreakpoint(pc);
871         }
872     };
874     if (this._lineMapInited)
875         arrayAndFlag(this._lineMap, line - 1, ~LINE_BREAK);    
876     
877     if (this.topLevel)
878         clearBP (this.topLevel);
880     for (var f in this.functions)
881         clearBP (this.functions[f]);
883     return found;
886 ScriptInstance.prototype.getScriptWrapperAtLine =
887 function si_getscript (line)
889     var targetScript = null;
890     var scriptWrapper;
891     
892     if (this.topLevel)
893     {
894         scriptWrapper = this.topLevel;
895         if (line >= scriptWrapper.jsdScript.baseLineNumber &&
896             line <= scriptWrapper.jsdScript.baseLineNumber +
897             scriptWrapper.jsdScript.lineExtent)
898         {
899             targetScript = scriptWrapper;
900         }
901     }    
903     for (var f in this.functions)
904     {
905         scriptWrapper = this.functions[f];
906         if ((line >= scriptWrapper.jsdScript.baseLineNumber &&
907              line <= scriptWrapper.jsdScript.baseLineNumber +
908              scriptWrapper.jsdScript.lineExtent) &&
909             (!targetScript ||
910              scriptWrapper.jsdScript.lineExtent <
911              targetScript.jsdScript.lineExtent))
912         {
913             targetScript = scriptWrapper;
914         }
915     }
917     return targetScript;
920 ScriptInstance.prototype.containsScriptTag =
921 function si_contains (tag)
923     return ((this.topLevel && this.topLevel.tag == tag) ||
924             (tag in this.functions));
927 ScriptInstance.prototype.guessFunctionNames =
928 function si_guessnames ()
930     var sourceLines = this._sourceText.lines;
931     var context = console.prefs["guessContext"];
932     var pattern = new RegExp (console.prefs["guessPattern"]);
933     var scanText;
934     
935     function getSourceContext (end)
936     {
937         var startLine = end - context;
938         if (startLine < 0)
939             startLine = 0;
941         var text = "";
942         
943         for (i = startLine; i <= targetLine; ++i)
944             text += String(sourceLines[i]);
945     
946         var pos = text.lastIndexOf ("function");
947         if (pos != -1)
948             text = text.substring(0, pos);
950         return text;
951     };
952         
953     for (var i in this.functions)
954     {
955         var scriptWrapper = this.functions[i];
956         if (scriptWrapper.jsdScript.functionName != "anonymous")
957             continue;
958         
959         var targetLine = scriptWrapper.jsdScript.baseLineNumber;
960         if (targetLine > sourceLines.length)
961         {
962             dd ("not enough source to guess function at line " + targetLine);
963             return;
964         }
966         scanText = getSourceContext(targetLine);
967         var ary = scanText.match (pattern);
968         if (ary)
969         {
970             if ("charset" in this._sourceText)
971                 ary[1] = toUnicode(ary[1], this._sourceText.charset);
972             
973             scriptWrapper.functionName = getMsg(MSN_FMT_GUESSEDNAME, ary[1]);
974         }
975         else
976         {
977             if ("guessFallback" in console)
978             {
979                 var name = console.guessFallback(scriptWrapper, scanText);
980                 if (name)
981                 {
982                     scriptWrapper.functionName = getMsg(MSN_FMT_GUESSEDNAME,
983                                                         name);
984                 }
985             }
986         }
987     }
989     dispatch ("hook-guess-complete", { scriptInstance: this });
992 function ScriptWrapper (jsdScript)
993 {    
994     this.jsdScript = jsdScript;
995     this.tag = jsdScript.tag;
996     this.functionName = jsdScript.functionName;
997     this.breakpointCount = 0;
998     this._lineMap = null;
999     this.breaks = new Object();
1002 ScriptWrapper.prototype.__defineGetter__ ("sourceText", sw_getsource);
1003 function sw_getsource ()
1005     if (!("_sourceText" in this))
1006     {
1007         if (!this.jsdScript.isValid)
1008             return null;
1009         this._sourceText = new PPSourceText(this);
1010     }
1011     
1012     return this._sourceText;
1015 ScriptWrapper.prototype.__defineGetter__ ("lineMap", sw_linemap);
1016 function sw_linemap ()
1018     if (!this._lineMap)
1019         this.addToLineMap(this._lineMap);
1020     
1021     return this._lineMap;
1024 ScriptWrapper.prototype.hasBreakpoint =
1025 function sw_hasbp (pc)
1027     var key = this.jsdScript.tag + ":" + pc;
1028     return key in console.breaks;
1031 ScriptWrapper.prototype.getBreakpoint =
1032 function sw_hasbp (pc)
1034     var key = this.jsdScript.tag + ":" + pc;
1035     if (key in console.breaks)
1036         return console.breaks[key];
1037     
1038     return null;
1041 ScriptWrapper.prototype.setBreakpoint =
1042 function sw_setbp (pc, parentBP, props)
1044     var key = this.jsdScript.tag + ":" + pc;
1045     
1046     //dd ("setting breakpoint in " + this.functionName + " " + key);
1047     
1048     if (key in console.breaks)
1049         return null;
1051     var brk = new BreakInstance (parentBP, this, pc);
1052     if (props)
1053         brk.setProperties(props);
1054     
1055     console.breaks[key] = brk;
1056     this.breaks[key] = brk;
1057     
1058     if (parentBP)
1059     {
1060         parentBP.childrenBP[key] = brk;
1061         brk.lineNumber = parentBP.lineNumber;
1062         brk.url = parentBP.url;
1063     }
1065     if ("_sourceText" in this)
1066     {
1067         var line = this.jsdScript.pcToLine(brk.pc, PCMAP_PRETTYPRINT);
1068         arrayOrFlag (this._sourceText.lineMap, line - 1, LINE_BREAK);
1069     }
1071     ++this.scriptInstance.breakpointCount;
1072     ++this.breakpointCount;
1073     
1074     if (this.scriptInstance._lineMapInited)
1075     {
1076         line = this.jsdScript.pcToLine (pc, PCMAP_SOURCETEXT);
1077         arrayOrFlag (this.scriptInstance._lineMap, line - 1, LINE_BREAK);
1078     }
1080     dispatch ("hook-break-set", { breakWrapper: brk });
1081     
1082     return brk;
1085 ScriptWrapper.prototype.clearBreakpoints =
1086 function sw_clearbps ()
1088     var found = false;
1089     
1090     for (b in this.breaks)
1091         found |= this.clearBreakpoint(this.breaks[b].pc);
1093     return found;
1096 ScriptWrapper.prototype.clearBreakpoint =
1097 function sw_clearbp (pc)
1099     var key = this.jsdScript.tag + ":" + pc;
1100     if (!(key in console.breaks))
1101         return false;
1103     var brk = console.breaks[key];
1105     if ("propsWindow" in brk)
1106         brk.propsWindow.close();
1107     
1108     delete console.breaks[key];
1109     delete this.breaks[key];
1111     if (brk.parentBP)
1112         delete brk.parentBP.childrenBP[key];
1114     var line;
1115     
1116     if ("_sourceText" in this && this.jsdScript.isValid)
1117     {
1118         line = this.jsdScript.pcToLine(brk.pc, PCMAP_PRETTYPRINT);
1119         this._sourceText.lineMap[line - 1] &= ~LINE_BREAK;
1120     }
1121     
1122     --this.scriptInstance.breakpointCount;
1123     --this.breakpointCount;
1125     if (this.scriptInstance._lineMapInited)
1126     {
1127         if (this.jsdScript.isValid)
1128         {
1129             line = this.jsdScript.pcToLine (pc, PCMAP_SOURCETEXT);
1130             if (!this.scriptInstance.hasBreakpoint(line))
1131                 this.scriptInstance._lineMap[line - 1] &= ~LINE_BREAK;
1132         }
1133         else
1134         {
1135             /* script is gone, no way to find out where the break actually
1136              * was, so we have to redo the whole map. */
1137             this.scriptInstance._lineMapInited = false;
1138             this.scriptInstance._lineMap.length = 0;
1139             var dummy = this.scriptInstance.lineMap;
1140         }
1141     }
1143     dispatch ("hook-break-clear", { breakWrapper: brk });
1145     if (this.jsdScript.isValid)
1146         this.jsdScript.clearBreakpoint (pc);
1148     return true;
1151 ScriptWrapper.prototype.addToLineMap =
1152 function sw_addmap (lineMap)
1153 {    
1154     var jsdScript = this.jsdScript;
1155     var end = jsdScript.baseLineNumber + jsdScript.lineExtent;
1156     if (!("enumeratingScripts" in console))
1157     {
1158         for (var i = jsdScript.baseLineNumber; i < end; ++i)
1159         {
1160             if (jsdScript.isLineExecutable(i, PCMAP_SOURCETEXT))
1161                 arrayOrFlag (lineMap, i - 1, LINE_BREAKABLE);
1162         }
1163     }
1165     for (i in this.breaks)
1166     {
1167         var line = jsdScript.pcToLine(this.breaks[i].pc, PCMAP_SOURCETEXT);
1168         arrayOrFlag (lineMap, line - 1, LINE_BREAK);
1169     }
1172 function getScriptWrapper(jsdScript)
1174     if (!ASSERT(jsdScript, "getScriptWrapper: null jsdScript"))
1175         return null;
1176     
1177     var tag = jsdScript.tag;
1178     if (tag in console.scriptWrappers)
1179         return console.scriptWrappers[tag];
1181     dd ("Can't find a wrapper for " + formatScript(jsdScript));
1182     return null;
1185 function BreakInstance (parentBP, scriptWrapper, pc)
1187     this._enabled = true;
1188     this.parentBP = parentBP;
1189     this.scriptWrapper = scriptWrapper;
1190     this.pc = pc;
1191     this.url = scriptWrapper.jsdScript.fileName;
1192     this.lineNumber = scriptWrapper.jsdScript.pcToLine (pc, PCMAP_SOURCETEXT);
1193     this.oneTime = false;
1194     this.triggerCount = 0;
1195     
1196     scriptWrapper.jsdScript.setBreakpoint (pc);
1199 BreakInstance.prototype.__defineGetter__ ("jsdURL", bi_getURL);
1200 function bi_getURL ()
1202     return ("x-jsd:break?url=" + encodeURIComponent(this.url) +
1203             "&lineNumber=" + this.lineNumber +
1204             "&conditionEnabled=" + this.conditionEnabled +
1205             "&condition=" + encodeURIComponent(this.condition) +
1206             "&passExceptions=" + this.passExceptions +
1207             "&logResult=" + this.logResult +
1208             "&resultAction=" + this.resultAction +
1209             "&enabled=" + this.enabled);
1212 BreakInstance.prototype.getProperties =
1213 function bi_getprops()
1215     var rv = new Object();
1217     rv.enabled = this._enabled;
1218     if ("_conditionEnabled" in this)
1219         rv.conditionEnabled = this._conditionEnabled;
1220     if ("_condition" in this)
1221         rv.condition = this._condition;
1222     if ("_passExceptions" in this)
1223         rv.passExceptions = this._passExceptions;
1224     if ("_logResult" in this)
1225         rv.logResult = this._logResult;
1226     if ("_resultAction" in this)
1227         rv.resultAction = this._resultAction;
1229     return rv;
1232 BreakInstance.prototype.setProperties =
1233 function bi_setprops(obj)
1235     for (var p in obj)
1236     {
1237         if (p.search(/pc|url|lineNumber/) == -1)
1238             this[p] = obj[p];
1239     }
1240     
1241     if ("propsWindow" in this)
1242         this.propsWindow.populateFromBreakpoint();
1245 BreakInstance.prototype.clearBreakpoint =
1246 function bi_clear()
1248     this.scriptWrapper.clearBreakpoint(this.pc);
1251 BreakInstance.prototype.__defineGetter__ ("enabled", bi_getEnabled);
1252 function bi_getEnabled ()
1254     return this._enabled;
1257 BreakInstance.prototype.__defineSetter__ ("enabled", bi_setEnabled);
1258 function bi_setEnabled (state)
1260     if (state != this._enabled)
1261     {
1262         this._enabled = state;
1263         if (state)
1264             this.scriptWrapper.jsdScript.setBreakpoint(this.pc);
1265         else
1266             this.scriptWrapper.jsdScript.clearBreakpoint(this.pc);
1267     }
1268     
1269     return state;
1272 BreakInstance.prototype.__defineGetter__ ("conditionEnabled", bi_getCondEnabled);
1273 function bi_getCondEnabled ()
1275     if ("_conditionEnabled" in this)
1276         return this._conditionEnabled;
1277     
1278     if (this.parentBP)
1279         return this.parentBP.conditionEnabled;
1281     return false;
1284 BreakInstance.prototype.__defineSetter__ ("conditionEnabled", bi_setCondEnabled);
1285 function bi_setCondEnabled (state)
1287     if (this.parentBP)
1288         return this.parentBP.conditionEnabled = state;
1289     
1290     return this._conditionEnabled = state;
1293 BreakInstance.prototype.__defineGetter__ ("condition", bi_getCondition);
1294 function bi_getCondition ()
1296     if ("_condition" in this)
1297         return this._condition;
1298     
1299     if (this.parentBP)
1300         return this.parentBP.condition;
1302     return "";
1305 BreakInstance.prototype.__defineSetter__ ("condition", bi_setCondition);
1306 function bi_setCondition (value)
1308     if (this.parentBP)
1309         return this.parentBP.condition = value;
1310     
1311     return this._condition = value;
1315 BreakInstance.prototype.__defineGetter__ ("passExceptions", bi_getException);
1316 function bi_getException ()
1318     if ("_passExceptions" in this)
1319         return this._passExceptions;
1320     
1321     if (this.parentBP)
1322         return this.parentBP.passExceptions;
1324     return false;
1327 BreakInstance.prototype.__defineSetter__ ("passExceptions", bi_setException);
1328 function bi_setException (state)
1330     if (this.parentBP)
1331         return this.parentBP.passExceptions = state;
1332     
1333     return this._passExceptions = state;
1337 BreakInstance.prototype.__defineGetter__ ("logResult", bi_getLogResult);
1338 function bi_getLogResult ()
1340     if ("_logResult" in this)
1341         return this._logResult;
1342     
1343     if (this.parentBP)
1344         return this.parentBP.logResult;
1346     return false;
1349 BreakInstance.prototype.__defineSetter__ ("logResult", bi_setLogResult);
1350 function bi_setLogResult (state)
1352     if (this.parentBP)
1353         return this.parentBP.logResult = state;
1354     
1355     return this._logResult = state;
1359 BreakInstance.prototype.__defineGetter__ ("resultAction", bi_getResultAction);
1360 function bi_getResultAction ()
1362     if ("_resultAction" in this)
1363         return this._resultAction;
1364     
1365     if (this.parentBP)
1366         return this.parentBP.resultAction;
1368     return BREAKPOINT_STOPALWAYS;
1371 BreakInstance.prototype.__defineSetter__ ("resultAction", bi_setResultAction);
1372 function bi_setResultAction (state)
1374     if (this.parentBP)
1375         return this.parentBP.resultAction = state;
1376     
1377     return this._resultAction = state;
1380 function FutureBreakpoint (url, lineNumber)
1382     this.url = url;
1383     this.lineNumber = lineNumber;
1384     this.enabled = true;
1385     this.childrenBP = new Object();
1386     this.conditionEnabled = false;
1387     this.condition = "";
1388     this.passExceptions = false;
1389     this.logResult = false;
1390     this.resultAction = BREAKPOINT_STOPALWAYS;
1393 FutureBreakpoint.prototype.__defineGetter__ ("jsdURL", fb_getURL);
1394 function fb_getURL ()
1396     return ("x-jsd:fbreak?url=" + encodeURIComponent(this.url) +
1397             "&lineNumber=" + this.lineNumber +
1398             "&conditionEnabled=" + this.conditionEnabled +
1399             "&condition=" + encodeURIComponent(this.condition) +
1400             "&passExceptions=" + this.passExceptions +
1401             "&logResult=" + this.logResult +
1402             "&resultAction=" + this.resultAction +
1403             "&enabled=" + this.enabled);
1407 FutureBreakpoint.prototype.getProperties =
1408 function fb_getprops()
1410     var rv = new Object();
1411     
1412     rv.conditionEnabled = this.conditionEnabled;
1413     rv.condition = this.condition;
1414     rv.passExceptions = this.passExceptions;
1415     rv.logResult = this.logResult;
1416     rv.resultAction = this.resultAction;
1418     return rv;
1421 FutureBreakpoint.prototype.setProperties =
1422 function fb_setprops(obj)
1424     for (var p in obj)
1425     {
1426         if (p.search(/url|lineNumber|childrenBP/) == -1)
1427             this[p] = obj[p];
1428     }
1429     
1430     if ("propsWindow" in this)
1431         this.propsWindow.populateFromBreakpoint();
1434 FutureBreakpoint.prototype.clearFutureBreakpoint =
1435 function fb_clear ()
1437     clearFutureBreakpoint (this.url, this.lineNumber);
1440 FutureBreakpoint.prototype.resetInstances =
1441 function fb_reseti ()
1443     for (var url in console.scriptManagers)
1444     {
1445         if (url.indexOf(this.url) != -1)
1446             console.scriptManagers[url].setBreakpoint(this.lineNumber);
1447     }
1450 FutureBreakpoint.prototype.clearInstances =
1451 function fb_cleari ()
1453     for (var url in console.scriptManagers)
1454     {
1455         if (url.indexOf(this.url) != -1)
1456             console.scriptManagers[url].clearBreakpoint(this.lineNumber);
1457     }
1460 function testBreakpoint(currentFrame, rv)
1462     var tag = currentFrame.script.tag;
1463     if (!(tag in console.scriptWrappers))
1464         return -1;
1465     
1466     var scriptWrapper = console.scriptWrappers[tag];
1467     var breakpoint = scriptWrapper.getBreakpoint(currentFrame.pc);
1468     if (!ASSERT(breakpoint, "can't find breakpoint for " +
1469                 formatFrame(currentFrame)))
1470     {
1471         return -1;
1472     }
1474     if (!ASSERT(breakpoint.enabled, "stopped at a disabled breakpoint?"))
1475         return RETURN_CONTINUE;
1477     ++breakpoint.triggerCount;
1478     if ("propsWindow" in breakpoint)
1479         breakpoint.propsWindow.onBreakpointTriggered();
1480     
1481     if (breakpoint.oneTime)
1482         scriptWrapper.clearBreakpoint(currentFrame.pc);
1484     if (breakpoint.conditionEnabled && breakpoint.condition)
1485     {
1486         var result = new Object();
1487         var script = "var __trigger__ = function (__count__) {" +
1488             breakpoint.condition + "}; __trigger__.apply(this, [" +
1489             breakpoint.triggerCount + "]);";
1490         if (!currentFrame.eval (script,
1491                                 JSD_URL_SCHEME + "breakpoint-condition",
1492                                 1, result))
1493         {
1494             /* condition raised an exception */
1495             if (breakpoint.passExceptions)
1496             {
1497                 rv.value = result.value;
1498                 return RETURN_THROW;
1499             }
1500                 
1501             display (MSG_ERR_CONDITION_FAILED, MT_ERROR);
1502             display (formatException(result.value.getWrappedValue()), MT_ERROR);
1503         }
1504         else
1505         {
1506             /* condition executed ok */
1507             if (breakpoint.logResult)
1508             {
1509                 display (result.value.stringValue, MT_LOG);
1510             }
1512             if (breakpoint.resultAction == BREAKPOINT_EARLYRETURN)
1513             {
1514                 rv.value = result.value;
1515                 return RETURN_VALUE;
1516             }
1518             if (breakpoint.resultAction == BREAKPOINT_STOPNEVER ||
1519                 (breakpoint.resultAction == BREAKPOINT_STOPTRUE &&
1520                  !result.value.booleanValue))
1521             {
1522                 return RETURN_CONTINUE;
1523             }            
1524         }
1525     }
1527     return -1;
1530 const EMODE_IGNORE = 0;
1531 const EMODE_TRACE  = 1;
1532 const EMODE_BREAK  = 2;
1534 const TMODE_IGNORE = 0;
1535 const TMODE_TRACE  = 1;
1536 const TMODE_BREAK  = 2;
1538 function debugTrap (frames, type, rv)
1540     var tn = "";
1541     var retcode = jsdIExecutionHook.RETURN_CONTINUE;
1543     //dd ("debugTrap");
1545     var frame = frames[0];
1546     
1547     $ = new Array();
1548     
1549     switch (type)
1550     {
1551         case jsdIExecutionHook.TYPE_BREAKPOINT:
1552             var bpResult = testBreakpoint(frame, rv);
1553             if (bpResult != -1)
1554                 return bpResult;
1555             tn = MSG_VAL_BREAKPOINT;
1556             break;
1557         case jsdIExecutionHook.TYPE_DEBUG_REQUESTED:
1558             tn = MSG_VAL_DEBUG;
1559             break;
1560         case jsdIExecutionHook.TYPE_DEBUGGER_KEYWORD:
1561             tn = MSG_VAL_DEBUGGER;
1562             break;
1563         case jsdIExecutionHook.TYPE_THROW:
1564             dd (dumpObjectTree(rv));
1565             display (getMsg(MSN_EXCEPTION_TRACE,
1566                             [rv.value.stringValue, formatFrame(frame)]),
1567                      MT_ETRACE);
1568             if (rv.value.jsClassName == "Error")
1569                 display (formatProperty(rv.value.getProperty("message")),
1570                          MT_EVAL_OUT);
1572             if (console.throwMode != TMODE_BREAK)
1573                 return jsdIExecutionHook.RETURN_CONTINUE_THROW;
1575             console.currentException = rv.value;
1576             retcode = jsdIExecutionHook.RETURN_CONTINUE_THROW;
1577             
1578             tn = MSG_VAL_THROW;
1579             break;
1580         case jsdIExecutionHook.TYPE_INTERRUPTED:
1581             
1582             if (!frame.script.functionName && 
1583                 isURLFiltered(frame.script.fileName))
1584             {
1585                 //dd ("filtered url: " + frame.script.fileName);
1586                 frame.script.flags |= SCRIPT_NOPROFILE | SCRIPT_NODEBUG;
1587                 return retcode;
1588             }
1590             var line;
1591             if (console.prefs["prettyprint"])
1592                 line = frame.script.pcToLine (frame.pc, PCMAP_PRETTYPRINT);
1593             else
1594                 line = frame.line;
1595             if (console._stepPast == 
1596                 frames.length + frame.script.fileName + line)
1597             {
1598                 //dd("stepPast: " + console._stepPast);
1599                 return retcode;
1600             }
1601             
1602             delete console._stepPast;
1603             setStopState(false);
1604             break;
1605         default:
1606             /* don't print stop/cont messages for other types */
1607     }
1609     console.jsds.functionHook = null;
1611     /* set our default return value */
1612     console._continueCodeStack.push (retcode);
1614     if (tn)
1615         display (getMsg(MSN_STOP, tn), MT_STOP);
1616     
1617     /* build an array of frames */
1618     console.frames = frames;
1619     
1620     console.trapType = type;
1621     
1622     try
1623     {    
1624         console.jsds.enterNestedEventLoop({onNest: eventLoopNested}); 
1625     }
1626     catch (ex)
1627     {
1628         dd ("caught " + ex + " while nested");
1629     }
1630     
1631     /* execution pauses here until someone calls exitNestedEventLoop() */
1633     clearCurrentFrame();
1634     rv.value = ("currentException" in console) ? console.currentException : null;
1636     delete console.frames;
1637     delete console.trapType;
1638     delete console.currentException;
1639     $ = new Array();
1640     
1641     dispatch ("hook-debug-continue");
1643     if (tn)
1644         display (getMsg(MSN_CONT, tn), MT_CONT);
1646     return console._continueCodeStack.pop();
1649 function eventLoopNested ()
1651     window.focus();
1652     window.getAttention();
1654     dispatch ("hook-debug-stop");
1657 function getCurrentFrame()
1659     if ("frames" in console)
1660         return console.frames[console._currentFrameIndex];
1662     return null;
1665 function getCurrentFrameIndex()
1667     if (typeof console._currentFrameIndex == "undefined")
1668         return -1;
1669     
1670     return console._currentFrameIndex;
1673 function setCurrentFrameByIndex (index)
1675     if (!console.frames)
1676         throw new BadMojo (ERR_NO_STACK);
1677     
1678     ASSERT (index >= 0 && index < console.frames.length, "index out of range");
1680     console._currentFrameIndex = index;
1681     var cf = console.frames[console._currentFrameIndex];
1682     dispatch ("set-eval-obj", { jsdValue: cf });
1683     console.stopFile = (cf.isNative) ? MSG_URL_NATIVE : cf.script.fileName;
1684     console.stopLine = cf.line;
1685     delete console._pp_stopLine;
1686     return cf;
1689 function clearCurrentFrame ()
1691     if (!console.frames)
1692         throw new BadMojo (ERR_NO_STACK);
1694     if (console.currentEvalObject instanceof jsdIStackFrame)
1695         dispatch ("set-eval-obj", { jsdValue: console.jsdConsole });
1696     
1697     delete console.stopLine;
1698     delete console._pp_stopLine;
1699     delete console.stopFile;
1700     delete console._currentFrameIndex;
1703 function formatArguments (v)
1705     if (!v)
1706         return "";
1708     var ary = new Array();
1709     var p = new Object();
1710     v.getProperties (p, {});
1711     p = p.value;
1712     for (var i = 0; i < p.length; ++i)
1713     {
1714         if (p[i].flags & jsdIProperty.FLAG_ARGUMENT)
1715             ary.push (getMsg(MSN_FMT_ARGUMENT,
1716                              [p[i].name.stringValue,
1717                               formatValue(p[i].value, FTYPE_SUMMARY)]));
1718     }
1719     
1720     return ary.join (MSG_COMMASP); 
1723 function formatFlags (flags)
1725     var s = "";
1726     
1727     if (flags & PROP_ENUMERATE)
1728         s += MSG_VF_ENUMERABLE;
1729     if (flags & PROP_READONLY)
1730         s += MSG_VF_READONLY;        
1731     if (flags & PROP_PERMANENT)
1732         s += MSG_VF_PERMANENT;
1733     if (flags & PROP_ALIAS)
1734         s += MSG_VF_ALIAS;
1735     if (flags & PROP_ARGUMENT)
1736         s += MSG_VF_ARGUMENT;
1737     if (flags & PROP_VARIABLE)
1738         s += MSG_VF_VARIABLE;
1739     if (flags & PROP_ERROR)
1740         s += MSG_VF_ERROR;
1741     if (flags & PROP_EXCEPTION)
1742         s += MSG_VF_EXCEPTION;
1743     if (flags & PROP_HINTED)
1744         s += MSG_VF_HINTED;
1746     return s;
1749 function formatProperty (p, formatType)
1751     if (!p)
1752         throw new BadMojo (ERR_REQUIRED_PARAM, "p");
1754     var s = formatFlags (p.flags);
1756     if (formatType == FTYPE_ARRAY)
1757     {
1758         var rv = formatValue (p.value, FTYPE_ARRAY);
1759         return [p.name.stringValue, rv[1] ? rv[1] : rv[0], rv[2], s];
1760     }
1761     
1762     return getMsg(MSN_FMT_PROPERTY, [s, p.name.stringValue,
1763                                      formatValue(p.value)]);
1766 function formatScript (script)
1768     if (!script)
1769         throw new BadMojo (ERR_REQUIRED_PARAM, "script");
1771     var functionName;
1772     if (script.tag in console.scriptWrappers)
1773         functionName = console.scriptWrappers[script.tag].functionName;
1774     else
1775         functionName = script.functionName;
1776     return getMsg (MSN_FMT_SCRIPT, [functionName, script.fileName]);
1779 function formatFrame (f)
1781     if (!f)
1782         throw new BadMojo (ERR_REQUIRED_PARAM, "f");
1783     var url = (f.isNative) ? MSG_URL_NATIVE : f.script.fileName;
1784     return getMsg (MSN_FMT_FRAME,
1785                    [f.functionName, formatArguments(f.scope), url, f.line]);
1788 function formatValue (v, formatType)
1790     if (!v)
1791         throw new BadMojo (ERR_REQUIRED_PARAM, "v");
1793     if (!(v instanceof jsdIValue))
1794         throw new BadMojo (ERR_INVALID_PARAM, "v", String(v));
1796     var type;
1797     var value;
1798         
1799     switch (v.jsType)
1800     {
1801         case jsdIValue.TYPE_BOOLEAN:
1802             type = MSG_TYPE_BOOLEAN;
1803             value = String(v.booleanValue);
1804             break;
1805         case jsdIValue.TYPE_DOUBLE:
1806             type = MSG_TYPE_DOUBLE;
1807             value = v.doubleValue;
1808             break;
1809         case jsdIValue.TYPE_INT:
1810             type = MSG_TYPE_INT;
1811             value = v.intValue;
1812             break;
1813         case jsdIValue.TYPE_FUNCTION:
1814             type = MSG_TYPE_FUNCTION;
1815             value = v.jsFunctionName;
1816             break;
1817         case jsdIValue.TYPE_NULL:
1818             type = MSG_TYPE_NULL;
1819             value = MSG_TYPE_NULL;
1820             break;
1821         case jsdIValue.TYPE_OBJECT:
1822             if (formatType == FTYPE_STD)
1823             {
1824                 type = MSG_TYPE_OBJECT;
1825                 value = getMsg(MSN_FMT_OBJECT, String(v.propertyCount));
1826             }
1827             else
1828             {
1829                 if (v.jsClassName)
1830                     if (v.jsClassName == "XPCWrappedNative_NoHelper")
1831                         type = MSG_CLASS_XPCOBJ;
1832                     else
1833                         type = v.jsClassName;
1834                 else
1835                     type = MSG_TYPE_OBJECT;
1836                 value = "{" + String(v.propertyCount) + "}";
1837             }
1838             break;
1839         case jsdIValue.TYPE_STRING:
1840             type = MSG_TYPE_STRING;
1841             var strval = v.stringValue;
1842             if (strval.length > console.prefs["maxStringLength"])
1843                 strval = getMsg(MSN_FMT_LONGSTR, strval.length);
1844             else
1845                 strval = strval.quote()
1846             value = strval;
1847             break;
1848         case jsdIValue.TYPE_VOID:
1849             type = MSG_TYPE_VOID;
1850             value = MSG_TYPE_VOID;            
1851             break;
1852         default:
1853             type = MSG_TYPE_UNKNOWN;
1854             value = MSG_TYPE_UNKNOWN;
1855             break;
1856     }
1858     if (formatType == FTYPE_SUMMARY)
1859         return getMsg (MSN_FMT_VALUE_SHORT, [type, value]);
1861     var className;
1862     if (v.jsClassName)
1863         if (v.jsClassName == "XPCWrappedNative_NoHelper")
1864             /* translate this long, unintuitive, and common class name into
1865              * something more palatable. */
1866             className = MSG_CLASS_XPCOBJ;
1867         else
1868             className = v.jsClassName;
1870     if (formatType == FTYPE_ARRAY)
1871         return [type, className, value];
1873     if (className)
1874         return getMsg (MSN_FMT_VALUE_LONG, [type, v.jsClassName, value]);
1876     return getMsg (MSN_FMT_VALUE_MED, [type, value]);
1880 function displayCallStack ()
1882     for (var i = 0; i < console.frames.length; ++i)
1883         displayFrame (console.frames[i], i);
1886 function displayProperties (v)
1888     if (!v)
1889         throw new BadMojo (ERR_REQUIRED_PARAM, "v");
1891     if (!(v instanceof jsdIValue))
1892         throw new BadMojo (ERR_INVALID_PARAM, "v", String(v));
1894     var p = new Object();
1895     v.getProperties (p, {});
1896     for (var i in p.value) display(formatProperty (p.value[i]), MT_EVAL_OUT);
1899 function displaySourceContext (sourceText, line, contextLines)
1901     function onSourceLoaded (status)
1902     {
1903         if (status == Components.results.NS_OK)
1904             displaySourceContext (sourceText, line, contextLines);
1905     }
1906     
1907     if (sourceText.isLoaded)
1908     {
1909         for (var i = line - contextLines; i <= line + contextLines; ++i)
1910         {
1911             if (i > 0 && i < sourceText.lines.length)
1912             {
1913                 var sourceLine;
1914                 if ("charset" in sourceText)
1915                 {
1916                     sourceLine = toUnicode(sourceText.lines[i - 1],
1917                                      sourceText.charset);
1918                 }
1919                 else
1920                 {
1921                     sourceLine = sourceText.lines[i - 1];
1922                 }
1923                 
1924                 display (getMsg(MSN_SOURCE_LINE, [zeroPad (i, 3), sourceLine]),
1925                          i == line ? MT_STEP : MT_SOURCE);
1926             }
1927         }
1928     }
1929     else
1930     {
1931         sourceText.loadSource (onSourceLoaded);
1932     }
1934     
1935 function displayFrame (jsdFrame, idx, showHeader, sourceContext)
1937     if (typeof idx == "undefined")
1938     {
1939         for (idx = 0; idx < console.frames.length; ++idx)
1940             if (jsdFrame == console.frames[idx])
1941                 break;
1942     
1943         if (idx >= console.frames.length)
1944             idx = MSG_VAL_UNKNOWN;
1945     }
1947     if (typeof showHeader == "undefined")
1948         showHeader = true;
1950     if (typeof sourceContext == "undefined")
1951         sourceContext = null;
1953     display(getMsg(MSN_FMT_FRAME_LINE, [idx, formatFrame(jsdFrame)]), MT_OUTPUT);
1955     if (!jsdFrame.isNative && sourceContext != null)
1956     {
1957         var jsdScript = jsdFrame.script;
1958         var scriptWrapper = getScriptWrapper(jsdScript);
1960         if (!ASSERT(scriptWrapper, "Couldn't get a script wrapper"))
1961             return;
1962         if (console.prefs["prettyprint"] && jsdScript.isValid)
1963         {
1964             displaySourceContext (scriptWrapper.sourceText,
1965                                   jsdScript.pcToLine(jsdFrame.pc,
1966                                                      PCMAP_PRETTYPRINT),
1967                                   sourceContext);
1968         }
1969         else
1970         {
1971             displaySourceContext (scriptWrapper.scriptInstance.sourceText,
1972                                   jsdFrame.line, sourceContext);
1973         }
1974     }
1977 function getFutureBreakpoint (urlPattern, lineNumber)
1979     var key = urlPattern + "#" + lineNumber;
1980     if (key in console.fbreaks)
1981         return console.fbreaks[key];
1982     
1983     return null;
1986 function setFutureBreakpoint (urlPattern, lineNumber, props)
1988     var key = urlPattern + "#" + lineNumber;
1990     if (key in console.fbreaks)
1991         return false;
1992     
1993     var url;
1994     
1995     for (url in console.scriptManagers)
1996     {
1997         if (url == urlPattern)
1998             console.scriptManagers[url].noteFutureBreakpoint(lineNumber, true);
1999     }    
2001     for (url in console.files)
2002     {
2003         if (url == urlPattern)
2004             console.files[url].noteFutureBreakpoint(lineNumber, true);
2005     }    
2007     var fbreak = new FutureBreakpoint (urlPattern, lineNumber);
2008     if (props)
2009         fbreak.setProperties(props);
2010     console.fbreaks[key] = fbreak;
2012     dispatch ("hook-fbreak-set", { fbreak: fbreak });
2014     return fbreak;
2017 function clearFutureBreakpoint (urlPattern, lineNumber)
2019     var key = urlPattern + "#" + lineNumber;
2020     if (!(key in console.fbreaks))
2021         return false;
2023     var i;
2024     var fbreak = console.fbreaks[key];
2025     if ("propsWindow" in fbreak)
2026         fbreak.propsWindow.close();
2027     
2028     delete console.fbreaks[key];
2030     for (i in fbreak.childrenBP)
2031         fbreak.childrenBP[i].parentBP = null;
2033     var url;
2034     
2035     for (url in console.scriptManagers)
2036     {
2037         if (url.indexOf(urlPattern) != -1)
2038             console.scriptManagers[url].noteFutureBreakpoint(lineNumber, false);
2039     }
2041     for (url in console.files)
2042     {
2043         if (url == urlPattern)
2044             console.files[url].noteFutureBreakpoint(lineNumber, false);
2045     }    
2047     dispatch ("hook-fbreak-clear", { fbreak: fbreak });
2049     return true;