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 Cc = Components.classes; 13 const Ci = Components.interfaces; 14 const Cu = Components.utils; 15 16 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 17 18 const PrefService = Cc["@mozilla.org/preferences-service;1"]; 19 const DebuggerService = Cc["@mozilla.org/js/jsd/debugger-service;1"]; 20 const ConsoleService = Cc["@mozilla.org/consoleservice;1"]; 21 const Timer = Cc["@mozilla.org/timer;1"]; 22 const ObserverServiceFactory = Cc["@mozilla.org/observer-service;1"]; 23 24 const jsdIDebuggerService = Ci.jsdIDebuggerService; 25 const jsdIScript = Ci.jsdIScript; 26 const jsdIStackFrame = Ci.jsdIStackFrame; 27 const jsdICallHook = Ci.jsdICallHook; 28 const jsdIExecutionHook = Ci.jsdIExecutionHook; 29 const jsdIErrorHook = Ci.jsdIErrorHook; 30 const jsdIFilter = Components.interfaces.jsdIFilter; 31 const nsISupports = Ci.nsISupports; 32 const nsIPrefBranch = Ci.nsIPrefBranch; 33 const nsIComponentRegistrar = Ci.nsIComponentRegistrar; 34 const nsIFactory = Ci.nsIFactory; 35 const nsIConsoleService = Ci.nsIConsoleService; 36 const nsITimer = Ci.nsITimer; 37 const nsITimerCallback = Ci.nsITimerCallback; 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 const BP_ERROR = 16; 80 const BP_TRACE = 32; // BP used to initiate traceCalls 81 82 const LEVEL_TOP = 1; 83 const LEVEL_EVAL = 2; 84 const LEVEL_EVENT = 3; 85 86 const COMPONENTS_FILTERS = [ 87 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$"), 88 new RegExp("^(file:/.*/)extensions/firebug@software\\.joehewitt\\.com/modules/.*\\.js$"), 89 new RegExp("^(file:/.*/extensions/)\\w+@mozilla\\.org/components/.*\\.js$"), 90 new RegExp("^(file:/.*/components/)ns[A-Z].*\\.js$"), 91 new RegExp("^(file:/.*/modules/)firebug-[^\\.]*\\.js$"), 92 new RegExp("^(file:/.*/Contents/MacOS/extensions/.*/components/).*\\.js$"), 93 new RegExp("^(file:/.*/modules/).*\\.jsm$"), 94 ]; 95 96 const reDBG = /DBG_(.*)/; 97 const reXUL = /\.xul$|\.xml$/; 98 99 Cu.import("resource://firebug/prefLoader.js"); 100 101 var getPref = PrefLoader.getPref; 102 103 // ********************************************************************************************* // 104 // Globals 105 106 //https://developer.mozilla.org/en/Using_JavaScript_code_modules 107 var EXPORTED_SYMBOLS = ["fbs"]; 108 109 var jsd, prefs; 110 var observerService; 111 112 var contextCount = 0; 113 114 var urlFilters = [ 115 'chrome://', 116 'XStringBundle', 117 'x-jsd:ppbuffer?type=function', // internal script for pretty printing 118 ]; 119 120 var clients = []; 121 var debuggers = []; 122 var netDebuggers = []; 123 var scriptListeners = []; 124 125 var hookFrameCount = 0; 126 127 var haltObject = null; // For reason unknown, fbs.haltDebugger will not work. 128 129 var breakpointCount = 0; 130 131 // These are an optimization I guess, marking whether we are using this feature anywhere. 132 var disabledCount = 0; 133 var monitorCount = 0; 134 var conditionCount = 0; 135 var runningUntil = null; 136 137 var errorBreakpoints = []; 138 139 var profileCount = 0; 140 var profileStart; 141 142 var enabledDebugger = false; 143 var reportNextError = false; 144 var errorInfo = null; 145 146 var timer = Timer.createInstance(nsITimer); 147 var waitingForTimer = false; 148 149 Cu.import("resource://firebug/fbtrace.js"); 150 151 // ********************************************************************************************* // 152 153 function frameId(frame, depth) 154 { 155 if (frame) 156 return frame.script.tag+"@"+frame.line+"^"+depth; 157 else 158 return "noIdForNoframe"; 159 } 160 161 // xxxHonza: duplicated in lib.js, there should be a shared module with this API. 162 function extend(l,r) 163 { 164 var newOb = {}; 165 for (var n in l) 166 newOb[n] = l[n]; 167 for (var n in r) 168 newOb[n] = r[n]; 169 return newOb; 170 } 171 172 // ********************************************************************************************* // 173 // jsdICallHook or jsdIExecutionHook. 174 175 var jsdHandlers = 176 { 177 hooks: [], 178 179 // Stage for activation when 'hook' is called 180 add: function(aHook) 181 { 182 if (!aHook) 183 ERROR("firebug-service.jsdHandlers.add: null hook"); 184 185 this.hooks.push(aHook); 186 187 if (FBTrace.DBG_FBS_STEP) 188 FBTrace.sysout("fbs.Hooks.add; " + aHook.mode + ", active hooks: " + 189 this.hooks.length); 190 }, 191 192 remove: function(aHook) 193 { 194 var i = this.hooks.indexOf(aHook); 195 if (i != -1) 196 { 197 this.hooks.splice(i, 1); 198 199 if (FBTrace.DBG_FBS_STEP) 200 FBTrace.sysout("fbs.Hooks.remove; " + aHook.mode + ", active hooks: " + 201 this.hooks.length); 202 } 203 else 204 { 205 ERROR("firebug-service.Hooks.unhook ERROR, no such hook " + 206 aHook.name, {aHook: aHook, Hooks: this}); 207 } 208 }, 209 210 // activate hooks 211 hook: function(frame) 212 { 213 if (FBTrace.DBG_FBS_STEP) 214 FBTrace.sysout("fbs.start hooks " + this.hooks.length + " hooks active " + 215 frameToString(frame), this); 216 217 for (var i = 0; i < this.hooks.length; i++) 218 { 219 var aHook = this.hooks[i]; 220 221 aHook.hook(frame); 222 223 if ("onFunctionCall" in aHook || "onFunctionReturn" in aHook) 224 fbs.hookFunctions(); 225 226 if ("onInterrupt" in aHook) 227 fbs.hookInterrupts(frame); 228 } 229 }, 230 231 unhook: function(frame) 232 { 233 if (FBTrace.DBG_FBS_STEP) 234 FBTrace.sysout("fbs.stop hooks "+this.hooks.length+" hooks active", this); 235 236 this.checkForUnhookFunctions(frame); 237 this.checkForUnhookInterrupts(frame); 238 }, 239 240 checkForUnhookFunctions: function(frame) 241 { 242 for (var i = 0; i < this.hooks.length; i++) 243 { 244 var aHook = this.hooks[i]; 245 246 aHook.unhook(frame); 247 248 if ("onFunctionCall" in aHook || "onFunctionReturn" in aHook) 249 return; 250 } 251 252 fbs.unhookFunctions(); 253 }, 254 255 checkForUnhookInterrupts: function(frame) 256 { 257 for (var i = 0; i < this.hooks.length; i++) 258 { 259 var aHook = this.hooks[i]; 260 261 if ("onInterrupt" in aHook) 262 return; 263 } 264 265 fbs.unhookInterrupts(); // none found 266 }, 267 268 dispatch: function(methodName, frame, type, rv) 269 { 270 for (var i = 0; i < this.hooks.length; i++) 271 { 272 var aHook = this.hooks[i]; 273 if (methodName in aHook) 274 { 275 if (FBTrace.DBG_FBS_STEP && FBTrace.DBG_DISPATCH) 276 FBTrace.sysout("fbs.jsdHandler.dispatch " + methodName + 277 " to " + aHook + " " + getCallFromType(type) + " frame: " + 278 frameToString(frame), this); 279 280 var rc = aHook[methodName].apply(aHook, [frame, type, rv]); 281 if (typeof(rc) != "undefined" || rc !== RETURN_CONTINUE) 282 return rc; 283 } 284 } 285 286 return RETURN_CONTINUE; 287 } 288 }; 289 290 // ********************************************************************************************* // 291 // Break on Next 292 293 function BreakOnNextCall(debuggr, context) 294 { 295 this.debuggr = debuggr; 296 this.context = context; 297 } 298 299 BreakOnNextCall.prototype = 300 { 301 mode: "BON", 302 303 hook: function(frame) 304 { 305 }, 306 307 unhook: function(frame) 308 { 309 }, 310 311 // the frame will be running the calling script 312 hit: function(frame, type) 313 { 314 fbs.cancelBreakOnNextCall(this.debuggr, this.context); 315 316 var rv = {}; 317 return fbs.routeBreakToDebuggr(frame, type, rv, this.debuggr); 318 }, 319 320 onFunctionCall: function(frame, type) 321 { 322 if (!this.context || !this.context.sourceFileByTag) 323 return ERROR("onFunctionCall ERROR invalid context "); 324 325 var lucky = this.context.getSourceFileByTag(frame.script.tag); 326 if (!lucky) // then function running the frame is not in this context 327 { 328 // then we could be running an outer function from a new compilation unit 329 if (!frame.callingFrame) 330 { 331 var val = {}; 332 333 // then the function could have just been added to the context 334 if (fbs.isTopLevelScript(frame, type, val)) 335 lucky = this.context.getSourceFileByTag(frame.script.tag); 336 } 337 } 338 339 if (lucky) // then we hit in a function in our context 340 { 341 if (FBTrace.DBG_FBS_STEP) 342 FBTrace.sysout("fbs.breakOnNextTopFunction hits at "+getCallFromType(type)+" at "+ 343 frame.script.fileName+" tag:"+(lucky?"LUCKY WINNER":frame.script.tag), 344 framesToString(frame)); 345 346 return this.hit(frame, type); 347 } 348 349 // else maybe we hit on a event unrelated to our context 350 }, 351 }; 352 353 // Stack segments 354 // When frame.callingFrame is null then we are starting a new stack segment 355 // FrameId track call stack 356 // segment0.fnc1 !callingFrame !this.callingFrameId 357 // segment0.fnc2 this.callingFrameId == segment0.fnc1 358 // ... 359 // segment1.fnc44 !callingFrame this.callingFrameId == fnc2.segment0 360 // segment1.fnc45 361 // 362 363 // ********************************************************************************************* // 364 // Stepper: Step Out Implementation 365 366 /** 367 * @class This object implements "step out" debugger feature. In other words run until 368 * the current function returns, then stop in the caller. 369 */ 370 function OutStepper(debuggr, context) 371 { 372 this.debuggr = debuggr; 373 this.context = context; 374 375 if (!this.debuggr) 376 ERROR("firebug-service.OutStepper no debuggr"); 377 } 378 379 OutStepper.prototype = 380 /** @lends OutStepper */ 381 { 382 mode: "STEP_OUT", 383 384 getCallingFrameId: function(frame) 385 { 386 if (frame.callingFrame) 387 return frameId(frame.callingFrame, this.depth); 388 389 var debuggr = fbs.reFindDebugger(frame, this.debuggr); 390 if (debuggr && debuggr.breakContext) 391 return debuggr.breakContext.getName(); // TODO segments 392 }, 393 394 hook: function(frame) 395 { 396 this.depth = 0; 397 398 if (frame) 399 { 400 this.startFrameId = frameId(frame, 0); 401 this.callingFrameId = this.getCallingFrameId(frame); 402 403 if (!this.callingFrameId) 404 ERROR("OutStepper.hook cannot find callingFrame ", this); 405 406 this.startFrameTag = frame.script.tag; 407 } 408 else 409 { 410 ERROR("OutStepper.hook no frame "); 411 } 412 413 if (FBTrace.DBG_FBS_STEP) 414 FBTrace.sysout("fbs." + this.mode+" hook with frame "+frameToString(frame)+ 415 " with callingFrameId "+this.callingFrameId, this); 416 417 return true; 418 }, 419 420 // the frame will be running the called script 421 onFunctionCall: function stepFunctionCall(frame, type) 422 { 423 var callingFrameId = this.getCallingFrameId(frame); 424 425 if (this.callingFrameId === callingFrameId) // then it is our caller 426 { 427 this.depth++; 428 this.callingFrameId = callingFrameId; // push new id for stepFunctionReturn 429 430 if (FBTrace.DBG_FBS_STEP) 431 FBTrace.sysout("fbs." + this.mode+" stepFunctionCall new depth "+this.depth+ 432 " new callingFrameId "+this.callingFrameId); 433 } 434 435 // else someone else, ignore it 436 }, 437 438 // the frame will be running the called script 439 onFunctionReturn: function stepFunctionReturn(frame, type) 440 { 441 var callingFrameId = this.getCallingFrameId(frame); 442 if (this.callingFrameId === callingFrameId) // then it is our caller 443 { 444 if (this.depth) // but we are not back to our caller 445 { 446 this.depth--; 447 this.callingFrameId = callingFrameId; // recursion 448 return; 449 } 450 else // then we are back to the frame we started on 451 { 452 if (frame.callingFrame) 453 return this.hit(frame.callingFrame, type); 454 455 if (FBTrace.DBG_FBS_STEP) 456 FBTrace.sysout("fbs.OutStepper.onFunctionReturn no calling frame " + 457 frameToString(frame) + ", " + getCallFromType(type), this); 458 459 jsdHandlers.unhook(frame); // we are done here 460 jsdHandlers.remove(this); 461 return; 462 } 463 } 464 465 // Then we are returning with out ever calling stepFunctionCall, 466 // but on a frame we care about. 467 if (!this.callingFrameId && callingFrameId) 468 { 469 // Then are returning from the frame we care about. 470 if (frame.script.tag === this.startFrameTag) 471 { 472 if (frame.callingFrame) 473 return this.hit(frame.callingFrame, type); 474 else 475 ERROR("Should be top level just exit", this); 476 } 477 478 ERROR("Returning from a frame we care about but not one we know " + 479 frameToString(frame), this); 480 } 481 482 // else it's is not a frame we care about 483 if (FBTrace.DBG_FBS_STEP) 484 FBTrace.sysout("fbs." + this.mode + ".onFunctionReturn callingFrameId " + 485 callingFrameId + " called frame " + frameToString(frame), this) 486 }, 487 488 unhook: function(frame) 489 { 490 }, 491 492 hit: function(frame, type, rv) 493 { 494 if (FBTrace.DBG_FBS_STEP) 495 FBTrace.sysout("fbs." + this.mode + " hit " + getCallFromType(type) + " at " + 496 frameToString(frame), this); 497 498 var debuggr = fbs.reFindDebugger(frame, this.debuggr); 499 if (debuggr) 500 { 501 jsdHandlers.unhook(frame); 502 jsdHandlers.remove(this); 503 504 rv = {}; 505 506 return fbs.breakIntoDebugger(debuggr, frame, type); 507 } 508 509 return ERROR("Hit but debuggr did not match "+this.debuggr.debuggerName+" in frame "+ 510 frameToString(frame), this); 511 }, 512 513 toString: function() 514 { 515 if (!this.context.getName) 516 FBTrace.sysout("fbs.this.context.getName ", this.context); 517 518 return this.mode + " for "+this.context.getName(); 519 }, 520 }; 521 522 // ********************************************************************************************* // 523 // Stepper: Step Over Implementation 524 525 /** 526 * @class This oject implements "step over". I's like {@link OutStepper}, but run a single 527 * line in this function. 528 */ 529 function LineStepper(debuggr, context) 530 { 531 this.context = context; 532 this.debuggr = debuggr; 533 } 534 535 LineStepper.prototype = extend(OutStepper.prototype, 536 /** @lends LineStepper */ 537 { 538 mode: "STEP_OVER", 539 540 hook: function hookLineStepper(frame) 541 { 542 OutStepper.prototype.hook.apply(this, arguments); // hook functions 543 544 this.lineFrameId = frameId(frame, this.depth); 545 this.stepFrameTag = frame.script.tag; 546 547 if (FBTrace.DBG_FBS_STEP) 548 FBTrace.sysout("fbs." + this.mode + ".hook " + frameToString(frame) + 549 " with lineFrameId " + this.lineFrameId, this); 550 }, 551 552 unhook: function unhookLineStepper(frame) 553 { 554 if (FBTrace.DBG_FBS_STEP) 555 FBTrace.sysout("fbs." + this.mode + ".hook; unhook " + frameToString(frame)); 556 }, 557 558 // jsdIExecutionHook, onExecute 559 onInterrupt: function stepLine(frame, type, rv) 560 { 561 if (this.stepFrameTag !== frame.script.tag) // then we stepped into another function 562 { 563 // We'd have much bettter performance if we set a new OutStepper here then remove 564 // interrupt hook until it hits. 565 return RETURN_CONTINUE; 566 } 567 568 // Sometimes the same line will have multiple interrupts, so check 569 // a unique id for the line and don't break until it changes 570 var frameLineId = frameId(frame, this.depth); 571 572 if (FBTrace.DBG_FBS_STEP) 573 FBTrace.sysout("fbs." + this.mode + " interruptHook pc:" + frame.pc + 574 " frameLineId: " + frameLineId + " vs " + this.lineFrameId + " running " + 575 frame.script.tag + " of " + frame.script.fileName + 576 " at " + frame.line + "." + frame.pc, this); 577 578 if (frameLineId != this.lineFrameId) 579 return this.hit(frame, type, rv); 580 else 581 return RETURN_CONTINUE; 582 }, 583 584 toString: function() 585 { 586 if (!this.context.getName) 587 FBTrace.sysout("fbs.this.context.getName ", this.context); 588 589 return this.mode + " for "+this.context.getName(); 590 }, 591 }); 592 593 // ********************************************************************************************* // 594 // Stepper: Step In Implementation 595 596 /** 597 * @class This oject implements "step in". I's like {@link OutStepper}, but if the line 598 * calls a function, stop on its first line 599 */ 600 function IntoStepper(debuggr, context) 601 { 602 this.context = context; 603 this.debuggr = debuggr; 604 } 605 606 IntoStepper.prototype = extend(LineStepper.prototype, 607 /** @lends IntoStepper */ 608 { 609 mode: "STEP_INTO", 610 611 hook: function(frame) 612 { 613 LineStepper.prototype.hook.apply(this, arguments); // hook functions and interrupts 614 }, 615 616 // the frame will be running the called script 617 onFunctionCall: function intoFunctionCall(frame, type) 618 { 619 var callingFrame = frame.callingFrame; 620 if (callingFrame) 621 { 622 // Skip functions running in the frame that is not in this context (issue 3077) 623 var lucky = this.context.getSourceFileByTag(frame.script.tag); 624 if (!lucky) 625 return; 626 627 // then we stepped into from our caller 628 if (this.stepFrameTag === callingFrame.script.tag) 629 return this.hit(frame, type); 630 631 // else someone else, ignore it 632 if (FBTrace.DBG_FBS_STEP) 633 FBTrace.sysout("fbs." + this.mode + ".intoFunctionCall no match " + 634 this.stepFrameTag + " vs " + callingFrame.script.tag, this); 635 } 636 637 // else this would be a top level call, do we want to check for 638 // another event from this context? 639 }, 640 }); 641 642 // ********************************************************************************************* // 643 // Function Stepper/Tracer 644 645 /** 646 * Tracer for learning about function stepping, not part of firebug 647 */ 648 function LogFunctionStepper() 649 { 650 //xxxjjb: not defined this.initialize(); 651 } 652 653 LogFunctionStepper.prototype = 654 { 655 hook: function() 656 { 657 fbs.inDebuggerSetupStack = true; 658 }, 659 660 stop: function() 661 { 662 delete fbs.stackDescription; 663 }, 664 665 // the frame will be running the called script 666 onFunctionCall: function logFunctionCall(frame, type) 667 { 668 if (fbs.inDebuggerSetupStack) // then we are still in the debugger set up code 669 return; 670 671 if (!fbs.stackDescription) 672 fbs.stackDescription = { oldestTag: frame.script.tag, depth: 1, entries: [] }; 673 else 674 fbs.stackDescription.depth++; 675 676 this.logFunction(frame, type); 677 }, 678 679 // the frame will be running the called script 680 onFunctionReturn: function logFunctionReturn(frame, type) 681 { 682 if (fbs.inDebuggerSetupStack) // then we are still in the debugger set up code 683 { 684 if (!frame.callingFrame) // then done with setup 685 delete fbs.inDebuggerSetupStack; 686 687 return; 688 } 689 690 if (!fbs.stackDescription) 691 fbs.stackDescription = { oldestTag: "Return first!", depth: 0, entries: [] }; 692 fbs.stackDescription.depth--; 693 694 this.logFunction(frame, type); 695 696 if (!frame.callingFrame) 697 { 698 var diff = (fbs.stackDescription.oldestTag !== frame.script.tag); 699 700 if (FBTrace.DBG_FBS_STEP) 701 FBTrace.sysout("fbs.Stack ends at depth "+fbs.stackDescription.depth + 702 (diff ? " NO Match on tag " : " tags match"), fbs.stackDescription.entries); 703 704 fbs.stackDescription.entries = []; 705 } 706 707 if (!fbs.stackDescription.depth) 708 delete fbs.stackDescription; 709 }, 710 711 logFunction: function(frame, type) 712 { 713 var typeName = getCallFromType(type); 714 var actualFrames = countFrames(frame); 715 fbs.stackDescription.entries.push(""+fbs.stackDescription.depth+ 716 ": "+typeName + 717 " (frameCount: "+actualFrames+") " + 718 " oldestTag "+fbs.stackDescription.oldestTag+ 719 " running "+frame.script.tag+" of "+frame.script.fileName+" at "+ 720 frame.line+"."+frame.pc); 721 }, 722 }; 723 724 // ********************************************************************************************* // 725 // Firebug Service 726 727 var fbs = 728 { 729 initialize: function() 730 { 731 if (FBTrace.DBG_FBS_ERRORS) 732 FBTrace.sysout("fbs.FirebugService Starting"); 733 734 fbs = this; 735 736 this.wrappedJSObject = this; // XXXjjb remove this and the one in debugger 737 this.timeStamp = new Date(); /* explore */ 738 739 Components.utils.import("resource://firebug/debuggerHalter.js"); 740 fbs.debuggerHalter = debuggerHalter; // ref to a function in a file that passes the jsdIFilter 741 742 fbs.restoreBreakpoints(); 743 744 this.onDebugRequests = 0; // the number of times we called onError but did not call onDebug 745 746 747 if (FBTrace.DBG_FBS_ERRORS) 748 this.osOut("FirebugService Starting, FBTrace should be up\n"); 749 750 this.profiling = false; 751 752 prefs = PrefService.getService(nsIPrefBranch); 753 fbs.prefDomain = "extensions.firebug"; 754 prefs.addObserver(fbs.prefDomain, fbs, false); 755 756 observerService = ObserverServiceFactory.getService(Ci.nsIObserverService); 757 observerService.addObserver(QuitApplicationGrantedObserver, "quit-application-granted", false); 758 observerService.addObserver(QuitApplicationRequestedObserver, "quit-application-requested", false); 759 observerService.addObserver(QuitApplicationObserver, "quit-application", false); 760 761 this.scriptsFilter = "all"; 762 this.alwayFilterURLsStarting = ["chrome://chromebug", "x-jsd:ppbuffer"]; // TODO allow override 763 this.onEvalScriptCreated.kind = "eval"; 764 this.onTopLevelScriptCreated.kind = "top-level"; 765 this.onEventScriptCreated.kind = "event"; 766 this.onXULScriptCreated.kind = "xul"; 767 this.pendingXULScripts = []; 768 769 this.onXScriptCreatedByTag = {}; // fbs functions by script tag 770 this.nestedScriptStack = []; // scripts contained in leveledScript that have not been drained 771 772 if (FBTrace.DBG_FBS_ERRORS) 773 FBTrace.sysout("fbs.FirebugService Initialized"); 774 }, 775 776 osOut: function(str) 777 { 778 if (!this.outChannel) 779 { 780 try 781 { 782 var appShellService = Components.classes["@mozilla.org/appshell/appShellService;1"]. 783 getService(Components.interfaces.nsIAppShellService); 784 this.hiddenWindow = appShellService.hiddenDOMWindow; 785 this.outChannel = "hidden"; 786 } 787 catch(exc) 788 { 789 consoleService = Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService); 790 consoleService.logStringMessage("Using consoleService because " + 791 "nsIAppShellService.hiddenDOMWindow not available "+exc); 792 793 this.outChannel = "service"; 794 } 795 } 796 797 if (this.outChannel === "hidden") // apparently can't call via JS function 798 this.hiddenWindow.dump(str); 799 else 800 consoleService.logStringMessage(str); 801 }, 802 803 shutdown: function() // call disableDebugger first 804 { 805 timer = null; 806 807 try 808 { 809 prefs.removeObserver(fbs.prefDomain, fbs, false); 810 } 811 catch (exc) 812 { 813 FBTrace.sysout("fbs.prefs.removeObserver ERROR "+exc, exc); 814 } 815 816 try 817 { 818 observerService.removeObserver(QuitApplicationGrantedObserver, "quit-application-granted"); 819 observerService.removeObserver(QuitApplicationRequestedObserver, "quit-application-requested"); 820 observerService.removeObserver(QuitApplicationObserver, "quit-application"); 821 } 822 catch (exc) 823 { 824 FBTrace.sysout("fbs.quit-application-observers removeObserver ERROR "+exc, exc); 825 } 826 827 if (!jsd) 828 return; 829 830 try 831 { 832 do 833 { 834 var depth = jsd.exitNestedEventLoop(); 835 } 836 while(depth > 0); 837 } 838 catch (exc) 839 { 840 // Seems to be the normal path... 841 // FBTrace.sysout("fbs.FirebugService, attempt to exitNestedEventLoop ERROR "+exc); 842 } 843 844 // make sure to unregister all the hooks 845 var hookNames = ["error", "script", "breakpoint", "debugger", "debug", "interrupt", 846 "throw", "topLevel", "function", "debug"]; 847 for each (var hook in hookNames) 848 { 849 try { 850 jsd[hook + "Hook"] = null; 851 } 852 catch (exc) 853 { 854 FBTrace.sysout("fbs.quit-application-observers removeObserver ERROR "+exc, exc); 855 } 856 } 857 jsd = null; 858 }, 859 860 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 861 // nsISupports 862 863 QueryInterface: function(iid) 864 { 865 if (!iid.equals(nsISupports)) 866 throw NS_ERROR_NO_INTERFACE; 867 868 return this; 869 }, 870 871 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 872 // nsIObserver 873 874 observe: function(subject, topic, data) 875 { 876 if(topic != "nsPref:changed") return; 877 fbs.obeyPrefs(); 878 }, 879 880 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 881 882 registerClient: function(client) // clients are essentially XUL windows 883 { 884 if (!client) 885 throw new Error("firebug-service cannot register client: "+client); 886 887 clients.push(client); 888 return clients.length; 889 }, 890 891 unregisterClient: function(client) 892 { 893 for (var i = 0; i < clients.length; ++i) 894 { 895 if (clients[i] == client) 896 { 897 clients.splice(i, 1); 898 break; 899 } 900 } 901 }, 902 903 // first one in will be last one called. Returns state enabledDebugger 904 registerDebugger: function(debuggrWrapper) 905 { 906 var debuggr = debuggrWrapper.wrappedJSObject; 907 908 if (debuggr) 909 { 910 var anyDebuggers = (debuggers.length === 1); 911 912 var i = debuggers.indexOf(debuggr); 913 if (i === -1) 914 { 915 debuggers.push(debuggr); 916 if (!anyDebuggers) 917 this.enableDebugger(); 918 } 919 920 if (FBTrace.DBG_FBS_FINDDEBUGGER || FBTrace.DBG_ACTIVATION) 921 FBTrace.sysout("fbs.registerDebugger have "+debuggers.length+ 922 " after reg debuggr.debuggerName: "+debuggr.debuggerName+" we are "+ 923 (enabledDebugger?"enabled":"not enabled")+" " + "On:"+(jsd?jsd.isOn:"no jsd")+ 924 " jsd.pauseDepth:"+(jsd?jsd.pauseDepth:"off")); 925 } 926 else 927 { 928 var err = new Error("firebug-service debuggers must have wrappedJSObject "); 929 err.debuggrWrapper = debuggrWrapper; 930 throw err; 931 } 932 933 try 934 { 935 if (debuggr.suspendActivity) 936 netDebuggers.push(debuggr); 937 } 938 catch(exc) 939 { 940 } 941 942 try 943 { 944 if (debuggr.onScriptCreated) // TODO xxxjjb: I don't know who uses this, remove it? 945 scriptListeners.push(debuggr); 946 } 947 catch(exc) 948 { 949 } 950 951 return debuggers.length; // 1.3.1 return to allow Debugger to check progress 952 }, 953 954 unregisterDebugger: function(debuggrWrapper) 955 { 956 var debuggr = debuggrWrapper.wrappedJSObject; 957 958 for (var i = 0; i < debuggers.length; ++i) 959 { 960 if (debuggers[i] == debuggr) 961 { 962 debuggers.splice(i, 1); 963 break; 964 } 965 } 966 967 for (var i = 0; i < netDebuggers.length; ++i) 968 { 969 if (netDebuggers[i] == debuggr) 970 { 971 netDebuggers.splice(i, 1); 972 break; 973 } 974 } 975 976 for (var i = 0; i < scriptListeners.length; ++i) 977 { 978 if (scriptListeners[i] == debuggr) 979 { 980 scriptListeners.splice(i, 1); 981 break; 982 } 983 } 984 985 if (debuggers.length == 0) 986 this.disableDebugger(); 987 988 if (FBTrace.DBG_FBS_FINDDEBUGGER || FBTrace.DBG_ACTIVATION) 989 FBTrace.sysout("fbs.unregisterDebugger have "+debuggers.length+ 990 " after unreg debuggr.debuggerName: "+debuggr.debuggerName+" we are "+ 991 (enabledDebugger?"enabled":"not enabled")+" jsd.isOn:"+(jsd?jsd.isOn:"no jsd")); 992 993 return debuggers.length; 994 }, 995 996 lockDebugger: function() 997 { 998 if (this.locked) 999 return; 1000 1001 this.locked = true; 1002 1003 dispatch(debuggers, "onLock", [true]); 1004 }, 1005 1006 unlockDebugger: function() 1007 { 1008 if (!this.locked) 1009 return; 1010 1011 this.locked = false; 1012 1013 dispatch(debuggers, "onLock", [false]); 1014 }, 1015 1016 getDebuggerByName: function(name) 1017 { 1018 if (!name) 1019 return; 1020 1021 for(var i = 0; i < debuggers.length; i++) 1022 if (debuggers[i].debuggerName === name) 1023 return debuggers[i]; 1024 }, 1025 1026 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 1027 1028 forceGarbageCollection: function() 1029 { 1030 jsd.GC(); // Force the engine to perform garbage collection. 1031 }, 1032 1033 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 1034 1035 enterNestedEventLoop: function(callback) 1036 { 1037 try 1038 { 1039 dispatch(netDebuggers, "suspendActivity"); 1040 this.activitySuspended = true; 1041 1042 fbs.nestedEventLoopDepth = jsd.enterNestedEventLoop( 1043 { 1044 onNest: function() 1045 { 1046 dispatch(netDebuggers, "resumeActivity"); 1047 callback.onNest(); 1048 } 1049 }); 1050 } 1051 catch(exc) 1052 { 1053 FBTrace.sysout("fbs.enterNestedEventLoop ERROR "+exc, exc); 1054 } 1055 finally 1056 { 1057 dispatch(netDebuggers, "resumeActivity"); 1058 this.activitySuspended = false; 1059 } 1060 1061 return fbs.nestedEventLoopDepth; 1062 }, 1063 1064 exitNestedEventLoop: function() 1065 { 1066 try 1067 { 1068 dispatch(netDebuggers, "suspendActivity"); 1069 return jsd.exitNestedEventLoop(); 1070 } 1071 catch (exc) 1072 { 1073 if (FBTrace.DBG_FBS_ERRORS) 1074 FBTrace.sysout("fbs: jsd.exitNestedEventLoop ERROR " + exc, exc); 1075 } 1076 }, 1077 1078 /** 1079 * We are running JS code for Firebug, but we want to break into the debugger with 1080 * a stack frame. 1081 * 1082 * @param debuggr Debugger object asking for break 1083 * @param fnOfFrame, function(frame) to run on break 1084 */ 1085 halt: function(debuggr, fnOfFrame) 1086 { 1087 if (!debuggr || !fnOfFrame) 1088 { 1089 if (FBTrace.DBG_FBS_ERRORS) 1090 FBTrace.sysout("fbs.halt call ERROR bad arguments", arguments); 1091 1092 return null; 1093 } 1094 1095 if (FBTrace.DBG_FBS_BP) 1096 FBTrace.sysout('fbs.halt jsd.isOn:'+jsd.isOn+' jsd.pauseDepth:'+jsd.pauseDepth+ 1097 " fbs.isChromeBlocked "+fbs.isChromeBlocked+" jsd.debuggerHook: "+ 1098 jsd.debuggerHook, jsd.debuggerHook); 1099 1100 // store for onDebugger 1101 haltObject = {haltDebugger: debuggr, haltCallBack: fnOfFrame}; 1102 1103 // call onDebugger via hook 1104 fbs.debuggerHalter(); 1105 return fbs.haltReturnValue; 1106 }, 1107 1108 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 1109 // Break on Next 1110 1111 // deprecated API 1112 // xxxjjb: BON should be entirely implemented by breakOnNextCall object, right? 1113 // xxxhonza: Debugger.suspend should be removed and replaced by breakOnNextCall 1114 1115 suspend: function(debuggr, context) 1116 { 1117 fbs.breakOnNextCall(debuggr, context); 1118 }, 1119 1120 breakOnNextCall: function(debuggr, context) 1121 { 1122 dispatch(debuggers, "onBreakingNext", [debuggr, context]); 1123 1124 if (context.breakOnNextHook) 1125 ERROR("firebug-service.breakOnNextCall already active ", context); 1126 1127 context.breakOnNextHook = new BreakOnNextCall(debuggr, context); 1128 1129 jsdHandlers.add(context.breakOnNextHook); 1130 jsdHandlers.hook(); // no frame arg 1131 }, 1132 1133 cancelBreakOnNextCall: function(debuggr, context) 1134 { 1135 jsdHandlers.unhook(/* no frame argument */); 1136 jsdHandlers.remove(context.breakOnNextHook); 1137 delete context.breakOnNextHook; 1138 }, 1139 1140 runUntil: function(sourceFile, lineNo, startFrame, debuggr) 1141 { 1142 // TODO per context 1143 runningUntil = this.addBreakpoint(BP_UNTIL, sourceFile, lineNo, null, debuggr); 1144 }, 1145 1146 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 1147 1148 setBreakpoint: function(sourceFile, lineNo, props, debuggr) 1149 { 1150 var bp = this.addBreakpoint(BP_NORMAL, sourceFile, lineNo, props, debuggr); 1151 if (bp) 1152 { 1153 dispatch(debuggers, "onToggleBreakpoint", [sourceFile.href, lineNo, true, bp]); 1154 fbs.saveBreakpoints(sourceFile.href); // after every call to onToggleBreakpoint 1155 return true; 1156 } 1157 return false; 1158 }, 1159 1160 clearBreakpoint: function(url, lineNo) 1161 { 1162 var bp = this.removeBreakpoint(BP_NORMAL, url, lineNo); 1163 if (bp) 1164 { 1165 dispatch(debuggers, "onToggleBreakpoint", [url, lineNo, false, bp]); 1166 fbs.saveBreakpoints(url); 1167 } 1168 return bp; 1169 }, 1170 1171 enableBreakpoint: function(url, lineNo) 1172 { 1173 var bp = this.findBreakpoint(url, lineNo); 1174 if (bp && bp.type & BP_NORMAL) 1175 { 1176 bp.disabled &= ~BP_NORMAL; 1177 dispatch(debuggers, "onToggleBreakpoint", [url, lineNo, true, bp]); 1178 fbs.saveBreakpoints(url); 1179 --disabledCount; 1180 } 1181 else 1182 { 1183 if (FBTrace.DBG_FBS_BP) 1184 FBTrace.sysout("fbs.enableBreakpoint no find for "+lineNo+"@"+url); 1185 } 1186 }, 1187 1188 disableBreakpoint: function(url, lineNo) 1189 { 1190 var bp = this.findBreakpoint(url, lineNo); 1191 if (bp && bp.type & BP_NORMAL) 1192 { 1193 bp.disabled |= BP_NORMAL; 1194 ++disabledCount; 1195 dispatch(debuggers, "onToggleBreakpoint", [url, lineNo, true, bp]); 1196 fbs.saveBreakpoints(url); 1197 } 1198 else 1199 { 1200 if (FBTrace.DBG_FBS_BP) 1201 FBTrace.sysout("fbs.disableBreakpoint no find for "+lineNo+"@"+url); 1202 } 1203 }, 1204 1205 isBreakpointDisabled: function(url, lineNo) 1206 { 1207 var bp = this.findBreakpoint(url, lineNo); 1208 if (bp && bp.type & BP_NORMAL) 1209 return bp.disabled & BP_NORMAL; 1210 else 1211 return false; 1212 }, 1213 1214 setBreakpointCondition: function(sourceFile, lineNo, condition, debuggr) 1215 { 1216 var bp = this.findBreakpoint(sourceFile.href, lineNo); 1217 if (!bp) 1218 bp = this.addBreakpoint(BP_NORMAL, sourceFile, lineNo, null, debuggr); 1219 1220 if (!bp) 1221 return; 1222 1223 if (bp.hitCount <= 0 ) 1224 { 1225 if (bp.condition && !condition) 1226 --conditionCount; 1227 else if (condition && !bp.condition) 1228 ++conditionCount; 1229 } 1230 1231 bp.condition = condition; 1232 1233 dispatch(debuggers, "onToggleBreakpoint", [sourceFile.href, lineNo, true, bp]); 1234 1235 fbs.saveBreakpoints(sourceFile.href); 1236 return bp; 1237 }, 1238 1239 getBreakpointCondition: function(url, lineNo) 1240 { 1241 var bp = this.findBreakpoint(url, lineNo); 1242 return bp ? bp.condition : ""; 1243 }, 1244 1245 clearAllBreakpoints: function(sourceFiles) 1246 { 1247 for (var i=0; i<sourceFiles.length; ++i) 1248 { 1249 var url = sourceFiles[i].href; 1250 if (!url) 1251 continue; 1252 1253 var urlBreakpointsTemp = fbs.getBreakpoints(url); 1254 1255 if (FBTrace.DBG_FBS_BP) 1256 { 1257 FBTrace.sysout("fbs.clearAllBreakpoints " + url + " urlBreakpoints: " + 1258 (urlBreakpointsTemp ? urlBreakpointsTemp.length : "null")); 1259 } 1260 1261 if (!urlBreakpointsTemp) 1262 continue; 1263 1264 // Clone before iteration the array is modified within the loop. 1265 var urlBreakpoints = []; 1266 urlBreakpoints.push.apply(urlBreakpoints, urlBreakpointsTemp); 1267 1268 for (var ibp=0; ibp<urlBreakpoints.length; ibp++) 1269 { 1270 var bp = urlBreakpoints[ibp]; 1271 this.clearBreakpoint(url, bp.lineNo); 1272 } 1273 } 1274 }, 1275 1276 // url is sourceFile.href, not jsd script.fileName 1277 enumerateBreakpoints: function(url, cb) 1278 { 1279 if (url) 1280 { 1281 var urlBreakpointsTemp = fbs.getBreakpoints(url); 1282 if (urlBreakpointsTemp) 1283 { 1284 // Clone before iteration (the array can be modified in the callback). 1285 var urlBreakpoints = []; 1286 urlBreakpoints.push.apply(urlBreakpoints, urlBreakpointsTemp); 1287 1288 for (var i = 0; i < urlBreakpoints.length; ++i) 1289 { 1290 var bp = urlBreakpoints[i]; 1291 if (bp.type & BP_NORMAL && !(bp.type & BP_ERROR) ) 1292 { 1293 if (bp.scriptsWithBreakpoint && bp.scriptsWithBreakpoint.length > 0) 1294 { 1295 var rc = cb.call.apply(bp, [url, bp.lineNo, bp, bp.scriptsWithBreakpoint]); 1296 if (rc) 1297 return [bp]; 1298 } 1299 else 1300 { 1301 var rc = cb.call.apply(bp, [url, bp.lineNo, bp]); 1302 if (rc) 1303 return [bp]; 1304 } 1305 } 1306 } 1307 } 1308 } 1309 else 1310 { 1311 var bps = []; 1312 var urls = fbs.getBreakpointURLs(); 1313 for (var i = 0; i < urls.length; i++) 1314 bps.push(this.enumerateBreakpoints(urls[i], cb)); 1315 1316 return bps; 1317 } 1318 }, 1319 1320 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 1321 // error breakpoints are a way of selecting breakpoint from the Console 1322 1323 setErrorBreakpoint: function(sourceFile, lineNo, debuggr) 1324 { 1325 var url = sourceFile.href; 1326 var index = this.findErrorBreakpoint(url, lineNo); 1327 if (index == -1) 1328 { 1329 try 1330 { 1331 var bp = this.addBreakpoint(BP_NORMAL | BP_ERROR, sourceFile, lineNo, null, debuggr); 1332 if (bp) 1333 { 1334 errorBreakpoints.push({href: url, lineNo: lineNo, type: BP_ERROR }); 1335 dispatch(debuggers, "onToggleErrorBreakpoint", [url, lineNo, true, debuggr]); 1336 fbs.saveBreakpoints(sourceFile.href); // after every call to onToggleBreakpoint 1337 } 1338 } 1339 catch(exc) 1340 { 1341 FBTrace.sysout("fbs.setErrorBreakpoint ERROR "+exc, exc); 1342 } 1343 } 1344 }, 1345 1346 clearErrorBreakpoint: function(url, lineNo, debuggr) 1347 { 1348 var index = this.findErrorBreakpoint(url, lineNo); 1349 if (index != -1) 1350 { 1351 var bp = this.removeBreakpoint(BP_NORMAL | BP_ERROR, url, lineNo); 1352 1353 errorBreakpoints.splice(index, 1); 1354 dispatch(debuggers, "onToggleErrorBreakpoint", [url, lineNo, false, debuggr]); 1355 1356 // after every call to onToggleBreakpoint 1357 fbs.saveBreakpoints(url); 1358 } 1359 }, 1360 1361 clearErrorBreakpoints: function(sourceFiles, debuggr) 1362 { 1363 for (var i=0; i<sourceFiles.length; ++i) 1364 { 1365 var url = sourceFiles[i].href; 1366 if (!url) 1367 continue; 1368 1369 fbs.enumerateErrorBreakpoints(url, 1370 { 1371 call: function(url, lineNo) 1372 { 1373 fbs.clearErrorBreakpoint(url, lineNo, debuggr); 1374 } 1375 }); 1376 } 1377 }, 1378 1379 hasErrorBreakpoint: function(url, lineNo) 1380 { 1381 return this.findErrorBreakpoint(url, lineNo) != -1; 1382 }, 1383 1384 enumerateErrorBreakpoints: function(url, cb) 1385 { 1386 // Clone breakpoints array before iteration. The callback could modify it. 1387 var copyBreakpoints = []; 1388 copyBreakpoints.push.apply(copyBreakpoints, errorBreakpoints); 1389 1390 if (url) 1391 { 1392 for (var i=0; i<copyBreakpoints.length; ++i) 1393 { 1394 var bp = copyBreakpoints[i]; 1395 if (bp.href == url) 1396 cb.call(bp.href, bp.lineNo, bp); 1397 } 1398 } 1399 else 1400 { 1401 for (var i=0; i<copyBreakpoints.length; ++i) 1402 { 1403 var bp = copyBreakpoints[i]; 1404 cb.call(bp.href, bp.lineNo, bp); 1405 } 1406 } 1407 }, 1408 1409 findErrorBreakpoint: function(url, lineNo) 1410 { 1411 for (var i = 0; i < errorBreakpoints.length; ++i) 1412 { 1413 var bp = errorBreakpoints[i]; 1414 if (bp.lineNo === lineNo && bp.href == url) 1415 return i; 1416 } 1417 1418 return -1; 1419 }, 1420 1421 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 1422 // JSD Handlers 1423 1424 addHandler: function(handler) 1425 { 1426 jsdHandlers.add(handler); 1427 jsdHandlers.hook(); 1428 }, 1429 1430 removeHandler: function(handler) 1431 { 1432 // First remove the hook and then call unhook. The 'unhook' function 1433 // checks for registered handlers and removes various JSD hooks only, if there 1434 // are no corresponding handlers. 1435 jsdHandlers.remove(handler); 1436 jsdHandlers.unhook(); 1437 }, 1438 1439 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 1440 1441 traceAll: function(urls, debuggr) 1442 { 1443 this.hookCalls(debuggr.onFunctionCall, false); // call on all passed urls 1444 }, 1445 1446 untraceAll: function(debuggr) 1447 { 1448 this.unhookFunctions(); // undo hookCalls() 1449 }, 1450 1451 traceCalls: function(sourceFile, lineNo, debuggr) 1452 { 1453 // set a breakpoint on the starting point 1454 var bp = this.monitor(sourceFile, lineNo, debuggr); 1455 bp.type |= BP_TRACE; 1456 1457 // when we hit the bp in onBreakPoint we being tracing. 1458 }, 1459 1460 untraceCalls: function(sourceFile, lineNo, debuggr) 1461 { 1462 var bp = lineNo != -1 ? this.findBreakpoint(url, lineNo) : null; 1463 if (bp) 1464 { 1465 bp.type &= ~BP_TRACE; 1466 this.unmonitor(sourceFile.href, lineNo); 1467 } 1468 }, 1469 1470 monitor: function(sourceFile, lineNo, debuggr) 1471 { 1472 if (lineNo == -1) 1473 return null; 1474 1475 var bp = this.addBreakpoint(BP_MONITOR, sourceFile, lineNo, null, debuggr); 1476 if (bp) 1477 { 1478 ++monitorCount; 1479 dispatch(debuggers, "onToggleMonitor", [sourceFile.href, lineNo, true]); 1480 } 1481 1482 return bp; 1483 }, 1484 1485 unmonitor: function(href, lineNo) 1486 { 1487 if (lineNo != -1 && this.removeBreakpoint(BP_MONITOR, href, lineNo)) 1488 { 1489 --monitorCount; 1490 dispatch(debuggers, "onToggleMonitor", [ href, lineNo, false]); 1491 } 1492 }, 1493 1494 isMonitored: function(url, lineNo) 1495 { 1496 var bp = lineNo != -1 ? this.findBreakpoint(url, lineNo) : null; 1497 return bp && bp.type & BP_MONITOR; 1498 }, 1499 1500 enumerateMonitors: function(url, cb) 1501 { 1502 if (url) 1503 { 1504 var urlBreakpoints = fbs.getBreakpoints(url); 1505 if (urlBreakpoints) 1506 { 1507 for (var i = 0; i < urlBreakpoints.length; ++i) 1508 { 1509 var bp = urlBreakpoints[i]; 1510 if (bp.type & BP_MONITOR) 1511 cb.call(url, bp.lineNo, bp); 1512 } 1513 } 1514 } 1515 else 1516 { 1517 for (var url in breakpoints) 1518 this.enumerateBreakpoints(url, cb); 1519 } 1520 }, 1521 1522 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 1523 1524 enumerateScripts: function(length) 1525 { 1526 var scripts = []; 1527 jsd.enumerateScripts( { 1528 enumerateScript: function(script) { 1529 var fileName = script.fileName; 1530 if ( !isFilteredURL(fileName) ) { 1531 scripts.push(script); 1532 } 1533 } 1534 }); 1535 1536 length.value = scripts.length; 1537 return scripts; 1538 }, 1539 1540 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 1541 1542 startProfiling: function() 1543 { 1544 if (!this.profiling) 1545 { 1546 this.profiling = true; 1547 profileStart = new Date(); 1548 1549 jsd.flags |= COLLECT_PROFILE_DATA; 1550 } 1551 1552 ++profileCount; 1553 }, 1554 1555 stopProfiling: function() 1556 { 1557 if (--profileCount == 0) 1558 { 1559 jsd.flags &= ~COLLECT_PROFILE_DATA; 1560 1561 var t = profileStart.getTime(); 1562 1563 this.profiling = false; 1564 profileStart = null; 1565 1566 return new Date().getTime() - t; 1567 } 1568 else 1569 return -1; 1570 }, 1571 1572 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 1573 1574 enableDebugger: function() 1575 { 1576 if (waitingForTimer) 1577 { 1578 timer.cancel(); 1579 waitingForTimer = false; 1580 } 1581 1582 if (enabledDebugger) 1583 return; 1584 1585 enabledDebugger = true; 1586 1587 this.obeyPrefs(); 1588 1589 if (!jsd) 1590 { 1591 jsd = DebuggerService.getService(jsdIDebuggerService); 1592 1593 if ( FBTrace.DBG_FBS_ERRORS ) 1594 FBTrace.sysout("fbs.enableDebugger gets jsd service, isOn:" + jsd.isOn + 1595 " initAtStartup:" + jsd.initAtStartup + " now have " + debuggers.length + 1596 " debuggers in " + clients.length + " clients"); 1597 1598 // This property has been removed from Fx40 1599 if (jsd.initAtStartup) 1600 jsd.initAtStartup = false; 1601 } 1602 1603 if (jsd.asyncOn) // then FF 4.0+ 1604 { 1605 if (!jsd.isOn) 1606 { 1607 if (FBTrace.DBG_ACTIVATION) 1608 { 1609 var startAsyncOn = new Date().getTime(); 1610 FBTrace.sysout("fbs.activation begin jsd.asyncOn " + startAsyncOn); 1611 } 1612 1613 jsd.asyncOn( // turn on jsd for the next event 1614 { 1615 onDebuggerActivated: function doDebuggerActivated() 1616 { 1617 // now we are in the next event and jsd is on. 1618 if (FBTrace.DBG_ACTIVATION) 1619 { 1620 var nowAsyncOn = new Date().getTime(); 1621 FBTrace.sysout("fbs.activation now we are in the " + 1622 "next event and JSD is ON " + nowAsyncOn + " delta: " + 1623 (nowAsyncOn - startAsyncOn) + "ms"); 1624 } 1625 1626 fbs.onDebuggerActivated(); 1627 fbs.onJSDebuggingActive(); 1628 } 1629 }); 1630 } 1631 else 1632 { 1633 fbs.onJSDebuggingActive(); 1634 } 1635 } 1636 else // FF 3.6- 1637 { 1638 if (!jsd.isOn) 1639 { 1640 if (FBTrace.DBG_FBS_ERRORS) 1641 FBTrace.sysout("fbs.Firefox 3.6 or earlier"); 1642 1643 jsd.on(); // this should be the only call to jsd.on(). 1644 fbs.onDebuggerActivated(); 1645 } 1646 fbs.onJSDebuggingActive(); 1647 } 1648 }, 1649 1650 onDebuggerActivated: function() 1651 { 1652 jsd.flags |= DISABLE_OBJECT_TRACE; 1653 1654 if (FBTrace.DBG_ACTIVATION) 1655 FBTrace.sysout("fbs.onDebuggerActivated"); 1656 1657 if (jsd.pauseDepth && FBTrace.DBG_FBS_ERRORS) 1658 FBTrace.sysout("fbs.enableDebugger found non-zero jsd.pauseDepth !! " + 1659 jsd.pauseDepth); 1660 }, 1661 1662 onJSDebuggingActive: function() 1663 { 1664 if (!this.filterChrome) 1665 this.createChromeBlockingFilters(); 1666 1667 var active = fbs.isJSDActive(); 1668 1669 dispatch(clients, "onJSDActivate", [active, "fbs enableDebugger"]); 1670 this.hookScripts(); 1671 1672 if (FBTrace.DBG_ACTIVATION) 1673 FBTrace.sysout("fbs.enableDebugger with active " + active); 1674 }, 1675 1676 obeyPrefs: function() 1677 { 1678 fbs.showStackTrace = getPref("showStackTrace"); 1679 fbs.breakOnErrors = getPref("breakOnErrors"); 1680 fbs.trackThrowCatch = getPref("trackThrowCatch"); 1681 1682 var pref = fbs.scriptsFilter; 1683 fbs.scriptsFilter = getPref("scriptsFilter"); 1684 var mustReset = (pref !== fbs.scriptsFilter); 1685 1686 if (FBTrace.DBG_OPTIONS) 1687 FBTrace.sysout("fbs.obeyPrefs mustReset = " + mustReset + " pref: " + pref + 1688 " fbs.scriptsFilter: " + fbs.scriptsFilter, fbs); 1689 1690 pref = fbs.filterSystemURLs; 1691 1692 // may not be exposed to users 1693 fbs.filterSystemURLs = getPref("filterSystemURLs"); 1694 mustReset = mustReset || (pref !== fbs.filterSystemURLs); 1695 1696 if (FBTrace.DBG_OPTIONS) 1697 FBTrace.sysout("fbs.obeyPrefs mustReset = " + mustReset + " pref: " + pref + 1698 " fbs.filterSystemURLs: " + fbs.filterSystemURLs); 1699 1700 if (mustReset && jsd && jsd.scriptHook) 1701 { 1702 fbs.unhookScripts(); 1703 fbs.hookScripts(); 1704 } 1705 1706 if (FBTrace.DBG_FBS_FUNCTION) 1707 { 1708 fbs.loggingFunctionCalls = new LogFunctionStepper(); 1709 jsdHandlers.add(fbs.loggingFunctionCalls); 1710 jsdHandlers.hook(); // no frame argument 1711 } 1712 else if (fbs.loggingFunctionCalls) 1713 { 1714 jsdHandlers.unhook("no frame argument"); 1715 fbs.jsdHandler.remove(fbs.loggingFunctionCalls); 1716 delete fbs.loggingFunctionCalls; 1717 } 1718 1719 FirebugPrefsObserver.syncFilter(); 1720 1721 try 1722 { 1723 if (FBTrace.DBG_OPTIONS) 1724 FBTrace.sysout("fbs.obeyPrefs showStackTrace:"+fbs.showStackTrace+ 1725 " breakOnErrors:"+fbs.breakOnErrors+" trackThrowCatch:"+fbs.trackThrowCatch+ 1726 " scriptFilter:"+fbs.scriptsFilter+" filterSystemURLs:"+fbs.filterSystemURLs); 1727 } 1728 catch (exc) 1729 { 1730 FBTrace.sysout("fbs.constructor getBoolPrefs FAILED with exception=", exc); 1731 } 1732 }, 1733 1734 disableDebugger: function() 1735 { 1736 if (!enabledDebugger) 1737 return; 1738 1739 if (!timer) // then we probably shutdown 1740 return; 1741 1742 enabledDebugger = false; 1743 1744 if (jsd.isOn) 1745 { 1746 jsd.pause(); 1747 fbs.unhookScripts(); 1748 1749 while (jsd.pauseDepth > 0) // unwind completely 1750 jsd.unPause(); 1751 1752 jsd.off(); 1753 } 1754 1755 var active = fbs.isJSDActive(); 1756 dispatch(clients, "onJSDDeactivate", [active, "fbs disableDebugger"]); 1757 1758 fbs.onXScriptCreatedByTag = {}; // clear any uncleared top level scripts 1759 1760 if (FBTrace.DBG_FBS_FINDDEBUGGER || FBTrace.DBG_ACTIVATION) 1761 FBTrace.sysout("fbs.disableDebugger jsd.isOn:"+jsd.isOn+" for enabledDebugger: "+ 1762 enabledDebugger); 1763 }, 1764 1765 // must support multiple calls 1766 pause: function(debuggerName) 1767 { 1768 if (!enabledDebugger || !jsd || !jsd.isOn) 1769 return "not enabled"; 1770 1771 var rejection = []; 1772 dispatch(clients, "onPauseJSDRequested", [rejection, debuggerName]); 1773 1774 // Number of rejections: 1775 // 0 - then everyone wants to pause 1776 // 1 - then everyone wants to pause (including the current active tab) 1777 if (rejection.length < 1) 1778 { 1779 if (jsd.pauseDepth == 0) // don't pause if we are paused. 1780 { 1781 jsd.pause(); 1782 fbs.unhookScripts(); 1783 } 1784 1785 var active = fbs.isJSDActive(); 1786 dispatch(clients, "onJSDDeactivate", [active, "pause depth "+jsd.pauseDepth]); 1787 } 1788 else // we don't want to pause 1789 { 1790 fbs.unPause(true); 1791 } 1792 1793 if (FBTrace.DBG_FBS_FINDDEBUGGER || FBTrace.DBG_ACTIVATION) 1794 { 1795 FBTrace.sysout("fbs.pause depth "+(jsd.isOn?jsd.pauseDepth:"jsd OFF")+" rejection "+ 1796 rejection.length+" from "+clients.length+" clients ", rejection); 1797 1798 // The next line gives NS_ERROR_NOT_AVAILABLE 1799 // FBTrace.sysout("fbs.pause depth "+(jsd.isOn?jsd.pauseDepth:"jsd OFF")+ 1800 // " rejection "+rejection.length+" from clients "+clients, rejection); 1801 } 1802 return jsd.pauseDepth; 1803 }, 1804 1805 unPause: function(force) 1806 { 1807 if (!jsd) 1808 return; 1809 1810 if (jsd.pauseDepth > 0 || force) 1811 { 1812 if (FBTrace.DBG_ACTIVATION && (!jsd.isOn || jsd.pauseDepth == 0) ) 1813 FBTrace.sysout("fbs.unpause while jsd.isOn is "+jsd.isOn+ 1814 " and hooked scripts pauseDepth:"+jsd.pauseDepth); 1815 1816 fbs.hookScripts(); 1817 1818 if(jsd.pauseDepth) 1819 var depth = jsd.unPause(); 1820 1821 var active = fbs.isJSDActive(); 1822 1823 if (FBTrace.DBG_ACTIVATION) 1824 FBTrace.sysout("fbs.unPause hooked scripts and unPaused, active:" + active + 1825 " depth " + depth + " jsd.isOn: " + jsd.isOn); 1826 1827 dispatch(clients, "onJSDActivate", [active, "unpause depth"+jsd.pauseDepth]); 1828 } 1829 else // we were not paused. 1830 { 1831 if (FBTrace.DBG_ACTIVATION) 1832 { 1833 var noAction = "("+jsd.pauseDepth+" || "+ !jsd.isOn+")"; 1834 FBTrace.sysout("fbs.unPause no action: (jsd.pauseDepth || !jsd.isOn) = " + 1835 noAction); 1836 } 1837 } 1838 1839 return jsd.pauseDepth; 1840 }, 1841 1842 isJSDActive: function() 1843 { 1844 return (jsd && jsd.isOn && (jsd.pauseDepth === 0) ); 1845 }, 1846 1847 // TODO delete once Chromebug works on BTI 1848 // re-transmit the message (string) with args [objs] to XUL windows. 1849 broadcast: function(message, args) 1850 { 1851 dispatch(clients, message, args); 1852 if (FBTrace.DBG_ACTIVATION) 1853 FBTrace.sysout("fbs.broadcast "+message+" to "+clients.length+" clients", clients); 1854 }, 1855 1856 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 1857 1858 normalizeURL: function(url) 1859 { 1860 // For some reason, JSD reports file URLs like "file:/" instead of "file:///", so they 1861 // don't match up with the URLs we get back from the DOM 1862 return url ? url.replace(/file:\/([^/])/, "file:///$1") : ""; 1863 }, 1864 1865 denormalizeURL: function(url) 1866 { 1867 // This should not be called. 1868 return url ? url.replace(/file:\/\/\//, "file:/") : ""; 1869 }, 1870 1871 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 1872 // jsd Hooks 1873 1874 // When engine encounters debugger keyword (only) 1875 onDebugger: function(frame, type, rv) 1876 { 1877 if (FBTrace.DBG_FBS_BP) 1878 FBTrace.sysout("fbs.onDebugger with haltDebugger="+ 1879 (haltObject?haltObject.haltDebugger:"null")+" in "+frame.script.fileName, 1880 frame.script); 1881 1882 try 1883 { 1884 if (FBTrace.DBG_FBS_SRCUNITS && fbs.isTopLevelScript(frame, type, rv)) 1885 FBTrace.sysout("fbs.onDebugger found topLevelScript "+ frame.script.tag); 1886 1887 if (FBTrace.DBG_FBS_SRCUNITS && fbs.isNestedScript(frame, type, rv)) 1888 FBTrace.sysout("fbs.onDebugger found nestedScript "+ frame.script.tag); 1889 1890 if (haltObject) 1891 { 1892 var peelOurselvesOff = frame; 1893 if (peelOurselvesOff.script.fileName.indexOf("debuggerHalter.js") > 0) 1894 peelOurselvesOff = frame.callingFrame; // remove debuggerHalter() 1895 1896 while (peelOurselvesOff && (peelOurselvesOff.script.fileName.indexOf("firebug-service.js") > 0 )) 1897 peelOurselvesOff = peelOurselvesOff.callingFrame; 1898 1899 while (peelOurselvesOff && (peelOurselvesOff.script.fileName.indexOf("/debugger.js") > 0 )) 1900 peelOurselvesOff = peelOurselvesOff.callingFrame; 1901 1902 if (peelOurselvesOff) 1903 { 1904 if (FBTrace.DBG_FBS_BP) 1905 FBTrace.sysout("fbs.onDebugger, " + 1906 (haltObject.haltCallBack ? "with" : "without") + 1907 " callback, adjusted newest frame: " + peelOurselvesOff.line + 1908 "@" + peelOurselvesOff.script.fileName + " frames: ", 1909 framesToString(frame)); 1910 1911 var debuggr = haltObject.haltDebugger; 1912 var callback = haltObject.haltCallBack; 1913 fbs.haltReturnValue = callback.apply(debuggr,[peelOurselvesOff]); 1914 } 1915 else 1916 { 1917 FBTrace.sysout("fbs.halt ERROR "+framesToString(frame)); 1918 fbs.haltReturnValue = "firebug-service.halt ERROR, no stack frames left "; 1919 } 1920 1921 return RETURN_CONTINUE; 1922 } 1923 else 1924 { 1925 var peelOurselvesOff = frame; 1926 if (peelOurselvesOff.script.fileName.indexOf("consoleExposed.js") > 0) 1927 peelOurselvesOff = frame.callingFrame; 1928 1929 var bp = this.findBreakpointByScript(peelOurselvesOff.script, peelOurselvesOff.pc); 1930 1931 // then breakpoints override debugger statements (to allow conditional 1932 // debugger statements); 1933 if (bp) 1934 return this.onBreakpoint(peelOurselvesOff, type, rv); 1935 else 1936 return fbs.routeBreakToDebuggr(peelOurselvesOff, type, rv); 1937 } 1938 } 1939 catch(exc) 1940 { 1941 if (FBTrace.DBG_FBS_ERRORS) 1942 FBTrace.sysout("fbs.onDebugger failed: "+exc,exc); 1943 1944 ERROR("onDebugger failed: "+exc, exc); 1945 return RETURN_CONTINUE; 1946 } 1947 finally 1948 { 1949 haltObject = null; 1950 } 1951 }, 1952 1953 // when the onError handler returns false 1954 onDebug: function(frame, type, rv) 1955 { 1956 if (FBTrace.DBG_FBS_ERRORS) 1957 { 1958 fbs.onDebugRequests--; 1959 FBTrace.sysout("fbs.onDebug (" + fbs.onDebugRequests + ") fileName=" + 1960 frame.script.fileName + " reportNextError=" + reportNextError + 1961 " breakOnErrors:" + this.breakOnErrors + " fbs.breakOnDebugCall: " + 1962 fbs.breakOnDebugCall); 1963 } 1964 1965 if (isFilteredURL(frame.script.fileName)) 1966 { 1967 reportNextError = false; 1968 return RETURN_CONTINUE; 1969 } 1970 1971 try 1972 { 1973 var breakOnNextError = this.needToBreakForError(reportNextError); 1974 var debuggr = (reportNextError || breakOnNextError) ? this.findDebugger(frame) : null; 1975 1976 if (reportNextError) 1977 { 1978 reportNextError = false; 1979 if (debuggr) 1980 { 1981 var hookReturn = debuggr.onError(frame, errorInfo, fbs.breakOnDebugCall); 1982 if (hookReturn >=0) 1983 return hookReturn; 1984 else if (hookReturn==-1) 1985 breakOnNextError = true; 1986 1987 if (breakOnNextError) 1988 debuggr = this.reFindDebugger(frame, debuggr); 1989 } 1990 } 1991 1992 if (breakOnNextError) 1993 { 1994 if (fbs.isTopLevelScript(frame, type, rv) && FBTrace.DBG_FBS_SRCUNITS) 1995 FBTrace.sysout("fbs.onDebug found topLevelScript "+ frame.script.tag); 1996 1997 if (fbs.isNestedScript(frame, type, rv) && FBTrace.DBG_FBS_SRCUNITS) 1998 FBTrace.sysout("fbs.onDebug found nestedScript "+ frame.script.tag); 1999 2000 breakOnNextError = false; 2001 delete fbs.breakOnDebugCall; 2002 2003 if (debuggr) 2004 return this.breakIntoDebugger(debuggr, frame, type); 2005 } 2006 } 2007 catch (exc) 2008 { 2009 ERROR("onDebug failed: "+exc); 2010 } 2011 2012 return RETURN_CONTINUE; 2013 }, 2014 2015 onBreakpoint: function(frame, type, val) 2016 { 2017 if (fbs.isTopLevelScript(frame, type, val)) 2018 { 2019 if (FBTrace.DBG_FBS_BP) 2020 FBTrace.sysout("fbs.onBreakpoint isTopLevel returning " + RETURN_CONTINUE); 2021 2022 return RETURN_CONTINUE; 2023 } 2024 2025 var bp = this.findBreakpointByScript(frame.script, frame.pc); 2026 if (bp) 2027 { 2028 var theDebugger = fbs.getDebuggerByName(bp.debuggerName); 2029 if (!theDebugger) 2030 theDebugger = this.findDebugger(frame); // sets debuggr.breakContext 2031 2032 var currFrameId = frameId(frame, 0); 2033 2034 // See issue 1179, should not break if we resumed from a single 2035 // step and have not advanced. 2036 // Only break on a breakpoint if a single-step didn't start on 2037 // the current line (issue 1098) 2038 for (var i=0; i<jsdHandlers.hooks.length; i++) 2039 { 2040 var handler = jsdHandlers.hooks[i]; 2041 if (handler.startFrameId == currFrameId) 2042 return RETURN_CONTINUE; 2043 } 2044 2045 if (disabledCount || monitorCount || conditionCount || runningUntil) 2046 { 2047 if (FBTrace.DBG_FBS_BP) 2048 { 2049 FBTrace.sysout("fbs.onBreakpoint("+getExecutionStopNameFromType(type)+ 2050 ") disabledCount:"+disabledCount+" monitorCount:"+monitorCount+ 2051 " conditionCount:"+conditionCount+" runningUntil:"+runningUntil, bp); 2052 } 2053 2054 if (bp.type & BP_ERROR) 2055 return RETURN_CONTINUE; // if onError gets called, then we will break 2056 2057 if (bp.type & BP_MONITOR && !(bp.disabled & BP_MONITOR)) 2058 { 2059 if (bp.type & BP_TRACE && !(bp.disabled & BP_TRACE) ) 2060 this.hookCalls(theDebugger.onFunctionCall, true); // TODO 2061 else 2062 theDebugger.onMonitorScript(frame); 2063 } 2064 2065 if (bp.type & BP_UNTIL) // then we hit the runningUntil breakpoint 2066 { 2067 if (runningUntil) 2068 { 2069 this.removeBreakpoint(BP_UNTIL, runningUntil.href, runningUntil.lineNo); 2070 runningUntil = null; 2071 } 2072 else 2073 { 2074 if (FBTrace.DBG_FBS_ERRORS) 2075 FBTrace.sysout("fbs.BP_UNTIL but not runningUntil!", bp); 2076 } 2077 2078 if (theDebugger) 2079 return this.breakIntoDebugger(theDebugger, frame, type); 2080 } 2081 else if (!(bp.type & BP_NORMAL) || bp.disabled & BP_NORMAL) 2082 { 2083 return RETURN_CONTINUE; 2084 } 2085 else if (bp.type & BP_NORMAL) 2086 { 2087 var passed = testBreakpoint(frame, bp); 2088 if (!passed) 2089 return RETURN_CONTINUE; 2090 } 2091 // type was normal, but passed test 2092 } 2093 else 2094 { 2095 // not special, just break for sure 2096 return this.breakIntoDebugger(theDebugger, frame, type); 2097 } 2098 } 2099 else 2100 { 2101 if (FBTrace.DBG_FBS_BP) 2102 FBTrace.sysout("fbs.onBreakpoint(" + getExecutionStopNameFromType(type) + 2103 ") NO bp match with frame.script.tag=" + frame.script.tag + 2104 " clearing and continuing"); 2105 2106 // We did not find a logical breakpoint to match the one set into JSD, so stop trying. 2107 frame.script.clearBreakpoint(frame.pc); 2108 return RETURN_CONTINUE; 2109 } 2110 2111 if (runningUntil) 2112 return RETURN_CONTINUE; 2113 else 2114 return fbs.routeBreakToDebuggr(frame, type, val); 2115 }, 2116 2117 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 2118 2119 onThrow: function(frame, type, rv) 2120 { 2121 if (isFilteredURL(frame.script.fileName)) 2122 return RETURN_CONTINUE_THROW; 2123 2124 if (rv && rv.value && rv.value.isValid) 2125 { 2126 var value = rv.value; 2127 if (value.jsClassName == "Error" && value.stringValue.indexOf("too much recursion") !== -1) 2128 { 2129 if (fbs._lastErrorCaller) 2130 { 2131 // then are unwinding recursion 2132 if (fbs._lastErrorCaller == frame.script.tag) 2133 { 2134 fbs._lastErrorCaller = 2135 frame.callingFrame ? frame.callingFrame.script.tag : null; 2136 2137 return RETURN_CONTINUE_THROW; 2138 } 2139 } 2140 else 2141 { 2142 fbs._lastErrorCaller = frame.callingFrame.script.tag; 2143 frame = fbs.discardRecursionFrames(frame); 2144 // go on to process the throw. 2145 } 2146 } 2147 else 2148 { 2149 delete fbs._lastErrorCaller; // throw is not recursion 2150 } 2151 } 2152 else 2153 { 2154 delete fbs._lastErrorCaller; // throw is not recursion either 2155 } 2156 2157 if (fbs.showStackTrace) 2158 { 2159 if (FBTrace.DBG_FBS_ERRORS) 2160 FBTrace.sysout("fbs.onThrow from tag:" + frame.script.tag + ":" + 2161 frame.script.fileName + "@" + frame.line + ": " + frame.pc); 2162 2163 var debuggr = this.findDebugger(frame); 2164 if (debuggr) 2165 return debuggr.onThrow(frame, rv); 2166 } 2167 2168 return RETURN_CONTINUE_THROW; 2169 }, 2170 2171 onError: function(message, fileName, lineNo, pos, flags, errnum, exc) 2172 { 2173 if (FBTrace.DBG_FBS_ERRORS) 2174 { 2175 var messageKind; 2176 if (flags & jsdIErrorHook.REPORT_ERROR) 2177 messageKind = "Error"; 2178 if (flags & jsdIErrorHook.REPORT_WARNING) 2179 messageKind = "Warning"; 2180 if (flags & jsdIErrorHook.REPORT_EXCEPTION) 2181 messageKind = "Uncaught-Exception"; 2182 if (flags & jsdIErrorHook.REPORT_STRICT) 2183 messageKind += "-Strict"; 2184 2185 FBTrace.sysout("fbs.onError ("+fbs.onDebugRequests+") with this.showStackTrace="+ 2186 this.showStackTrace+" and this.breakOnErrors="+this.breakOnErrors+" kind="+ 2187 messageKind+" msg="+message+"@"+fileName+":"+lineNo+"."+pos, 2188 (exc ? exc.getWrappedValue() : "No exc object")); 2189 } 2190 2191 delete fbs.breakOnDebugCall; 2192 2193 if (exc) 2194 { 2195 var exception = exc.getWrappedValue(); 2196 fbs.enumerateErrorBreakpoints(exception.fileName, {call: function breakIfMatch(url, lineNo, bp) 2197 { 2198 // An error breakpoint is in this file 2199 if (exception.lineNumber == bp.lineNo) 2200 { 2201 fbs.breakOnDebugCall = true; 2202 2203 if (FBTrace.DBG_FBS_ERRORS) 2204 FBTrace.sysout("fbs.onError setting breakOnDebugCall for " + url + "@" + 2205 exception.lineNumber); 2206 } 2207 }}); 2208 } 2209 2210 // Global to pass info to onDebug. Some duplicate values to support different apis 2211 // Do not store the exception object itself |exc|, errofInfo is a global variable 2212 // and it would keep the page (that is producing the error) in the memory 2213 // (see bug 669730) 2214 errorInfo = { errorMessage: message, sourceName: fileName, lineNumber: lineNo, 2215 message: message, fileName: fileName, lineNo: lineNo, 2216 columnNumber: pos, flags: flags, category: "js", errnum: errnum }; 2217 2218 if (message == "out of memory") // bail 2219 { 2220 if (FBTrace.DBG_FBS_ERRORS) 2221 fbs.osOut("fbs.onError sees out of memory "+fileName+":"+lineNo+"\n"); 2222 return true; 2223 } 2224 2225 reportNextError = { fileName: fileName, lineNo: lineNo }; 2226 2227 if (FBTrace.DBG_FBS_ERRORS) 2228 fbs.onDebugRequests++; 2229 2230 return false; // Drop into onDebug, sometimes only 2231 }, 2232 2233 onTopLevel: function(frame, type) 2234 { 2235 if (FBTrace.DBG_TOPLEVEL) 2236 FBTrace.sysout("fbs.onTopLevel " + getCallFromType(type) + " with delegate " + 2237 fbs.onTopLevelDelegate + " " + frame.script.tag + " " + frame.script.fileName); 2238 2239 if (fbs.onTopLevelDelegate) 2240 fbs.onTopLevelDelegate(frame, type) 2241 }, 2242 2243 isTopLevelScript: function(frame, type, val) 2244 { 2245 var scriptTag = frame.script.tag; 2246 2247 if (FBTrace.DBG_FBS_SRCUNITS) 2248 FBTrace.sysout("fbs.isTopLevelScript frame.script.tag="+frame.script.tag ); 2249 2250 if (scriptTag in this.onXScriptCreatedByTag) 2251 { 2252 if (FBTrace.DBG_FBS_TRACKFILES) 2253 trackFiles.def(frame); 2254 2255 var onXScriptCreated = this.onXScriptCreatedByTag[scriptTag]; 2256 2257 if (FBTrace.DBG_FBS_BP) 2258 FBTrace.sysout("fbs.isTopLevelScript(" + getExecutionStopNameFromType(type) + 2259 ") with frame.script.tag=" + frame.script.tag + " onXScriptCreated:" + 2260 onXScriptCreated.kind); 2261 2262 delete this.onXScriptCreatedByTag[scriptTag]; 2263 frame.script.clearBreakpoint(0); 2264 2265 try 2266 { 2267 var sourceFile = onXScriptCreated(frame, type, val); 2268 } 2269 catch (e) 2270 { 2271 FBTrace.sysout("fbs.isTopLevelScript called onXScriptCreated and " + 2272 "it didn't end well:", e); 2273 } 2274 2275 if (FBTrace.DBG_FBS_SRCUNITS) 2276 { 2277 var msg = "Top Scripts Uncleared:"; 2278 for (var p in this.onXScriptCreatedByTag) 2279 msg += (p + "|"); 2280 FBTrace.sysout(msg); 2281 } 2282 2283 if (!sourceFile || !sourceFile.breakOnZero || sourceFile.breakOnZero != scriptTag) 2284 { 2285 return true; 2286 } 2287 else 2288 { 2289 // sourceFile.breakOnZero matches the script we have halted. 2290 if (FBTrace.DBG_FBS_BP) 2291 FBTrace.sysout("fbs.isTopLevelScript breakOnZero, continuing for " + 2292 "user breakpoint"); 2293 } 2294 } 2295 2296 return false; 2297 }, 2298 2299 /** 2300 * If true, emergency bailout: a frame is running a script which has not been 2301 * processed as source 2302 */ 2303 isNestedScript: function(frame, type, val) 2304 { 2305 if (fbs.nestedScriptStack.length === 0 || 2306 fbs.nestedScriptStack.indexOf(frame.script) === -1) 2307 { 2308 return false; 2309 } 2310 2311 try 2312 { 2313 var sourceFile = fbs.onTopLevelScriptCreated(frame, type, val); 2314 } 2315 catch (e) 2316 { 2317 FBTrace.sysout("fbs.isNestedScript called onXScriptCreated and it didn't end well:", e); 2318 } 2319 2320 return true; 2321 }, 2322 2323 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 2324 2325 onXULScriptCreated: function(frame, type, val, noNestTest) 2326 { 2327 // A XUL script hit a breakpoint 2328 try 2329 { 2330 var outerScript = frame.script; 2331 var innerScripts = []; 2332 for (var i=0; i<fbs.pendingXULScripts.length; i++) 2333 { 2334 // Take all the pending script from the same file as part of this sourcefile 2335 if (fbs.pendingXULScripts[i].fileName === outerScript.fileName) 2336 { 2337 var innerScript = fbs.pendingXULScripts[i]; 2338 innerScripts.push(innerScript); 2339 if (innerScript.isValid) 2340 innerScript.clearBreakpoint(0); 2341 2342 fbs.pendingXULScripts.splice(i,1); 2343 } 2344 } 2345 2346 var debuggr = fbs.findDebugger(frame); // sets debuggr.breakContext 2347 if (debuggr) 2348 { 2349 innerScripts.push(outerScript); 2350 var innerScriptEnumerator = 2351 { 2352 index: 0, 2353 max: innerScripts.length, 2354 hasMoreElements: function() { return this.index < this.max;}, 2355 getNext: function() { return innerScripts[this.index++]; }, 2356 }; 2357 2358 var sourceFile = debuggr.onXULScriptCreated(frame, outerScript, 2359 innerScriptEnumerator); 2360 fbs.resetBreakpoints(sourceFile, debuggr); 2361 } 2362 else 2363 { 2364 if (FBTrace.DBG_FBS_CREATION || FBTrace.DBG_FBS_SRCUNITS) 2365 FBTrace.sysout("fbs.onEventScriptCreated no debuggr for " + frame.script.tag + 2366 ":" + frame.script.fileName); 2367 } 2368 } 2369 catch(exc) 2370 { 2371 if (FBTrace.DBG_FBS_ERRORS) 2372 FBTrace.sysout("fbs.onXULScriptCreated ERROR " + exc, exc); 2373 } 2374 }, 2375 2376 onEventScriptCreated: function(frame, type, val, noNestTest) 2377 { 2378 if (fbs.showEvents) 2379 { 2380 try 2381 { 2382 if (!noNestTest) 2383 { 2384 // In onScriptCreated we saw a script with baseLineNumber = 1. 2385 // We marked it as event and nested. 2386 // Now we know its event, not nested. 2387 if (fbs.nestedScriptStack.length > 0) 2388 { 2389 fbs.nestedScriptStack.splice(0,1); 2390 } 2391 else 2392 { 2393 if (FBTrace.DBG_FBS_SRCUNITS) // these seem to be harmless, but... 2394 { 2395 var script = frame.script; 2396 FBTrace.sysout("fbs.onEventScriptCreated no nestedScriptStack: " + 2397 script.tag + "@(" + script.baseLineNumber + "-" + 2398 (script.baseLineNumber+script.lineExtent) + ")" + 2399 script.fileName); 2400 2401 FBTrace.sysout("fbs.onEventScriptCreated name: \'" + 2402 script.functionName + "\'"); 2403 2404 try 2405 { 2406 FBTrace.sysout(script.functionSource); 2407 } 2408 catch (exc) 2409 { 2410 /*Bug 426692 */ 2411 } 2412 } 2413 } 2414 } 2415 2416 var debuggr = fbs.findDebugger(frame); // sets debuggr.breakContext 2417 if (debuggr) 2418 { 2419 var sourceFile = debuggr.onEventScriptCreated(frame, frame.script, 2420 fbs.getNestedScriptEnumerator()); 2421 fbs.resetBreakpoints(sourceFile, debuggr); 2422 } 2423 else 2424 { 2425 if (FBTrace.DBG_FBS_CREATION || FBTrace.DBG_FBS_SRCUNITS) 2426 FBTrace.sysout("fbs.onEventScriptCreated no debuggr for " + 2427 frame.script.tag + ":" + frame.script.fileName); 2428 } 2429 } 2430 catch(exc) 2431 { 2432 if (FBTrace.DBG_ERRORS) 2433 FBTrace.sysout("fbs.onEventScriptCreated failed: " + exc, exc); 2434 } 2435 2436 if (FBTrace.DBG_FBS_CREATION || FBTrace.DBG_FBS_SRCUNITS) 2437 FBTrace.sysout("fbs.onEventScriptCreated frame.script.tag:" + frame.script.tag + 2438 " href: " + (sourceFile ? sourceFile.href : "no sourceFile"), sourceFile); 2439 } 2440 2441 fbs.clearNestedScripts(); 2442 return sourceFile; 2443 }, 2444 2445 onEvalScriptCreated: function(frame, type, val) 2446 { 2447 if (fbs.showEvals) 2448 { 2449 try 2450 { 2451 if (!frame.callingFrame) 2452 { 2453 if (FBTrace.DBG_FBS_CREATION || FBTrace.DBG_FBS_SRCUNITS) 2454 FBTrace.sysout("fbs.No calling Frame for eval frame.script.fileName:" + 2455 frame.script.fileName); 2456 2457 // These are eval-like things called by native code. They come from .xml files 2458 // They should be marked as evals but we'll treat them like event handlers for now. 2459 return fbs.onEventScriptCreated(frame, type, val, true); 2460 } 2461 2462 // In onScriptCreated we found a no-name script, set a bp in PC=0, and a flag. 2463 // onBreakpoint saw the flag, cleared the flag, and sent us here. 2464 // Start by undoing our damage 2465 var outerScript = frame.script; 2466 2467 // sets debuggr.breakContext 2468 var debuggr = fbs.findDebugger(frame); 2469 if (debuggr) 2470 { 2471 var sourceFile = debuggr.onEvalScriptCreated(frame, outerScript, 2472 fbs.getNestedScriptEnumerator()); 2473 fbs.resetBreakpoints(sourceFile, debuggr); 2474 } 2475 else 2476 { 2477 if (FBTrace.DBG_FBS_CREATION || FBTrace.DBG_FBS_SRCUNITS) 2478 FBTrace.sysout("fbs.onEvalScriptCreated no debuggr for " + 2479 outerScript.tag + ":" + outerScript.fileName); 2480 } 2481 } 2482 catch (exc) 2483 { 2484 ERROR("onEvalScriptCreated failed: "+exc); 2485 2486 if (FBTrace.DBG_FBS_ERRORS) 2487 FBTrace.sysout("fbs.onEvalScriptCreated failed:", exc); 2488 } 2489 } 2490 2491 fbs.clearNestedScripts(); 2492 2493 if (FBTrace.DBG_FBS_CREATION || FBTrace.DBG_FBS_SRCUNITS) 2494 FBTrace.sysout("fbs.onEvalScriptCreated outerScript.tag:" + outerScript.tag + 2495 " href: " + (sourceFile ? sourceFile.href : "no sourceFile")); 2496 2497 return sourceFile; 2498 }, 2499 2500 onTopLevelScriptCreated: function(frame, type, val) 2501 { 2502 try 2503 { 2504 // In onScriptCreated we may have found a script at baseLineNumber=1 2505 // Now we know its not an event 2506 if (fbs.nestedScriptStack.length > 0) 2507 { 2508 var firstScript = fbs.nestedScriptStack[0]; 2509 if (firstScript.tag in fbs.onXScriptCreatedByTag) 2510 { 2511 delete fbs.onXScriptCreatedByTag[firstScript.tag]; 2512 if (firstScript.isValid) 2513 firstScript.clearBreakpoint(0); 2514 2515 if (FBTrace.DBG_FBS_SRCUNITS) 2516 FBTrace.sysout("fbs.onTopLevelScriptCreated clear bp@0 for " + 2517 "firstScript.tag: " + firstScript.tag); 2518 } 2519 } 2520 2521 // On compilation of a top-level (global-appending) function. 2522 // After this top-level script executes we lose the jsdIScript so we can't 2523 // build its line table. Therefore we need to build it here. 2524 2525 // sets debuggr.breakContext 2526 var debuggr = fbs.findDebugger(frame); 2527 if (debuggr) 2528 { 2529 var sourceFile = debuggr.onTopLevelScriptCreated(frame, frame.script, 2530 fbs.getNestedScriptEnumerator()); 2531 2532 if (FBTrace.DBG_FBS_SRCUNITS) 2533 FBTrace.sysout("fbs.onTopLevelScriptCreated got sourceFile:" + sourceFile + 2534 " using " + fbs.nestedScriptStack.length + " nestedScripts"); 2535 2536 fbs.resetBreakpoints(sourceFile, debuggr); 2537 } 2538 else 2539 { 2540 // modules end up here? 2541 if (FBTrace.DBG_FBS_SRCUNITS) 2542 FBTrace.sysout("fbs.onTopLevelScriptCreated no debuggr for " + frame.script.tag); 2543 } 2544 } 2545 catch (exc) 2546 { 2547 FBTrace.sysout("fbs.onTopLevelScriptCreated FAILED: " + exc, exc); 2548 ERROR("onTopLevelScriptCreated ERROR: " + exc); 2549 } 2550 2551 fbs.clearNestedScripts(); 2552 2553 if (FBTrace.DBG_FBS_CREATION || FBTrace.DBG_FBS_SRCUNITS) 2554 FBTrace.sysout("fbs.onTopLevelScriptCreated script.tag:" + frame.script.tag + 2555 " href: " + (sourceFile ? sourceFile.href : "no sourceFile")); 2556 2557 return sourceFile; 2558 }, 2559 2560 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 2561 2562 getNestedScriptEnumerator: function() 2563 { 2564 var enumer = 2565 { 2566 index: 0, 2567 hasMoreElements: function() 2568 { 2569 return (this.index < fbs.nestedScriptStack.length); 2570 }, 2571 getNext: function() 2572 { 2573 var rv = fbs.nestedScriptStack[this.index]; 2574 this.index++; 2575 return rv; 2576 } 2577 }; 2578 return enumer; 2579 }, 2580 2581 clearNestedScripts: function() 2582 { 2583 var innerScripts = fbs.nestedScriptStack; 2584 for (var i=0; i<innerScripts.length; i++) 2585 { 2586 var script = innerScripts[i]; 2587 if (script.isValid && script.baseLineNumber == 1) 2588 { 2589 // Clear helper breakpoints that are set automatically when a new script 2590 // is created. But avoid cases where the user has a breakpoint on the 2591 // first line in a function (issue 3985). 2592 if (!fbs.findBreakpointByScript(script, 0)) 2593 script.clearBreakpoint(0); 2594 2595 if (this.onXScriptCreatedByTag[script.tag]) 2596 delete this.onXScriptCreatedByTag[script.tag]; 2597 } 2598 } 2599 2600 fbs.nestedScriptStack = []; 2601 }, 2602 2603 onScriptCreated: function(script) 2604 { 2605 if (!fbs) 2606 { 2607 if (FBTrace.DBG_FBS_CREATION || FBTrace.DBG_FBS_SRCUNITS || FBTrace.DBG_FBS_TRACKFILES) 2608 FBTrace.sysout("fbs.onScriptCreated " + script.tag + 2609 ", but no fbs for script.fileName=" + script.fileName); 2610 return; 2611 } 2612 2613 try 2614 { 2615 var fileName = script.fileName; 2616 2617 if (FBTrace.DBG_FBS_TRACKFILES) 2618 trackFiles.add(fileName); 2619 2620 if (isFilteredURL(fileName) || fbs.isChromebug(fileName)) 2621 { 2622 try 2623 { 2624 if (FBTrace.DBG_FBS_CREATION || FBTrace.DBG_FBS_SRCUNITS) 2625 FBTrace.sysout("fbs.onScriptCreated " + script.tag + 2626 ": filename filtered:\'" + 2627 fileName + "\'" + (fbs.filterConsoleInjections ? 2628 " console injection" : "")); 2629 } 2630 catch (exc) 2631 { 2632 FBTrace.sysout("fbs.onScriptCreated " + script.tag + " filtered msg ERROR \'" + 2633 script.fileName+"\'"); /*? Bug 426692 */ 2634 } 2635 2636 if (FBTrace.DBG_FBS_TRACKFILES) 2637 trackFiles.drop(fileName); 2638 2639 return; 2640 } 2641 2642 if (FBTrace.DBG_FBS_CREATION || FBTrace.DBG_FBS_SRCUNITS) 2643 FBTrace.sysout("fbs.onScriptCreated: " + script.tag + "@(" + script.baseLineNumber + 2644 "-" + (script.baseLineNumber+script.lineExtent) + ")" + script.fileName); 2645 2646 if (script.lineExtent > 80000 && FBTrace.DBG_FBS_SRCUNITS) 2647 FBTrace.sysout("fbs.BOGUS line extent (" + script.lineExtent + 2648 ") for " + script.fileName); 2649 2650 if (FBTrace.DBG_FBS_CREATION) 2651 { 2652 try 2653 { 2654 FBTrace.sysout("fbs.onScriptCreated: \'"+script.functionName+"\'", 2655 script.functionSource); 2656 } 2657 catch (exc) 2658 { 2659 FBTrace.sysout("fbs.onScriptCreated " + script.tag + " ERROR \'" + 2660 script.fileName + "\'"); /*? Bug 426692 */ 2661 } 2662 } 2663 2664 if (reXUL.test(script.fileName)) 2665 { 2666 fbs.onXScriptCreatedByTag[script.tag] = fbs.onXULScriptCreated; 2667 fbs.pendingXULScripts.push(script); 2668 2669 // Stop in the first one called and assign all with this fileName to sourceFile. 2670 script.setBreakpoint(0); 2671 } 2672 else if (!script.functionName) // top or eval-level 2673 { 2674 // We need to detect eval() and grab its source. 2675 var hasCaller = fbs.createdScriptHasCaller(); 2676 if (FBTrace.DBG_FBS_SRCUNITS) 2677 FBTrace.sysout("fbs.top or eval case createdScriptHasCaller " + hasCaller); 2678 2679 if (hasCaller) 2680 { 2681 // components end up here 2682 fbs.onXScriptCreatedByTag[script.tag] = this.onEvalScriptCreated; 2683 } 2684 else 2685 { 2686 fbs.onXScriptCreatedByTag[script.tag] = this.onTopLevelScriptCreated; 2687 } 2688 2689 script.setBreakpoint(0); 2690 2691 if (FBTrace.DBG_FBS_CREATION || FBTrace.DBG_FBS_SRCUNITS || FBTrace.DBG_FBS_BP) 2692 { 2693 FBTrace.sysout("fbs.onScriptCreated: set BP at PC 0 in " + 2694 (hasCaller ? "eval" : "top") + " level tag=" + script.tag + ":" + 2695 script.fileName + " jsd depth:" + (jsd.isOn ? jsd.pauseDepth + 2696 "" : "OFF")); 2697 } 2698 } 2699 else if (script.baseLineNumber == 1) 2700 { 2701 // could be a 1) Browser-generated event handler or 2702 // 2) a nested script at the top of a file 2703 // One way to tell is assume both then wait to see which we hit first: 2704 // 1) bp at pc=0 for this script or 2) for a top-level on at the same filename 2705 2706 if (FBTrace.DBG_FBS_SRCUNITS) 2707 { 2708 var hasCaller = fbs.createdScriptHasCaller(); 2709 FBTrace.sysout("fbs.browser generated createdScriptHasCaller " + hasCaller); 2710 } 2711 2712 fbs.onXScriptCreatedByTag[script.tag] = this.onEventScriptCreated; // for case 1 2713 script.setBreakpoint(0); 2714 2715 fbs.nestedScriptStack.push(script); // for case 2 2716 2717 if (FBTrace.DBG_FBS_CREATION) 2718 FBTrace.sysout("fbs.onScriptCreated: set BP at PC 0 in event level tag=" + 2719 script.tag); 2720 } 2721 else 2722 { 2723 fbs.nestedScriptStack.push(script); 2724 2725 if (FBTrace.DBG_FBS_CREATION) 2726 FBTrace.sysout("fbs.onScriptCreated: nested function named: " + 2727 script.functionName); 2728 2729 dispatch(scriptListeners, "onScriptCreated", [script, fileName, script.baseLineNumber]); 2730 } 2731 } 2732 catch(exc) 2733 { 2734 ERROR("onScriptCreated failed: " + exc); 2735 FBTrace.sysout("fbs.onScriptCreated failed:", exc); 2736 } 2737 }, 2738 2739 createdScriptHasCaller: function() 2740 { 2741 if (FBTrace.DBG_FBS_SRCUNITS) 2742 dumpComponentsStack("createdScriptHasCaller "); 2743 2744 // createdScriptHasCaller 2745 var frame = Components.stack; 2746 2747 // onScriptCreated 2748 frame = frame.caller; 2749 if (!frame) 2750 return frame; 2751 2752 // hook apply 2753 frame = frame.caller; 2754 if (!frame) 2755 return frame; 2756 2757 // native interpret? 2758 frame = frame.caller; 2759 if (!frame) 2760 return frame; 2761 2762 // our creator ... or null if we are top level 2763 frame = frame.caller; 2764 return frame; 2765 }, 2766 2767 onScriptDestroyed: function(script) 2768 { 2769 if (!fbs) 2770 return; 2771 2772 if (script.tag in fbs.onXScriptCreatedByTag) 2773 delete fbs.onXScriptCreatedByTag[script.tag]; 2774 2775 try 2776 { 2777 var fileName = script.fileName; 2778 if (isFilteredURL(fileName)) 2779 return; 2780 2781 if (FBTrace.DBG_FBS_CREATION) 2782 FBTrace.sysout('fbs.onScriptDestroyed '+script.tag); 2783 2784 dispatch(scriptListeners,"onScriptDestroyed",[script]); 2785 } 2786 catch(exc) 2787 { 2788 ERROR("onScriptDestroyed failed: "+exc); 2789 FBTrace.sysout("fbs.onScriptDestroyed failed: ", exc); 2790 } 2791 }, 2792 2793 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 2794 2795 createFilter: function(pattern, pass) 2796 { 2797 var filter = { 2798 globalObject: null, 2799 flags: pass ? (jsdIFilter.FLAG_ENABLED | jsdIFilter.FLAG_PASS) : jsdIFilter.FLAG_ENABLED, 2800 urlPattern: pattern, 2801 startLine: 0, 2802 endLine: 0 2803 }; 2804 return filter; 2805 }, 2806 2807 setChromeBlockingFilters: function() 2808 { 2809 if (!fbs.isChromeBlocked) 2810 { 2811 2812 jsd.appendFilter(this.noFilterHalter); // must be first 2813 jsd.appendFilter(this.noFilterTrace); // must be second 2814 jsd.appendFilter(this.filterChrome); 2815 jsd.appendFilter(this.filterComponents); 2816 jsd.appendFilter(this.filterFirebugComponents); 2817 jsd.appendFilter(this.filterModules); 2818 jsd.appendFilter(this.filterStringBundle); 2819 jsd.appendFilter(this.filterPrettyPrint); 2820 jsd.appendFilter(this.filterWrapper); 2821 2822 for (var i = 0; i < this.componentFilters.length; i++) 2823 jsd.appendFilter(this.componentFilters[i]); 2824 2825 fbs.isChromeBlocked = true; 2826 2827 if (FBTrace.DBG_FBS_BP) 2828 this.traceFilters("setChromeBlockingFilters with " + 2829 this.componentFilters.length + " component filters"); 2830 } 2831 }, 2832 2833 removeChromeBlockingFilters: function() 2834 { 2835 try 2836 { 2837 if (fbs.isChromeBlocked) 2838 { 2839 if (!this.filterChrome) 2840 FBTrace.sysout("fbs.removeChromeBlockingFilters is confused ", this); 2841 2842 jsd.removeFilter(this.filterChrome); 2843 jsd.removeFilter(this.filterComponents); 2844 jsd.removeFilter(this.filterFirebugComponents); 2845 jsd.removeFilter(this.filterModules); 2846 jsd.removeFilter(this.filterStringBundle); 2847 jsd.removeFilter(this.filterPrettyPrint); 2848 jsd.removeFilter(this.filterWrapper); 2849 jsd.removeFilter(this.noFilterHalter); 2850 jsd.removeFilter(this.noFilterTrace); 2851 2852 for (var i = 0; i < this.componentFilters.length; i++) 2853 jsd.removeFilter(this.componentFilters[i]); 2854 2855 fbs.isChromeBlocked = false; 2856 } 2857 } 2858 catch (err) 2859 { 2860 FBTrace.sysout("fbs.removeChromeBlockingFilters; EXCEPTION " + err, err); 2861 } 2862 2863 if (FBTrace.DBG_FBS_BP) 2864 this.traceFilters("removeChromeBlockingFilters"); 2865 }, 2866 2867 // call after components are loaded. 2868 createChromeBlockingFilters: function() 2869 { 2870 try 2871 { 2872 this.filterModules = this.createFilter("*/firefox/modules/*"); 2873 this.filterComponents = this.createFilter("*/firefox/components/*"); 2874 this.filterFirebugComponents = this.createFilter("*/modules/firebug-*"); 2875 this.filterStringBundle = this.createFilter("XStringBundle"); 2876 this.filterChrome = this.createFilter("chrome://*"); 2877 this.filterPrettyPrint = this.createFilter("x-jsd:ppbuffer*"); 2878 this.filterWrapper = this.createFilter("XPCSafeJSObjectWrapper.cpp"); 2879 this.noFilterHalter = this.createFilter("resource://firebug/debuggerHalter.js", true); 2880 this.noFilterTrace = this.createFilter("chrome://firebug/content/console/consoleExposed.js", true); 2881 2882 // jsdIFilter does not allow full regexp matching. 2883 // So to filter components, we filter their directory names, which we obtain 2884 // by looking for scripts that match regexps 2885 2886 var componentsUnfound = []; 2887 for (var i=0; i<COMPONENTS_FILTERS.length; ++i) 2888 componentsUnfound.push(COMPONENTS_FILTERS[i]); 2889 2890 this.componentFilters = []; 2891 2892 jsd.enumerateScripts( { 2893 enumerateScript: function(script) 2894 { 2895 var fileName = script.fileName; 2896 for (var i=0; i<componentsUnfound.length; ++i) 2897 { 2898 if (componentsUnfound[i].test(fileName)) 2899 { 2900 var match = componentsUnfound[i].exec(fileName); 2901 fbs.componentFilters.push(fbs.createFilter(match[1])); 2902 componentsUnfound.splice(i, 1); 2903 return; 2904 } 2905 } 2906 } 2907 }); 2908 } 2909 catch (exc) 2910 { 2911 FBTrace.sysout("fbs.createChromeblockingFilters ERROR >>>>>>>>>>>>>>>>> "+exc, exc); 2912 } 2913 2914 if (FBTrace.DBG_FBS_BP) 2915 { 2916 FBTrace.sysout("fbs.createChromeBlockingFilters considered "+COMPONENTS_FILTERS.length+ 2917 " regexps and created "+this.componentFilters.length+ 2918 " filters with unfound: "+componentsUnfound.length, componentsUnfound); 2919 fbs.traceFilters("createChromeBlockingFilters"); 2920 } 2921 }, 2922 2923 traceFilters: function(from) 2924 { 2925 FBTrace.sysout("fbs.traceFilters from "+from); 2926 2927 jsd.enumerateFilters({ enumerateFilter: function(filter) 2928 { 2929 FBTrace.sysout("fbs.jsdIFilter "+filter.urlPattern, filter); 2930 }}); 2931 }, 2932 2933 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 2934 2935 eachJSContext: function(callback) 2936 { 2937 var enumeratedContexts = []; 2938 jsd.enumerateContexts({ enumerateContext: function(jscontext) 2939 { 2940 try 2941 { 2942 if (!jscontext.isValid) 2943 return; 2944 2945 var wrappedGlobal = jscontext.globalObject; 2946 if (!wrappedGlobal) 2947 return; 2948 2949 var unwrappedGlobal = wrappedGlobal.getWrappedValue(); 2950 if (!unwrappedGlobal) 2951 return; 2952 2953 if (unwrappedGlobal instanceof Ci.nsISupports) 2954 var global = new XPCNativeWrapper(unwrappedGlobal); 2955 else 2956 var global = unwrappedGlobal; 2957 2958 if (FBTrace.DBG_FBS_JSCONTEXTS) 2959 FBTrace.sysout("fbs.getJSContexts jsIContext tag:"+jscontext.tag+ 2960 (jscontext.isValid?" - isValid\n":" - NOT valid\n")); 2961 2962 if (global) 2963 { 2964 callback(global, jscontext.tag); 2965 } 2966 else 2967 { 2968 if (FBTrace.DBG_FBS_JSCONTEXTS) 2969 FBTrace.sysout("fbs.getJSContexts no global object tag:"+jscontext.tag); 2970 return; // skip this 2971 } 2972 2973 enumeratedContexts.push(jscontext); 2974 } 2975 catch(e) 2976 { 2977 FBTrace.sysout("fbs.jscontext dump FAILED "+e, e); 2978 } 2979 }}); 2980 2981 return enumeratedContexts; 2982 }, 2983 2984 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 2985 2986 getOutermostScope: function(frame) 2987 { 2988 var scope = frame.scope; 2989 if (scope) 2990 { 2991 while(scope.jsParent) 2992 scope = scope.jsParent; 2993 2994 // These are just determined by trial and error. 2995 if (scope.jsClassName == "Window" || scope.jsClassName == "ChromeWindow" || 2996 scope.jsClassName == "ModalContentWindow") 2997 { 2998 lastWindowScope = wrapIfNative(scope.getWrappedValue()); 2999 return lastWindowScope; 3000 } 3001 3002 /* if (scope.jsClassName == "DedicatedWorkerGlobalScope") 3003 { 3004 //var workerScope = new XPCNativeWrapper(scope.getWrappedValue()); 3005 3006 //if (FBTrace.DBG_FBS_FINDDEBUGGER) 3007 // FBTrace.sysout("fbs.getOutermostScope found WorkerGlobalScope: "+scope.jsClassName, workerScope); 3008 // https://bugzilla.mozilla.org/show_bug.cgi?id=507930 if (FBTrace.DBG_FBS_FINDDEBUGGER) 3009 // FBTrace.sysout("fbs.getOutermostScope found WorkerGlobalScope.location: "+workerScope.location, workerScope.location); 3010 return null; // https://bugzilla.mozilla.org/show_bug.cgi?id=507783 3011 } 3012 */ 3013 if (scope.jsClassName == "Sandbox") 3014 { 3015 // Drop one frame see attachConsoleInjector 3016 var fileName = this.safeGetUrlFromFrame(frame); 3017 if (fileName && fileName.indexOf("console/consoleInjector.js") > 0) 3018 { 3019 if (frame.callingFrame) 3020 return fbs.getOutermostScope(frame.callingFrame); 3021 } 3022 3023 var proto = scope.jsPrototype; 3024 3025 // this is the path if we have web page in a sandbox 3026 if (proto.jsClassName == "XPCNativeWrapper") 3027 { 3028 proto = proto.jsParent; 3029 if (proto.jsClassName == "Window") 3030 return wrapIfNative(proto.getWrappedValue()); 3031 } 3032 else 3033 { 3034 return wrapIfNative(scope.getWrappedValue()); 3035 } 3036 } 3037 3038 if (FBTrace.DBG_FBS_FINDDEBUGGER) 3039 FBTrace.sysout("fbs.getOutermostScope found scope chain bottom, not Window: " + 3040 scope.jsClassName, scope); 3041 3042 return wrapIfNative(scope.getWrappedValue()); // not a window or a sandbox 3043 } 3044 else 3045 { 3046 return null; 3047 } 3048 }, 3049 3050 findDebugger: function(frame) 3051 { 3052 if (debuggers.length < 1) 3053 return; 3054 3055 var checkFrame = frame; 3056 while (checkFrame) // We may stop in a component, but want the callers Window 3057 { 3058 // the outermost lexical scope of the function running the frame 3059 var frameScopeRoot = this.getOutermostScope(checkFrame); 3060 if (frameScopeRoot) 3061 break; 3062 3063 if (FBTrace.DBG_FBS_FINDDEBUGGER) 3064 FBTrace.sysout("fbs.findDebugger no frame Window, looking to older stackframes", 3065 checkFrame); 3066 3067 checkFrame = checkFrame.callingFrame; 3068 } 3069 3070 if (!checkFrame && FBTrace.DBG_FBS_FINDDEBUGGER) 3071 FBTrace.sysout("fbs.findDebugger fell thru bottom of stack", frame); 3072 3073 // frameScopeRoot should be the top window for the scope of the frame function 3074 // or null 3075 var the_debuggr = fbs.askDebuggersForSupport(frameScopeRoot, frame); 3076 if (the_debuggr) 3077 return the_debuggr; 3078 3079 if (FBTrace.DBG_FBS_FINDDEBUGGER) 3080 FBTrace.sysout("fbs.findDebugger no debuggr on bottom frame", frame); 3081 3082 return null; 3083 }, 3084 3085 isChromebug: function(location) 3086 { 3087 // TODO this is a kludge: isFilteredURL stops users from seeing firebug but 3088 // chromebug has to disable the filter 3089 3090 if (location) 3091 { 3092 if (location.indexOf("chrome://chromebug/") >= 0 || 3093 location.indexOf("chrome://fb4cb/") >= 0) 3094 { 3095 return true; 3096 } 3097 } 3098 return false; 3099 }, 3100 3101 getLocationSafe: function(global) 3102 { 3103 try 3104 { 3105 // then we have a window, it will be an nsIDOMWindow, right? 3106 if (global && global.location) 3107 return global.location.toString(); 3108 else if (global && global.tag) 3109 return "global_tag_"+global.tag; 3110 } 3111 catch (exc) 3112 { 3113 // FF3 gives (NS_ERROR_INVALID_POINTER) [nsIDOMLocation.toString] 3114 } 3115 3116 return null; 3117 }, 3118 3119 safeGetUrlFromFrame: function(frame) 3120 { 3121 try 3122 { 3123 if (frame) 3124 return frame.script.fileName; 3125 } 3126 catch (err) 3127 { 3128 } 3129 return ""; 3130 }, 3131 3132 askDebuggersForSupport: function(global, frame) 3133 { 3134 if (FBTrace.DBG_FBS_FINDDEBUGGER) 3135 FBTrace.sysout("fbs.askDebuggersForSupport using global " + global + " for " + 3136 frame.script.fileName); 3137 3138 if (global && fbs.isChromebug(fbs.getLocationSafe(global))) 3139 return false; 3140 3141 if (FBTrace.DBG_FBS_FINDDEBUGGER) 3142 FBTrace.sysout("fbs.askDebuggersForSupport " + debuggers.length + 3143 " debuggers to check for " + frame.script.fileName, debuggers); 3144 3145 for (var i=debuggers.length-1; i>=0; i--) 3146 { 3147 try 3148 { 3149 var debuggr = debuggers[i]; 3150 if (debuggr.supportsGlobal(global, frame)) 3151 { 3152 if (!debuggr.breakContext) 3153 FBTrace.sysout("fbs.Debugger with no breakContext:",debuggr.supportsGlobal); 3154 3155 if (FBTrace.DBG_FBS_FINDDEBUGGER) 3156 FBTrace.sysout("fbs.findDebugger found debuggr (" + debuggr.debuggerName + 3157 ") at " + i + " with breakContext " + debuggr.breakContext.getName() + 3158 " for global " + fbs.getLocationSafe(global) + " while processing " + 3159 frame.script.fileName); 3160 3161 return debuggr; 3162 } 3163 } 3164 catch (exc) 3165 { 3166 FBTrace.sysout("fbs.askDebuggersForSupport ERROR: " + exc,exc); 3167 } 3168 } 3169 return null; 3170 }, 3171 3172 dumpIValue: function(value) 3173 { 3174 var listValue = {value: null}, lengthValue = {value: 0}; 3175 value.getProperties(listValue, lengthValue); 3176 3177 for (var i=0; i<lengthValue.value; ++i) 3178 { 3179 var prop = listValue.value[i]; 3180 try 3181 { 3182 var name = unwrapIValue(prop.name); 3183 FBTrace.sysout("fbs." + i + "]" + name + "=" + unwrapIValue(prop.value)); 3184 } 3185 catch (e) 3186 { 3187 FBTrace.sysout("fbs." + i + "]" + e); 3188 } 3189 } 3190 }, 3191 3192 reFindDebugger: function(frame, debuggr) 3193 { 3194 var frameScopeRoot = this.getOutermostScope(frame); 3195 if (frameScopeRoot && debuggr.supportsGlobal(frameScopeRoot, frame)) 3196 return debuggr; 3197 3198 if (FBTrace.DBG_FBS_FINDDEBUGGER) 3199 FBTrace.sysout("fbs.reFindDebugger debuggr " + debuggr.debuggerName + 3200 " does not support frameScopeRoot " + frameScopeRoot, frameScopeRoot); 3201 3202 return null; 3203 }, 3204 3205 discardRecursionFrames: function(frame) 3206 { 3207 var i = 0; 3208 var rest = 0; 3209 var mark = frame; // a in abcabcabcdef 3210 var point = frame; 3211 3212 while (point = point.callingFrame) 3213 { 3214 i++; 3215 3216 // then we found a repeating caller abcabcdef 3217 if (point.script.tag == mark.script.tag) 3218 { 3219 mark = point; 3220 rest = i; 3221 } 3222 } 3223 3224 // here point is null and mark is the last repeater, abcdef 3225 if (FBTrace.DBG_FBS_ERRORS) 3226 FBTrace.sysout("fbs.discardRecursionFrames dropped " + rest + " of " + i, mark); 3227 3228 return mark; 3229 }, 3230 3231 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 3232 // Breakpoints 3233 3234 // jsd breakpoints are on a PC in a jsdIScript 3235 // Users breakpoint on a line of source 3236 // Because test.js can be included multiple times, the URL+line number from the UI is not unique. 3237 // sourcefile.href != script.fileName, generally script.fileName cannot be used. 3238 // If the source is compiled, then we have zero, one, or more jsdIScripts on a line. 3239 // If zero, cannot break at that line 3240 // If one, set a jsd breakpoint 3241 // If more than one, set jsd breakpoint on each script 3242 // Else we know that the source will be compiled before it is run. 3243 // Save the sourceFile.href+line and set the jsd breakpoint when we compile 3244 // Venkman called these "future" breakpoints 3245 // We cannot prevent future breakpoints on lines that have no script. 3246 // Break onCreate with error? 3247 addBreakpoint: function(type, sourceFile, lineNo, props, debuggr) 3248 { 3249 var url = sourceFile.href; 3250 var bp = this.findBreakpoint(url, lineNo); 3251 if (bp && bp.type & type) 3252 return null; 3253 3254 if (bp) 3255 { 3256 bp.type |= type; 3257 3258 if (debuggr) 3259 { 3260 bp.debuggerName = debuggr.debuggerName; 3261 } 3262 else 3263 { 3264 if (FBTrace.DBG_FBS_BP) 3265 FBTrace.sysout("fbs.addBreakpoint with no debuggr:"); 3266 } 3267 } 3268 else 3269 { 3270 bp = this.recordBreakpoint(type, url, lineNo, debuggr, props, sourceFile); 3271 } 3272 3273 if (FBTrace.DBG_FBS_BP) 3274 FBTrace.sysout("fbs.addBreakpoint for "+url, [bp, sourceFile]); 3275 3276 return bp; 3277 }, 3278 3279 recordBreakpoint: function(type, url, lineNo, debuggr, props, sourceFile) 3280 { 3281 var urlBreakpoints = fbs.getBreakpoints(url); 3282 if (!urlBreakpoints) 3283 urlBreakpoints = []; 3284 3285 if (typeof(lineNo) !== 'number') 3286 throw new Error("firebug-service line numbers must be numbers "+lineNo+"@"+url); 3287 3288 var bp = {type: type, href: url, lineNo: lineNo, disabled: 0, 3289 debuggerName: debuggr.debuggerName, 3290 condition: "", onTrue: true, hitCount: -1, hit: 0}; 3291 3292 if (props) 3293 { 3294 bp.condition = props.condition; 3295 bp.onTrue = props.onTrue; 3296 bp.hitCount = props.hitCount; 3297 if (bp.condition || bp.hitCount > 0) 3298 ++conditionCount; 3299 if(props.disabled) 3300 { 3301 bp.disabled |= BP_NORMAL; 3302 ++disabledCount; 3303 } 3304 } 3305 3306 urlBreakpoints.push(bp); 3307 fbs.setJSDBreakpoint(sourceFile, bp); 3308 fbs.setBreakpoints(url, urlBreakpoints); 3309 ++breakpointCount; 3310 3311 return bp; 3312 }, 3313 3314 removeBreakpoint: function(type, url, lineNo) 3315 { 3316 var urlBreakpoints = fbs.getBreakpoints(url); 3317 3318 if (FBTrace.DBG_FBS_BP) 3319 FBTrace.sysout("fbs.removeBreakpoint for "+url+", need to check bps="+ 3320 (urlBreakpoints?urlBreakpoints.length:"none")); 3321 3322 if (!urlBreakpoints) 3323 return false; 3324 3325 for (var i=0; i<urlBreakpoints.length; ++i) 3326 { 3327 var bp = urlBreakpoints[i]; 3328 if (FBTrace.DBG_FBS_BP) 3329 FBTrace.sysout("fbs.removeBreakpoint checking bp.lineNo vs lineNo=" + bp.lineNo + 3330 " vs " + lineNo); 3331 3332 if (bp.lineNo === lineNo) 3333 { 3334 bp.type &= ~type; 3335 if (!bp.type) 3336 { 3337 if (bp.scriptsWithBreakpoint) 3338 { 3339 for (var j=0; j<bp.scriptsWithBreakpoint.length; j++) 3340 { 3341 var script = bp.scriptsWithBreakpoint[j]; 3342 if (script && script.isValid) 3343 { 3344 try 3345 { 3346 script.clearBreakpoint(bp.pc[j]); 3347 if (FBTrace.DBG_FBS_BP) 3348 FBTrace.sysout("fbs.removeBreakpoint in tag=" + script.tag + 3349 " at " + lineNo + "@" + url); 3350 } 3351 catch (exc) 3352 { 3353 FBTrace.sysout("fbs.Firebug service failed to remove breakpoint in " + 3354 script.tag + " at lineNo=" + lineNo + " pcmap:" + bp.pcmap); 3355 } 3356 } 3357 } 3358 } 3359 3360 // else this was a future breakpoint that never hit or a script that was GCed 3361 3362 urlBreakpoints.splice(i, 1); 3363 --breakpointCount; 3364 3365 if (bp.disabled) 3366 --disabledCount; 3367 3368 if (bp.condition || bp.hitCount > 0) 3369 --conditionCount; 3370 3371 fbs.setBreakpoints(url, urlBreakpoints); 3372 } 3373 3374 return bp; 3375 } 3376 } 3377 3378 if (FBTrace.DBG_FBS_BP) 3379 FBTrace.sysout("fbs.removeBreakpoint no find for " + lineNo + "@" + url + " in " + 3380 urlBreakpoints.length, urlBreakpoints); 3381 3382 return false; 3383 }, 3384 3385 findBreakpoint: function(url, lineNo) 3386 { 3387 var urlBreakpoints = fbs.getBreakpoints(url); 3388 if (urlBreakpoints) 3389 { 3390 for (var i = 0; i < urlBreakpoints.length; ++i) 3391 { 3392 var bp = urlBreakpoints[i]; 3393 if (bp.lineNo === lineNo) 3394 return bp; 3395 } 3396 } 3397 3398 if (FBTrace.DBG_FBS_BP) 3399 FBTrace.sysout("fbs.findBreakpoint no find for "+lineNo+"@"+url, urlBreakpoints); 3400 3401 return null; 3402 }, 3403 3404 // When we are called, scripts have been compiled so all relevant breakpoints are not "future" 3405 findBreakpointByScript: function(script, pc) 3406 { 3407 var urlsWithBreakpoints = fbs.getBreakpointURLs(); 3408 for (var iURL = 0; iURL < urlsWithBreakpoints.length; iURL++) 3409 { 3410 var url = urlsWithBreakpoints[iURL]; 3411 var urlBreakpoints = fbs.getBreakpoints(url); 3412 if (urlBreakpoints) 3413 { 3414 for (var iBreakpoint = 0; iBreakpoint < urlBreakpoints.length; ++iBreakpoint) 3415 { 3416 var bp = urlBreakpoints[iBreakpoint]; 3417 if (bp.scriptsWithBreakpoint) 3418 { 3419 for (var iScript = 0; iScript < bp.scriptsWithBreakpoint.length; iScript++) 3420 { 3421 if (FBTrace.DBG_FBS_BP) 3422 { 3423 var vs = (bp.scriptsWithBreakpoint[iScript] ? 3424 bp.scriptsWithBreakpoint[iScript].tag + "@" + bp.pc[iScript] : 3425 "future") + " on " + url; 3426 3427 FBTrace.sysout("fbs.findBreakpointByScript[" + iURL + "," + iBreakpoint + 3428 "," + iScript + "]" + " looking for " + script.tag + "@" + pc + 3429 " vs " + vs); 3430 } 3431 3432 if (bp.scriptsWithBreakpoint[iScript] && 3433 (bp.scriptsWithBreakpoint[iScript].tag == script.tag) && 3434 (bp.pc[iScript] == pc)) 3435 { 3436 return bp; 3437 } 3438 } 3439 } 3440 } 3441 } 3442 } 3443 3444 return null; 3445 }, 3446 3447 // the sourcefile has just been created after compile 3448 resetBreakpoints: function(sourceFile, debuggr) 3449 { 3450 // If the new script is replacing an old script with a breakpoint still 3451 var url = sourceFile.href; 3452 var urlBreakpoints = fbs.getBreakpoints(url); 3453 3454 if (FBTrace.DBG_FBS_BP) 3455 { 3456 try 3457 { 3458 var msg = "resetBreakpoints: breakpoints["+sourceFile.href; 3459 msg += "]="+(urlBreakpoints?urlBreakpoints.length:"NONE")+"\n"; 3460 FBTrace.sysout(msg); 3461 } 3462 catch (exc) 3463 { 3464 FBTrace.sysout("fbs.Failed to give resetBreakpoints trace in url: " + url + 3465 " because " + exc + " for urlBreakpoints=", urlBreakpoints); 3466 } 3467 } 3468 3469 if (urlBreakpoints) 3470 { 3471 if (FBTrace.DBG_FBS_BP) 3472 FBTrace.sysout("fbs.resetBreakpoints total bp=" + urlBreakpoints.length + 3473 " for url=" + url); 3474 3475 fbs.deleteBreakpoints(url); 3476 3477 for (var i=0; i<urlBreakpoints.length; ++i) 3478 { 3479 var bp = urlBreakpoints[i]; 3480 bp = fbs.recordBreakpoint(bp.type, url, bp.lineNo, debuggr, bp, sourceFile); 3481 3482 if (bp.type & BP_ERROR) 3483 { 3484 var existingBP = null; 3485 fbs.enumerateErrorBreakpoints(url, {call: function checkExisting(url, lineNo, bp) 3486 { 3487 // An error breakpoint is in this file 3488 if (lineNo == bp.lineNo) 3489 existingBP = true; 3490 }}); 3491 3492 if (!existingBP) 3493 errorBreakpoints.push(bp); // TODO implement as hashtable errorBreakpoints[url@lineNo] 3494 } 3495 3496 if (bp.disabled & BP_NORMAL) 3497 { 3498 if (FBTrace.DBG_FBS_BP) 3499 FBTrace.sysout("fbs.resetBreakpoints: mark breakpoint disabled: " + 3500 bp.lineNo + "@" + sourceFile); 3501 3502 fbs.disableBreakpoint(url, bp.lineNo); 3503 } 3504 else 3505 { 3506 if (FBTrace.DBG_FBS_BP) 3507 FBTrace.sysout("fbs.resetBreakpoints: "+bp.lineNo+"@"+sourceFile); 3508 } 3509 } 3510 } 3511 else 3512 { 3513 if (FBTrace.DBG_FBS_BP) 3514 FBTrace.sysout("fbs.resetBreakpoints no breakpoints for "+url); 3515 } 3516 }, 3517 3518 setJSDBreakpoint: function(sourceFile, bp) 3519 { 3520 var scripts = sourceFile.getScriptsAtLineNumber(bp.lineNo); 3521 if (!scripts) 3522 { 3523 if (FBTrace.DBG_FBS_BP) 3524 FBTrace.sysout("fbs.setJSDBreakpoint: NO inner scripts: "+bp.lineNo+"@"+sourceFile); 3525 3526 if (!sourceFile.outerScript || !sourceFile.outerScript.isValid) 3527 { 3528 if (FBTrace.DBG_FBS_BP) 3529 FBTrace.sysout("fbs.setJSDBreakpoint: NO valid outerScript\n"); 3530 return; 3531 } 3532 3533 scripts = [sourceFile.outerScript]; 3534 } 3535 3536 if (!bp.scriptsWithBreakpoint) 3537 { 3538 bp.scriptsWithBreakpoint = []; 3539 bp.pc = []; 3540 } 3541 3542 for (var i=0; i<scripts.length; i++) 3543 { 3544 var script = scripts[i]; 3545 if (!script.isValid) 3546 { 3547 if (FBTrace.DBG_FBS_BP) 3548 FBTrace.sysout("fbs.setJSDBreakpoint: tag " + script.tag + ", " + i + "/" + 3549 scripts.length + " is invalid"); 3550 continue; 3551 } 3552 3553 var haveScript = false; 3554 for (var j=0; j<bp.scriptsWithBreakpoint.length; j++) 3555 { 3556 if (bp.scriptsWithBreakpoint[j].tag === script.tag) 3557 { 3558 haveScript = true; 3559 break; 3560 } 3561 } 3562 3563 if (haveScript) 3564 continue; 3565 3566 var pcmap = sourceFile.pcmap_type; 3567 if (!pcmap) 3568 { 3569 if (FBTrace.DBG_FBS_ERRORS) 3570 FBTrace.sysout("fbs.setJSDBreakpoint pcmap undefined " + 3571 sourceFile, sourceFile); 3572 3573 pcmap = PCMAP_SOURCETEXT; 3574 } 3575 3576 // we subtraced this offset when we showed the user so lineNo is a user line 3577 // number; now we need to talk 3578 // to jsd its line world 3579 var jsdLine = bp.lineNo + sourceFile.getBaseLineOffset(); 3580 // test script.isLineExecutable(jsdLineNo, pcmap) ?? 3581 3582 var isExecutable = false; 3583 try 3584 { 3585 isExecutable = script.isLineExecutable(jsdLine, pcmap); 3586 } 3587 catch(e) 3588 { 3589 // guess not then... 3590 } 3591 3592 if (isExecutable) 3593 { 3594 var pc = script.lineToPc(jsdLine, pcmap); 3595 var pcToLine = script.pcToLine(pc, pcmap); // avoid calling this unless we have to... 3596 3597 if (pcToLine == jsdLine) 3598 { 3599 script.setBreakpoint(pc); 3600 3601 bp.scriptsWithBreakpoint.push(script); 3602 bp.pc.push(pc); 3603 bp.pcmap = pcmap; 3604 bp.jsdLine = jsdLine; 3605 3606 if (pc == 0) // signal the breakpoint handler to break for user 3607 sourceFile.breakOnZero = script.tag; 3608 3609 if (FBTrace.DBG_FBS_BP) 3610 FBTrace.sysout("fbs.setJSDBreakpoint tag: " + script.tag + " line.pc@url=" + 3611 bp.lineNo + "." + pc + "@" + sourceFile.href + " using offset:" + 3612 sourceFile.getBaseLineOffset() + " jsdLine: " + jsdLine + 3613 " pcToLine: " + pcToLine + 3614 (isExecutable ? " isExecuable" : " notExecutable"), 3615 {sourceFile: sourceFile, script: script}); 3616 } 3617 else 3618 { 3619 if (FBTrace.DBG_FBS_BP) 3620 FBTrace.sysout("fbs.setJSDBreakpoint LINE MISMATCH for tag: " + 3621 script.tag + " line.pc@url=" + bp.lineNo + "." + pc + "@" + 3622 sourceFile.href + " using offset:" + sourceFile.getBaseLineOffset() + 3623 " jsdLine: " + jsdLine + " pcToLine: " + pcToLine + 3624 (isExecutable ? " isExecuable" : " notExecutable"), sourceFile); 3625 } 3626 } 3627 else 3628 { 3629 if (FBTrace.DBG_FBS_BP) 3630 FBTrace.sysout("fbs.setJSDBreakpoint NOT isExecutable tag: " + script.tag + 3631 " jsdLine@url=" + jsdLine + "@" + sourceFile.href + " pcmap:" + 3632 pcmap + " baselineOffset:" + sourceFile.getBaseLineOffset(), script); 3633 } 3634 } 3635 }, 3636 3637 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 3638 3639 saveBreakpoints: function(url) 3640 { 3641 // Do not call fbs.setBreakpoints() it calls us. 3642 try 3643 { 3644 var urlBreakpoints = fbs.getBreakpoints(url); 3645 3646 if (!urlBreakpoints || !urlBreakpoints.length) 3647 { 3648 fbs.breakpointStore.removeItem(url); 3649 fbs.deleteBreakpoints(url); 3650 return; 3651 } 3652 3653 var cleanBPs = []; 3654 for(var i = 0; i < urlBreakpoints.length; i++) 3655 { 3656 var bp = urlBreakpoints[i]; 3657 3658 // Do not store breakpoins for "Run until this line". These are not 3659 // visible in Firebug UI and so, it is not possible to remove them. 3660 // Note that there can be cases where such breakpoint is not removed 3661 // by RunUntil (e.g. crash). 3662 if (bp.type == BP_UNTIL) 3663 continue; 3664 3665 var cleanBP = {}; 3666 for (var p in bp) 3667 cleanBP[p] = bp[p]; 3668 3669 delete cleanBP.scriptsWithBreakpoint; // not JSON-able 3670 delete cleanBP.pc; // co-indexed with scriptsWithBreakpoint 3671 delete cleanBP.debuggerName; 3672 cleanBPs.push(cleanBP); 3673 } 3674 3675 fbs.breakpointStore.setItem(url, cleanBPs); 3676 } 3677 catch (exc) 3678 { 3679 FBTrace.sysout("fbs.saveBreakpoints ERROR " + exc, exc); 3680 } 3681 }, 3682 3683 setBreakpoints: function(url, urlBreakpoints) 3684 { 3685 fbs.breakpoints[url] = urlBreakpoints; 3686 fbs.saveBreakpoints(url); 3687 }, 3688 3689 getBreakpoints: function(url) 3690 { 3691 return fbs.breakpoints[url]; 3692 }, 3693 3694 deleteBreakpoints: function(url) 3695 { 3696 delete fbs.breakpoints[url]; 3697 }, 3698 3699 getBreakpointURLs: function() 3700 { 3701 var breakpointStore = this.getBreakpointStore(); 3702 if (!breakpointStore) 3703 return []; 3704 3705 return breakpointStore.getKeys(); 3706 }, 3707 3708 getBreakpointStore: function() 3709 { 3710 if (this.breakpointStore) 3711 return this.breakpointStore; 3712 3713 try 3714 { 3715 Components.utils.import("resource://firebug/storageService.js"); 3716 3717 if (typeof(StorageService) != "undefined") 3718 { 3719 this.breakpointStore = StorageService.getStorage("breakpoints.json"); 3720 } 3721 else 3722 { 3723 ERROR("firebug-service breakpoint StorageService ERROR"); 3724 3725 this.breakpointStore = 3726 { 3727 setItem: function(){}, 3728 removeItem: function(){}, 3729 getKeys: function(){return [];}, 3730 clear: function(){}, 3731 }; 3732 } 3733 3734 return this.breakpointStore; 3735 } 3736 catch(exc) 3737 { 3738 // Throws another exception since fbs is null. 3739 //ERROR("firebug-service restoreBreakpoints ERROR "+exc); 3740 3741 // xxxHonza: why I can't see this log in the Tracing Console? 3742 FBTrace.sysout("fbs.restoreBreakpoints ERROR " + exc, exc); 3743 } 3744 }, 3745 3746 restoreBreakpoints: function() 3747 { 3748 this.breakpoints = {}; 3749 3750 var breakpointStore = fbs.getBreakpointStore(); 3751 if (!breakpointStore) 3752 return; 3753 3754 var urls = fbs.getBreakpointURLs(); 3755 for (var i=0; i<urls.length; i++) 3756 { 3757 var url = urls[i]; 3758 var bps = breakpointStore.getItem(url); 3759 3760 // Do not restore "Run unit this line" breakpoints. This should solve complaints 3761 // about Firebug breaking in the source even if there are no breakpoints in 3762 // Firebug UI. 3763 if (bps.type == BP_UNTIL) 3764 continue; 3765 3766 this.breakpoints[url] = bps; 3767 3768 for (var j=0; j<bps.length; j++) 3769 { 3770 var bp = bps[j]; 3771 if (bp.condition) 3772 ++conditionCount; 3773 if (bp.disabled) 3774 ++disabledCount; 3775 if (bp.type & BP_MONITOR) 3776 ++monitorCount; 3777 } 3778 } 3779 3780 if (FBTrace.DBG_FBS_BP) 3781 { 3782 FBTrace.sysout("fbs.restoreBreakpoints "+urls.length+", disabledCount:"+disabledCount+ 3783 " monitorCount:"+monitorCount+" conditionCount:"+conditionCount+", restored ", 3784 this.breakpoints); 3785 3786 for (var p in this.breakpoints) 3787 FBTrace.sysout("fbs.restoreBreakpoints restored "+p+" condition "+ 3788 this.breakpoints[p].condition); 3789 } 3790 }, 3791 3792 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 3793 // When (debugger keyword and not halt)||(bp and BP_UNTIL) || (onBreakPoint && no conditions) 3794 // || interuptHook (single stepping). 3795 // rv is ignored 3796 routeBreakToDebuggr: function(frame, type, rv, stepStayOnDebuggr) 3797 { 3798 try 3799 { 3800 // avoid step_out from web page to chrome 3801 if (stepStayOnDebuggr) 3802 { 3803 var debuggr = this.reFindDebugger(frame, stepStayOnDebuggr); 3804 if (FBTrace.DBG_FBS_STEP) 3805 FBTrace.sysout("fbs.routeBreakToDebuggr type="+getExecutionStopNameFromType(type)+ 3806 " stepStayOnDebuggr "+stepStayOnDebuggr+" debuggr:"+(debuggr?debuggr:"null")); 3807 3808 if (!debuggr) // then the frame is not for our debugger 3809 return RETURN_CONTINUE; // This means that we will continue to take interrupts until when? 3810 } 3811 else 3812 { 3813 var debuggr = this.findDebugger(frame); 3814 3815 if (FBTrace.DBG_FBS_STEP) 3816 FBTrace.sysout("fbs.routeBreakToDebuggr type="+getExecutionStopNameFromType(type)+ 3817 " debuggr:"+(debuggr?debuggr:"null")); 3818 } 3819 3820 if (debuggr) 3821 return this.breakIntoDebugger(debuggr, frame, type); 3822 } 3823 catch(exc) 3824 { 3825 if (FBTrace.DBG_FBS_ERRORS) 3826 FBTrace.sysout("fbs.routeBreakToDebuggr failed: "+exc,exc); 3827 ERROR("routeBreakToDebuggr failed: "+exc, exc); 3828 } 3829 3830 return RETURN_CONTINUE; 3831 }, 3832 3833 breakIntoDebugger: function(debuggr, frame, type) 3834 { 3835 if (FBTrace.DBG_FBS_STEP || FBTrace.DBG_FBS_BP) 3836 FBTrace.sysout("fbs.breakIntoDebugger called "+debuggr.debuggerName+ 3837 " fbs.isChromeBlocked:"+fbs.isChromeBlocked); 3838 3839 // Before we break, clear information about previous stepping session 3840 this.stopStepping(frame); 3841 3842 // Break into the debugger - execution will stop here until the user resumes 3843 var returned; 3844 try 3845 { 3846 var debuggr = this.reFindDebugger(frame, debuggr); 3847 returned = debuggr.onBreak(frame, type); 3848 } 3849 catch (exc) 3850 { 3851 ERROR("breakIntoDebugger ERROR "+exc, exc); 3852 returned = RETURN_CONTINUE; 3853 } 3854 3855 // Execution resumes now. Check if the user requested stepping and if so 3856 // install the necessary hooks 3857 this.startStepping(frame); 3858 3859 if (FBTrace.DBG_FBS_STEP || FBTrace.DBG_FBS_BP) 3860 FBTrace.sysout("fbs.breakIntoDebugger returning "+returned+" from "+ 3861 debuggr.debuggerName+" with jsd.pauseDepth "+jsd.pauseDepth+" and functionHook "+ 3862 jsd.functionHook); 3863 3864 return returned; 3865 }, 3866 3867 needToBreakForError: function(reportNextError) 3868 { 3869 return this.breakOnErrors || fbs.breakOnDebugCall; 3870 }, 3871 3872 // debuggr calls us to stage stepping 3873 step: function(mode, context, debuggr) 3874 { 3875 var stepper; 3876 3877 if (mode === STEP_INTO) 3878 stepper = new IntoStepper(debuggr, context); 3879 else if (mode === STEP_OVER) 3880 stepper = new LineStepper(debuggr, context); 3881 else if (mode === STEP_OUT) 3882 stepper = new OutStepper(debuggr, context); 3883 3884 if (stepper) 3885 jsdHandlers.add(stepper); 3886 else 3887 ERROR("fbs.step ERROR unknown mode "+mode); 3888 3889 // The actual stepping starts after we resume the debuggger. Stepping is always 3890 // done when the execution/debugger is paused, so we need to resume and break e.g. 3891 // on the next line. 3892 }, 3893 3894 startStepping: function(frame) // if needed 3895 { 3896 jsdHandlers.hook(frame); 3897 }, 3898 3899 stopStepping: function(frame, context) 3900 { 3901 jsdHandlers.unhook(frame); 3902 }, 3903 3904 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 3905 // Hook Interupts 3906 3907 hookInterrupts: function(frame) 3908 { 3909 // TODO move the try code in hook() to dispatch 3910 jsd.interruptHook = { onExecute: hook(this.onInterrupt, RETURN_CONTINUE)}; 3911 3912 if (frame) 3913 ScriptInterrupter.enable(frame.script); 3914 3915 if (FBTrace.DBG_FBS_STEP) 3916 FBTrace.sysout("fbs.set InterruptHook frame.script.tag: "+ 3917 (frame?frame.script.tag:"<no frame>"), ScriptInterrupter); 3918 }, 3919 3920 onInterrupt: function(frame, type, rv) 3921 { 3922 return jsdHandlers.dispatch("onInterrupt", frame, type); 3923 }, 3924 3925 unhookInterrupts: function(frame) 3926 { 3927 jsd.interruptHook = null; 3928 3929 ScriptInterrupter.disableAll(); 3930 }, 3931 3932 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 3933 // Hook Functions 3934 3935 hookFunctions: function() 3936 { 3937 if (FBTrace.DBG_FBS_STEP) 3938 FBTrace.sysout("fbs.set functionHook"); 3939 3940 jsd.functionHook = { onCall: hook(this.onFunction, true) }; 3941 jsd.topLevelHook = { onCall: hook(this.onFunction, true) }; 3942 }, 3943 3944 onFunction: function(frame, type) // called in try/catch block with this === fbs 3945 { 3946 switch (type) 3947 { 3948 case TYPE_TOPLEVEL_START: // fall through 3949 case TYPE_FUNCTION_CALL: // the frame will be running the called script 3950 { 3951 jsdHandlers.dispatch("onFunctionCall", frame, shiftCallType(type)); 3952 break; 3953 } 3954 case TYPE_TOPLEVEL_END: // fall through 3955 case TYPE_FUNCTION_RETURN: // the frame will be running the called script 3956 { 3957 jsdHandlers.dispatch("onFunctionReturn", frame, shiftCallType(type)); 3958 break; 3959 } 3960 } 3961 }, 3962 3963 unhookFunctions: function() 3964 { 3965 jsd.functionHook = null; 3966 jsd.topLevelHook = null; 3967 }, 3968 3969 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 3970 // Hook Scripts 3971 3972 hookScripts: function() 3973 { 3974 if (FBTrace.DBG_FBS_STEP || FBTrace.DBG_FBS_TRACKFILES) 3975 FBTrace.sysout("fbs.set scriptHook\n"); 3976 3977 jsd.scriptHook = { 3978 onScriptCreated: hook(this.onScriptCreated), 3979 onScriptDestroyed: hook(this.onScriptDestroyed) 3980 }; 3981 3982 if (fbs.filterSystemURLs) 3983 fbs.removeChromeBlockingFilters(); 3984 3985 jsd.clearFilters(); 3986 3987 if (fbs.filterSystemURLs) 3988 fbs.setChromeBlockingFilters(); 3989 3990 jsd.debuggerHook = { onExecute: hook(this.onDebugger, RETURN_CONTINUE) }; 3991 jsd.debugHook = { onExecute: hook(this.onDebug, RETURN_CONTINUE) }; 3992 jsd.breakpointHook = { onExecute: hook(this.onBreakpoint, RETURN_CONTINUE) }; 3993 jsd.throwHook = { onExecute: hook(this.onThrow, RETURN_CONTINUE_THROW) }; 3994 jsd.errorHook = { onError: hook(this.onError, true) }; 3995 }, 3996 3997 unhookScripts: function() 3998 { 3999 jsd.scriptHook = null; 4000 fbs.removeChromeBlockingFilters(); 4001 4002 if (FBTrace.DBG_FBS_STEP || FBTrace.DBG_FBS_TRACKFILES) 4003 FBTrace.sysout("fbs.unset scriptHook\n"); 4004 }, 4005 4006 // TODO rewrite as a Stepper 4007 // xxxJJB: perhaps xxxHonza could implement the stepper, but what the code is responsible for? 4008 // xxxhonza: traceAll and trace a function. I don't think these work anyway, a good start is to remove the old code. 4009 hookCalls: function(callBack, unhookAtBottom) 4010 { 4011 var contextCached = null; 4012 4013 function callHook(frame, type) 4014 { 4015 switch (type) 4016 { 4017 case TYPE_FUNCTION_CALL: 4018 { 4019 ++hookFrameCount; 4020 4021 if (FBTrace.DBG_FBS_STEP) 4022 FBTrace.sysout("fbs.callHook TYPE_FUNCTION_CALL "+frame.script.fileName); 4023 4024 contextCached = callBack(contextCached, frame, hookFrameCount, true); 4025 4026 break; 4027 } 4028 case TYPE_FUNCTION_RETURN: 4029 { 4030 if (hookFrameCount <= 0) // ignore returns until we have started back in 4031 return; 4032 4033 --hookFrameCount; 4034 4035 if (FBTrace.DBG_FBS_STEP) 4036 FBTrace.sysout("fbs.functionHook TYPE_FUNCTION_RETURN " + 4037 frame.script.fileName); 4038 4039 // stack empty 4040 if (unhookAtBottom && hookFrameCount == 0) 4041 fbs.unhookFunctions(); 4042 4043 contextCached = callBack(contextCached, frame, hookFrameCount, false); 4044 break; 4045 } 4046 } 4047 } 4048 4049 if (jsd.functionHook) 4050 { 4051 if (FBTrace.DBG_FBS_ERRORS) 4052 FBTrace.sysout("fbs.hookCalls cannot set functionHook, one is already set"); 4053 return; 4054 } 4055 4056 if (FBTrace.DBG_FBS_STEP) 4057 FBTrace.sysout("fbs.set callHook"); 4058 4059 hookFrameCount = 0; 4060 jsd.functionHook = { onCall: callHook }; 4061 }, 4062 4063 getJSD: function() 4064 { 4065 return jsd; // for debugging fbs 4066 }, 4067 4068 dumpFileTrack: function(moreFiles) 4069 { 4070 if (moreFiles) 4071 trackFiles.merge(moreFiles); 4072 trackFiles.dump(); 4073 }, 4074 }; 4075 4076 // ********************************************************************************************* // 4077 // Script Interrupt Manager 4078 4079 var ScriptInterrupter = 4080 { 4081 entries: {}, 4082 4083 enable: function(script) 4084 { 4085 if (!script.enableSingleStepInterrupts) 4086 return; 4087 4088 if (this.entries[script.tag]) 4089 return; 4090 4091 try 4092 { 4093 script.enableSingleStepInterrupts(true); 4094 } 4095 catch (e) 4096 { 4097 if (FBTrace.DBG_ERRORS) 4098 FBTrace.sysout("fbs.ScriptInterrupter.enable; EXCEPTION"); 4099 } 4100 4101 this.entries[script.tag] = { 4102 script: script 4103 } 4104 }, 4105 4106 disable: function(script) 4107 { 4108 if (!script.enableSingleStepInterrupts) 4109 return; 4110 4111 var entry = this.entries[script.tag]; 4112 if (!entry) 4113 return; 4114 4115 try 4116 { 4117 script.enableSingleStepInterrupts(false); 4118 } 4119 catch (e) 4120 { 4121 if (FBTrace.DBG_ERRORS) 4122 FBTrace.sysout("fbs.ScriptInterrupter.disable; EXCEPTION"); 4123 } 4124 4125 delete this.entries[script.tag]; 4126 }, 4127 4128 disableAll: function() 4129 { 4130 for (var tag in this.entries) 4131 { 4132 var entry = this.entries[tag]; 4133 if (!entry.script.enableSingleStepInterrupts) 4134 return; 4135 4136 try 4137 { 4138 entry.script.enableSingleStepInterrupts(false); 4139 } 4140 catch (e) 4141 { 4142 if (FBTrace.DBG_ERRORS) 4143 FBTrace.sysout("fbs.ScriptInterrupter.disable; EXCEPTION"); 4144 } 4145 } 4146 4147 this.entries = {}; 4148 } 4149 }; 4150 4151 // ********************************************************************************************* // 4152 // Local Helpers 4153 4154 function getStepName(mode) 4155 { 4156 if (mode == STEP_OVER) 4157 return "STEP_OVER"; 4158 4159 if (mode == STEP_INTO) 4160 return "STEP_INTO"; 4161 4162 if (mode == STEP_OUT) 4163 return "STEP_OUT"; 4164 4165 if (mode == STEP_SUSPEND) 4166 return "STEP_SUSPEND"; 4167 4168 return "(not a step mode)"; 4169 } 4170 4171 // called by enumerateScripts, onThrow, onDebug, onScriptCreated/Destroyed. 4172 function isFilteredURL(rawJSD_script_filename) 4173 { 4174 if (!rawJSD_script_filename) 4175 return true; 4176 if (fbs.filterConsoleInjections) 4177 return true; 4178 if (rawJSD_script_filename[0] == 'h') 4179 return false; 4180 if (rawJSD_script_filename == "XPCSafeJSObjectWrapper.cpp") 4181 return true; 4182 if (fbs.filterSystemURLs) 4183 return systemURLStem(rawJSD_script_filename); 4184 4185 for (var i=0; i<fbs.alwayFilterURLsStarting.length; i++) 4186 { 4187 if (rawJSD_script_filename.indexOf(fbs.alwayFilterURLsStarting[i]) != -1) 4188 return true; 4189 } 4190 4191 return false; 4192 } 4193 4194 function systemURLStem(rawJSD_script_filename) 4195 { 4196 // attempt to optimize stream of similar urls 4197 if (this.url_class) 4198 { 4199 if (rawJSD_script_filename.substr(0, this.url_class.length) == this.url_class) 4200 return this.url_class; 4201 } 4202 4203 this.url_class = deepSystemURLStem(rawJSD_script_filename); 4204 return this.url_class; 4205 } 4206 4207 function deepSystemURLStem(rawJSD_script_filename) 4208 { 4209 for (var i=0; i<urlFilters.length; ++i) 4210 { 4211 var filter = urlFilters[i]; 4212 if ( rawJSD_script_filename.substr(0,filter.length) == filter ) 4213 return filter; 4214 } 4215 4216 for (var i=0; i<COMPONENTS_FILTERS.length; ++i) 4217 { 4218 if (COMPONENTS_FILTERS[i].test(rawJSD_script_filename)) 4219 { 4220 var match = COMPONENTS_FILTERS[i].exec(rawJSD_script_filename); 4221 urlFilters.push(match[1]); // cache this for future calls 4222 return match[1]; 4223 } 4224 } 4225 4226 return false; 4227 } 4228 4229 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 4230 4231 function dispatch(listeners, name, args) 4232 { 4233 var totalListeners = listeners.length; 4234 for (var i=0; i<totalListeners; ++i) 4235 { 4236 var listener = listeners[i]; 4237 if (listener.hasOwnProperty(name) && listener[name]) 4238 listener[name].apply(listener, args); 4239 } 4240 4241 //if (FBTrace.DBG_FBS_ERRORS) 4242 // FBTrace.sysout("fbs.dispatch "+name+" to "+listeners.length+" listeners"); 4243 } 4244 4245 function hook(fn, rv) 4246 { 4247 return function() 4248 { 4249 try 4250 { 4251 return fn.apply(fbs, arguments); 4252 } 4253 catch (exc) 4254 { 4255 var msg = "Error in hook: "+ exc; 4256 ERROR(msg, exc); 4257 return rv; 4258 } 4259 } 4260 } 4261 4262 var lastWindowScope = null; 4263 function wrapIfNative(obj) 4264 { 4265 try 4266 { 4267 if (obj instanceof Ci.nsISupports) 4268 return XPCNativeWrapper(obj); 4269 else 4270 return obj; 4271 } 4272 catch(exc) 4273 { 4274 if (FBTrace.DBG_FBS_ERRORS) 4275 FBTrace.sysout("fbs.wrapIfNative FAILED: "+exc, obj); 4276 } 4277 } 4278 4279 function getRootWindow(win) 4280 { 4281 for (; win; win = win.parent) 4282 { 4283 if (!win.parent || win == win.parent || !(win.parent instanceof Window)) 4284 return win; 4285 } 4286 return null; 4287 } 4288 4289 function countFrames(frame) 4290 { 4291 var frameCount = 0; 4292 try 4293 { 4294 for (; frame; frame = frame.callingFrame) 4295 ++frameCount; 4296 } 4297 catch(exc) 4298 { 4299 } 4300 4301 return frameCount; 4302 } 4303 4304 function framesToString(frame) 4305 { 4306 var str = ""; 4307 while (frame) 4308 { 4309 str += frameToString(frame)+"\n"; 4310 frame = frame.callingFrame; 4311 } 4312 4313 return str; 4314 } 4315 4316 function frameToString(frame) 4317 { 4318 if (!frame) 4319 return "< no frame >"; 4320 4321 if (!frame.script) 4322 { 4323 ERROR("frameToString bad frame "+typeof(frame), frame); 4324 return "<bad frame>"; 4325 } 4326 4327 return frame.script.tag+" in "+frame.script.fileName+"@"+frame.line+"(pc="+frame.pc+")"; 4328 } 4329 4330 function dumpComponentsStack(from) 4331 { 4332 var msg = []; 4333 for (var frame = Components.stack; frame; frame = frame.caller) 4334 msg.push( {desc:frame.filename + "@" + frame.lineNumber +": "+ 4335 frame.sourceLine,frame:frame} ); 4336 4337 FBTrace.sysout("fbs." + from + " has stack size:" + msg.length, msg); 4338 } 4339 4340 function testBreakpoint(frame, bp) 4341 { 4342 if (FBTrace.DBG_FBS_BP) 4343 FBTrace.sysout("fbs.testBreakpoint "+bp.condition, bp); 4344 4345 if (bp.condition && bp.condition != "") 4346 { 4347 var result = {}; 4348 frame.scope.refresh(); 4349 4350 if (frame.eval(bp.condition, "", 1, result)) 4351 { 4352 if (bp.onTrue) 4353 { 4354 if (!result.value.booleanValue) 4355 return false; 4356 } 4357 else 4358 { 4359 var value = unwrapIValue(result.value); 4360 if (typeof bp.lastValue == "undefined") 4361 { 4362 bp.lastValue = value; 4363 return false; 4364 } 4365 else 4366 { 4367 if (bp.lastValue == value) 4368 return false; 4369 bp.lastValue = value; 4370 } 4371 } 4372 } 4373 } 4374 4375 ++bp.hit; 4376 4377 if (bp.hitCount > 0) 4378 { 4379 if (bp.hit < bp.hitCount) 4380 return false; 4381 } 4382 4383 return true; 4384 } 4385 4386 function remove(list, item) 4387 { 4388 var index = list.indexOf(item); 4389 if (index != -1) 4390 list.splice(index, 1); 4391 } 4392 4393 function unwrapIValue(object) 4394 { 4395 var unwrapped = object.getWrappedValue(); 4396 try 4397 { 4398 if (unwrapped) 4399 return XPCSafeJSObjectWrapper(unwrapped); 4400 } 4401 catch (exc) 4402 { 4403 if (FBTrace.DBG_ERRORS) 4404 FBTrace.sysout("fbs.unwrapIValue ERROR for "+object, 4405 {exc: exc, object: object, unwrapped: unwrapped}); 4406 } 4407 } 4408 4409 // ********************************************************************************************* // 4410 // Preferences 4411 4412 var FirebugPrefsObserver = 4413 { 4414 syncFilter: function() 4415 { 4416 var filter = fbs.scriptsFilter; 4417 fbs.showEvents = (filter == "all" || filter == "events"); 4418 fbs.showEvals = (filter == "all" || filter == "evals"); 4419 4420 if (FBTrace.DBG_OPTIONS) 4421 FBTrace.sysout("fbs.showEvents "+fbs.showEvents+" fbs.showEvals "+fbs.showEvals); 4422 } 4423 }; 4424 4425 // ********************************************************************************************* // 4426 // Application Observers 4427 4428 var QuitApplicationGrantedObserver = 4429 { 4430 observe: function(subject, topic, data) 4431 { 4432 if (FBTrace.DBG_FBS_ERRORS) 4433 FBTrace.sysout("fbs.QuitApplicationGrantedObserver " + topic + " start"); 4434 } 4435 }; 4436 4437 var QuitApplicationRequestedObserver = 4438 { 4439 observe: function(subject, topic, data) 4440 { 4441 if (FBTrace.DBG_FBS_ERRORS) 4442 FBTrace.sysout("fbs.QuitApplicationRequestedObserver " + topic); 4443 } 4444 }; 4445 4446 var QuitApplicationObserver = 4447 { 4448 observe: function(subject, topic, data) 4449 { 4450 if (FBTrace.DBG_FBS_ERRORS) 4451 FBTrace.sysout("fbs.QuitApplicationObserver " + topic); 4452 4453 fbs.disableDebugger(); 4454 fbs.shutdown(); 4455 fbs = null; 4456 4457 if (FBTrace.DBG_FBS_ERRORS) 4458 FBTrace.sysout("fbs.QuitApplicationObserver " + topic + " end"); 4459 } 4460 }; 4461 4462 // ********************************************************************************************* // 4463 // Console Service 4464 4465 // xxxJJB: Support for console logging could be moved into separate module, correct? 4466 4467 var consoleService = null; 4468 4469 function ERROR(text, exc) 4470 { 4471 if (!consoleService) 4472 consoleService = ConsoleService.getService(nsIConsoleService); 4473 4474 try 4475 { 4476 fbs.unhookInterrupts(); // Stop and clear everything 4477 fbs.unhookFunctions(); 4478 fbs.disableDebugger(); 4479 jsdHandlers.list = []; 4480 consoleService.logStringMessage("ERROR: "+text); 4481 4482 var frame = Components.stack; 4483 frame = frame.caller; // drop this frame we are in now. 4484 for ( ; frame; frame = frame.caller) 4485 consoleService.logStringMessage(frame.filename + "@" + frame.lineNumber + ";"); 4486 4487 FBTrace.sysout(text, exc); 4488 } 4489 catch(exc) 4490 { 4491 var msg = exc.toString() +" "+(exc.fileName || exc.sourceName) + "@" + exc.lineNumber; 4492 Cu.reportError("firebug-service ERROR in ERROR: "+msg); 4493 } 4494 finally 4495 { 4496 fbs.enableDebugger(); // we were enabled to get ERROR, so we hope all is cleared up now. 4497 } 4498 } 4499 4500 function getExecutionStopNameFromType(type) 4501 { 4502 switch (type) 4503 { 4504 case jsdIExecutionHook.TYPE_INTERRUPTED: return "interrupted"; 4505 case jsdIExecutionHook.TYPE_BREAKPOINT: return "breakpoint"; 4506 case jsdIExecutionHook.TYPE_DEBUG_REQUESTED: return "debug requested"; 4507 case jsdIExecutionHook.TYPE_DEBUGGER_KEYWORD: return "debugger_keyword"; 4508 case jsdIExecutionHook.TYPE_THROW: return "interrupted"; 4509 default: return "unknown("+type+")"; 4510 } 4511 } 4512 4513 function getCallFromType(type) 4514 { 4515 var typeName = type; 4516 switch(type) 4517 { 4518 case TYPE_FUNCTION_RETURN: { typeName = "FUNCTION_RETURN"; break; } 4519 case TYPE_FUNCTION_CALL: { typeName = "FUNCTION_CALL"; break; } 4520 case TYPE_TOPLEVEL_START: { typeName = "TOPLEVEL_START"; break; } 4521 case TYPE_TOPLEVEL_END: { typeName = "TOPLEVEL_END"; break; } 4522 } 4523 return typeName; 4524 } 4525 4526 function shiftCallType(type) 4527 { 4528 return type + 10; 4529 } 4530 4531 // ********************************************************************************************* // 4532 // Chromebug Tracing 4533 4534 // xxxJJB, shouldn't the following code be part of Chromebug (could be done as part of splitting 4535 // this file into more modules?) 4536 // xxxhonza, yes 4537 4538 function getTmpFile() 4539 { 4540 var file = Components.classes["@mozilla.org/file/directory_service;1"]. 4541 getService(Components.interfaces.nsIProperties). 4542 get("TmpD", Components.interfaces.nsIFile); 4543 file.append("fbs.tmp"); 4544 file.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0666); 4545 4546 FBTrace.sysout("fbs.opened tmp file "+file.path); 4547 4548 return file; 4549 } 4550 4551 function getTmpStream(file) 4552 { 4553 // file is nsIFile, data is a string 4554 var foStream = Cc["@mozilla.org/network/file-output-stream;1"]. 4555 createInstance(Ci.nsIFileOutputStream); 4556 4557 // use 0x02 | 0x10 to open file for appending. 4558 foStream.init(file, 0x02 | 0x08 | 0x20, 0666, 0); 4559 // write, create, truncate 4560 // In a c file operation, we have no need to set file mode with or operation, 4561 // directly using "r" or "w" usually. 4562 4563 return foStream; 4564 } 4565 4566 var trackFiles = 4567 { 4568 allFiles: {}, 4569 4570 add: function(fileName) 4571 { 4572 var name = new String(fileName); 4573 this.allFiles[name] = []; 4574 }, 4575 4576 drop: function(fileName) 4577 { 4578 var name = new String(fileName); 4579 this.allFiles[name].push("dropped"); 4580 }, 4581 4582 def: function(frame) 4583 { 4584 var frameGlobal = fbs.getOutermostScope(frame); 4585 var scope = frame.scope; 4586 if (scope) 4587 { 4588 while(scope.jsParent) 4589 scope = scope.jsParent; 4590 } 4591 4592 var scopeName = fbs.getLocationSafe(frameGlobal); 4593 if (!scopeName) 4594 scopeName = frameGlobal + ""; 4595 4596 scopeName = scope.jsClassName + ": "+scopeName; 4597 4598 var name = new String(frame.script.fileName); 4599 if (!(name in this.allFiles)) 4600 this.allFiles[name]=["not added"]; 4601 4602 this.allFiles[name].push(scopeName); 4603 }, 4604 4605 merge: function(moreFiles) 4606 { 4607 for (var p in moreFiles) 4608 { 4609 if (p in this.allFiles) 4610 this.allFiles[p] = this.allFiles[p].concat(moreFiles[p]); 4611 else 4612 this.allFiles[p] = moreFiles[p]; 4613 } 4614 }, 4615 4616 dump: function() 4617 { 4618 var n = 0; 4619 for (var p in this.allFiles) 4620 { 4621 tmpout( (++n) + ") "+p); 4622 var where = this.allFiles[p]; 4623 if (where.length > 0) 4624 { 4625 for (var i = 0; i < where.length; i++) 4626 tmpout(", "+where[i]); 4627 tmpout("\n"); 4628 } 4629 else 4630 { 4631 tmpout(" none\n"); 4632 } 4633 } 4634 }, 4635 }; 4636 4637 function tmpout(text) 4638 { 4639 if (!fbs.foStream) 4640 fbs.foStream = getTmpStream(getTmpFile()); 4641 4642 fbs.foStream.write(text, text.length); 4643 } 4644 4645 // ********************************************************************************************* // 4646 // Initialization 4647 4648 fbs.initialize(); 4649 4650 // ********************************************************************************************* // 4651