1 /* See license.txt for terms of usage */ 2 3 FBL.ns(function() { with (FBL) { 4 5 // ************************************************************************************************ 6 // Constants 7 8 const Cc = Components.classes; 9 const Ci = Components.interfaces; 10 const jsdIScript = Ci.jsdIScript; 11 const jsdIStackFrame = Ci.jsdIStackFrame; 12 const jsdIExecutionHook = Ci.jsdIExecutionHook; 13 const nsISupports = Ci.nsISupports; 14 const nsICryptoHash = Ci.nsICryptoHash; 15 const nsIURI = Ci.nsIURI; 16 17 const PCMAP_SOURCETEXT = jsdIScript.PCMAP_SOURCETEXT; 18 const PCMAP_PRETTYPRINT = jsdIScript.PCMAP_PRETTYPRINT; 19 20 const RETURN_VALUE = jsdIExecutionHook.RETURN_RET_WITH_VAL; 21 const RETURN_THROW_WITH_VAL = jsdIExecutionHook.RETURN_THROW_WITH_VAL; 22 const RETURN_CONTINUE = jsdIExecutionHook.RETURN_CONTINUE; 23 const RETURN_CONTINUE_THROW = jsdIExecutionHook.RETURN_CONTINUE_THROW; 24 const RETURN_ABORT = jsdIExecutionHook.RETURN_ABORT; 25 26 const TYPE_THROW = jsdIExecutionHook.TYPE_THROW; 27 const TYPE_DEBUGGER_KEYWORD = jsdIExecutionHook.TYPE_DEBUGGER_KEYWORD; 28 29 const STEP_OVER = 1; 30 const STEP_INTO = 2; 31 const STEP_OUT = 3; 32 33 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 34 35 const tooltipTimeout = 300; 36 37 const reLineNumber = /^[^\\]?#(\d*)$/; 38 39 const reEval = /\s*eval\s*\(([^)]*)\)/m; // eval ( $1 ) 40 const reHTM = /\.[hH][tT][mM]/; 41 const reFunction = /\s*Function\s*\(([^)]*)\)/m; 42 const reTooMuchRecursion = /too\smuch\srecursion/; 43 44 // ************************************************************************************************ 45 46 Firebug.Debugger = extend(Firebug.ActivableModule, 47 { 48 dispatchName: "debugger", 49 fbs: fbs, // access to firebug-service in chromebug under browser.xul.DOM.Firebug.Debugger.fbs /*@explore*/ 50 51 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 52 // Debugging 53 54 evaluate: function(js, context, scope) 55 { 56 var frame = context.currentFrame; 57 if (!frame) 58 return; 59 60 frame.scope.refresh(); // XXX what's this do? 61 62 var result = {}; 63 var scriptToEval = js; 64 65 // This seem to be safe; eval'ing a getter property in content that tries to 66 // be evil and get Components.classes results in a permission denied error. 67 var ok = frame.eval(scriptToEval, "", 1, result); 68 69 var value = unwrapIValue(result.value); 70 if (ok) 71 return value; 72 else 73 throw value; 74 }, 75 76 getCurrentFrameKeys: function(context) 77 { 78 var globals = keys(context.getGlobalScope().wrappedJSObject); // return is safe 79 80 if (context.currentFrame) 81 return this.getFrameKeys(context.currentFrame, globals); 82 83 return globals; 84 }, 85 86 getFrameKeys: function(frame, names) 87 { 88 var listValue = {value: null}, lengthValue = {value: 0}; 89 frame.scope.getProperties(listValue, lengthValue); 90 91 for (var i = 0; i < lengthValue.value; ++i) 92 { 93 var prop = listValue.value[i]; 94 var name = unwrapIValue(prop.name); 95 names.push(name); 96 } 97 return names; 98 }, 99 100 focusWatch: function(context) 101 { 102 if (Firebug.isDetached()) 103 Firebug.chrome.focus(); 104 else 105 Firebug.toggleBar(true); 106 107 Firebug.chrome.selectPanel("script"); 108 109 var watchPanel = context.getPanel("watches", true); 110 if (watchPanel) 111 { 112 Firebug.CommandLine.isReadyElsePreparing(context); 113 watchPanel.editNewWatch(); 114 } 115 }, 116 117 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 118 119 beginInternalOperation: function() // stop debugger operations like breakOnErrors 120 { 121 var state = {breakOnErrors: Firebug.breakOnErrors}; 122 Firebug.breakOnErrors = false; 123 return state; 124 }, 125 126 endInternalOperation: function(state) // pass back the object given by beginInternalOperation 127 { 128 Firebug.breakOnErrors = state.breakOnErrors; 129 return true; 130 }, 131 132 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 133 134 halt: function(fn) 135 { 136 this.haltCallback = fn; // called in this.onHalt as fn(frame); 137 fbs.halt(this); 138 139 debuggerHalter(); // a function with a URL that passes jsdIFilter and says "debugger;" 140 141 if (this.haltCallback) // so we have a second try 142 { 143 FBTrace.sysout("debugger did not halt jsd: ", jsd); 144 if (Firebug.CommandLine.isReadyElsePreparing(FirebugContext)) 145 Firebug.CommandLine.evaluate("debugger;", FirebugContext); 146 } 147 148 if(FBTrace.DBG_BP) 149 FBTrace.sysout("debugger.halt, completed debugger stmt"); 150 }, 151 152 breakNow: function() 153 { 154 Firebug.Debugger.halt(function(frame) 155 { 156 if (FBTrace.DBG_UI_LOOP) 157 FBTrace.sysout("debugger.breakNow: frame "+frame.script.fileName, frame); 158 159 for (; frame && frame.isValid; frame = frame.callingFrame) 160 { 161 var fileName = frame.script.fileName; 162 if (fileName && fileName.indexOf("chrome://firebug/") != 0 && 163 fileName.indexOf("/components/firebug-") == -1) 164 break; 165 } 166 167 if (frame) 168 Firebug.Debugger.onBreak(frame, 3); 169 else 170 { 171 // XXXrobc no-op, added for detrace 172 if (FBTrace.DBG_UI_LOOP) 173 FBTrace.sysout("debugger.breakNow: no frame that is not firebug"); 174 } 175 }); 176 }, 177 178 stop: function(context, frame, type, rv) 179 { 180 if (context.stopped) 181 return RETURN_CONTINUE; 182 183 if (!this.isAlwaysEnabled()) 184 return RETURN_CONTINUE; 185 186 if (FBTrace.DBG_UI_LOOP) 187 FBTrace.sysout("debugger.stop "+context.getName()+" frame",frame); 188 189 var executionContext; 190 try 191 { 192 executionContext = frame.executionContext; 193 } 194 catch (exc) 195 { 196 if (FBTrace.DBG_UI_LOOP) 197 FBTrace.sysout("debugger.stop no executionContext, exit"); 198 199 // Can't proceed with an execution context - it happens sometimes. 200 return RETURN_CONTINUE; 201 } 202 203 context.debugFrame = frame; 204 context.stopped = true; 205 206 var hookReturn = dispatch2(this.fbListeners,"onStop",[context,frame, type,rv]); 207 if ( hookReturn && hookReturn >= 0 ) 208 { 209 delete context.stopped; 210 delete context.debugFrame; 211 delete context; 212 if (FBTrace.DBG_UI_LOOP) 213 FBTrace.sysout("debugger.stop extension vetoed stop with hookReturn "+hookReturn); 214 215 return hookReturn; 216 } 217 218 try 219 { 220 // We will pause here until resume is called 221 var depth = fbs.enterNestedEventLoop({onNest: bindFixed(this.startDebugging, this, context)}); 222 // For some reason we don't always end up here 223 if (FBTrace.DBG_UI_LOOP) FBTrace.sysout("debugger.stop, depth:"+depth+" context:"+context.getName()); 224 } 225 catch (exc) 226 { 227 // Just ignore exceptions that happened while in the nested loop 228 if (FBTrace.DBG_ERRORS) 229 FBTrace.sysout("debugger exception in nested event loop: ", exc); 230 else // else /*@explore*/ 231 ERROR("debugger exception in nested event loop: "+exc+"\n"); 232 } 233 234 this.stopDebugging(context); 235 236 dispatch(this.fbListeners,"onResume",[context]); 237 238 if (context.aborted) 239 { 240 delete context.aborted; 241 return RETURN_ABORT; 242 } 243 else 244 return RETURN_CONTINUE; 245 }, 246 247 resume: function(context) 248 { 249 if (FBTrace.DBG_UI_LOOP) 250 FBTrace.sysout("debugger.resume, context.stopped:"+context.stopped+"\n"); 251 252 this.thaw(context); 253 254 var depth = fbs.exitNestedEventLoop(); 255 if (FBTrace.DBG_UI_LOOP) FBTrace.sysout("debugger.resume, depth:"+depth+"\n"); 256 }, 257 258 abort: function(context) 259 { 260 if (context.stopped) 261 { 262 context.aborted = true; 263 this.resume(context); 264 } 265 }, 266 267 stepOver: function(context) 268 { 269 if (!context.debugFrame || !context.debugFrame.isValid) 270 return; 271 272 fbs.step(STEP_OVER, context.debugFrame, this); 273 this.resume(context); 274 }, 275 276 stepInto: function(context) 277 { 278 if (!context.debugFrame || !context.debugFrame.isValid) 279 return; 280 281 fbs.step(STEP_INTO, context.debugFrame, this); 282 this.resume(context); 283 }, 284 285 stepOut: function(context) 286 { 287 if (!context.debugFrame || !context.debugFrame.isValid) 288 return; 289 290 fbs.step(STEP_OUT, context.debugFrame); 291 this.resume(context); 292 }, 293 294 suspend: function(context) 295 { 296 if (context.stopped) 297 return; 298 fbs.suspend(this, context); 299 }, 300 301 unSuspend: function(context) 302 { 303 fbs.stopStepping(); // TODO per context 304 }, 305 306 runUntil: function(context, sourceFile, lineNo) 307 { 308 if (FBTrace.DBG_UI_LOOP) 309 FBTrace.sysout("runUntil "+lineNo+" @"+sourceFile); 310 311 if (!context.debugFrame || !context.debugFrame.isValid) 312 return; 313 314 fbs.runUntil(sourceFile, lineNo, context.debugFrame, this); 315 this.resume(context); 316 }, 317 318 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 319 320 freeze: function(context) 321 { 322 var executionContext = context.debugFrame.executionContext; 323 try { 324 executionContext.scriptsEnabled = false; 325 326 if (context.window instanceof Ci.nsIInterfaceRequestor) 327 { 328 context.eventSuppressor = context.window.getInterface(Ci.nsIDOMWindowUtils); 329 if (context.eventSuppressor) 330 context.eventSuppressor.suppressEventHandling(true); 331 } 332 333 if (FBTrace.DBG_UI_LOOP) 334 FBTrace.sysout("debugger.stop try to disable scripts "+(context.eventSuppressor?"and events":"but not events")+" in "+context.getName()+" executionContext.tag "+executionContext.tag+".scriptsEnabled: "+executionContext.scriptsEnabled); 335 336 } catch (exc) { 337 // This attribute is only valid for contexts which implement nsIScriptContext. 338 if (FBTrace.DBG_UI_LOOP) FBTrace.sysout("debugger.stop, freeze exception in "+context.getName(), exc); 339 } 340 }, 341 342 thaw: function(context) 343 { 344 var executionContext = context.debugFrame.executionContext; 345 try { 346 if (executionContext.isValid) 347 { 348 if (context.eventSuppressor) 349 { 350 context.eventSuppressor.suppressEventHandling(false); 351 delete context.eventSuppressor; 352 } 353 354 executionContext.scriptsEnabled = true; 355 } 356 else 357 { 358 if (FBTrace.DBG_UI_LOOP) 359 FBTrace.sysout("debugger.stop "+executionContext.tag+" executionContext is not valid"); 360 } 361 if (FBTrace.DBG_UI_LOOP) 362 FBTrace.sysout("debugger.stop try to ensable scripts "+(context.eventSuppressor?"with events suppressed":"events enabled")+" in "+context.getName()+" executionContext.tag "+executionContext.tag+".scriptsEnabled: "+executionContext.scriptsEnabled); 363 } catch (exc) { 364 if (FBTrace.DBG_UI_LOOP) FBTrace.sysout("debugger.stop, scriptsEnabled = true exception:", exc); 365 } 366 367 }, 368 369 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 370 // Breakpoints 371 372 setBreakpoint: function(sourceFile, lineNo) 373 { 374 fbs.setBreakpoint(sourceFile, lineNo, null, Firebug.Debugger); 375 }, 376 377 clearBreakpoint: function(sourceFile, lineNo) 378 { 379 fbs.clearBreakpoint(sourceFile.href, lineNo); 380 }, 381 382 setErrorBreakpoint: function(sourceFile, line) 383 { 384 fbs.setErrorBreakpoint(sourceFile, line, Firebug.Debugger); 385 }, 386 387 clearErrorBreakpoint: function(sourceFile, line) 388 { 389 fbs.clearErrorBreakpoint(sourceFile, line, Firebug.Debugger); 390 }, 391 392 clearAllBreakpoints: function(context) 393 { 394 if (context) 395 { 396 var sourceFiles = sourceFilesAsArray(context.sourceFileMap); 397 fbs.clearAllBreakpoints(sourceFiles, Firebug.Debugger); 398 } 399 else 400 { 401 fbs.enumerateBreakpoints(null, {call: function(url, lineNo, bp) // null means all urls 402 { 403 if (bp.debugger !== this) // skip breakpoints of other debuggers. 404 return; 405 406 if (Firebug.filterSystemURLs) // then there are not system urls, clear all 407 fbs.clearBreakpoint(url, lineNo); 408 else 409 { 410 if (!isSystemURL(url)) // if there are system urls, leave them 411 fbs.clearBreakpoint(url, lineNo); 412 } 413 }}); 414 } 415 }, 416 417 enableAllBreakpoints: function(context) 418 { 419 if (FBTrace.DBG_BP) 420 FBTrace.sysout("enableAllBreakpoints sourceFileMap:", context.sourceFileMap); 421 for (var url in context.sourceFileMap) 422 { 423 fbs.enumerateBreakpoints(url, {call: function(url, lineNo) 424 { 425 fbs.enableBreakpoint(url, lineNo); 426 }}); 427 } 428 }, 429 430 disableAllBreakpoints: function(context) 431 { 432 for (var url in context.sourceFileMap) 433 { 434 fbs.enumerateBreakpoints(url, {call: function(url, lineNo) 435 { 436 fbs.disableBreakpoint(url, lineNo); 437 }}); 438 } 439 }, 440 441 getBreakpointCount: function(context) 442 { 443 var count = 0; 444 for (var url in context.sourceFileMap) 445 { 446 fbs.enumerateBreakpoints(url, 447 { 448 call: function(url, lineNo) 449 { 450 ++count; 451 } 452 }); 453 454 fbs.enumerateErrorBreakpoints(url, 455 { 456 call: function(url, lineNo) 457 { 458 ++count; 459 } 460 }); 461 } 462 return count; 463 }, 464 465 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 466 // Debugging and monitoring 467 468 traceAll: function(context) 469 { 470 fbs.traceAll(sourceURLsAsArray(context), this); 471 }, 472 473 untraceAll: function(context) 474 { 475 fbs.untraceAll(this); 476 }, 477 478 monitorFunction: function(fn, mode) 479 { 480 if (typeof(fn) == "function" || fn instanceof Function) 481 { 482 var script = findScriptForFunctionInContext(FirebugContext, fn); 483 if (script) 484 this.monitorScript(fn, script, mode); 485 } 486 }, 487 488 unmonitorFunction: function(fn, mode) 489 { 490 if (typeof(fn) == "function" || fn instanceof Function) 491 { 492 var script = findScriptForFunctionInContext(FirebugContext, fn); 493 if (script) 494 this.unmonitorScript(fn, script, mode); 495 } 496 }, 497 498 monitorScript: function(fn, script, mode) 499 { 500 var scriptInfo = Firebug.SourceFile.getSourceFileAndLineByScript(FirebugContext, script); 501 if (scriptInfo) 502 { 503 if (mode == "debug") 504 this.setBreakpoint(scriptInfo.sourceFile, scriptInfo.lineNo, null, this); 505 else if (mode == "monitor") 506 fbs.monitor(scriptInfo.sourceFile, scriptInfo.lineNo, Firebug.Debugger); 507 } 508 }, 509 510 unmonitorScript: function(fn, script, mode) 511 { 512 var scriptInfo = Firebug.SourceFile.getSourceFileAndLineByScript(FirebugContext, script); 513 if (scriptInfo) 514 { 515 if (mode == "debug") 516 this.clearBreakpoint(scriptInfo.sourceFile, scriptInfo.lineNo); 517 else if (mode == "monitor") 518 fbs.unmonitor(scriptInfo.sourceFile, scriptInfo.lineNo); 519 } 520 }, 521 522 traceCalls: function(context, fn) 523 { 524 if (typeof(fn) == "function" || fn instanceof Function) 525 { 526 var script = findScriptForFunctionInContext(context, fn); 527 if (script) 528 this.traceScriptCalls(context, script); 529 else 530 { 531 if (FBTrace.DBG_ERRORS) 532 FBTrace.sysout("debugger.traceCalls no script found for "+fn, fn); 533 } 534 } 535 }, 536 537 untraceCalls: function(context, fn) 538 { 539 if (typeof(fn) == "function" || fn instanceof Function) 540 { 541 var script = findScriptForFunctionInContext(context, fn); 542 if (script) 543 this.untraceScriptCalls(context, script); 544 } 545 }, 546 547 traceScriptCalls: function(context, script) 548 { 549 var scriptInfo = Firebug.SourceFile.getSourceFileAndLineByScript(context, script); 550 if (scriptInfo) 551 fbs.traceCalls(scriptInfo.sourceFile, scriptInfo.lineNo, Firebug.Debugger); 552 }, 553 554 untraceScriptCalls: function(context, script) 555 { 556 var scriptInfo = Firebug.SourceFile.getSourceFileAndLineByScript(context, script); 557 if (scriptInfo) 558 fbs.untraceCalls(scriptInfo.sourceFile, scriptInfo.lineNo, Firebug.Debugger); 559 }, 560 561 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 562 // UI Stuff 563 564 /* 565 * Called when a nestedEventLoop begins 566 */ 567 startDebugging: function(context) 568 { 569 if (FBTrace.DBG_UI_LOOP) FBTrace.sysout("startDebugging enter context.stopped:"+context.stopped+" for context: "+context.getName()+"\n"); 570 try { 571 572 this.freeze(context); 573 574 fbs.lockDebugger(); 575 576 context.currentFrame = context.debugFrame; 577 578 context.executingSourceFile = Firebug.SourceFile.getSourceFileByScript(context, context.currentFrame.script); 579 580 if (!context.executingSourceFile) // bail out, we don't want the user stuck in debug with out source. 581 { 582 if (FBTrace.DBG_UI_LOOP) 583 FBTrace.sysout("startDebugging resuming, no sourceFile for "+context.debugFrame.script.fileName, context.debugFrame.script.functionSource); 584 this.resume(context); 585 return; 586 } 587 588 var currentBreakable = Firebug.chrome.getGlobalAttribute("cmd_breakOnNext", "breakable"); 589 590 if (FBTrace.DBG_BP) 591 FBTrace.sysout("debugger.startDebugging; currentBreakable "+currentBreakable+" in " + context.getName()); 592 593 if (currentBreakable == "false") // then we are armed but we broke 594 Firebug.chrome.setGlobalAttribute("cmd_breakOnNext", "breakable", "true"); 595 596 if (context != FirebugContext || Firebug.isDetached()) 597 Firebug.showContext(context.browser, context); // Make FirebugContext = context and sync the UI 598 599 if (Firebug.isMinimized()) // then open the UI to show we are stopped 600 Firebug.unMinimize(); 601 602 this.syncCommands(context); 603 this.syncListeners(context); 604 605 // Update Break on Next lightning. 606 var panel = context.getPanel("script", true); 607 Firebug.Breakpoint.updatePanelTab(panel, false); 608 609 Firebug.chrome.select(context.currentFrame, "script", null, true); 610 Firebug.chrome.focus(); 611 } 612 catch(exc) 613 { 614 if (FBTrace.DBG_ERRORS) 615 FBTrace.sysout("Resuming debugger: error during debugging loop: "+exc, exc); 616 Firebug.Console.log("Resuming debugger: error during debugging loop: "+exc); 617 this.resume(context); 618 } 619 620 dispatch(this.fbListeners, "onStartDebugging", [context]); 621 622 if (FBTrace.DBG_UI_LOOP) FBTrace.sysout("startDebugging exit context.stopped:"+context.stopped+" for context: "+context.getName()+"\n"); 623 }, 624 625 /* 626 * Called in the main event loop, from jsd, after we have exited the nested event loop 627 */ 628 629 stopDebugging: function(context) 630 { 631 if (FBTrace.DBG_UI_LOOP) FBTrace.sysout("stopDebugging enter context: "+context.getName()+"\n"); 632 try 633 { 634 fbs.unlockDebugger(); 635 636 // If the user reloads the page while the debugger is stopped, then 637 // the current context will be destroyed just before 638 if (context && context.window && !context.aborted) 639 { 640 delete context.stopped; 641 delete context.debugFrame; 642 delete context.currentFrame; 643 644 var chrome = Firebug.chrome; 645 646 this.syncCommands(context); 647 this.syncListeners(context); 648 649 chrome.syncSidePanels(); 650 651 var panel = context.getPanel("script", true); 652 if (panel && panel == Firebug.chrome.getSelectedPanel()) 653 panel.showNoStackFrame(); // unhighlight and remove toolbar-status line 654 655 if (panel) 656 panel.highlight(false); 657 658 context.executingSourceFile = null; 659 delete context.breakLineNumber; 660 } 661 } 662 catch (exc) 663 { 664 if (FBTrace.DBG_UI_LOOP) FBTrace.sysout("debugger.stopDebugging FAILS", exc); 665 // If the window is closed while the debugger is stopped, 666 // then all hell will break loose here 667 ERROR(exc); 668 } 669 }, 670 671 syncCommands: function(context) 672 { 673 var chrome = Firebug.chrome; 674 if (!chrome) 675 { 676 if (FBTrace.DBG_ERRORS) 677 FBTrace.sysout("debugger.syncCommand, context with no chrome: "+context.getGlobalScope()); 678 return; 679 } 680 681 if (context.stopped) 682 { 683 chrome.setGlobalAttribute("fbDebuggerButtons", "stopped", "true"); 684 chrome.setGlobalAttribute("cmd_resumeExecution", "disabled", "false"); 685 chrome.setGlobalAttribute("cmd_stepOver", "disabled", "false"); 686 chrome.setGlobalAttribute("cmd_stepInto", "disabled", "false"); 687 chrome.setGlobalAttribute("cmd_stepOut", "disabled", "false"); 688 } 689 else 690 { 691 chrome.setGlobalAttribute("fbDebuggerButtons", "stopped", "false"); 692 chrome.setGlobalAttribute("cmd_stepOver", "disabled", "true"); 693 chrome.setGlobalAttribute("cmd_stepInto", "disabled", "true"); 694 chrome.setGlobalAttribute("cmd_stepOut", "disabled", "true"); 695 chrome.setGlobalAttribute("cmd_resumeExecution", "disabled", "true"); 696 } 697 }, 698 699 syncListeners: function(context) 700 { 701 var chrome = Firebug.chrome; 702 703 if (context.stopped) 704 this.attachListeners(context, chrome); 705 else 706 this.detachListeners(context, chrome); 707 }, 708 709 attachListeners: function(context, chrome) 710 { 711 this.keyListeners = 712 [ 713 chrome.keyCodeListen("F8", null, bind(this.resume, this, context), true), 714 chrome.keyListen("/", isControl, bind(this.resume, this, context)), 715 chrome.keyCodeListen("F10", null, bind(this.stepOver, this, context), true), 716 chrome.keyListen("'", isControl, bind(this.stepOver, this, context)), 717 chrome.keyCodeListen("F11", null, bind(this.stepInto, this, context)), 718 chrome.keyListen(";", isControl, bind(this.stepInto, this, context)), 719 chrome.keyCodeListen("F11", isShift, bind(this.stepOut, this, context)), 720 chrome.keyListen(",", isControlShift, bind(this.stepOut, this, context)) 721 ]; 722 }, 723 724 detachListeners: function(context, chrome) 725 { 726 if (this.keyListeners) 727 { 728 for (var i = 0; i < this.keyListeners.length; ++i) 729 chrome.keyIgnore(this.keyListeners[i]); 730 delete this.keyListeners; 731 } 732 }, 733 734 showPanel: function(browser, panel) 735 { 736 if (panel && panel.name == "script") 737 { 738 this.syncCommands(panel.context); 739 this.ableWatchSidePanel(panel.context); 740 if (FBTrace.DBG_PANELS) FBTrace.sysout("debugger.showPanel this.location:"+this.location); 741 } 742 }, 743 744 suspendFirebug: function() 745 { 746 Firebug.suspendFirebug(); 747 }, 748 749 resumeFirebug: function() 750 { 751 Firebug.resumeFirebug(); 752 }, 753 754 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 755 756 supportsWindow: function(win) 757 { 758 if (!this.isAlwaysEnabled()) 759 return false; 760 761 var context = ( (win && TabWatcher) ? TabWatcher.getContextByWindow(win) : null); 762 763 this.breakContext = context; 764 return !!context; 765 }, 766 767 supportsGlobal: function(frameWin) // This is call from fbs for almost all fbs operations 768 { 769 var context = ( (frameWin && TabWatcher) ? TabWatcher.getContextByWindow(frameWin) : null); 770 if (!context) 771 return false; 772 773 // Apparently the frameWin is a XPCSafeJSObjectWrapper that looks like a Window. 774 // Since this is method called a lot make a hacky fast check on _getFirebugConsoleElement 775 if (!frameWin._getFirebugConsoleElement && !context.stopped) 776 { 777 this.injectConsole(context, frameWin); 778 } 779 else 780 { 781 if (FBTrace.DBG_CONSOLE) 782 FBTrace.sysout("debugger.supportsGlobal frameWin._getFirebugConsoleElement exists", frameWin); 783 } 784 785 this.breakContext = context; 786 //FBTrace.sysout("debugger.js this.breakContext "+this.breakContext.getName()); 787 return true; 788 }, 789 790 injectConsole: function(context, frameWin) 791 { 792 if (context.notificationSourceFile) 793 { 794 delete context.sourceFileMap[context.notificationSourceFile.href]; 795 delete context.notificationSourceFile; 796 } 797 if (Firebug.Console.isAlwaysEnabled()) 798 { 799 // This is how the console is injected ahead of JS running on the page 800 fbs.filterConsoleInjections = true; 801 try 802 { 803 var consoleReady = Firebug.Console.isReadyElsePreparing(context, frameWin); 804 } 805 catch(exc) 806 { 807 if (FBTrace.DBG_ERRORS) 808 FBTrace.sysout("debugger.supportsGlobal !frameWin._getFirebugConsoleElement consoleReady FAILS: "+exc, exc); 809 } 810 finally 811 { 812 fbs.filterConsoleInjections = false; 813 } 814 if (FBTrace.DBG_CONSOLE) 815 FBTrace.sysout("debugger.supportsGlobal !frameWin._getFirebugConsoleElement consoleReady:"+consoleReady, frameWin); 816 } 817 else 818 { 819 if (FBTrace.DBG_CONSOLE) 820 FBTrace.sysout("debugger.supportsGlobal !frameWin._getFirebugConsoleElement console NOT enabled ", frameWin); 821 } 822 }, 823 824 onLock: function(state) 825 { 826 // XXXjoe For now, trying to see if it's ok to have multiple contexts 827 // debugging simultaneously - otherwise we need this 828 //if (this.context != this.debugContext) 829 { 830 // XXXjoe Disable step/continue buttons 831 } 832 }, 833 834 onBreak: function(frame, type) 835 { 836 try { 837 var context = this.breakContext; 838 839 if (!context) 840 context = this.getContextByFrame(frame); 841 842 if (FBTrace.DBG_BP) 843 FBTrace.sysout("debugger.onBreak "+(this.breakContext?" no breakContext, tried getContextByFrame ":"breakContext: ") + (context ? context.getName() : " none!"), getJSDStackDump(frame) ); 844 845 delete this.breakContext; 846 847 if (!context) 848 return RETURN_CONTINUE; 849 850 if (type == TYPE_DEBUGGER_KEYWORD && frame.functionName === 'firebugDebuggerTracer') 851 { 852 var trace = FBL.getCorrectedStackTrace(frame, context); 853 if (trace) 854 { 855 trace.frames = trace.frames.slice(1).reverse(); // drop the firebugDebuggerTracer and reorder 856 Firebug.Console.log(trace, context, "stackTrace"); 857 } 858 859 if(FBTrace.DBG_BP) 860 FBTrace.sysout("debugger.onBreak "+(trace?"debugger trace":" debugger no trace!")); 861 862 return RETURN_CONTINUE; 863 } 864 865 return this.stop(context, frame, type); 866 } 867 catch (exc) 868 { 869 if (FBTrace.DBG_ERRORS || FBTrace.DBG_BP) 870 FBTrace.sysout("debugger.onBreak FAILS", exc); 871 throw exc; 872 } 873 }, 874 875 onHalt: function(frame) 876 { 877 var callback = this.haltCallback; 878 delete this.haltCallback; 879 880 if (callback) 881 callback(frame); 882 883 return RETURN_CONTINUE; 884 }, 885 886 onThrow: function(frame, rv) 887 { 888 // onThrow is called for throw and for any catch that does not succeed. 889 var context = this.breakContext; 890 delete this.breakContext; 891 892 if (!context) 893 { 894 FBTrace.sysout("debugger.onThrow, no context, try to get from frame\n"); 895 context = this.getContextByFrame(frame); 896 } 897 if (FBTrace.DBG_BP) FBTrace.sysout("debugger.onThrow context:"+(context?context.getName():"undefined")+"\n"); 898 if (!context) 899 return RETURN_CONTINUE_THROW; 900 901 if (!fbs.trackThrowCatch) 902 return RETURN_CONTINUE_THROW; 903 904 try 905 { 906 var isCatch = this.isCatchFromPreviousThrow(frame, context); 907 if (!isCatch) 908 { 909 context.thrownStackTrace = getCorrectedStackTrace(frame, context); 910 if (FBTrace.DBG_BP) FBTrace.sysout("debugger.onThrow reset context.thrownStackTrace", context.thrownStackTrace.frames); 911 } 912 else 913 { 914 if (FBTrace.DBG_BP) FBTrace.sysout("debugger.onThrow isCatch\n"); 915 } 916 } 917 catch (exc) 918 { 919 FBTrace.sysout("onThrow FAILS: "+exc+"\n"); 920 } 921 922 if (dispatch2(this.fbListeners,"onThrow",[context, frame, rv])) 923 return this.stop(context, frame, TYPE_THROW, rv); 924 return RETURN_CONTINUE_THROW; 925 }, 926 927 isCatchFromPreviousThrow: function(frame, context) 928 { 929 if (context.thrownStackTrace) 930 { 931 var trace = context.thrownStackTrace.frames; 932 if (trace.length > 1) // top of stack is [0] 933 { 934 var curFrame = frame; 935 var curFrameSig = curFrame.script.tag +"."+curFrame.pc; 936 for (var i = 1; i < trace.length; i++) 937 { 938 var preFrameSig = trace[i].signature(); 939 if (FBTrace.DBG_ERRORS && FBTrace.DBG_STACK) FBTrace.sysout("debugger.isCatchFromPreviousThrow "+curFrameSig+"=="+preFrameSig+"\n"); 940 if (curFrameSig == preFrameSig) 941 { 942 return true; // catch from previous throw (or do we need to compare whole stack? 943 } 944 } 945 // We looked at the previous stack and did not match the current frame 946 } 947 } 948 return false; 949 }, 950 951 onMonitorScript: function(frame) 952 { 953 var context = this.breakContext; 954 delete this.breakContext; 955 956 if (!context) 957 context = this.getContextByFrame(frame); 958 if (!context) 959 return RETURN_CONTINUE; 960 961 frame = getStackFrame(frame, context); 962 963 dispatch(this.fbListeners,"onMonitorScript",[context, frame]); 964 }, 965 966 onFunctionCall: function(context, frame, depth, calling) 967 { 968 if (!context) 969 context = this.getContextByFrame(frame); 970 if (!context) 971 return RETURN_CONTINUE; 972 973 frame = getStackFrame(frame, context); 974 975 dispatch(this.fbListeners,"onFunctionCall",[context, frame, depth, calling]); 976 977 return context; // returned as first arg on next call from same trace 978 }, 979 980 onError: function(frame, error) 981 { 982 var context = this.breakContext; 983 delete this.breakContext; 984 985 try 986 { 987 if (FBTrace.DBG_ERRORS) FBTrace.sysout("debugger.onError: "+error.errorMessage+" in "+(context?context.getName():"no context"), error); 988 989 if (reTooMuchRecursion.test(error.errorMessage)) 990 frame = fbs.discardRecursionFrames(frame); 991 992 Firebug.errorStackTrace = getCorrectedStackTrace(frame, context); 993 if (FBTrace.DBG_ERRORS) 994 FBTrace.sysout("debugger.onError errorStackTrace ", Firebug.errorStackTrace); 995 996 if (Firebug.breakOnErrors) 997 { 998 context.breakingCause = { 999 title: $STR("Break on Error"), 1000 message: error.message, 1001 copyAction: bindFixed(FirebugReps.ErrorMessage.copyError, 1002 FirebugReps.ErrorMessage, error) 1003 }; 1004 } 1005 else 1006 { 1007 delete context.breakingCause; 1008 } 1009 } 1010 catch (exc) 1011 { 1012 if (FBTrace.DBG_ERRORS) 1013 FBTrace.sysout("debugger.onError getCorrectedStackTrace FAILED:", exc); 1014 } 1015 1016 var hookReturn = dispatch2(this.fbListeners,"onError",[context, frame, error]); 1017 1018 if (Firebug.breakOnErrors) 1019 { 1020 //xxxHonza: for now, the BON options are disabled to keep things simple. 1021 // Deactivate "Break On All Errors" only if the other options says so. 1022 //if (!Firebug.persistBreakOnError) 1023 // Firebug.setPref(Firebug.servicePrefDomain, "breakOnErrors", false); 1024 1025 // Switch of Break on Next tab lightning. 1026 var panel = context.getPanel("console", true); 1027 //Firebug.Breakpoint.updatePanelTab(panel, false); 1028 1029 return -1; // break 1030 } 1031 1032 if (hookReturn) 1033 return hookReturn; 1034 1035 return -2; /* let firebug service decide to break or not */ 1036 }, 1037 1038 onUncaughtException: function(errorInfo) 1039 { 1040 var context = this.breakContext; 1041 delete this.breakContext; 1042 1043 Firebug.Errors.logScriptError(context, errorInfo, false); 1044 return -2; 1045 }, 1046 1047 onEvalScriptCreated: function(frame, outerScript, innerScripts) 1048 { 1049 try 1050 { 1051 if (FBTrace.DBG_EVAL) FBTrace.sysout("debugger.onEvalLevelScript script.fileName="+outerScript.fileName+"\n"); 1052 var context = this.breakContext; 1053 delete this.breakContext; 1054 1055 var sourceFile = this.getEvalLevelSourceFile(frame, context, innerScripts); 1056 1057 if (FBTrace.DBG_EVAL) 1058 FBTrace.sysout("debugger.onEvalScriptCreated url="+sourceFile.href, FBL.getCorrectedStackTrace(frame, context)); 1059 1060 dispatch(this.fbListeners,"onEvalScriptCreated",[context, frame, sourceFile.href]); 1061 return sourceFile; 1062 } 1063 catch (e) 1064 { 1065 if (FBTrace.DBG_EVAL || FBTrace.DBG_ERRORS) 1066 FBTrace.sysout("onEvalScriptCreated FaILS ", e); 1067 } 1068 }, 1069 1070 onEventScriptCreated: function(frame, outerScript, innerScripts) 1071 { 1072 if (FBTrace.DBG_EVENTS) FBTrace.sysout("debugger.onEventScriptCreated script.fileName="+outerScript.fileName+"\n"); 1073 var context = this.breakContext; 1074 delete this.breakContext; 1075 1076 var script = frame.script; 1077 var creatorURL = normalizeURL(frame.script.fileName); 1078 var innerScriptArray = []; 1079 try { 1080 var source = script.functionSource; 1081 1082 while (innerScripts.hasMoreElements()) 1083 { 1084 var inner = innerScripts.getNext(); 1085 source += "\n"+inner.functionSource; 1086 innerScriptArray.push(inner); 1087 } 1088 1089 } catch (exc) { 1090 /*Bug 426692 */ 1091 var source = creatorURL + "/"+getUniqueId(); 1092 } 1093 1094 var lines = splitLines(source); 1095 1096 var urlDescribed = this.getDynamicURL(context, normalizeURL(frame.script.fileName), source, "event"); 1097 var url = urlDescribed.href; 1098 1099 context.sourceCache.invalidate(url); 1100 context.sourceCache.storeSplitLines(url, lines); 1101 1102 var sourceFile = new Firebug.EventSourceFile(url, frame.script, "event:"+script.functionName+"."+script.tag, lines, new ArrayEnumerator(innerScriptArray)); 1103 this.watchSourceFile(context, sourceFile); 1104 1105 if (FBTrace.DBG_EVENTS) 1106 FBTrace.sysout("debugger.onEventScriptCreated url="+sourceFile.href+"\n"); 1107 1108 if (FBTrace.DBG_EVENTS) 1109 FBTrace.sysout("debugger.onEventScriptCreated sourceFileMap:", context.sourceFileMap); 1110 if (FBTrace.DBG_SOURCEFILES) 1111 FBTrace.sysout("debugger.onEventScriptCreated sourcefile="+sourceFile.toString()+" -> "+context.getName()+"\n"); 1112 1113 dispatch(this.fbListeners,"onEventScriptCreated",[context, frame, url]); 1114 return sourceFile; 1115 }, 1116 1117 // We just compiled a bunch of JS, eg a script tag in HTML. We are about to run the outerScript. 1118 onTopLevelScriptCreated: function(frame, outerScript, innerScripts) 1119 { 1120 if (FBTrace.DBG_TOPLEVEL) FBTrace.sysout("debugger("+this.debuggerName+").onTopLevelScriptCreated script.fileName="+outerScript.fileName+"\n"); 1121 var context = this.breakContext; 1122 delete this.breakContext; 1123 1124 // This is our only chance to get the linetable for the outerScript since it will run and be GC next. 1125 var script = frame.script; 1126 var url = normalizeURL(script.fileName); 1127 1128 if (FBTrace.DBG_TOPLEVEL) FBTrace.sysout("debugger.onTopLevelScriptCreated outerScript.tag="+outerScript.tag+" has fileName="+outerScript.fileName+"\n"); 1129 1130 var sourceFile = context.sourceFileMap[url]; 1131 if (sourceFile && (sourceFile instanceof Firebug.TopLevelSourceFile) ) // TODO test multiple script tags in one html file 1132 { 1133 if (FBTrace.DBG_SOURCEFILES) FBTrace.sysout("debugger.onTopLevelScriptCreated reuse sourcefile="+sourceFile.toString()+" -> "+context.getName()+" ("+context.uid+")"+"\n"); 1134 if (!sourceFile.outerScript || !sourceFile.outerScript.isValid) 1135 sourceFile.outerScript = outerScript; 1136 Firebug.SourceFile.addScriptsToSourceFile(sourceFile, outerScript, innerScripts); 1137 } 1138 else 1139 { 1140 sourceFile = new Firebug.TopLevelSourceFile(url, script, script.lineExtent, innerScripts); 1141 this.watchSourceFile(context, sourceFile); 1142 if (FBTrace.DBG_SOURCEFILES) FBTrace.sysout("debugger.onTopLevelScriptCreated create sourcefile="+sourceFile.toString()+" -> "+context.getName()+" ("+context.uid+")"+"\n"); 1143 } 1144 1145 dispatch(this.fbListeners,"onTopLevelScriptCreated",[context, frame, sourceFile.href]); 1146 return sourceFile; 1147 }, 1148 1149 getContextByFrame: function(frame) 1150 { 1151 var win = getFrameScopeWindowAncestor(frame); 1152 return win ? TabWatcher.getContextByWindow(win) : null; 1153 }, 1154 1155 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 1156 1157 watchSourceFile: function(context, sourceFile) 1158 { 1159 context.addSourceFile(sourceFile); // store in the context and notify listeners 1160 //fbs.watchSourceFile(sourceFile); // tell the service to watch this file 1161 }, 1162 1163 unwatchSourceFile: function(context, sourceFile) 1164 { 1165 //fbs.unwatchSourceFile(sourceFile); 1166 context.removeSourceFile(sourceFile); 1167 }, 1168 1169 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 1170 1171 onToggleBreakpoint: function(url, lineNo, isSet, props) 1172 { 1173 if (props.debugger != this) // then not for us 1174 { 1175 if (FBTrace.DBG_BP) FBTrace.sysout("debugger("+this.debuggerName+").onToggleBreakpoint ignoring toggle for "+(props.debugger?props.debugger.debuggerName:props.debugger)+" target "+lineNo+"@"+url+"\n"); 1176 return; 1177 } 1178 1179 for (var i = 0; i < TabWatcher.contexts.length; ++i) 1180 { 1181 var context = TabWatcher.contexts[i]; 1182 var sourceFile = context.sourceFileMap[url]; 1183 if (sourceFile) { 1184 if (FBTrace.DBG_BP) 1185 FBTrace.sysout("debugger("+this.debuggerName+").onToggleBreakpoint found context "+context.getName()); 1186 1187 if (!isSet && context.dynamicURLhasBP) 1188 this.checkDynamicURLhasBP(context); 1189 1190 var panel = context.getPanel("script", true); 1191 if (!panel) 1192 { 1193 if (FBTrace.DBG_ERRORS) 1194 FBTrace.sysout("onToggleBreakpoint no panel in context "+context.getName()); 1195 return; 1196 } 1197 1198 panel.context.invalidatePanels("breakpoints"); 1199 1200 var sourceBox = panel.getSourceBoxByURL(url); 1201 if (!sourceBox) 1202 { 1203 if (FBTrace.DBG_BP) 1204 FBTrace.sysout("debugger("+this.debuggerName+").onToggleBreakpoint context "+i+" script panel no sourcebox for url: "+url, panel.sourceBoxes); 1205 } 1206 1207 var row = sourceBox.getLineNode(lineNo); 1208 if (FBTrace.DBG_BP) 1209 FBTrace.sysout(i+") onToggleBreakpoint getLineNode="+row+" lineNo="+lineNo+" context:"+context.getName()+"\n"); 1210 if (!row) 1211 continue; // we *should* only be called for lines in the viewport... 1212 1213 row.setAttribute("breakpoint", isSet); 1214 if (isSet && props) 1215 { 1216 row.setAttribute("condition", props.condition ? "true" : "false"); 1217 if (props.condition) // issue 1371 1218 { 1219 var watchPanel = this.ableWatchSidePanel(context); 1220 watchPanel.addWatch(props.condition); 1221 } 1222 row.setAttribute("disabledBreakpoint", new Boolean(props.disabled).toString()); 1223 } 1224 else 1225 { 1226 row.removeAttribute("condition"); 1227 if (props.condition) 1228 { 1229 var watchPanel = this.ableWatchSidePanel(context); 1230 watchPanel.removeWatch(props.condition); 1231 watchPanel.rebuild(); 1232 } 1233 row.removeAttribute("disabledBreakpoint"); 1234 } 1235 dispatch(this.fbListeners, "onToggleBreakpoint", [context, url, lineNo, isSet]); 1236 return; 1237 } 1238 } 1239 if (FBTrace.DBG_BP) 1240 FBTrace.sysout("debugger("+this.debuggerName+").onToggleBreakpoint no find context"); 1241 }, 1242 1243 onToggleErrorBreakpoint: function(url, lineNo, isSet) 1244 { 1245 for (var i = 0; i < TabWatcher.contexts.length; ++i) 1246 { 1247 var context = TabWatcher.contexts[i]; 1248 var panel = context.getPanel("console", true); 1249 if (panel) 1250 { 1251 panel.context.invalidatePanels("breakpoints"); 1252 1253 for (var row = panel.panelNode.firstChild; row; row = row.nextSibling) 1254 { 1255 var error = row.firstChild.repObject; 1256 if (error instanceof ErrorMessage && error.href == url && error.lineNo == lineNo) 1257 { 1258 if (isSet) 1259 setClass(row.firstChild, "breakForError"); 1260 else 1261 removeClass(row.firstChild, "breakForError"); 1262 1263 dispatch(this.fbListeners, "onToggleErrorBreakpoint", [context, url, lineNo, isSet]); 1264 } 1265 } 1266 } 1267 } 1268 }, 1269 1270 onToggleMonitor: function(url, lineNo, isSet) 1271 { 1272 for (var i = 0; i < TabWatcher.contexts.length; ++i) 1273 { 1274 var panel = TabWatcher.contexts[i].getPanel("console", true); 1275 if (panel) 1276 panel.context.invalidatePanels("breakpoints"); 1277 } 1278 }, 1279 1280 checkDynamicURLhasBP: function (context) 1281 { 1282 context.dynamicURLhasBP = false; 1283 for (var url in context.sourceFileMap) 1284 { 1285 var sourceFile = context.sourceFileMap[url]; 1286 if (sourceFile.isEval() || sourceFile.isEvent()) 1287 { 1288 fbs.enumerateBreakpoints(url, {call: function setDynamicIfSet(url, lineNo) 1289 { 1290 context.dynamicURLhasBP = true; 1291 }}); 1292 } 1293 if (context.dynamicURLhasBP) 1294 break; 1295 } 1296 if (FBTrace.DBG_SOURCEFILES || FBTrace.DBG_BP) 1297 FBTrace.sysout("debugger.checkDynamicURLhasBP "+context.dynamicURLhasBP); 1298 }, 1299 1300 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 1301 // XXXjjb this code is not called, because I found the scheme for detecting Function too complex. 1302 // I'm leaving it here to remind us that we need to support new Function(). 1303 onFunctionConstructor: function(frame, ctor_script) 1304 { 1305 try 1306 { 1307 var context = this.breakContext; 1308 delete this.breakContext; 1309 1310 var sourceFile = this.createSourceFileForFunctionConstructor(frame, ctor_script, context); 1311 1312 if (FBTrace.DBG_EVAL) 1313 { 1314 FBTrace.sysout("debugger.onFunctionConstructor tag="+ctor_script.tag+" url="+sourceFile.href+"\n"); 1315 FBTrace.sysout( traceToString(FBL.getCorrectedStackTrace(frame, context))+"\n" ); 1316 } 1317 1318 dispatch(this.fbListeners,"onFunctionConstructor",[context, frame, ctor_script, sourceFile.href]); 1319 return sourceFile.href; 1320 } 1321 catch(exc) 1322 { 1323 ERROR("debugger.onFunctionConstructor failed: "+exc); 1324 if (FBTrace.DBG_EVAL) 1325 FBTrace.sysout("debugger.onFunctionConstructor failed: ",exc); 1326 return null; 1327 } 1328 1329 }, 1330 1331 createSourceFileForFunctionConstructor: function(caller_frame, ctor_script, context) 1332 { 1333 var ctor_expr = null; // this.getConstructorExpression(caller_frame, context); 1334 if (FBTrace.DBG_EVAL) FBTrace.sysout("createSourceFileForFunctionConstructor ctor_expr:"+ctor_expr+"\n"); 1335 if (ctor_expr) 1336 var source = this.getEvalBody(caller_frame, "lib.createSourceFileForFunctionConstructor ctor_expr", 1, ctor_expr); 1337 else 1338 var source = " bah createSourceFileForFunctionConstructor"; //ctor_script.functionSource; 1339 1340 if (FBTrace.DBG_EVAL) FBTrace.sysout("createSourceFileForFunctionConstructor source:"+source+"\n"); 1341 var url = this.getDynamicURL(context, normalizeURL(caller_frame.script.fileName), source, "Function"); 1342 1343 var lines = context.sourceCache.store(url.href, source); 1344 var sourceFile = new Firebug.FunctionConstructorSourceFile(url, caller_frame.script, ctor_expr, lines.length); 1345 this.watchSourceFile(context, sourceFile); 1346 1347 if (FBTrace.DBG_SOURCEFILES) FBTrace.sysout("debugger.onNewFunction sourcefile="+sourceFile.toString()+" -> "+context.getName()+"\n"); 1348 1349 return sourceFile; 1350 }, 1351 1352 getConstructorExpression: function(caller_frame, context) 1353 { 1354 // We believe we are just after the ctor call. 1355 var decompiled_lineno = getLineAtPC(caller_frame, context); 1356 if (FBTrace.DBG_EVAL) FBTrace.sysout("debugger.getConstructoreExpression decompiled_lineno:"+decompiled_lineno+"\n"); 1357 1358 var decompiled_lines = splitLines(caller_frame.script.functionSource); // TODO place in sourceCache? 1359 if (FBTrace.DBG_EVAL) FBTrace.sysout("debugger.getConstructoreExpression decompiled_lines:",decompiled_lines); 1360 1361 var candidate_line = decompiled_lines[decompiled_lineno - 1]; // zero origin 1362 if (FBTrace.DBG_EVAL) FBTrace.sysout("debugger.getConstructoreExpression candidate_line:"+candidate_line+"\n"); 1363 1364 if (candidate_line && candidate_line != null) 1365 { 1366 var m = reFunction.exec(candidate_line); 1367 if (m) 1368 var arguments = m[1]; // TODO Lame: need to count parens, with escapes and quotes 1369 } 1370 if (FBTrace.DBG_EVAL) FBTrace.sysout("debugger.getConstructoreExpression arguments:"+arguments+"\n"); 1371 if (arguments) // need to break down commas and get last arg. 1372 { 1373 var lastComma = arguments.lastIndexOf(','); 1374 return arguments.substring(lastComma+1); // if -1 then 0 1375 } 1376 return null; 1377 }, 1378 // end of guilt trip 1379 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 1380 1381 // Called by debugger.onEval() to store eval() source. 1382 // The frame has the blank-function-name script and it is not the top frame. 1383 // The frame.script.fileName is given by spidermonkey as file of the first eval(). 1384 // The frame.script.baseLineNumber is given by spidermonkey as the line of the first eval() call 1385 // The source that contains the eval() call is the source of our caller. 1386 // If our caller is a file, the source of our caller is at frame.script.baseLineNumber 1387 // If our caller is an eval, the source of our caller is TODO Check Test Case 1388 getEvalLevelSourceFile: function(frame, context, innerScripts) 1389 { 1390 var eval_expr = this.getEvalExpression(frame, context); 1391 if (FBTrace.DBG_EVAL) FBTrace.sysout("getEvalLevelSourceFile eval_expr:"+eval_expr+"\n"); 1392 1393 if (eval_expr && !Firebug.decompileEvals) 1394 { 1395 var source = this.getEvalBody(frame, "lib.getEvalLevelSourceFile.getEvalBody", 1, eval_expr); 1396 var mapType = PCMAP_SOURCETEXT; 1397 } 1398 else 1399 { 1400 var source = frame.script.functionSource; // XXXms - possible crash on OSX FF2 1401 var mapType = PCMAP_PRETTYPRINT; 1402 } 1403 1404 var lines = splitLines(source); 1405 1406 if (FBTrace.DBG_EVAL) 1407 FBTrace.sysout("getEvalLevelSourceFile "+lines.length+ "lines, mapType:"+((mapType==PCMAP_SOURCETEXT)?"SOURCE":"PRETTY")+" source:"+source+"\n"); 1408 1409 var url = this.getDynamicURL(context, normalizeURL(frame.script.fileName), lines, "eval"); 1410 1411 context.sourceCache.invalidate(url.href); 1412 context.sourceCache.storeSplitLines(url.href, lines); 1413 1414 var sourceFile = new Firebug.EvalLevelSourceFile(url, frame.script, eval_expr, lines, mapType, innerScripts); 1415 this.watchSourceFile(context, sourceFile); 1416 1417 if (FBTrace.DBG_SOURCEFILES) 1418 FBTrace.sysout("debugger.getEvalLevelSourceFile sourcefile="+sourceFile.toString()+" -> "+context.getName()+"\n"); 1419 1420 return sourceFile; 1421 }, 1422 1423 getDynamicURL: function(context, callerURL, lines, kind) 1424 { 1425 var url = this.getURLFromLastLine(context, lines); 1426 if (url) 1427 return url; 1428 1429 var url = this.getSequentialURL(context, callerURL, kind); 1430 if (url) 1431 return url; 1432 1433 var url = this.getURLFromMD5(callerURL, lines, kind); 1434 if (url) 1435 return url; 1436 1437 var url = this.getDataURLForScript(callerURL, lines); 1438 if (url) 1439 return url; 1440 1441 return url; 1442 }, 1443 1444 getURLFromLastLine: function(context, lines) 1445 { 1446 var url = null; 1447 // Ignores any trailing whitespace in |source| 1448 const reURIinComment = /\/\/@\ssourceURL=\s*(\S*?)\s*$/m; 1449 var m = reURIinComment.exec(lines[lines.length - 1]); 1450 if (m) 1451 { 1452 // add context info to the sourceURL so eval'd sources are grouped correctly in the source file list 1453 if (m[1] && m[1].indexOf('://') == -1) { 1454 var loc = context.window.location; 1455 if (m[1].charAt(0) != '/') m[1] = '/'+m[1]; // prepend leading slash if necessary 1456 m[1] = loc.protocol + '//' + loc.host + m[1]; // prepend protocol and host 1457 } 1458 1459 var href = new String(m[1]); 1460 1461 url = {href: href, kind: "source"}; 1462 if (FBTrace.DBG_SOURCEFILES) 1463 FBTrace.sysout("debugger.getURLFromLastLine "+url.href, url); 1464 } 1465 else 1466 { 1467 if (FBTrace.DBG_SOURCEFILES) 1468 FBTrace.sysout("debugger.getURLFromLastLine no match"+lines[lines.length - 1]); 1469 } 1470 return url; 1471 }, 1472 1473 getSequentialURL: function(context, callerURL, kind) 1474 { 1475 var url = null; 1476 if (!context.dynamicURLhasBP) 1477 { 1478 // If no breakpoints live in dynamic code then we don't need to compare 1479 // the previous and reloaded source. In that case let's use a cheap URL. 1480 var href = new String(callerURL + (kind ? "/"+kind+"/" : "/nokind/")+"seq/" +(context.dynamicURLIndex++)); 1481 url = {href: href, kind: "seq"}; 1482 if (FBTrace.DBG_SOURCEFILES || isNaN(context.dynamicURLIndex) ) 1483 FBTrace.sysout("debugger.getSequentialURL context:"+context.getName()+" url:"+url.href+" index: "+context.dynamicURLIndex, url); 1484 } 1485 return url; 1486 }, 1487 1488 getURLFromMD5: function(callerURL, lines, kind) 1489 { 1490 this.hash_service.init(this.nsICryptoHash.MD5); 1491 var source = lines.join('\n'); // we could double loop, would that be any faster? 1492 byteArray = []; 1493 for (var j = 0; j < source.length; j++) 1494 { 1495 byteArray.push( source.charCodeAt(j) ); 1496 } 1497 this.hash_service.update(byteArray, byteArray.length); 1498 var hash = this.hash_service.finish(true); 1499 1500 // encoding the hash should be ok, it should be information-preserving? Or at least reversable? 1501 var href= new String(callerURL + (kind ? "/"+kind+"/" : "/nokind/")+"MD5/" + encodeURIComponent(hash)); 1502 url = {href: href, kind: "MD5"}; 1503 if (FBTrace.DBG_SOURCEFILES) 1504 FBTrace.sysout("debugger.getURLFromMD5 "+url.href, url); 1505 return url; 1506 }, 1507 1508 getDataURLForScript: function(callerURL, lines) 1509 { 1510 var url = null; 1511 var href = null; 1512 if (!source) 1513 href = "eval."+script.tag; 1514 else 1515 { 1516 // data:text/javascript;fileName=x%2Cy.js;baseLineNumber=10,<the-url-encoded-data> 1517 href = new String("data:text/javascript;"); 1518 href += "fileName="+encodeURIComponent(callerURL); 1519 var source = lines.join('\n'); 1520 //url += ";"+ "baseLineNumber="+encodeURIComponent(script.baseLineNumber) + 1521 href +="," + encodeURIComponent(source); 1522 } 1523 url = {href:href, kind:"data"}; 1524 if (FBTrace.DBG_SOURCEFILES) 1525 FBTrace.sysout("debugger.getDataURLForScript "+url.href, url); 1526 return url; 1527 }, 1528 1529 // ******************************************************************************** 1530 getEvalExpression: function(frame, context) 1531 { 1532 var expr = this.getEvalExpressionFromEval(frame, context); // eval in eval 1533 1534 return (expr) ? expr : this.getEvalExpressionFromFile(normalizeURL(frame.script.fileName), frame.script.baseLineNumber, context); 1535 }, 1536 1537 getEvalExpressionFromFile: function(url, lineNo, context) 1538 { 1539 if (context && context.sourceCache) 1540 { 1541 var in_url = FBL.reJavascript.exec(url); 1542 if (in_url) 1543 { 1544 var m = reEval.exec(in_url[1]); 1545 if (m) 1546 return m[1]; 1547 else 1548 return null; 1549 } 1550 1551 var htm = reHTM.exec(url); 1552 if (htm) { 1553 lineNo = lineNo + 1; // embedded scripts seem to be off by one? XXXjjb heuristic 1554 } 1555 // Walk backwards from the first line in the function until we find the line which 1556 // matches the pattern above, which is the eval call 1557 var line = ""; 1558 for (var i = 0; i < 3; ++i) 1559 { 1560 line = context.sourceCache.getLine(url, lineNo-i) + line; 1561 if (line && line != null) 1562 { 1563 var m = reEval.exec(line); 1564 if (m) 1565 return m[1]; 1566 } 1567 } 1568 } 1569 return null; 1570 }, 1571 1572 getEvalExpressionFromEval: function(frame, context) 1573 { 1574 var callingFrame = frame.callingFrame; 1575 var sourceFile = Firebug.SourceFile.getSourceFileByScript(context, callingFrame.script); 1576 if (sourceFile) 1577 { 1578 if (FBTrace.DBG_EVAL) { 1579 FBTrace.sysout("debugger.getEvalExpressionFromEval sourceFile.href="+sourceFile.href+"\n"); 1580 FBTrace.sysout("debugger.getEvalExpressionFromEval callingFrame.pc="+callingFrame.pc 1581 +" callingFrame.script.baseLineNumber="+callingFrame.script.baseLineNumber+"\n"); 1582 } 1583 var lineNo = callingFrame.script.pcToLine(callingFrame.pc, PCMAP_SOURCETEXT); 1584 lineNo = lineNo - callingFrame.script.baseLineNumber + 1; 1585 var url = sourceFile.href; 1586 1587 if (FBTrace.DBG_EVAL && !context.sourceCache) 1588 FBTrace.sysout("debugger.getEvalExpressionFromEval context.sourceCache null??\n"); 1589 1590 // Walk backwards from the first line in the function until we find the line which 1591 // matches the pattern above, which is the eval call 1592 var line = ""; 1593 for (var i = 0; i < 3; ++i) 1594 { 1595 line = context.sourceCache.getLine(url, lineNo-i) + line; 1596 if (FBTrace.DBG_EVAL) 1597 FBTrace.sysout("debugger.getEvalExpressionFromEval lineNo-i="+lineNo+"-"+i+"="+(lineNo-i)+" line:"+line+"\n"); 1598 if (line && line != null) 1599 { 1600 var m = reEval.exec(line); 1601 if (m) 1602 return m[1]; // TODO Lame: need to count parens, with escapes and quotes 1603 } 1604 } 1605 } 1606 return null; 1607 }, 1608 1609 getEvalBody: function(frame, asName, asLine, evalExpr) 1610 { 1611 if (evalExpr && !Firebug.decompileEvals) 1612 { 1613 var result_src = {}; 1614 var evalThis = "new String("+evalExpr+");"; 1615 var evaled = frame.eval(evalThis, asName, asLine, result_src); 1616 1617 if (evaled) 1618 { 1619 var src = unwrapIValue(result_src.value); 1620 return src; 1621 } 1622 else 1623 { 1624 var source; 1625 if(evalExpr == "function(p,a,c,k,e,r") 1626 source = "/packer/ JS compressor detected"; 1627 else 1628 source = frame.script.functionSource; 1629 return source+" /* !eval("+evalThis+")) */"; 1630 } 1631 } 1632 else 1633 { 1634 return frame.script.functionSource; // XXXms - possible crash on OSX FF2 1635 } 1636 }, 1637 1638 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 1639 // extends Module 1640 1641 initialize: function() 1642 { 1643 this.nsICryptoHash = Components.interfaces["nsICryptoHash"]; 1644 1645 this.debuggerName = window.location.href+"--"+FBL.getUniqueId(); /*@explore*/ 1646 this.toString = function() { return this.debuggerName; } /*@explore*/ 1647 if (FBTrace.DBG_INITIALIZE) 1648 FBTrace.sysout("debugger.initialize "+ this.debuggerName); 1649 1650 this.hash_service = CCSV("@mozilla.org/security/hash;1", "nsICryptoHash"); 1651 1652 $("cmd_breakOnErrors").setAttribute("checked", Firebug.breakOnErrors); 1653 $("cmd_decompileEvals").setAttribute("checked", Firebug.decompileEvals); 1654 1655 this.wrappedJSObject = this; // how we communicate with fbs 1656 this.panelName = "script"; 1657 1658 // This is a service operation, a way of encapsulating fbs which is in turn implementing this 1659 // simple service. We could implment a whole component for this service, but it hardly makes sense. 1660 Firebug.broadcast = function encapsulateFBSBroadcast(message, args) 1661 { 1662 fbs.broadcast(message, args); 1663 } 1664 1665 this.onFunctionCall = bind(this.onFunctionCall, this); 1666 Firebug.ActivableModule.initialize.apply(this, arguments); 1667 }, 1668 1669 /* 1670 * per-XUL window registration; this method just allows us to keep fbs in this file. 1671 * @param clientAPI an object that implements functions called by fbs for clients. 1672 */ 1673 registerClient: function(clientAPI) 1674 { 1675 return fbs.registerClient(clientAPI); 1676 }, 1677 1678 unregisterClient: function(clientAPI) 1679 { 1680 fbs.unregisterClient(clientAPI); 1681 }, 1682 1683 enable: function() 1684 { 1685 if (this.isAlwaysEnabled()) 1686 this.registerDebugger(); // allow callbacks for jsd 1687 }, 1688 1689 disable: function() 1690 { 1691 this.unregisterDebugger(); 1692 }, 1693 1694 initializeUI: function() 1695 { 1696 Firebug.ActivableModule.initializeUI.apply(this, arguments); 1697 this.filterButton = $("fbScriptFilterMenu"); 1698 this.filterMenuUpdate(); 1699 }, 1700 1701 initContext: function(context, persistedState) 1702 { 1703 if (persistedState) 1704 context.dynamicURLhasBP = persistedState.dynamicURLhasBP; 1705 1706 context.dynamicURLIndex = 1; // any dynamic urls need to be unique to the context. 1707 1708 Firebug.ActivableModule.initContext.apply(this, arguments); 1709 }, 1710 1711 reattachContext: function(browser, context) 1712 { 1713 this.filterButton = Firebug.chrome.$("fbScriptFilterMenu"); // connect to the button in the new window, not 'window' 1714 this.filterMenuUpdate(); 1715 Firebug.ActivableModule.reattachContext.apply(this, arguments); 1716 }, 1717 1718 loadedContext: function(context) 1719 { 1720 var watchPanel = this.ableWatchSidePanel(context); 1721 var needNow = watchPanel && watchPanel.watches; 1722 var watchPanelState = Firebug.getPanelState({name: "watches", context: context}); 1723 var needPersistent = watchPanelState && watchPanelState.watches; 1724 if (needNow || needPersistent) 1725 { 1726 Firebug.CommandLine.isReadyElsePreparing(context); 1727 if (watchPanel) 1728 { 1729 context.setTimeout(function refreshWatchesAfterCommandLineReady() 1730 { 1731 watchPanel.refresh(); 1732 }); 1733 } 1734 } 1735 1736 if (FBTrace.DBG_SOURCEFILES) 1737 FBTrace.sysout("debugger("+this.debuggerName+").loadedContext enabled on load: "+context.onLoadWindowContent+" context.sourceFileMap", context.sourceFileMap); 1738 }, 1739 1740 unwatchWindow: function(context, win) // clean up the source file map in case the frame is being reloaded. 1741 { 1742 var scriptTags = win.document.getElementsByTagName("script"); 1743 for (var i = 0; i < scriptTags.length; i++) 1744 { 1745 var src = scriptTags[i].getAttribute("src"); 1746 src = src ? src : safeGetWindowLocation(win); 1747 1748 // If the src is not in the source map, try to use absolute url. 1749 if (!context.sourceFileMap[src]) 1750 src = absoluteURL(src, win.location.href); 1751 1752 delete context.sourceFileMap[src]; 1753 1754 if (FBTrace.DBG_SOURCEFILES) 1755 FBTrace.sysout("debugger.unWatchWindow; delete sourceFileMap entry for " + src); 1756 } 1757 if (scriptTags.length > 0) 1758 context.invalidatePanels('script'); 1759 }, 1760 1761 destroyContext: function(context, persistedState) 1762 { 1763 Firebug.ActivableModule.destroyContext.apply(this, arguments); 1764 1765 if (context.stopped) 1766 { 1767 TabWatcher.cancelNextLoad = true; // the abort will call resume, but the nestedEventLoop will continue the load. 1768 this.abort(context); 1769 } 1770 1771 if(persistedState) 1772 { 1773 if (context.dynamicURLhasBP) 1774 persistedState.dynamicURLhasBP = context.dynamicURLhasBP; 1775 else 1776 delete persistedState.dynamicURLhasBP; 1777 } 1778 }, 1779 1780 updateOption: function(name, value) 1781 { 1782 if (name == "breakOnErrors") 1783 $("cmd_breakOnErrors").setAttribute("checked", value); 1784 else if (name == "decompileEvals") 1785 $("cmd_decompileEvals").setAttribute("checked", value); 1786 }, 1787 1788 getObjectByURL: function(context, url) 1789 { 1790 var sourceFile = getSourceFileByHref(url, context); 1791 if (sourceFile) 1792 return new SourceLink(sourceFile.href, 0, "js"); 1793 }, 1794 1795 shutdown: function() 1796 { 1797 fbs.unregisterDebugger(this); 1798 }, 1799 1800 registerDebugger: function() // 1.3.1 safe for multiple calls 1801 { 1802 if (FBTrace.DBG_INITIALIZE) 1803 FBTrace.sysout("registerDebugger this.registered: "+this.registered); 1804 1805 if (this.registered) 1806 return; 1807 this.registered = true; 1808 1809 var check = fbs.registerDebugger(this); // this will eventually set 'jsd' on the statusIcon 1810 1811 if (FBTrace.DBG_INITIALIZE) 1812 FBTrace.sysout("debugger.registerDebugger "+check+" debuggers"); 1813 }, 1814 1815 unregisterDebugger: function() // 1.3.1 safe for multiple calls 1816 { 1817 if (FBTrace.DBG_INITIALIZE) 1818 FBTrace.sysout("debugger.unregisterDebugger this.registered: "+this.registered); 1819 1820 if (!this.registered) 1821 return; 1822 1823 if (Firebug.Profiler.isProfiling()) // stay registered if we are profiling across a reload. 1824 return; 1825 1826 var check = fbs.unregisterDebugger(this); 1827 1828 this.registered = false; 1829 1830 if (FBTrace.DBG_ACTIVATION) 1831 FBTrace.sysout("debugger.unregisterDebugger: "+check+" debuggers"); 1832 }, 1833 1834 onSourceFileCreated: function(context, sourceFile) 1835 { 1836 // This event can come at any time, eg by frame reloads or ajax, so we need to update the display. 1837 context.invalidatePanels("script", "breakpoints"); 1838 }, 1839 1840 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 1841 // extends ActivableModule 1842 1843 onPanelEnable: function(panelName) 1844 { 1845 if (panelName != this.panelName) 1846 return; 1847 1848 this.registerDebugger(); 1849 1850 if (FirebugContext && !fbs.isJSDActive()) 1851 fbs.unPause(); 1852 1853 if (FBTrace.DBG_PANELS || FBTrace.DBG_ACTIVATION) FBTrace.sysout("debugger.onPanelEnable with panelName: "+panelName); 1854 }, 1855 1856 onPanelDisable: function(panelName) 1857 { 1858 if (panelName != this.panelName) 1859 return; 1860 1861 if (this.dependents.length > 0) 1862 { 1863 for(var i = 0; i < this.dependents.length; i++) 1864 { 1865 if (this.dependents[i].isAlwaysEnabled()) 1866 { 1867 var name = this.dependents[0].dispatchName; // TODO getName() for modules required. 1868 if (FirebugContext) 1869 Firebug.Console.log("Cannot disable the script panel, "+name+" panel requires it", FirebugContext); 1870 if (FBTrace.DBG_PANELS) FBTrace.sysout("debugger.onPanelDisable rejected: "+ name+" dependent, with panelName: "+panelName); 1871 return; 1872 } 1873 } 1874 } 1875 // else no dependents enabled: 1876 this.unregisterDebugger(); 1877 1878 if (FBTrace.DBG_PANELS || FBTrace.DBG_ACTIVATION) FBTrace.sysout("debugger.onPanelDisable with panelName: "+panelName); 1879 this.clearAllBreakpoints(); 1880 }, 1881 1882 onDependentModuleChange: function(dependentAddedOrRemoved) 1883 { 1884 if (this.dependents.length > 0) // then we have dependents now 1885 { 1886 if (!this.isAlwaysEnabled()) // then we need to enable 1887 { 1888 this.setDefaultState(true); 1889 if (FirebugContext) 1890 Firebug.Console.log("enabling javascript debugger to support "+dependentAddedOrRemoved.dispatchName, FirebugContext); 1891 } 1892 } 1893 }, 1894 1895 onSuspendFirebug: function() 1896 { 1897 if (!Firebug.Debugger.isAlwaysEnabled()) 1898 return; 1899 1900 var paused = fbs.pause(); // can be called multiple times. 1901 1902 if (FBTrace.DBG_ACTIVATION) 1903 FBTrace.sysout("debugger.onSuspendFirebug paused: "+paused+" isAlwaysEnabled " +Firebug.Debugger.isAlwaysEnabled()+"\n"); 1904 1905 if (!paused) // then we failed to suspend, undo 1906 return true; 1907 1908 return false; 1909 }, 1910 1911 onResumeFirebug: function() 1912 { 1913 if (!Firebug.Debugger.isAlwaysEnabled()) 1914 return; 1915 1916 var unpaused = fbs.unPause(); 1917 1918 if (FBTrace.DBG_ACTIVATION) 1919 FBTrace.sysout("debugger.onResumeFirebug unpaused: "+unpaused+" isAlwaysEnabled " +Firebug.Debugger.isAlwaysEnabled()); 1920 if (FBTrace.DBG_ERRORS && !this.registered) 1921 FBTrace.sysout("debugger.onResumeFirebug but debugger not registered! *** "); 1922 }, 1923 1924 ableWatchSidePanel: function(context) 1925 { 1926 if (Firebug.Console.isAlwaysEnabled()) 1927 { 1928 var watchPanel = context.getPanel("watches", true); 1929 if (watchPanel) 1930 return watchPanel; 1931 } 1932 1933 return null; 1934 }, 1935 1936 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 1937 // Menu in toolbar. 1938 1939 onScriptFilterMenuTooltipShowing: function(tooltip, context) 1940 { 1941 if (FBTrace.DBG_OPTIONS) 1942 FBTrace.sysout("onScriptFilterMenuTooltipShowing not implemented"); 1943 }, 1944 1945 onScriptFilterMenuCommand: function(event, context) 1946 { 1947 var menu = event.target; 1948 Firebug.setPref(Firebug.servicePrefDomain, "scriptsFilter", menu.value); 1949 Firebug.Debugger.filterMenuUpdate(); 1950 }, 1951 1952 menuFullLabel: 1953 { 1954 static: $STR("ScriptsFilterStatic"), 1955 evals: $STR("ScriptsFilterEval"), 1956 events: $STR("ScriptsFilterEvent"), 1957 all: $STR("ScriptsFilterAll"), 1958 }, 1959 1960 menuShortLabel: 1961 { 1962 static: $STR("ScriptsFilterStaticShort"), 1963 evals: $STR("ScriptsFilterEvalShort"), 1964 events: $STR("ScriptsFilterEventShort"), 1965 all: $STR("ScriptsFilterAllShort"), 1966 }, 1967 1968 onScriptFilterMenuPopupShowing: function(menu, context) 1969 { 1970 if (this.menuTooltip) 1971 this.menuTooltip.fbEnabled = false; 1972 1973 var items = menu.getElementsByTagName("menuitem"); 1974 var value = this.filterButton.value; 1975 1976 for (var i=0; i<items.length; i++) 1977 { 1978 var option = items[i].value; 1979 if (!option) 1980 continue; 1981 1982 if (option == value) 1983 items[i].setAttribute("checked", "true"); 1984 1985 items[i].label = Firebug.Debugger.menuFullLabel[option]; 1986 } 1987 1988 return true; 1989 }, 1990 1991 onScriptFilterMenuPopupHiding: function(tooltip, context) 1992 { 1993 if (this.menuTooltip) 1994 this.menuTooltip.fbEnabled = true; 1995 1996 return true; 1997 }, 1998 1999 filterMenuUpdate: function() 2000 { 2001 var value = Firebug.getPref(Firebug.servicePrefDomain, "scriptsFilter"); 2002 this.filterButton.value = value; 2003 this.filterButton.label = this.menuShortLabel[value]; 2004 this.filterButton.removeAttribute("disabled"); 2005 this.filterButton.setAttribute("value", value); 2006 if (FBTrace.DBG_OPTIONS) 2007 FBTrace.sysout("debugger.filterMenuUpdate value: "+value+" label:"+this.filterButton.label+'\n'); 2008 }, 2009 }); 2010 2011 // ************************************************************************************************ 2012 2013 2014 Firebug.ScriptPanel = function() {}; 2015 2016 2017 /* 2018 * object used to markup Javascript source lines. 2019 * In the namespace Firebug.ScriptPanel. 2020 */ 2021 2022 Firebug.ScriptPanel.decorator = extend(new Firebug.SourceBoxDecorator, 2023 { 2024 decorate: function(sourceBox, sourceFile) 2025 { 2026 this.markExecutableLines(sourceBox); 2027 this.setLineBreakpoints(sourceBox.repObject, sourceBox) 2028 }, 2029 2030 markExecutableLines: function(sourceBox) 2031 { 2032 var sourceFile = sourceBox.repObject; 2033 if (FBTrace.DBG_BP || FBTrace.DBG_LINETABLE) FBTrace.sysout("debugger.markExecutableLines START: "+sourceFile.toString(), sourceFile.getLineRanges()); 2034 var lineNo = sourceBox.firstViewableLine; 2035 while( lineNode = sourceBox.getLineNode(lineNo) ) 2036 { 2037 var script = sourceFile.scriptsIfLineCouldBeExecutable(lineNo, true); 2038 2039 if (FBTrace.DBG_LINETABLE) FBTrace.sysout("debugger.markExecutableLines ["+lineNo+"]="+(script?script.tag:"X")+"\n"); 2040 if (script) 2041 lineNode.setAttribute("executable", "true"); 2042 else 2043 lineNode.removeAttribute("executable"); 2044 2045 lineNo++; 2046 } 2047 if (FBTrace.DBG_BP || FBTrace.DBG_LINETABLE) 2048 FBTrace.sysout("debugger.markExecutableLines DONE: "+sourceFile.toString()+"\n"); 2049 }, 2050 2051 setLineBreakpoints: function(sourceFile, sourceBox) 2052 { 2053 fbs.enumerateBreakpoints(sourceFile.href, {call: function(url, line, props, script) 2054 { 2055 var scriptRow = sourceBox.getLineNode(line); 2056 if (scriptRow) 2057 { 2058 scriptRow.setAttribute("breakpoint", "true"); 2059 if (props.disabled) 2060 scriptRow.setAttribute("disabledBreakpoint", "true"); 2061 if (props.condition) 2062 scriptRow.setAttribute("condition", "true"); 2063 } 2064 if (FBTrace.DBG_LINETABLE) 2065 FBTrace.sysout("debugger.setLineBreakpoints found "+scriptRow+" for "+line+"@"+sourceFile.href+"\n"); 2066 }}); 2067 }, 2068 }); 2069 2070 Firebug.ScriptPanel.prototype = extend(Firebug.SourceBoxPanel, 2071 { 2072 /* 2073 * Framework connection 2074 */ 2075 updateSourceBox: function(sourceBox) 2076 { 2077 if (this.scrollInfo && (this.scrollInfo.location == this.location)) 2078 this.scrollToLine(this.location, this.scrollInfo.previousCenterLine); 2079 delete this.scrollInfo; 2080 }, 2081 2082 /* 2083 * Framework connection 2084 */ 2085 getSourceType: function() 2086 { 2087 return "js"; 2088 }, 2089 2090 /* 2091 * Framework connection 2092 */ 2093 getDecorator: function(sourceBox) 2094 { 2095 return Firebug.ScriptPanel.decorator; 2096 }, 2097 2098 initialize: function(context, doc) 2099 { 2100 this.location = null; 2101 Firebug.SourceBoxPanel.initialize.apply(this, arguments); 2102 }, 2103 2104 // ************************************************************************************* 2105 showFunction: function(fn) 2106 { 2107 var sourceLink = findSourceForFunction(fn, this.context); 2108 if (sourceLink) 2109 { 2110 this.showSourceLink(sourceLink); 2111 } 2112 else 2113 { 2114 if (FBTrace.DBG_ERRORS) FBTrace.sysout("no sourcelink for function"); // want to avoid the debugger panel if possible 2115 } 2116 }, 2117 2118 showSourceLink: function(sourceLink) 2119 { 2120 var sourceFile = getSourceFileByHref(sourceLink.href, this.context); 2121 if (sourceFile) 2122 { 2123 this.navigate(sourceFile); 2124 if (sourceLink.line) 2125 { 2126 this.scrollToLine(sourceLink.href, sourceLink.line, this.jumpHighlightFactory(sourceLink.line, this.context)); 2127 dispatch([Firebug.A11yModel], "onShowSourceLink", [this, sourceLink.line]); 2128 } 2129 if (sourceLink == this.selection) // then clear it so the next link will scroll and highlight. 2130 delete this.selection; 2131 } 2132 }, 2133 2134 showStackFrame: function(frame) 2135 { 2136 if (!frame || (frame && !frame.isValid)) 2137 { 2138 if (FBTrace.DBG_STACK) FBTrace.sysout("showStackFrame no valid frame\n"); 2139 this.showNoStackFrame(); 2140 return; 2141 } 2142 2143 this.context.currentFrame = frame; 2144 var sourceFile = Firebug.SourceFile.getSourceFileByScript(this.context, this.context.currentFrame.script); 2145 if (!sourceFile) 2146 {