home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 June / PersonalComputerWorld-June2009-CoverdiscCD.iso / Software / Freeware / Firebug 1.3.3 / firebug-1.3.3-fx.xpi / components / firebug-service.js < prev    next >
Encoding:
Text File  |  2009-02-19  |  65.0 KB  |  2,131 lines

  1. /* See license.txt for terms of usage */
  2.  
  3. // Debug lines are marked with /*@explore*/ at column 120                                                             /*@explore*/
  4. // Use variable name "fileName" for href returned by JSD, file:/ not same as DOM
  5. // Use variable name "url" for normalizedURL, file:/// comparable to DOM
  6. // Convert from fileName to URL with normalizeURL
  7. // We probably don't need denormalizeURL since we don't send .fileName back to JSD
  8.  
  9. // ************************************************************************************************
  10. // Constants
  11.  
  12. const CLASS_ID = Components.ID("{a380e9c0-cb39-11da-a94d-0800200c9a66}");
  13. const CLASS_NAME = "Firebug Service";
  14. const CONTRACT_ID = "@joehewitt.com/firebug;1";
  15. const Cc = Components.classes;
  16. const Ci = Components.interfaces;
  17.  
  18. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  19.  
  20. const PrefService = Cc["@mozilla.org/preferences-service;1"];
  21. const DebuggerService = Cc["@mozilla.org/js/jsd/debugger-service;1"];
  22. const ConsoleService = Cc["@mozilla.org/consoleservice;1"];
  23. const Timer = Cc["@mozilla.org/timer;1"];
  24.  
  25. const jsdIDebuggerService = Ci.jsdIDebuggerService;
  26. const jsdIScript = Ci.jsdIScript;
  27. const jsdIStackFrame = Ci.jsdIStackFrame;
  28. const jsdICallHook = Ci.jsdICallHook;
  29. const jsdIExecutionHook = Ci.jsdIExecutionHook;
  30. const jsdIErrorHook = Ci.jsdIErrorHook;
  31. const nsISupports = Ci.nsISupports;
  32. const nsIPrefBranch = Ci.nsIPrefBranch;
  33. const nsIPrefBranch2 = Ci.nsIPrefBranch2;
  34. const nsIComponentRegistrar = Ci.nsIComponentRegistrar;
  35. const nsIFactory = Ci.nsIFactory;
  36. const nsIConsoleService = Ci.nsIConsoleService;
  37. const nsITimer = Ci.nsITimer;
  38.  
  39. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  40.  
  41. const NS_ERROR_NO_INTERFACE = Components.results.NS_ERROR_NO_INTERFACE;
  42. const NS_ERROR_NOT_IMPLEMENTED = Components.results.NS_ERROR_NOT_IMPLEMENTED;
  43. const NS_ERROR_NO_AGGREGATION = Components.results.NS_ERROR_NO_AGGREGATION;
  44.  
  45. const PCMAP_SOURCETEXT = jsdIScript.PCMAP_SOURCETEXT;
  46. const PCMAP_PRETTYPRINT = jsdIScript.PCMAP_PRETTYPRINT;
  47.  
  48. const COLLECT_PROFILE_DATA = jsdIDebuggerService.COLLECT_PROFILE_DATA;
  49. const DISABLE_OBJECT_TRACE = jsdIDebuggerService.DISABLE_OBJECT_TRACE;
  50. const HIDE_DISABLED_FRAMES = jsdIDebuggerService.HIDE_DISABLED_FRAMES;
  51. const DEBUG_WHEN_SET = jsdIDebuggerService.DEBUG_WHEN_SET;
  52. const MASK_TOP_FRAME_ONLY = jsdIDebuggerService.MASK_TOP_FRAME_ONLY;
  53.  
  54. const TYPE_FUNCTION_CALL = jsdICallHook.TYPE_FUNCTION_CALL;
  55. const TYPE_FUNCTION_RETURN = jsdICallHook.TYPE_FUNCTION_RETURN;
  56. const TYPE_TOPLEVEL_START = jsdICallHook.TYPE_TOPLEVEL_START;
  57. const TYPE_TOPLEVEL_END = jsdICallHook.TYPE_TOPLEVEL_END;
  58.  
  59. const RETURN_CONTINUE = jsdIExecutionHook.RETURN_CONTINUE;
  60. const RETURN_VALUE = jsdIExecutionHook.RETURN_RET_WITH_VAL;
  61. const RETURN_THROW_WITH_VAL = jsdIExecutionHook.RETURN_THROW_WITH_VAL;
  62. const RETURN_CONTINUE_THROW = jsdIExecutionHook.RETURN_CONTINUE_THROW;
  63.  
  64. const NS_OS_TEMP_DIR = "TmpD"
  65.  
  66. const STEP_OVER = 1;
  67. const STEP_INTO = 2;
  68. const STEP_OUT = 3;
  69. const STEP_SUSPEND = 4;  
  70.  
  71. const TYPE_ONE_SHOT = nsITimer.TYPE_ONE_SHOT;
  72.  
  73. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  74.  
  75. const BP_NORMAL = 1;
  76. const BP_MONITOR = 2;
  77. const BP_UNTIL = 4;
  78. const BP_ONRELOAD = 8;  // XXXjjb: This is a mark for the UI to test
  79.  
  80. const LEVEL_TOP = 1;
  81. const LEVEL_EVAL = 2;
  82. const LEVEL_EVENT = 3;
  83.  
  84. const COMPONENTS_FILTERS = [
  85.     new RegExp("^(file:/.*/)extensions/%7B[\\da-fA-F]{8}-[\\da-fA-F]{4}-[\\da-fA-F]{4}-[\\da-fA-F]{4}-[\\da-fA-F]{12}%7D/components/.*\\.js$"),
  86.     new RegExp("^(file:/.*/)extensions/firebug@software\\.joehewitt\\.com/components/.*\\.js$"),
  87.     new RegExp("^(file:/.*/extensions/)\\w+@mozilla\\.org/components/.*\\.js$"),
  88.     new RegExp("^(file:/.*/components/)ns[A-Z].*\\.js$"),
  89.     new RegExp("^(file:/.*/components/)firebug-service\\.js$"),
  90.     new RegExp("^(file:/.*/Contents/MacOS/extensions/.*/components/).*\\.js$"),
  91.     new RegExp("^(file:/.*/modules/).*\\.jsm$"),
  92.     ];
  93.  
  94. const COMPONENTS_RE =  new RegExp("/components/[^/]*\\.js$");
  95.  
  96. const reDBG = /DBG_(.*)/;
  97.  
  98. // ************************************************************************************************
  99. // Globals
  100.  
  101. var jsd, fbs, prefs;
  102.  
  103. var contextCount = 0;
  104.  
  105. var urlFilters = [
  106.     'chrome://',
  107.     'XStringBundle',
  108.     'x-jsd:ppbuffer?type=function', // internal script for pretty printing
  109.     ];
  110.  
  111.  
  112. var clients = [];
  113. var debuggers = [];
  114. var netDebuggers = [];
  115. var scriptListeners = [];
  116.  
  117. var stepMode = 0;
  118. var stepFrame;
  119. var stepFrameLineId;
  120. var stepFrameCount;
  121. var hookFrameCount = 0;
  122.  
  123. var haltDebugger = null;
  124.  
  125. var breakpoints = {};
  126. var breakpointCount = 0;
  127. var disabledCount = 0;
  128. var monitorCount = 0;
  129. var conditionCount = 0;
  130. var runningUntil = null;
  131.  
  132. var errorBreakpoints = [];
  133.  
  134. var profileCount = 0;
  135. var profileStart;
  136.  
  137. var enabledDebugger = false;
  138. var reportNextError = false;
  139. var breakOnNextError = false;
  140. var errorInfo = null;
  141.  
  142. var timer = Timer.createInstance(nsITimer);
  143. var waitingForTimer = false;
  144.  
  145. var FBTrace = null;
  146.  
  147. // ************************************************************************************************
  148.  
  149. function FirebugService()
  150. {
  151.     FBTrace = Cc["@joehewitt.com/firebug-trace-service;1"]
  152.                  .getService(Ci.nsISupports).wrappedJSObject.getTracer("extensions.firebug");
  153.  
  154.     fbs = this;
  155.  
  156.     this.wrappedJSObject = this;
  157.     this.timeStamp = new Date();  /* explore */
  158.     this.breakpoints = breakpoints; // so chromebug can see it /* explore */
  159.  
  160.     this.enabled = false;
  161.     this.profiling = false;
  162.  
  163.     prefs = PrefService.getService(nsIPrefBranch2);
  164.  
  165.     var observerService = Cc["@mozilla.org/observer-service;1"]
  166.         .getService(Ci.nsIObserverService);
  167.     observerService.addObserver(QuitApplicationGrantedObserver, "quit-application-granted", false);
  168.     observerService.addObserver(QuitApplicationRequestedObserver, "quit-application-requested", false);
  169.     observerService.addObserver(QuitApplicationObserver, "quit-application", false);                                                                                                                     /*@explore*/
  170.  
  171.     this.scriptsFilter = "all";
  172.     // XXXjj For some reason the command line will not function if we allow chromebug to see it.?
  173.     this.alwayFilterURLsStarting = ["chrome://chromebug", "x-jsd:ppbuffer", "chrome://firebug/content/commandLine.js"];  // TODO allow override
  174.     this.onEvalScriptCreated.kind = "eval"; /*@explore*/
  175.     this.onTopLevelScriptCreated.kind = "top-level"; /*@explore*/
  176.     this.onEventScriptCreated.kind = "event"; /*@explore*/
  177.  
  178.     this.onXScriptCreatedByTag = {}; // fbs functions by script tag
  179.     this.nestedScriptStack = Components.classes["@mozilla.org/array;1"]
  180.                         .createInstance(Components.interfaces.nsIMutableArray);  // scripts contained in leveledScript that have not been drained
  181. }
  182.  
  183. FirebugService.prototype =
  184. {
  185.     shutdown: function()
  186.     {
  187.         timer = null;
  188.  
  189.         if (!jsd)
  190.             return;
  191.  
  192.         try
  193.         {
  194.             do
  195.             {
  196.                 var depth = jsd.exitNestedEventLoop();
  197.                 //FBTrace.sysout("FirebugService.shutdown exitNestedEventLoop "+depth); // just in case we are not making progress...
  198.             }
  199.             while(depth > 0);
  200.         }
  201.         catch (exc)
  202.         {
  203.             // Seems to be the normal path...FBTrace.sysout("FirebugService, attempt to exitNestedEventLoop fails "+exc);
  204.         }
  205.  
  206.         jsd.off();
  207.         jsd = null;
  208.         //FBTrace.sysout("FirebugService.shutdown\n");
  209.     },
  210.  
  211.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  212.     // nsISupports
  213.  
  214.     QueryInterface: function(iid)
  215.     {
  216.         if (!iid.equals(nsISupports))
  217.             throw NS_ERROR_NO_INTERFACE;
  218.  
  219.         return this;
  220.     },
  221.  
  222.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  223.  
  224.     get lastErrorWindow()
  225.     {
  226.         var win = this._lastErrorWindow;
  227.         this._lastErrorWindow = null; // Release to avoid leaks
  228.         return win;
  229.     },
  230.  
  231.     countContext: function(on)
  232.     {
  233.         contextCount += on ? 1 : -1;
  234.  
  235.         if (on && contextCount == 1)
  236.         {
  237.             this.enabled = true;
  238.             dispatch(clients, "enable");
  239.         }
  240.         else if (contextCount == 0)
  241.         {
  242.             this.enabled = false;
  243.             dispatch(clients, "disable");
  244.         }
  245.  
  246.         return true;
  247.     },
  248.  
  249.     registerClient: function(client)  // clients are essentially XUL windows
  250.     {
  251.         clients.push(client);
  252.     },
  253.  
  254.     unregisterClient: function(client)
  255.     {
  256.         for (var i = 0; i < clients.length; ++i)
  257.         {
  258.             if (clients[i] == client)
  259.             {
  260.                 clients.splice(i, 1);
  261.                 break;
  262.             }
  263.         }
  264.     },
  265.  
  266.     registerDebugger: function(debuggrWrapper)  // first one in will be last one called. Returns state enabledDebugger
  267.     {
  268.         var debuggr = debuggrWrapper.wrappedJSObject;
  269.  
  270.         if (debuggr)
  271.         {
  272.             debuggers.push(debuggr);
  273.             if (debuggers.length == 1)
  274.                 this.enableDebugger();
  275.         }
  276.         else
  277.             throw "firebug-service debuggers must have wrappedJSObject";
  278.  
  279.         try {
  280.             if (debuggr.suspendActivity)
  281.                 netDebuggers.push(debuggr);
  282.         } catch(exc) {
  283.         }
  284.         try {
  285.             if (debuggr.onScriptCreated)
  286.                 scriptListeners.push(debuggr);
  287.         } catch(exc) {
  288.         }
  289.         return  debuggers.length;  // 1.3.1 return to allow Debugger to check progress
  290.     },
  291.  
  292.     unregisterDebugger: function(debuggrWrapper)
  293.     {
  294.         var debuggr = debuggrWrapper.wrappedJSObject;
  295.  
  296.         for (var i = 0; i < debuggers.length; ++i)
  297.         {
  298.             if (debuggers[i] == debuggr)
  299.             {
  300.                 debuggers.splice(i, 1);
  301.                 break;
  302.             }
  303.         }
  304.  
  305.         for (var i = 0; i < netDebuggers.length; ++i)
  306.         {
  307.             if (netDebuggers[i] == debuggr)
  308.             {
  309.                 netDebuggers.splice(i, 1);
  310.                 break;
  311.             }
  312.         }
  313.         for (var i = 0; i < scriptListeners.length; ++i)
  314.         {
  315.             if (scriptListeners[i] == debuggr)
  316.             {
  317.                 scriptListeners.splice(i, 1);
  318.                 break;
  319.             }
  320.         }
  321.  
  322.         if (debuggers.length == 0)
  323.             this.disableDebugger();
  324.         
  325.         return debuggers.length;
  326.     },
  327.  
  328.     lockDebugger: function()
  329.     {
  330.         if (this.locked)
  331.             return;
  332.  
  333.         this.locked = true;
  334.  
  335.         dispatch(debuggers, "onLock", [true]);
  336.     },
  337.  
  338.     unlockDebugger: function()
  339.     {
  340.         if (!this.locked)
  341.             return;
  342.  
  343.         this.locked = false;
  344.  
  345.         dispatch(debuggers, "onLock", [false]);
  346.     },
  347.  
  348.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  349.  
  350.     enterNestedEventLoop: function(callback)
  351.     {
  352.         dispatch(netDebuggers, "suspendActivity");
  353.         fbs.nestedEventLoopDepth = jsd.enterNestedEventLoop({
  354.             onNest: function()
  355.             {
  356.                 dispatch(netDebuggers, "resumeActivity");
  357.                 callback.onNest();
  358.             }
  359.         });
  360.         dispatch(netDebuggers, "resumeActivity");
  361.         return fbs.nestedEventLoopDepth;
  362.     },
  363.  
  364.     exitNestedEventLoop: function()
  365.     {
  366.         dispatch(netDebuggers, "suspendActivity");
  367.         try
  368.         {
  369.             return jsd.exitNestedEventLoop();
  370.         }
  371.         catch (exc)
  372.         {
  373.             FBTrace.sysout("fbs: jsd.exitNestedEventLoop FAILS "+exc);
  374.         }
  375.     },
  376.  
  377.     halt: function(debuggr)
  378.     {
  379.         haltDebugger = debuggr;
  380.     },
  381.  
  382.     step: function(mode, startFrame)
  383.     {
  384.         stepMode = mode;
  385.         stepFrame = startFrame;
  386.         stepFrameCount = countFrames(startFrame);
  387.         stepFrameLineId = stepFrameCount + startFrame.script.fileName + startFrame.line;
  388.                                                                                                                        /*@explore*/
  389.     },
  390.  
  391.     suspend: function()
  392.     {
  393.         stepMode = STEP_SUSPEND;
  394.         stepFrameLineId = null;
  395.         this.hookInterrupts();
  396.     },
  397.  
  398.     runUntil: function(sourceFile, lineNo, startFrame, debuggr)
  399.     {
  400.         runningUntil = this.addBreakpoint(BP_UNTIL, sourceFile, lineNo, null, debuggr);
  401.         stepFrameCount = countFrames(startFrame);
  402.         stepFrameLineId = stepFrameCount + startFrame.script.fileName + startFrame.line;
  403.     },
  404.  
  405.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  406.  
  407.     setBreakpoint: function(sourceFile, lineNo, props, debuggr)
  408.     {
  409.         var bp = this.addBreakpoint(BP_NORMAL, sourceFile, lineNo, props, debuggr);
  410.         if (bp)
  411.         {
  412.             dispatch(debuggers, "onToggleBreakpoint", [sourceFile.href, lineNo, true, bp]);
  413.             return true;
  414.         }
  415.         return false;
  416.     },
  417.  
  418.     clearBreakpoint: function(url, lineNo)
  419.     {
  420.         var bp = this.removeBreakpoint(BP_NORMAL, url, lineNo);
  421.         if (bp)
  422.             dispatch(debuggers, "onToggleBreakpoint", [url, lineNo, false, bp]);
  423.         else /*@explore*/
  424.         {
  425.         }
  426.     },
  427.  
  428.     enableBreakpoint: function(url, lineNo)
  429.     {
  430.         var bp = this.findBreakpoint(url, lineNo);
  431.         if (bp && bp.type & BP_NORMAL)
  432.         {
  433.             bp.disabled &= ~BP_NORMAL;
  434.             dispatch(debuggers, "onToggleBreakpoint", [url, lineNo, true, bp]);
  435.             --disabledCount;
  436.         }
  437.         else {
  438.         }
  439.     },
  440.  
  441.     disableBreakpoint: function(url, lineNo)
  442.     {
  443.         var bp = this.findBreakpoint(url, lineNo);
  444.         if (bp && bp.type & BP_NORMAL)
  445.         {
  446.             bp.disabled |= BP_NORMAL;
  447.             ++disabledCount;
  448.             dispatch(debuggers, "onToggleBreakpoint", [url, lineNo, true, bp]);
  449.         }
  450.         else
  451.         {
  452.         }
  453.  
  454.     },
  455.  
  456.     isBreakpointDisabled: function(url, lineNo)
  457.     {
  458.         var bp = this.findBreakpoint(url, lineNo);
  459.         if (bp && bp.type & BP_NORMAL)
  460.             return bp.disabled & BP_NORMAL;
  461.         else
  462.             return false;
  463.     },
  464.  
  465.     setBreakpointCondition: function(sourceFile, lineNo, condition, debuggr)
  466.     {
  467.         var bp = this.findBreakpoint(sourceFile.href, lineNo);
  468.         if (!bp)
  469.         {
  470.             bp = this.addBreakpoint(BP_NORMAL, sourceFile, lineNo, null, debuggr);
  471.         }
  472.  
  473.         if (!bp)
  474.             return;
  475.  
  476.         if (bp.hitCount <= 0 )
  477.         {
  478.             if (bp.condition && !condition)
  479.             {
  480.                 --conditionCount;
  481.             }
  482.             else if (condition && !bp.condition)
  483.             {
  484.                 ++conditionCount;
  485.             }
  486.         }
  487.         bp.condition = condition;
  488.  
  489.         dispatch(debuggers, "onToggleBreakpoint", [sourceFile.href, lineNo, true, bp]);
  490.     },
  491.  
  492.     getBreakpointCondition: function(url, lineNo)
  493.     {
  494.         var bp = this.findBreakpoint(url, lineNo);
  495.         return bp ? bp.condition : "";
  496.     },
  497.  
  498.     clearAllBreakpoints: function(sourceFiles)
  499.     {
  500.         for (var i = 0; i < sourceFiles.length; ++i)
  501.         {
  502.             var url = sourceFiles[i].href;
  503.             if (!url)
  504.                 continue;
  505.  
  506.             var urlBreakpoints = breakpoints[url];
  507.             if (!urlBreakpoints)
  508.                 continue;
  509.  
  510.             urlBreakpoints = urlBreakpoints.slice();
  511.             for (var j = 0; j < urlBreakpoints.length; ++j)
  512.             {
  513.                 var bp = urlBreakpoints[j];
  514.                 this.clearBreakpoint(url, bp.lineNo);
  515.             }
  516.          }
  517.     },
  518.  
  519.  
  520.     enumerateBreakpoints: function(url, cb)  // url is sourceFile.href, not jsd script.fileName
  521.     {
  522.         if (url)
  523.         {
  524.             var urlBreakpoints = breakpoints[url];
  525.             if (urlBreakpoints)
  526.             {
  527.                 for (var i = 0; i < urlBreakpoints.length; ++i)
  528.                 {
  529.                     var bp = urlBreakpoints[i];
  530.                     if (bp.type & BP_NORMAL)
  531.                     {
  532.                         if (bp.scriptsWithBreakpoint && bp.scriptsWithBreakpoint.length > 0)
  533.                         {
  534.                             for (var j = 0; j < bp.scriptsWithBreakpoint.length; j++)
  535.                             {
  536.                                 var rc = cb.call(url, bp.lineNo, bp.scriptsWithBreakpoint[j], bp);
  537.                                 if (rc)
  538.                                     return [bp];
  539.                             }
  540.                         } else {
  541.                             var rc = cb.call(url, bp.lineNo, null, bp);
  542.                             if (rc)
  543.                                 return [bp];
  544.                         }
  545.                     }
  546.                 }
  547.             }
  548.         }
  549.         else
  550.         {
  551.             var bps = [];
  552.             for (var url in breakpoints)
  553.                 bps.push(this.enumerateBreakpoints(url, cb));
  554.             return bps;
  555.         }
  556.     },
  557.  
  558.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  559.     // error breakpoints are a way of selectively breaking on errors.  see needToBreakForError
  560.     //
  561.     setErrorBreakpoint: function(url, lineNo, debuggr)
  562.     {
  563.         var index = this.findErrorBreakpoint(url, lineNo);
  564.         if (index == -1)
  565.         {
  566.              errorBreakpoints.push({href: url, lineNo: lineNo });
  567.              dispatch(debuggers, "onToggleErrorBreakpoint", [url, lineNo, true, debuggr]);
  568.         }
  569.     },
  570.  
  571.     clearErrorBreakpoint: function(url, lineNo, debuggr)
  572.     {
  573.         var index = this.findErrorBreakpoint(url, lineNo);
  574.         if (index != -1)
  575.         {
  576.             errorBreakpoints.splice(index, 1);
  577.  
  578.             dispatch(debuggers, "onToggleErrorBreakpoint", [url, lineNo, false, debuggr]);
  579.         }
  580.     },
  581.  
  582.     hasErrorBreakpoint: function(url, lineNo)
  583.     {
  584.         return this.findErrorBreakpoint(url, lineNo) != -1;
  585.     },
  586.  
  587.     enumerateErrorBreakpoints: function(url, cb)
  588.     {
  589.         if (url)
  590.         {
  591.             for (var i = 0; i < errorBreakpoints.length; ++i)
  592.             {
  593.                 var bp = errorBreakpoints[i];
  594.                 if (bp.href == url)
  595.                     cb.call(bp.href, bp.lineNo);
  596.             }
  597.         }
  598.         else
  599.         {
  600.             for (var i = 0; i < errorBreakpoints.length; ++i)
  601.             {
  602.                 var bp = errorBreakpoints[i];
  603.                 cb.call(bp.href, bp.lineNo);
  604.             }
  605.         }
  606.     },
  607.  
  608.     findErrorBreakpoint: function(url, lineNo)
  609.     {
  610.         for (var i = 0; i < errorBreakpoints.length; ++i)
  611.         {
  612.             var bp = errorBreakpoints[i];
  613.             if (bp.lineNo == lineNo && bp.href == url)
  614.                 return i;
  615.         }
  616.  
  617.         return -1;
  618.     },
  619.  
  620.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  621.  
  622.     monitor: function(sourceFile, lineNo, debuggr)
  623.     {
  624.         if (lineNo != -1 && this.addBreakpoint(BP_MONITOR, sourceFile, lineNo, null, debuggr))
  625.         {
  626.             ++monitorCount;
  627.             dispatch(debuggers, "onToggleMonitor", [sourceFile.href, lineNo, true]);
  628.         }
  629.     },
  630.  
  631.     unmonitor: function(sourceFile, lineNo)
  632.     {
  633.         if (lineNo != -1 && this.removeBreakpoint(BP_MONITOR, sourceFile.href, lineNo))
  634.         {
  635.             --monitorCount;
  636.             dispatch(debuggers, "onToggleMonitor", [sourceFile.href, lineNo, false]);
  637.         }
  638.     },
  639.  
  640.     isMonitored: function(url, lineNo)
  641.     {
  642.         var bp = lineNo != -1 ? this.findBreakpoint(url, lineNo) : null;
  643.         return bp && bp.type & BP_MONITOR;
  644.     },
  645.  
  646.     enumerateMonitors: function(url, cb)
  647.     {
  648.         if (url)
  649.         {
  650.             var urlBreakpoints = breakpoints[url];
  651.             if (urlBreakpoints)
  652.             {
  653.                 for (var i = 0; i < urlBreakpoints.length; ++i)
  654.                 {
  655.                     var bp = urlBreakpoints[i];
  656.                     if (bp.type & BP_MONITOR)
  657.                         cb.call(url, bp.lineNo);
  658.                 }
  659.             }
  660.         }
  661.         else
  662.         {
  663.             for (var url in breakpoints)
  664.                 this.enumerateBreakpoints(url, cb);
  665.         }
  666.     },
  667.  
  668.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  669.  
  670.     enumerateScripts: function(length)
  671.     {
  672.         var scripts = [];
  673.         jsd.enumerateScripts( {
  674.             enumerateScript: function(script) {
  675.                 var fileName = script.fileName;
  676.                 if ( !isFilteredURL(fileName) ) {
  677.                     scripts.push(script);
  678.                 }
  679.             }
  680.         });
  681.         length.value = scripts.length;
  682.         return scripts;
  683.     },
  684.  
  685.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  686.  
  687.     startProfiling: function()
  688.     {
  689.         if (!this.profiling)
  690.         {
  691.             this.profiling = true;
  692.             profileStart = new Date();
  693.  
  694.             jsd.flags |= COLLECT_PROFILE_DATA;
  695.         }
  696.  
  697.         ++profileCount;
  698.     },
  699.  
  700.     stopProfiling: function()
  701.     {
  702.         if (--profileCount == 0)
  703.         {
  704.             jsd.flags &= ~COLLECT_PROFILE_DATA;
  705.  
  706.             var t = profileStart.getTime();
  707.  
  708.             this.profiling = false;
  709.             profileStart = null;
  710.  
  711.             return new Date().getTime() - t;
  712.         }
  713.         else
  714.             return -1;
  715.     },
  716.  
  717.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  718.  
  719.     enableDebugger: function()
  720.     {
  721.         if (waitingForTimer)
  722.         {
  723.             timer.cancel();
  724.             waitingForTimer = false;
  725.         }
  726.         if (enabledDebugger)
  727.             return;
  728.  
  729.         enabledDebugger = true;
  730.  
  731.         this.obeyPrefs();
  732.  
  733.         if (jsd)
  734.         {
  735.             if (!jsd.isOn)
  736.                 jsd.on();
  737.  
  738.             while(jsd.pauseDepth)  // 1.3.1 unwind completely before dispatch (port to 1.4)
  739.                 jsd.unPause();
  740.             
  741.             dispatch(clients, "onJSDActivate", [jsd]);
  742.             this.hookScripts();
  743.         }
  744.         else
  745.         {
  746.             jsd = DebuggerService.getService(jsdIDebuggerService);
  747.             jsd.on();
  748.             jsd.flags |= DISABLE_OBJECT_TRACE;
  749.  
  750.             dispatch(clients, "onJSDActivate", [jsd]);
  751.  
  752.             this.hookScripts();
  753.  
  754.             jsd.debuggerHook = { onExecute: hook(this.onDebugger, RETURN_CONTINUE) };
  755.             jsd.debugHook = { onExecute: hook(this.onDebug, RETURN_CONTINUE) };
  756.             jsd.breakpointHook = { onExecute: hook(this.onBreakpoint, RETURN_CONTINUE) };
  757.             jsd.throwHook = { onExecute: hook(this.onThrow, RETURN_CONTINUE_THROW) };
  758.             jsd.errorHook = { onError: hook(this.onError, true) };
  759.         }
  760.     },
  761.  
  762.     obeyPrefs: function()
  763.     {
  764.         this.showStackTrace = prefs.getBoolPref("extensions.firebug-service.showStackTrace");
  765.         this.breakOnErrors = prefs.getBoolPref("extensions.firebug-service.breakOnErrors");
  766.         this.trackThrowCatch = prefs.getBoolPref("extensions.firebug-service.trackThrowCatch");
  767.         this.scriptsFilter = prefs.getCharPref("extensions.firebug-service.scriptsFilter");
  768.         this.filterSystemURLs = prefs.getBoolPref("extensions.firebug-service.filterSystemURLs");  // may not be exposed to users
  769.  
  770.         FirebugPrefsObserver.syncFilter();
  771.  
  772.         try {                                                                                                              /*@explore*/
  773.             // CREATION and BP generate a huge trace                                                                     /*@explore*/
  774.             FBTrace.DBG_FBS_CREATION = FBTrace.DBG_FBS_FF_START ? FBTrace.DBG_FBS_CREATION : false;  /*@explore*/
  775.             FBTrace.DBG_FBS_BP = FBTrace.DBG_FBS_FF_START ? FBTrace.DBG_FBS_BP : false;              /*@explore*/
  776.         }                                                                                                                  /*@explore*/
  777.         catch (exc)                                                                                                        /*@explore*/
  778.         {                                                                                                                  /*@explore*/
  779.             FBTrace.dumpProperties("firebug-service: constructor getBoolPrefs FAILED with exception=",exc);                        /*@explore*/
  780.         }
  781.     },
  782.  
  783.     disableDebugger: function()
  784.     {
  785.         if (!enabledDebugger)
  786.             return;
  787.  
  788.         if (!timer)  // then we probably shutdown
  789.             return;
  790.  
  791.  // 1.3.1 timer is confusing and seems unnecessary here Port 1.4
  792. //        timer.init({observe: function()  
  793. //        {
  794.             enabledDebugger = false;
  795.  
  796.             jsd.pause();
  797.             fbs.unhookScripts();
  798.             jsd.off();
  799.             dispatch(clients, "onJSDDeactivate", [jsd]);
  800.  //       }}, 500, TYPE_ONE_SHOT);
  801.  
  802.         //waitingForTimer = true;
  803.     },
  804.  
  805.     pause: function()  // must support multiple calls
  806.     {
  807.         if (!this.suspended)  // marker only UI in debugger.js
  808.             this.suspended = jsd.pause();
  809.         dispatch(clients, "onJSDDeactivate", [jsd]);
  810.         return this.suspended;
  811.     },
  812.  
  813.     unPause: function()
  814.     {
  815.         if (this.suspended)
  816.         {
  817.             var depth = jsd.unPause();
  818.             if ( (this.suspended !=  1 || depth != 0) && FBTrace.DBG_FBS_ERRORS)
  819.                 FBTrace.sysout("fbs.resume unpause mismatch this.suspended "+this.suspended+" unpause depth "+depth);
  820.             delete this.suspended;
  821.             dispatch(clients, "onJSDActivate", [jsd]);
  822.             return depth;
  823.         }
  824.         return null;
  825.     },
  826.  
  827.     isJSDActive: function()
  828.     {
  829.         return (jsd && jsd.isOn && (jsd.pauseDepth == 0) );
  830.     },
  831.  
  832.     broadcast: function(message, args)  // re-transmit the message (string) with args [objs] to XUL windows.
  833.     {
  834.         dispatch(clients, message, args);
  835.     },
  836.  
  837.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  838.     // jsd Hooks
  839.  
  840.     // When (debugger keyword and not halt)||(bp and BP_UNTIL) || (onBreakPoint && no conditions)
  841.     // || interuptHook.  rv is ignored
  842.     onBreak: function(frame, type, rv)
  843.     {
  844.         try
  845.         {
  846.             // avoid step_out from web page to chrome
  847.             fbs.stayInOneDebugger = true;  // XXXjjb make option in chromebug
  848.             if (type==jsdIExecutionHook.TYPE_INTERRUPTED && fbs.last_debuggr && fbs.stayInOneDebugger)
  849.             {
  850.                 var debuggr = this.reFindDebugger(frame, fbs.last_debuggr);
  851.                 if (!debuggr)
  852.                      jsd.interruptHook = null;
  853.             }
  854.             else
  855.                 var debuggr = this.findDebugger(frame);
  856.             
  857.             if (debuggr)
  858.                 return this.breakIntoDebugger(debuggr, frame, type);
  859.         }
  860.         catch(exc)
  861.         {
  862.             ERROR("onBreak failed: "+exc);
  863.         }
  864.         return RETURN_CONTINUE;
  865.     },
  866.  
  867.     // When engine encounters debugger keyword (only)
  868.     onDebugger: function(frame, type, rv)
  869.     {
  870.         try {
  871.             if (haltDebugger)
  872.             {
  873.                 var debuggr = haltDebugger;
  874.                 haltDebugger = null;
  875.                 return debuggr.onHalt(frame);
  876.             }
  877.             else
  878.                 return this.onBreak(frame, type, rv);
  879.             }
  880.          catch(exc)
  881.          {
  882.             return RETURN_CONTINUE;
  883.          }
  884.     },
  885.  
  886.     // when the onError handler returns false 
  887.     onDebug: function(frame, type, rv)
  888.     {
  889.         if ( isFilteredURL(frame.script.fileName) )
  890.             return RETURN_CONTINUE;
  891.         try
  892.         {
  893.             var debuggr = (reportNextError || breakOnNextError) ? this.findDebugger(frame) : null;
  894.  
  895.             if (reportNextError)
  896.             {
  897.                 reportNextError = false;
  898.                 if (debuggr)
  899.                 {
  900.                     var hookReturn = debuggr.onError(frame, errorInfo);
  901.                     if (hookReturn >=0)
  902.                         return hookReturn;
  903.                     else if (hookReturn==-1)
  904.                         breakOnNextError = true;
  905.                     if (breakOnNextError)
  906.                         debuggr = this.reFindDebugger(frame, debuggr);
  907.                 }
  908.             }
  909.  
  910.             if (breakOnNextError)
  911.             {
  912.                 breakOnNextError = false;
  913.                 if (debuggr)
  914.                     return this.breakIntoDebugger(debuggr, frame, type);
  915.             }
  916.         } catch (exc) {
  917.             ERROR("onDebug failed: "+exc);
  918.         }
  919.         return RETURN_CONTINUE;
  920.     },
  921.  
  922.     onBreakpoint: function(frame, type, val)
  923.     {
  924.         var scriptTag = frame.script.tag;
  925.         if (scriptTag in this.onXScriptCreatedByTag)
  926.         {
  927.             var onXScriptCreated = this.onXScriptCreatedByTag[scriptTag];
  928.             delete this.onXScriptCreatedByTag[scriptTag];
  929.             frame.script.clearBreakpoint(0);
  930.             try {
  931.                 var sourceFile = onXScriptCreated(frame, type, val);
  932.             } catch (e) {
  933.                 FBTrace.dumpProperties("onBreakpoint called onXScriptCreated and it didn't end well:",e);
  934.             }
  935.  
  936.             if (!sourceFile || !sourceFile.breakOnZero || sourceFile.breakOnZero != scriptTag)
  937.                 return RETURN_CONTINUE;
  938.             else  // sourceFile.breakOnZero matches the script we have halted.
  939.             {
  940.             }
  941.         }
  942.  
  943.  
  944.         var bp = this.findBreakpointByScript(frame.script, frame.pc);
  945.         if (bp)
  946.         {
  947.             // See issue 1179, should not break if we resumed from a single step and have not advanced.
  948.             if (disabledCount || monitorCount || conditionCount || runningUntil)
  949.             {
  950.                 if (bp.type & BP_MONITOR && !(bp.disabled & BP_MONITOR))
  951.                     bp.debugger.onCall(frame);
  952.  
  953.                 if (bp.type & BP_UNTIL)
  954.                 {
  955.                     this.stopStepping();
  956.                     if (bp.debugger)
  957.                         return this.breakIntoDebugger(bp.debugger, frame, type);
  958.                 }
  959.                 else if (!(bp.type & BP_NORMAL) || bp.disabled & BP_NORMAL)
  960.                 {
  961.                     return  RETURN_CONTINUE;
  962.                 }
  963.                 else if (bp.type & BP_NORMAL)
  964.                 {
  965.                     var passed = testBreakpoint(frame, bp);
  966.                     if (!passed)
  967.                         return RETURN_CONTINUE;
  968.                 }
  969.                 // type was normal, but passed test
  970.             }
  971.             else  // not special, just break for sure
  972.                 return this.breakIntoDebugger(bp.debugger, frame, type);
  973.         }
  974.         else
  975.         {
  976.         }
  977.  
  978.         if (runningUntil)
  979.             return RETURN_CONTINUE;
  980.         else
  981.             return this.onBreak(frame, type, val);
  982.     },
  983.  
  984.     onThrow: function(frame, type, rv)
  985.     {
  986.         if ( isFilteredURL(frame.script.fileName) )
  987.             return RETURN_CONTINUE_THROW;
  988.         // Remember the error where the last exception is thrown - this will
  989.         // be used later when the console service reports the error, since
  990.         // it doesn't currently report the window where the error occured
  991.  
  992.         this._lastErrorWindow =  getFrameGlobal(frame);
  993.  
  994.         if (fbs.trackThrowCatch)
  995.         {
  996.             var debuggr = this.findDebugger(frame);
  997.             if (debuggr)
  998.                 return debuggr.onThrow(frame, rv);
  999.         }
  1000.  
  1001.         return RETURN_CONTINUE_THROW;
  1002.     },
  1003.  
  1004.     onError: function(message, fileName, lineNo, pos, flags, errnum, exc)
  1005.     {
  1006.         errorInfo = { message: message, fileName: fileName, lineNo: lineNo, pos: pos, flags: flags, errnum: errnum, exc: exc };
  1007.  
  1008.         if (message=="out of memory")  // bail
  1009.             return true;
  1010.  
  1011.         if (this.showStackTrace)
  1012.         {
  1013.             reportNextError = true;
  1014.             return false; // Drop into onDebug, sometimes only
  1015.         }
  1016.         else
  1017.         {
  1018.             return !this.needToBreakForError(fileName, lineNo);
  1019.         }
  1020.     },
  1021.  
  1022.     onEventScriptCreated: function(frame, type, val, noNestTest)
  1023.     {
  1024.         if (fbs.showEvents)
  1025.         {
  1026.             try
  1027.             {
  1028.                if (!noNestTest)
  1029.                 {
  1030.                     // In onScriptCreated we saw a script with baseLineNumber = 1. We marked it as event and nested.
  1031.                     // Now we know its event, not nested.
  1032.                     if (fbs.nestedScriptStack.length > 0)
  1033.                     {
  1034.                         fbs.nestedScriptStack.removeElementAt(0);
  1035.                     }
  1036.                     else
  1037.                     {
  1038.                     }
  1039.                 }
  1040.  
  1041.                 var debuggr = fbs.findDebugger(frame);  // sets debuggr.breakContext
  1042.                 if (debuggr)
  1043.                 {
  1044.                     var sourceFile = debuggr.onEventScriptCreated(frame, frame.script, fbs.nestedScriptStack.enumerate());
  1045.                     fbs.resetBreakpoints(sourceFile);
  1046.                 }
  1047.                 else
  1048.                 {
  1049.                 }
  1050.             } catch(exc) {
  1051.                 FBTrace.dumpProperties("onEventScriptCreated failed: ", exc);
  1052.                 ERROR("onEventScriptCreated failed: "+exc);
  1053.             }
  1054.         }
  1055.  
  1056.         fbs.clearNestedScripts();
  1057.         return sourceFile;
  1058.     },
  1059.  
  1060.     onEvalScriptCreated: function(frame, type, val)
  1061.     {
  1062.         if (fbs.showEvals)
  1063.         {
  1064.             try
  1065.             {
  1066.                 if (!frame.callingFrame)
  1067.                 {
  1068.                     return fbs.onEventScriptCreated(frame, type, val, true);
  1069.                 }
  1070.                 // In onScriptCreated we found a no-name script, set a bp in PC=0, and a flag.
  1071.                 // onBreakpoint saw the flag, cleared the flag, and sent us here.
  1072.                 // Start by undoing our damage
  1073.                 var outerScript = frame.script;
  1074.  
  1075.                 var debuggr = fbs.findDebugger(frame);  // sets debuggr.breakContext
  1076.                 if (debuggr)
  1077.                 {
  1078.                     var sourceFile = debuggr.onEvalScriptCreated(frame, outerScript, fbs.nestedScriptStack.enumerate());
  1079.                     fbs.resetBreakpoints(sourceFile);
  1080.                 }
  1081.                 else
  1082.                 {
  1083.                 }
  1084.             }
  1085.             catch (exc)
  1086.             {
  1087.                 ERROR("onEvalScriptCreated failed: "+exc);
  1088.             }
  1089.         }
  1090.  
  1091.         fbs.clearNestedScripts();
  1092.         return sourceFile;
  1093.     },
  1094.  
  1095.     onTopLevelScriptCreated: function(frame, type, val)
  1096.     {
  1097.         try
  1098.         {
  1099.             // In onScriptCreated we may have found a script at baseLineNumber=1
  1100.             // Now we know its not an event
  1101.             if (fbs.nestedScriptStack.length > 0)
  1102.             {
  1103.                 var firstScript = fbs.nestedScriptStack.queryElementAt(0, jsdIScript);
  1104.                 if (firstScript.tag in fbs.onXScriptCreatedByTag)
  1105.                 {
  1106.                     delete  fbs.onXScriptCreatedByTag[firstScript.tag];
  1107.                     firstScript.clearBreakpoint(0);
  1108.                 }
  1109.             }
  1110.  
  1111.             // On compilation of a top-level (global-appending) function.
  1112.             // After this top-level script executes we lose the jsdIScript so we can't build its line table.
  1113.             // Therefore we need to build it here.
  1114.             var debuggr = fbs.findDebugger(frame);  // sets debuggr.breakContext
  1115.             if (debuggr)
  1116.             {
  1117.                 var sourceFile = debuggr.onTopLevelScriptCreated(frame, frame.script, fbs.nestedScriptStack.enumerate());
  1118.                 fbs.resetBreakpoints(sourceFile, frame.script.baseLineNumber+frame.script.lineExtent);
  1119.             }
  1120.             else
  1121.             {
  1122.             }
  1123.         }
  1124.         catch (exc)
  1125.         {
  1126.             FBTrace.dumpProperties("onTopLevelScriptCreated FAILED: ", exc);
  1127.             ERROR("onTopLevelScriptCreated Fails: "+exc);
  1128.         }
  1129.  
  1130.         fbs.clearNestedScripts();
  1131.         return sourceFile;
  1132.     },
  1133.  
  1134.     clearNestedScripts: function()
  1135.     {
  1136.         var innerScripts = fbs.nestedScriptStack.enumerate();
  1137.         while (innerScripts.hasMoreElements())
  1138.         {
  1139.             var script = innerScripts.getNext();
  1140.             if (script.isValid && script.baseLineNumber == 1)
  1141.             {
  1142.                 script.clearBreakpoint(0);
  1143.                 if (this.onXScriptCreatedByTag[script.tag])
  1144.                     delete this.onXScriptCreatedByTag[script.tag];
  1145.             }
  1146.         }
  1147.         fbs.nestedScriptStack.clear();
  1148.     },
  1149.  
  1150.     onScriptCreated: function(script)
  1151.     {
  1152.         if (!fbs)
  1153.         {
  1154.              return;
  1155.         }
  1156.  
  1157.         try
  1158.         {
  1159.             var fileName = script.fileName;
  1160.             if (isFilteredURL(fileName))
  1161.             {
  1162.                 try {
  1163.                 } catch (exc) { /*Bug 426692 */ } /*@explore*/
  1164.                 return;
  1165.             }
  1166.  
  1167.             if (!script.functionName) // top or eval-level
  1168.             {
  1169.                 // We need to detect eval() and grab its source.
  1170.                 var hasCaller = fbs.createdScriptHasCaller();
  1171.                 if (hasCaller)
  1172.                     fbs.onXScriptCreatedByTag[script.tag] = this.onEvalScriptCreated;
  1173.                 else
  1174.                     fbs.onXScriptCreatedByTag[script.tag] = this.onTopLevelScriptCreated;
  1175.  
  1176.                 script.setBreakpoint(0);
  1177.             }
  1178.             else if (script.baseLineNumber == 1)
  1179.             {
  1180.                 // could be a 1) Browser-generated event handler or 2) a nested script at the top of a file
  1181.                 // One way to tell is assume both then wait to see which we hit first:
  1182.                 // 1) bp at pc=0 for this script or 2) for a top-level on at the same filename
  1183.  
  1184.                 fbs.onXScriptCreatedByTag[script.tag] = this.onEventScriptCreated; // for case 1
  1185.                 script.setBreakpoint(0);
  1186.  
  1187.                 fbs.nestedScriptStack.appendElement(script, false);  // for case 2
  1188.  
  1189.             }
  1190.             else
  1191.             {
  1192.                 fbs.nestedScriptStack.appendElement(script, false);
  1193.                 dispatch(scriptListeners,"onScriptCreated",[script, fileName, script.baseLineNumber]);
  1194.             }
  1195.         }
  1196.         catch(exc)
  1197.         {
  1198.             ERROR("onScriptCreated failed: "+exc);
  1199.             FBTrace.dumpProperties("onScriptCreated failed: ", exc);
  1200.         }
  1201.     },
  1202.  
  1203.     createdScriptHasCaller: function()
  1204.     {
  1205.         var frame = Components.stack; // createdScriptHasCaller
  1206.         frame = frame.caller;         // onScriptCreated
  1207.         if (!frame) return frame;
  1208.         frame = frame.caller;         // native jsd?
  1209.         if (!frame) return frame;
  1210.         frame = frame.caller;         // hook apply
  1211.         if (!frame) return frame;
  1212.         frame = frame.caller;         // native interpret?
  1213.         if (!frame) return frame;
  1214.         frame = frame.caller;         // our creator ... or null if we are top level
  1215.         return frame;
  1216.     },
  1217.  
  1218.     onScriptDestroyed: function(script)
  1219.     {
  1220.         if (!fbs)
  1221.              return;
  1222.         if (script.tag in fbs.onXScriptCreatedByTag)
  1223.             delete  fbs.onXScriptCreatedByTag[script.tag];
  1224.  
  1225.         try
  1226.         {
  1227.             var fileName = script.fileName;
  1228.             if (isFilteredURL(fileName))
  1229.                 return;
  1230.             dispatch(scriptListeners,"onScriptDestroyed",[script]);
  1231.         }
  1232.         catch(exc)
  1233.         {
  1234.             ERROR("onScriptDestroyed failed: "+exc);
  1235.             FBTrace.dumpProperties("onScriptDestroyed failed: ", exc);
  1236.         }
  1237.     },
  1238.  
  1239.     dumpContexts: function()
  1240.     {
  1241.  
  1242.         jsd.enumerateContexts( {enumerateContext: function(jscontext)
  1243.         {
  1244.                 FBTrace.sysout("\n");
  1245.                 try
  1246.                 {
  1247.                     var global = jscontext.globalObject.getWrappedValue();
  1248.                     FBTrace.sysout("jsIContext tag:"+jscontext.tag+(jscontext.isValid?" - isValid\n":" - NOT valid\n"));
  1249.                     //FBTrace.dumpProperties("global object:\n", global);
  1250.                     if (global)
  1251.                     {
  1252.                         var document = global.document;
  1253.                         if (document)
  1254.                         {
  1255.                             FBTrace.sysout("global document.location: "+document.location);
  1256.                         }
  1257.                         else
  1258.                         {
  1259.                             FBTrace.sysout("global without document\n");
  1260.                             FBTrace.sysout("global type: "+typeof(global));
  1261.                             FBTrace.sysout("global properties and interfaces", global);
  1262.                         }
  1263.                     }
  1264.                     else
  1265.                         FBTrace.sysout("no global object\n");
  1266.  
  1267.  
  1268.                     if (jscontext.privateData)
  1269.                     {
  1270.                         FBTrace.dumpProperties("jscontext.privateData", jscontext.privateData);
  1271.                     }
  1272.  
  1273.                 }
  1274.                 catch(e)
  1275.                 {
  1276.                     FBTrace.sysout("jscontext dump FAILED "+e);
  1277.                 }
  1278.                 /*
  1279.                  * jsdIContext has jsdIEphemeral, nsISupports, jsdIContext
  1280.                  * jsdIContext.wrappedContext has nsISupports and nsITimerCallback, nothing interesting
  1281.                  * jsdIContext.JSContext is undefined
  1282.                  */
  1283.                 /*
  1284.                 var nsITimerCallback = Components.interfaces["nsITimerCallback"];
  1285.                 if (context instanceof nsITimerCallback)
  1286.                 {
  1287.                     var asTimer = context.QueryInterface(nsITimerCallback);
  1288.                     FBTrace.dumpProperties(nsITimerCallback, asTimer );
  1289.                 }
  1290.                 FBTrace.sysout("\n\n");*/
  1291.         }});
  1292.     },
  1293.  
  1294.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1295.  
  1296.     findDebugger: function(frame)
  1297.     {
  1298.         if (debuggers.length < 1)
  1299.             return;
  1300.  
  1301.         var checkFrame = frame;
  1302.         while (checkFrame) // We may stop in a component, but want the callers Window
  1303.         {
  1304.             var frameWin = getFrameScopeWindowAncestor(checkFrame);
  1305.             if (frameWin) 
  1306.                 break;
  1307.             
  1308.             checkFrame = checkFrame.callingFrame;
  1309.         }
  1310.         
  1311.         if (!checkFrame && FBTrace.DBG_FBS_FINDDEBUGGER)
  1312.             FBTrace.sysout("fbs.findDebugger fell thru bottom of stack", frame);
  1313.             
  1314.         fbs.last_debuggr = fbs.askDebuggersForSupport(frameWin);
  1315.         if (fbs.last_debuggr)
  1316.              return fbs.last_debuggr;
  1317.         else
  1318.             return null;
  1319.     },
  1320.  
  1321.     getLocationFiltered: function(global)
  1322.     {
  1323.         var location = fbs.getLocationSafe(global);
  1324.         // TODO this is kludge isFilteredURL stops users from seeing firebug but chromebug has to disable the filter
  1325.         if (location && location.indexOf("chrome://chromebug/") == -1)
  1326.             return location;
  1327.         else
  1328.             return null;
  1329.     },
  1330.  
  1331.     getLocationSafe: function(global)
  1332.     {
  1333.         try
  1334.         {
  1335.             if (global && global.location)  // then we have a window, it will be an nsIDOMWindow, right?
  1336.                 return global.location.toString();
  1337.         }
  1338.         catch (exc)
  1339.         {
  1340.             // FF3 gives (NS_ERROR_INVALID_POINTER) [nsIDOMLocation.toString]
  1341.         }
  1342.         return null;
  1343.     },
  1344.     
  1345.     askDebuggersForSupport: function(global)
  1346.     {
  1347.         if (!global || !fbs.getLocationFiltered(global))
  1348.             return false;
  1349.         
  1350.         for ( var i = debuggers.length - 1; i >= 0; i--)
  1351.         {
  1352.             try
  1353.             {
  1354.                 var debuggr = debuggers[i];
  1355.                 if (debuggr.supportsGlobal(global))
  1356.                 {
  1357.                     if (!debuggr.breakContext)
  1358.                         FBTrace.dumpProperties("Debugger with no breakContext:",debuggr.supportsGlobal);
  1359.                     return debuggr;
  1360.                 }
  1361.             }
  1362.             catch (exc)
  1363.             {
  1364.                 FBTrace.sysout("firebug-service askDebuggersForSupport FAILS: ",exc);
  1365.             }
  1366.         }
  1367.         return null;
  1368.     },
  1369.     
  1370.     dumpIValue: function(value)
  1371.     {
  1372.         var listValue = {value: null}, lengthValue = {value: 0};
  1373.         value.getProperties(listValue, lengthValue);
  1374.         for (var i = 0; i < lengthValue.value; ++i)
  1375.         {
  1376.             var prop = listValue.value[i];
  1377.             try {
  1378.             var name = prop.name.getWrappedValue();
  1379.             FBTrace.sysout(i+"]"+name+"="+prop.value.getWrappedValue());
  1380.             } catch (e) {
  1381.             FBTrace.sysout(i+"]"+e);
  1382.             }
  1383.         }
  1384.     },
  1385.  
  1386.     reFindDebugger: function(frame, debuggr)
  1387.     {
  1388.         var global = getFrameScopeWindowAncestor(frame); 
  1389.         if (global && debuggr.supportsGlobal(global)) return debuggr;
  1390.         
  1391.         return null;
  1392.     },
  1393.  
  1394.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1395.     // jsd breakpoints are on a PC in a jsdIScript
  1396.     // Users breakpoint on a line of source
  1397.     // Because test.js can be included multiple times, the URL+line number from the UI is not unique.
  1398.     // sourcefile.href != script.fileName, generally script.fileName cannot be used.
  1399.     // If the source is compiled, then we have zero, one, or more jsdIScripts on a line.
  1400.     //    If zero, cannot break at that line
  1401.     //    If one, set a jsd breakpoint
  1402.     //    If more than one, set jsd breakpoint on each script
  1403.     // Else we know that the source will be compiled before it is run.
  1404.     //    Save the sourceFile.href+line and set the jsd breakpoint when we compile
  1405.     //    Venkman called these "future" breakpoints
  1406.     //    We cannot prevent future breakpoints on lines that have no script.  Break onCreate with error?
  1407.  
  1408.     addBreakpoint: function(type, sourceFile, lineNo, props, debuggr)
  1409.     {
  1410.         var url = sourceFile.href;
  1411.         var bp = this.findBreakpoint(url, lineNo);
  1412.         if (bp && bp.type & type)
  1413.             return null;
  1414.  
  1415.         if (bp)
  1416.         {
  1417.             bp.type |= type;
  1418.  
  1419.             if (debuggr)
  1420.                 bp.debugger = debuggr;
  1421.             else  
  1422.             {
  1423.             }
  1424.         }
  1425.         else
  1426.         {
  1427.             bp = this.recordBreakpoint(type, url, lineNo, debuggr, props);
  1428.             fbs.setJSDBreakpoint(sourceFile, bp);
  1429.         }
  1430.         return bp;
  1431.     },
  1432.  
  1433.     recordBreakpoint: function(type, url, lineNo, debuggr, props)
  1434.     {
  1435.         var urlBreakpoints = breakpoints[url];
  1436.         if (!urlBreakpoints)
  1437.             breakpoints[url] = urlBreakpoints = [];
  1438.  
  1439.         var bp = {type: type, href: url, lineNo: lineNo, disabled: 0,
  1440.             debugger: debuggr,
  1441.             condition: "", onTrue: true, hitCount: -1, hit: 0};
  1442.         if (props)
  1443.         {
  1444.             bp.condition = props.condition;
  1445.             bp.onTrue = props.onTrue;
  1446.             bp.hitCount = props.hitCount;
  1447.             if (bp.condition || bp.hitCount > 0)
  1448.                 ++conditionCount;
  1449.             if(props.disabled)
  1450.             {
  1451.                 bp.disabled |= BP_NORMAL;
  1452.                 ++disabledCount;
  1453.             }
  1454.         }
  1455.         urlBreakpoints.push(bp);
  1456.         ++breakpointCount;
  1457.         return bp;
  1458.     },
  1459.  
  1460.     removeBreakpoint: function(type, url, lineNo, script) // xxxJJB script arg not used?
  1461.     {
  1462.         var urlBreakpoints = breakpoints[url];
  1463.         if (!urlBreakpoints)
  1464.             return false;
  1465.  
  1466.         for (var i = 0; i < urlBreakpoints.length; ++i)
  1467.         {
  1468.             var bp = urlBreakpoints[i];
  1469.             if (bp.lineNo == lineNo)
  1470.             {
  1471.                 bp.type &= ~type;
  1472.                 if (!bp.type)
  1473.                 {
  1474.                     if (bp.scriptsWithBreakpoint)
  1475.                     {
  1476.                         for (var j = 0; j < bp.scriptsWithBreakpoint.length; j++)
  1477.                         {
  1478.                             var script = bp.scriptsWithBreakpoint[j];
  1479.                             if (script && script.isValid)
  1480.                             {
  1481.                                 try
  1482.                                 {
  1483.                                     script.clearBreakpoint(bp.pc[j]);
  1484.                                 }
  1485.                                 catch (exc)
  1486.                                 {
  1487.                                     FBTrace.sysout("Firebug service failed to remove breakpoint in "+script.tag+" at lineNo="+lineNo+" pcmap:"+bp.pcmap);
  1488.                                 }
  1489.                             }
  1490.                         }
  1491.                     }
  1492.                     // else this was a future breakpoint that never hit or a script that was GCed
  1493.  
  1494.                     urlBreakpoints.splice(i, 1);
  1495.                     --breakpointCount;
  1496.  
  1497.                     if (bp.disabled)
  1498.                         --disabledCount;
  1499.  
  1500.                     if (bp.condition || bp.hitCount > 0)
  1501.                     {
  1502.                         --conditionCount;
  1503.                     }
  1504.  
  1505.  
  1506.                     if (!urlBreakpoints.length)
  1507.                         delete breakpoints[url];
  1508.  
  1509.                 }
  1510.                 return bp;
  1511.             }
  1512.         }
  1513.  
  1514.         return false;
  1515.     },
  1516.  
  1517.     findBreakpoint: function(url, lineNo)
  1518.     {
  1519.         var urlBreakpoints = breakpoints[url];
  1520.         if (urlBreakpoints)
  1521.         {
  1522.             for (var i = 0; i < urlBreakpoints.length; ++i)
  1523.             {
  1524.                 var bp = urlBreakpoints[i];
  1525.                 if (bp.lineNo == lineNo)
  1526.                     return bp;
  1527.             }
  1528.         }
  1529.  
  1530.         return null;
  1531.     },
  1532.  
  1533.     // When we are called, scripts have been compiled so all relevant breakpoints are not "future"
  1534.     findBreakpointByScript: function(script, pc)
  1535.     {
  1536.         for (var url in breakpoints)
  1537.         {
  1538.             var urlBreakpoints = breakpoints[url];
  1539.             if (urlBreakpoints)
  1540.             {
  1541.                 for (var i = 0; i < urlBreakpoints.length; ++i)
  1542.                 {
  1543.                     var bp = urlBreakpoints[i];
  1544.                     if (bp.scriptsWithBreakpoint)
  1545.                     {
  1546.                         for (var j = 0; j < bp.scriptsWithBreakpoint.length; j++)
  1547.                         {
  1548.                             if ( bp.scriptsWithBreakpoint[j] && (bp.scriptsWithBreakpoint[j].tag == script.tag) && (bp.pc[j] == pc) )
  1549.                                 return bp;
  1550.                         }
  1551.                     }
  1552.                 }
  1553.             }
  1554.         }
  1555.  
  1556.         return null;
  1557.     },
  1558.  
  1559.     resetBreakpoints: function(sourceFile, lastLineNumber) // the sourcefile has just been created after compile
  1560.     {
  1561.         // If the new script is replacing an old script with a breakpoint still
  1562.         var url = sourceFile.href;
  1563.         var urlBreakpoints = breakpoints[url];
  1564.         if (urlBreakpoints)
  1565.         {
  1566.             for (var i = 0; i < urlBreakpoints.length; ++i)
  1567.             {
  1568.                 var bp = urlBreakpoints[i];
  1569.                 fbs.setJSDBreakpoint(sourceFile, bp);
  1570.                 if (lastLineNumber && !bp.jsdLine && !(bp.disabled & BP_NORMAL) && (bp.lineNo < lastLineNumber))
  1571.                 {
  1572.                      fbs.disableBreakpoint(url, bp.lineNo);
  1573.                 }
  1574.             }
  1575.         }
  1576.     },
  1577.  
  1578.     setJSDBreakpoint: function(sourceFile, bp)
  1579.     {
  1580.         var scripts = sourceFile.getScriptsAtLineNumber(bp.lineNo);
  1581.         if (!scripts)
  1582.         {
  1583.              if (!sourceFile.outerScript || !sourceFile.outerScript.isValid)
  1584.              {
  1585.                 return;
  1586.              }
  1587.              scripts = [sourceFile.outerScript];
  1588.         }
  1589.  
  1590.         bp.scriptsWithBreakpoint = [];
  1591.         bp.pc = [];
  1592.         for (var i = 0; i < scripts.length; i++)
  1593.         {
  1594.             var script = scripts[i];
  1595.             if (!script.isValid)
  1596.                 continue;
  1597.  
  1598.             var pcmap = sourceFile.pcmap_type;
  1599.             if (!pcmap)
  1600.             {
  1601.                 pcmap = PCMAP_SOURCETEXT;
  1602.             }
  1603.             // we subtraced this offset when we showed the user so lineNo is a user line number; now we need to talk
  1604.             // to jsd its line world
  1605.             var jsdLine = bp.lineNo + sourceFile.getBaseLineOffset();
  1606.             // test script.isLineExecutable(jsdLineNo, pcmap) ??
  1607.             
  1608.             var isExecutable = false;
  1609.             try {
  1610.                  isExecutable = script.isLineExecutable(jsdLine, pcmap);
  1611.             } catch(e) {
  1612.                 // guess not then...
  1613.             }
  1614.             if (isExecutable)
  1615.             {
  1616.                 var pc = script.lineToPc(jsdLine, pcmap); 
  1617.                 var pcToLine = script.pcToLine(pc, pcmap);  // avoid calling this unless we have to...
  1618.             
  1619.                 if (pcToLine == jsdLine)
  1620.                 {
  1621.                     script.setBreakpoint(pc);
  1622.  
  1623.                     bp.scriptsWithBreakpoint.push(script);
  1624.                     bp.pc.push(pc);
  1625.                     bp.pcmap = pcmap;
  1626.                     bp.jsdLine = jsdLine;
  1627.  
  1628.                     if (pc == 0)  // signal the breakpoint handler to break for user
  1629.                         sourceFile.breakOnZero = script.tag;
  1630.  
  1631.                 }
  1632.                 else
  1633.                 {
  1634.                 }
  1635.             }
  1636.             else
  1637.             {
  1638.             }    
  1639.          }
  1640.     },
  1641.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1642.  
  1643.     breakIntoDebugger: function(debuggr, frame, type)
  1644.     {
  1645.         // Before we break, clear information about previous stepping session
  1646.         this.stopStepping();
  1647.  
  1648.         // Break into the debugger - execution will stop here until the user resumes
  1649.         var returned;
  1650.         try
  1651.         {
  1652.             returned = debuggr.onBreak(frame, type);
  1653.         }
  1654.         catch (exc)
  1655.         {
  1656.             ERROR(exc);
  1657.             returned = RETURN_CONTINUE;
  1658.         }
  1659.  
  1660.         // Execution resumes now. Check if the user requested stepping and if so
  1661.         // install the necessary hooks
  1662.         hookFrameCount = countFrames(frame);
  1663.         this.startStepping();
  1664.  
  1665.         return returned;
  1666.     },
  1667.  
  1668.     needToBreakForError: function(fileName, lineNo)
  1669.     {
  1670.         return breakOnNextError =
  1671.             this.breakOnErrors || this.findErrorBreakpoint(normalizeURL(fileName), lineNo) != -1;
  1672.     },
  1673.  
  1674.     startStepping: function()
  1675.     {
  1676.         if (!stepMode && !runningUntil)
  1677.             return;
  1678.  
  1679.         this.hookFunctions();
  1680.  
  1681.         if (stepMode == STEP_OVER || stepMode == STEP_INTO)
  1682.             this.hookInterrupts();
  1683.     },
  1684.  
  1685.     stopStepping: function()
  1686.     {
  1687.         stepMode = 0;
  1688.         stepFrame = null;
  1689.         stepFrameCount = 0;
  1690.         stepFrameLineId = null;
  1691.  
  1692.         if (runningUntil)
  1693.         {
  1694.             this.removeBreakpoint(BP_UNTIL, runningUntil.href, runningUntil.lineNo);
  1695.             runningUntil = null;
  1696.         }
  1697.  
  1698.         jsd.interruptHook = null;
  1699.         jsd.functionHook = null;
  1700.     },
  1701.  
  1702.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1703.  
  1704.     hookFunctions: function()
  1705.     {
  1706.         function functionHook(frame, type)
  1707.         {
  1708.             switch (type)
  1709.             {
  1710.                 case TYPE_FUNCTION_CALL:
  1711.                 {
  1712.                     ++hookFrameCount;
  1713.  
  1714.                     if (stepMode == STEP_OVER)
  1715.                         jsd.interruptHook = null;
  1716.  
  1717.                     break;
  1718.                 }
  1719.                 case TYPE_FUNCTION_RETURN:
  1720.                 {
  1721.                     --hookFrameCount;
  1722.                     if (hookFrameCount == 0) {  // stack empty
  1723.                         if ( (stepMode == STEP_INTO) || (stepMode == STEP_OVER) ) {
  1724.                             fbs.stopStepping();
  1725.                             stepMode = STEP_SUSPEND; // break on next
  1726.                             fbs.hookInterrupts();
  1727.                         } 
  1728.                         else
  1729.                         {
  1730.                             fbs.stopStepping();
  1731.                         }
  1732.                     }
  1733.                     else if (stepMode == STEP_OVER) 
  1734.                     {
  1735.                         if (hookFrameCount <= stepFrameCount)
  1736.                             fbs.hookInterrupts();
  1737.                     }
  1738.                     else if (stepMode == STEP_OUT)
  1739.                     {
  1740.                         if (hookFrameCount < stepFrameCount)
  1741.                             fbs.hookInterrupts();
  1742.                     }
  1743.  
  1744.                     break;
  1745.                 }
  1746.             }
  1747.         }
  1748.  
  1749.         jsd.functionHook = { onCall: functionHook };
  1750.     },
  1751.  
  1752.     hookInterrupts: function()
  1753.     {
  1754.         function interruptHook(frame, type, rv)
  1755.         {
  1756.             if ( isFilteredURL(frame.script.fileName) )
  1757.                 return RETURN_CONTINUE;
  1758.  
  1759.             // Sometimes the same line will have multiple interrupts, so check
  1760.             // a unique id for the line and don't break until it changes
  1761.             var frameLineId = hookFrameCount + frame.script.fileName + frame.line;
  1762.             if (frameLineId != stepFrameLineId)
  1763.                 return fbs.onBreak(frame, type, rv);
  1764.             else
  1765.                 return RETURN_CONTINUE;
  1766.         }
  1767.  
  1768.         jsd.interruptHook = { onExecute: interruptHook };
  1769.     },
  1770.  
  1771.     hookScripts: function()
  1772.     {
  1773.         jsd.scriptHook = {
  1774.             onScriptCreated: hook(this.onScriptCreated),
  1775.             onScriptDestroyed: hook(this.onScriptDestroyed)
  1776.         };
  1777.  
  1778.     },
  1779.  
  1780.     unhookScripts: function()
  1781.     {
  1782.         jsd.scriptHook = null;
  1783.     },
  1784.  
  1785. };
  1786.  
  1787. function getStepName(mode)
  1788. {
  1789.     if (mode==STEP_OVER) return "STEP_OVER";
  1790.     if (mode==STEP_INTO) return "STEP_INTO";
  1791.     if (mode==STEP_OUT) return "STEP_OUT";
  1792.     if (mode==STEP_SUSPEND) return "STEP_SUSPEND";
  1793. }
  1794.  
  1795. // ************************************************************************************************
  1796.  
  1797. var FirebugFactory =
  1798. {
  1799.     createInstance: function (outer, iid)
  1800.     {
  1801.         if (outer != null)
  1802.             throw NS_ERROR_NO_AGGREGATION;
  1803.  
  1804.         FirebugFactory.initializeService();
  1805.         return (new FirebugService()).QueryInterface(iid);
  1806.     },
  1807.     initializeService: function()
  1808.     {
  1809.         if (!prefs)
  1810.            prefs = PrefService.getService(nsIPrefBranch2);
  1811.  
  1812.         var filterSystemURLs =  prefs.getBoolPref("extensions.firebug-service.filterSystemURLs");
  1813.         if (filterSystemURLs)  // do not turn jsd on unless we want to see chrome
  1814.             return;
  1815.  
  1816.         try
  1817.         {
  1818.             var jsd = DebuggerService.getService(jsdIDebuggerService);
  1819.             jsd.initAtStartup = true;
  1820.         }
  1821.         catch (exc)
  1822.         {
  1823.         }
  1824.     }
  1825. };
  1826.  
  1827. // ************************************************************************************************
  1828.  
  1829. var FirebugModule =
  1830. {
  1831.     registerSelf: function (compMgr, fileSpec, location, type)
  1832.     {
  1833.         compMgr = compMgr.QueryInterface(nsIComponentRegistrar);
  1834.         compMgr.registerFactoryLocation(CLASS_ID, CLASS_NAME, CONTRACT_ID, fileSpec, location, type);
  1835.     },
  1836.  
  1837.     unregisterSelf: function(compMgr, fileSpec, location)
  1838.     {
  1839.         compMgr = compMgr.QueryInterface(nsIComponentRegistrar);
  1840.         compMgr.unregisterFactoryLocation(CLASS_ID, location);
  1841.     },
  1842.  
  1843.     getClassObject: function (compMgr, cid, iid)
  1844.     {
  1845.         if (!iid.equals(nsIFactory))
  1846.             throw NS_ERROR_NOT_IMPLEMENTED;
  1847.  
  1848.         if (cid.equals(CLASS_ID))
  1849.             return FirebugFactory;
  1850.  
  1851.         throw NS_ERROR_NO_INTERFACE;
  1852.     },
  1853.  
  1854.     canUnload: function(compMgr)
  1855.     {
  1856.         return true;
  1857.     }
  1858. };
  1859.  
  1860. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1861.  
  1862. function NSGetModule(compMgr, fileSpec)
  1863. {
  1864.     return FirebugModule;
  1865. }
  1866.  
  1867. // ************************************************************************************************
  1868. // Local Helpers
  1869.  
  1870. function normalizeURL(url)
  1871. {
  1872.     // For some reason, JSD reports file URLs like "file:/" instead of "file:///", so they
  1873.     // don't match up with the URLs we get back from the DOM
  1874.     return url ? url.replace(/file:\/([^/])/, "file:///$1") : "";
  1875. }
  1876.  
  1877. function denormalizeURL(url)
  1878. {
  1879.     // This should not be called.
  1880.     return url ? url.replace(/file:\/\/\//, "file:/") : "";
  1881. }
  1882.  
  1883. function isFilteredURL(rawJSD_script_filename)
  1884. {
  1885.     if (!rawJSD_script_filename)
  1886.         return true;
  1887.     if (rawJSD_script_filename[0] == 'h')
  1888.         return false;
  1889.     if (rawJSD_script_filename == "XPCSafeJSObjectWrapper.cpp")
  1890.         return true;
  1891.     if (fbs.filterSystemURLs)
  1892.         return systemURLStem(rawJSD_script_filename);
  1893.     for (var i = 0; i < fbs.alwayFilterURLsStarting.length; i++)
  1894.         if (rawJSD_script_filename.indexOf(fbs.alwayFilterURLsStarting[i]) != -1)
  1895.             return true;
  1896.     return false;
  1897. }
  1898.  
  1899. function systemURLStem(rawJSD_script_filename)
  1900. {
  1901.     if (this.url_class)  // attempt to optimize stream of similar urls
  1902.     {
  1903.         if ( rawJSD_script_filename.substr(0,this.url_class.length) == this.url_class )
  1904.             return this.url_class;
  1905.     }
  1906.     this.url_class = deepSystemURLStem(rawJSD_script_filename);
  1907.     return this.url_class;
  1908. }
  1909.  
  1910. function deepSystemURLStem(rawJSD_script_filename)
  1911. {
  1912.     for( var i = 0; i < urlFilters.length; ++i )
  1913.     {
  1914.         var filter = urlFilters[i];
  1915.         if ( rawJSD_script_filename.substr(0,filter.length) == filter )
  1916.             return filter;
  1917.     }
  1918.     for( var i = 0; i < COMPONENTS_FILTERS.length; ++i )
  1919.     {
  1920.         if ( COMPONENTS_FILTERS[i].test(rawJSD_script_filename) )
  1921.         {
  1922.             var match = COMPONENTS_FILTERS[i].exec(rawJSD_script_filename);
  1923.             urlFilters.push(match[1]);
  1924.             return match[1];
  1925.         }
  1926.     }
  1927.     return false;
  1928. }
  1929.  
  1930. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1931.  
  1932. function dispatch(listeners, name, args)
  1933. {
  1934.     for (var i = 0; i < listeners.length; ++i)
  1935.     {
  1936.         var listener = listeners[i];
  1937.         if ( listener.hasOwnProperty(name) )
  1938.             listener[name].apply(listener, args);
  1939.     }
  1940. }
  1941.  
  1942. function hook(fn, rv)
  1943. {
  1944.     return function()
  1945.     {
  1946.         try
  1947.         {
  1948.             return fn.apply(fbs, arguments);
  1949.         }
  1950.         catch (exc)
  1951.         {
  1952.             var msg =  "Error in hook: "+ exc +" fn=\n"+fn+"\n stack=\n";
  1953.             for (var frame = Components.stack; frame; frame = frame.caller)
  1954.                 msg += frame.filename + "@" + frame.line + ";\n";
  1955.                ERROR(msg);
  1956.             return rv;
  1957.         }
  1958.     }
  1959. }
  1960.  
  1961. function getFrameScopeWindowAncestor(frame)  // walk script scope chain to bottom, null unless a Window
  1962. {
  1963.     var scope = frame.scope;
  1964.     if (scope)
  1965.     {    
  1966.         while(scope.jsParent)
  1967.             scope = scope.jsParent;
  1968.     
  1969.         if (scope.jsClassName == "Window" || scope.jsClassName == "ChromeWindow")
  1970.             return  scope.getWrappedValue();
  1971.         
  1972.     }
  1973.     else
  1974.         return null;
  1975. }
  1976.  
  1977. function getFrameGlobal(frame)
  1978. {
  1979.     var jscontext = frame.executionContext;
  1980.     if (!jscontext)
  1981.     {
  1982.         return getFrameWindow(frame);
  1983.     }
  1984.     var frameGlobal = jscontext.globalObject.getWrappedValue();
  1985.     if (frameGlobal)
  1986.         return frameGlobal;
  1987.     else
  1988.     {
  1989.         return getFrameWindow(frame);
  1990.     }
  1991. }
  1992.  
  1993. function getFrameWindow(frame)
  1994. {
  1995.     if (debuggers.length < 1)  // too early, frame.eval will crash FF2
  1996.             return;
  1997.     try
  1998.     {
  1999.         var result = {};
  2000.         frame.eval("window", "", 1, result);
  2001.         var win = result.value.getWrappedValue();
  2002.         return getRootWindow(win);
  2003.     }
  2004.     catch (exc)
  2005.     {
  2006.         return null;
  2007.     }
  2008. }
  2009.  
  2010. function getRootWindow(win)
  2011. {
  2012.     for (; win; win = win.parent)
  2013.     {
  2014.         if (!win.parent || win == win.parent || !(win.parent instanceof Window) )
  2015.             return win;
  2016.     }
  2017.     return null;
  2018. }
  2019.  
  2020. function countFrames(frame)
  2021. {
  2022.     var frameCount = 0;
  2023.     for (; frame; frame = frame.callingFrame)
  2024.         ++frameCount;
  2025.     return frameCount;
  2026. }
  2027.  
  2028. function testBreakpoint(frame, bp)
  2029. {
  2030.     if ( bp.condition && bp.condition != "" )
  2031.     {
  2032.         var result = {};
  2033.         frame.scope.refresh();
  2034.         if (frame.eval(bp.condition, "", 1, result))
  2035.         {
  2036.             if (bp.onTrue)
  2037.             {
  2038.                 if (!result.value.booleanValue)
  2039.                     return false;
  2040.             } else
  2041.             {
  2042.                 var value = result.value.getWrappedValue();
  2043.                 if (typeof bp.lastValue == "undefined")
  2044.                 {
  2045.                     bp.lastValue = value;
  2046.                     return false;
  2047.                 } else
  2048.                 {
  2049.                     if (bp.lastValue == value)
  2050.                         return false;
  2051.                     bp.lastValue = value;
  2052.                 }
  2053.             }
  2054.         }
  2055.     }
  2056.     ++bp.hit;
  2057.     if ( bp.hitCount > 0 )
  2058.     {
  2059.         if ( bp.hit < bp.hitCount )
  2060.             return false;
  2061.     }
  2062.     return true;
  2063. }
  2064.  
  2065. function remove(list, item)
  2066. {
  2067.     var index = list.indexOf(item);
  2068.     if (index != -1)
  2069.         list.splice(index, 1);
  2070. }
  2071.  
  2072. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  2073.  
  2074. var FirebugPrefsObserver =
  2075. {
  2076.     syncFilter: function()
  2077.     {
  2078.         var filter = fbs.scriptsFilter;
  2079.         fbs.showEvents = (filter == "all" || filter == "events");
  2080.         fbs.showEvals = (filter == "all" || filter == "evals");
  2081.     }
  2082. };
  2083.  
  2084. var QuitApplicationGrantedObserver =
  2085. {
  2086.     observe: function(subject, topic, data)
  2087.     {
  2088.         fbs.shutdown();
  2089.     }
  2090. };
  2091. var QuitApplicationRequestedObserver =
  2092. {
  2093.     observe: function(subject, topic, data)
  2094.     {
  2095.     }
  2096. };
  2097. var QuitApplicationObserver =
  2098. {
  2099.     observe: function(subject, topic, data)
  2100.     {
  2101.         fbs = null;
  2102.     }
  2103. };
  2104.  
  2105. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  2106.  
  2107. var consoleService = null;
  2108.  
  2109. function ERROR(text)
  2110. {
  2111.     FBTrace.dumpProperties(text);
  2112.  
  2113.     if (!consoleService)
  2114.         consoleService = ConsoleService.getService(nsIConsoleService);
  2115.  
  2116.     consoleService.logStringMessage(text + "");
  2117. }
  2118.  
  2119. function getExecutionStopNameFromType(type)                                                                            /*@explore*/
  2120. {                                                                                                                      /*@explore*/
  2121.     switch (type)                                                                                                      /*@explore*/
  2122.     {                                                                                                                  /*@explore*/
  2123.         case jsdIExecutionHook.TYPE_INTERRUPTED: return "interrupted";                                                 /*@explore*/
  2124.         case jsdIExecutionHook.TYPE_BREAKPOINT: return "breakpoint";                                                   /*@explore*/
  2125.         case jsdIExecutionHook.TYPE_DEBUG_REQUESTED: return "debug requested";                                         /*@explore*/
  2126.         case jsdIExecutionHook.TYPE_DEBUGGER_KEYWORD: return "debugger_keyword";                                       /*@explore*/
  2127.         case jsdIExecutionHook.TYPE_THROW: return "interrupted";                                                       /*@explore*/
  2128.         default: return "unknown("+type+")";                                                                           /*@explore*/
  2129.     }                                                                                                                  /*@explore*/
  2130. }                                                                                                                      /*@explore*/
  2131.