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