1 /* See license.txt for terms of usage */ 2 3 define([ 4 "firebug/lib/object", 5 "firebug/firebug", 6 "firebug/chrome/firefox", 7 "arch/compilationunit", 8 "firebug/lib/xpcom", 9 "firebug/chrome/reps", 10 "firebug/lib/locale", 11 "firebug/lib/wrapper", 12 "firebug/lib/url", 13 "firebug/js/sourceLink", 14 "firebug/js/stackFrame", 15 "firebug/lib/css", 16 "firebug/chrome/window", 17 "firebug/lib/string", 18 "firebug/lib/array", 19 "firebug/trace/debug", 20 "firebug/js/fbs", 21 "firebug/lib/events", 22 "firebug/console/errors", 23 ], 24 function(Obj, Firebug, Firefox, CompilationUnit, Xpcom, FirebugReps, Locale, 25 Wrapper, Url, SourceLink, StackFrame, Css, Win, Str, Arr, Debug, FBS, Events) { 26 27 // ********************************************************************************************* // 28 // Constants 29 30 const Cc = Components.classes; 31 const Ci = Components.interfaces; 32 const jsdIScript = Ci.jsdIScript; 33 const jsdIStackFrame = Ci.jsdIStackFrame; 34 const jsdIExecutionHook = Ci.jsdIExecutionHook; 35 const nsISupports = Ci.nsISupports; 36 const nsICryptoHash = Ci.nsICryptoHash; 37 const nsIURI = Ci.nsIURI; 38 39 const PCMAP_SOURCETEXT = jsdIScript.PCMAP_SOURCETEXT; 40 const PCMAP_PRETTYPRINT = jsdIScript.PCMAP_PRETTYPRINT; 41 42 const RETURN_VALUE = jsdIExecutionHook.RETURN_RET_WITH_VAL; 43 const RETURN_THROW_WITH_VAL = jsdIExecutionHook.RETURN_THROW_WITH_VAL; 44 const RETURN_CONTINUE = jsdIExecutionHook.RETURN_CONTINUE; 45 const RETURN_CONTINUE_THROW = jsdIExecutionHook.RETURN_CONTINUE_THROW; 46 const RETURN_ABORT = jsdIExecutionHook.RETURN_ABORT; 47 const RETURN_HOOK_ERROR = jsdIExecutionHook.RETURN_HOOK_ERROR; 48 49 const TYPE_THROW = jsdIExecutionHook.TYPE_THROW; 50 const TYPE_DEBUGGER_KEYWORD = jsdIExecutionHook.TYPE_DEBUGGER_KEYWORD; 51 52 const STEP_OVER = 1; 53 const STEP_INTO = 2; 54 const STEP_OUT = 3; 55 56 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 57 58 const tooltipTimeout = 300; 59 60 const reEval = /\s*eval\s*\(([^)]*)\)/m; // eval ( $1 ) 61 const reHTM = /\.[hH][tT][mM]/; 62 const reFunction = /\s*Function\s*\(([^)]*)\)/m; 63 const reTooMuchRecursion = /too\smuch\srecursion/; 64 65 var jsd = Cc["@mozilla.org/js/jsd/debugger-service;1"].getService(Ci.jsdIDebuggerService); 66 67 // ************************************************************************************************ 68 69 Firebug.Debugger = Obj.extend(Firebug.ActivableModule, 70 { 71 dispatchName: "debugger", 72 fbs: FBS, // access to firebug-service in chromebug under browser.xul.Dom.Firebug.Debugger.fbs 73 74 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 75 // Debugging 76 77 // moz 78 hasValidStack: function(context) 79 { 80 return context.stopped && context.currentFrame.isValid; 81 }, 82 83 // on bti, method of stack 84 evaluate: function(js, context, scope) // TODO remote: move to backend, proxy to front 85 { 86 var frame = context.currentFrame; 87 if (!frame) 88 return; 89 90 frame.scope.refresh(); // XXX what's this do? 91 92 var result = {}; 93 var scriptToEval = js; 94 95 // This seem to be safe; eval'ing a getter property in content that tries to 96 // be evil and get Components.classes results in a permission denied error. 97 var ok = frame.eval(scriptToEval, "", 1, result); 98 99 var value = Wrapper.unwrapIValue(result.value, Firebug.viewChrome); 100 if (ok) 101 return value; 102 else 103 throw value; 104 }, 105 106 // on bti (not called in firebug source) 107 evaluateInCallingFrame: function(js, fileName, lineNo) 108 { 109 return this.halt(function evalInFrame(frame) 110 { 111 FBTrace.sysout("evaluateInCallingFrame " + js + " fileName: " + 112 frame.script.fileName + " stack: " + StackFrame.getJSDStackDump(frame)); 113 114 var result = {}; 115 var ok = frame.eval(js, fileName, lineNo, result); 116 var value = Wrapper.unwrapIValue(result.value, Firebug.viewChrome); 117 118 if (ok) 119 return value; 120 else 121 throw value; 122 }); 123 }, 124 125 /** 126 * Used by autocomplete in commandLine 127 * @return array of locally visible property names for each scope we are in 128 */ 129 getCurrentFrameKeys: function(context) // TODO remote, on bti 130 { 131 // return is safe 132 var globals = Arr.keys(Wrapper.getContentView(context.getGlobalScope())); 133 if (context.currentFrame) 134 return this.getFrameKeys(context.currentFrame, globals); 135 136 return globals; 137 }, 138 139 /** 140 * private to Debugger, returns list of strings 141 */ 142 getFrameKeys: function(frame, names) // moz 143 { 144 var scope = frame.scope; 145 while (scope) 146 { 147 var listValue = {value: null}, lengthValue = {value: 0}; 148 scope.getProperties(listValue, lengthValue); 149 150 for (var i=0; i<lengthValue.value; ++i) 151 { 152 var prop = listValue.value[i]; 153 var name = Wrapper.unwrapIValue(prop.name); 154 names.push(name); 155 } 156 157 scope = scope.jsParent; 158 } 159 160 return names; 161 }, 162 163 // @Deprecated see chrome.js 164 focusWatch: function(context) // TODO moved 165 { 166 return Firebug.chrome.focusWatch(context); 167 }, 168 169 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 170 // Private to Debugger 171 172 // moz 173 beginInternalOperation: function() // stop debugger operations like breakOnErrors 174 { 175 var state = {breakOnErrors: Firebug.breakOnErrors}; 176 Firebug.breakOnErrors = false; 177 return state; 178 }, 179 180 // moz 181 endInternalOperation: function(state) // pass back the object given by beginInternalOperation 182 { 183 Firebug.breakOnErrors = state.breakOnErrors; 184 return true; 185 }, 186 187 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 188 189 // moz 190 halt: function(fnOfFrame) 191 { 192 if(FBTrace.DBG_BP) 193 FBTrace.sysout('debugger.halt '+fnOfFrame); 194 195 return FBS.halt(this, fnOfFrame); 196 }, 197 198 // on bti 199 getCurrentStackTrace: function(context) 200 { 201 var trace = null; 202 203 Firebug.Debugger.halt(function(frame) 204 { 205 if (FBTrace.DBG_STACK) 206 FBTrace.sysout("lib.getCurrentStackTrace frame:", frame); 207 208 trace = StackFrame.getCorrectedStackTrace(frame, context); 209 210 if (FBTrace.DBG_STACK) 211 FBTrace.sysout("lib.getCurrentStackTrace trace:", trace.toString().split('\n')); 212 }); 213 214 return trace; 215 }, 216 217 // Used by FBTest 218 breakAsIfDebugger: function(frame) 219 { 220 // should return 'this' but also sets this.breakContext 221 var debuggr = FBS.findDebugger(frame); 222 FBS.breakIntoDebugger(debuggr, frame, 3); 223 }, 224 225 // This URL prefix is used to skip frames from chrome URLs. Note that sometimes chrome URLs 226 // are used even in web pages, but only in rare cases so don't worry about it. 227 // Don't be specific like: chrome://firebug/ since frames coming from extensions e.g. 228 // chrome://firecookie/ wouldn't be skipped then. 229 breakNowURLPrefix: "chrome://", 230 231 // on bti 232 breakNow: function(context) 233 { 234 Firebug.Debugger.halt(function haltAnalysis(frame) 235 { 236 if (FBTrace.DBG_UI_LOOP) 237 FBTrace.sysout("debugger.breakNow: frame "+frame.script.fileName+" context "+ 238 context.getName(), StackFrame.getJSDStackDump(frame) ); 239 240 for (; frame && frame.isValid; frame = frame.callingFrame) 241 { 242 var fileName = frame.script.fileName; 243 if (!fileName) 244 continue; 245 else if (Str.hasPrefix(fileName, Firebug.Debugger.breakNowURLPrefix)) 246 continue; 247 else if (fileName.indexOf("/modules/firebug-") != -1) 248 continue; 249 else 250 break; 251 } 252 253 if (frame) 254 { 255 Firebug.Debugger.breakContext = context; 256 257 // I just made up a type that won't match TYPE_DEBUGGER_KEYWORD 258 Firebug.Debugger.onBreak(frame, "halt"); 259 } 260 else 261 { 262 if (FBTrace.DBG_UI_LOOP) 263 FBTrace.sysout("debugger.breakNow: no frame that not starting with "+ 264 Firebug.Debugger.breakNowURLPrefix); 265 } 266 }); 267 }, 268 269 // moz, called by back end 270 stop: function(context, frame, type, rv) 271 { 272 if (context.stopped) 273 return RETURN_CONTINUE; 274 275 if (!this.isAlwaysEnabled()) 276 return RETURN_CONTINUE; 277 278 if (FBTrace.DBG_UI_LOOP) 279 FBTrace.sysout("debugger.stop "+context.getName()+" frame",frame); 280 281 // Do not break if the user is on another tab 282 if (Firefox.getCurrentURI().spec !== context.window.location.toString()) 283 { 284 if (FBTrace.DBG_UI_LOOP) 285 { 286 var current = Firefox.getCurrentURI().spec; 287 var prev = context.window.location.toString(); 288 var locations = {current: current, context: prev}; 289 290 FBTrace.sysout("debugger.stop ERROR break is not in current window ", locations); 291 } 292 return RETURN_CONTINUE; 293 } 294 295 context.stoppedFrame = frame; // the frame we stopped in, don't change this elsewhere. 296 context.currentFrame = frame; // the frame we show to user, depends on selection 297 context.stopped = true; 298 299 var hookReturn = Firebug.connection.dispatch("onStop",[context,frame, type,rv]); 300 if ( hookReturn && hookReturn >= 0 ) 301 { 302 delete context.stopped; 303 delete context.stoppedFrame; 304 delete context.currentFrame; 305 306 if (FBTrace.DBG_UI_LOOP) 307 { 308 FBTrace.sysout("debugger.stop extension vetoed stop with hookReturn " + 309 hookReturn); 310 } 311 312 return hookReturn; 313 } 314 315 try 316 { 317 this.freeze(context); 318 319 // If firebug hits a breakpoint in an event handler, which used setCapture 320 // the entire browser window is unclickable (see issue 5064) 321 context.window.document.releaseCapture(); 322 323 // We will pause here until resume is called 324 var depth = FBS.enterNestedEventLoop({ 325 onNest: Obj.bindFixed(this.startDebugging, this, context) 326 }); 327 328 // For some reason we don't always end up here 329 330 if (FBTrace.DBG_UI_LOOP) 331 FBTrace.sysout("debugger.stop, nesting depth:"+depth+" jsd.pauseDepth: "+ 332 jsd.pauseDepth+" context:"+context.getName()); 333 } 334 catch (exc) 335 { 336 // Just ignore exceptions that happened while in the nested loop 337 if (FBTrace.DBG_ERRORS) 338 FBTrace.sysout("debugger exception in nested event loop: "+exc, exc); 339 else 340 Debug.ERROR("debugger exception in nested event loop: "+exc+"\n"); 341 } 342 finally 343 { 344 this.thaw(context); 345 } 346 347 this.stopDebugging(context); 348 349 Firebug.connection.dispatch("onResume",[context]); 350 351 if (context.aborted) 352 { 353 delete context.aborted; 354 return RETURN_ABORT; 355 } 356 else if (Firebug.rerun) 357 { 358 setTimeout(function reExecute() 359 { 360 var rerun = context.savedRerun = Firebug.rerun; 361 delete Firebug.rerun; 362 363 if (FBTrace.DBG_UI_LOOP) 364 FBTrace.sysout("Firebug.debugger.reExecute ", {rerun: rerun}); 365 366 // fire the prestored script 367 function successConsoleFunction(result, context) 368 { 369 if (FBTrace.DBG_UI_LOOP) 370 FBTrace.sysout("Firebug.debugger.reExecute success", result); 371 Firebug.connection.dispatch( "onRerunComplete", [true, result]); 372 } 373 374 function exceptionFunction(result, context) 375 { 376 if (FBTrace.DBG_ERRORS) 377 FBTrace.sysout("Firebug.debugger.reExecute FAILED "+result, result); 378 Firebug.connection.dispatch( "onRerunComplete", [false, result]); 379 } 380 381 Firebug.CommandLine.evaluate("window._firebug.rerunFunction()", context, null, 382 context.window, successConsoleFunction, exceptionFunction); 383 }); 384 385 if (FBTrace.DBG_UI_LOOP) 386 FBTrace.sysout("Firebug.debugger.reExecute return "+RETURN_HOOK_ERROR); 387 388 return RETURN_HOOK_ERROR; 389 } 390 else 391 { 392 return RETURN_CONTINUE; 393 } 394 }, 395 396 // on bti 397 rerun: function(context) 398 { 399 if(!context.stopped) 400 { 401 FBTrace.sysout("debugger.rerun FAILS: not stopped"); 402 return; 403 } 404 405 if (Firebug.rerun) 406 { 407 FBTrace.sysout("debugger.rerun FAILS: Firebug.rerun in progress"); 408 return; 409 } 410 411 Firebug.rerun = this.getRerun(context); 412 413 // now continue but abort the current call stack. 414 this.resume(context); // the Firebug.rerun will signal abort stack 415 }, 416 417 // moz 418 getRerun: function(context) 419 { 420 if (FBTrace.DBG_UI_LOOP) 421 FBTrace.sysout("debugger.rerun for "+context.getName()); 422 try 423 { 424 // walk back to the oldest frame, but not top level 425 var frame = context.stoppedFrame; 426 while (frame.callingFrame && frame.callingFrame.script.functionName) 427 { 428 frame = frame.callingFrame; 429 430 if (frame.script.functionName == "_firebugRerun") // re-reRun 431 { 432 if (FBTrace.DBG_UI_LOOP) 433 FBTrace.sysout("getRerun re-rerun ", context.savedRerun); 434 return context.savedRerun; 435 } 436 } 437 438 // In this oldest frame we have element.onclick(event) or window.foo() 439 // We want to cause the page to run this again after we abort this call stack. 440 function getStoreRerunInfoScript(fnName) 441 { 442 var str = "if (!window._firebug)window._firebug={};\n"; 443 str += "window._firebug.rerunThis = this;\n"; 444 str += "window._firebug.rerunArgs = [];\n" 445 str += "if (arguments && arguments.length) for (var i = 0; i < arguments.length; i++) window._firebug.rerunArgs.push(arguments[i]);\n" 446 str += "window._firebug.rerunFunctionName = "+fnName+";\n" 447 str +="window._firebug.rerunFunction = function _firebugRerun() { "+fnName+".apply(window._firebug.rerunThis, window._firebug.rerunArgs); }" 448 return str; 449 } 450 451 var rerun = {}; 452 453 var fnName = StackFrame.getFunctionName(frame.script, context, frame, true); 454 rerun.script = getStoreRerunInfoScript(fnName); 455 var jsdFunctionName = frame.script.functionName; 456 457 // now run the script that stores the rerun info in the page 458 var result = {}; 459 var ok = frame.eval(rerun.script, context.window.location + "/RerunScript", 1, result); 460 461 // If the eval goes off somewhere wacky, the frame may be invalid by this point. 462 if (FBTrace.DBG_UI_LOOP) 463 FBTrace.sysout("debugger.rerun "+ok+" and result: "+result+" for "+context.getName(), 464 {result: result, rerun: rerun, functionName: jsdFunctionName}); 465 } 466 catch(exc) 467 { 468 if (FBTrace.DBG_ERRORS) 469 FBTrace.sysout("debugger.rerun FAILS for "+context.getName()+" because "+exc, 470 {exc:exc, rerun: rerun}); 471 } 472 473 return rerun; 474 }, 475 476 // bti 477 resume: function(context) 478 { 479 if (FBTrace.DBG_UI_LOOP) 480 FBTrace.sysout("debugger.resume, context.stopped:"+context.stopped+"\n"); 481 482 // this will cause us to return to just after the enterNestedEventLoop call 483 var depth = FBS.exitNestedEventLoop(); 484 485 486 if (FBTrace.DBG_UI_LOOP) 487 FBTrace.sysout("debugger.resume, depth:"+depth+"\n"); 488 }, 489 490 // bti 491 abort: function(context) 492 { 493 if (context.stopped) 494 { 495 context.aborted = true; 496 this.thaw(context); 497 this.resume(context); 498 FBS.unPause(true); 499 } 500 }, 501 502 // bti 503 stepOver: function(context) 504 { 505 if (!context.stoppedFrame || !context.stoppedFrame.isValid) 506 return; 507 508 FBS.step(STEP_OVER, context, this); 509 this.resume(context); 510 }, 511 512 stepInto: function(context) 513 { 514 if (!context.stoppedFrame || !context.stoppedFrame.isValid) 515 return; 516 517 FBS.step(STEP_INTO, context, this); 518 this.resume(context); 519 }, 520 521 stepOut: function(context) 522 { 523 if (!context.stoppedFrame || !context.stoppedFrame.isValid) 524 return; 525 526 FBS.step(STEP_OUT, context, this); 527 this.resume(context); 528 }, 529 530 suspend: function(context) 531 { 532 if (context.stopped) 533 return; 534 535 FBS.suspend(this, context); 536 }, 537 538 unSuspend: function(context) 539 { 540 FBS.stopStepping(null, context); // TODO per context 541 FBS.cancelBreakOnNextCall(this, context) 542 }, 543 544 runUntil: function(context, compilationUnit, lineNo) 545 { 546 if (FBTrace.DBG_UI_LOOP) 547 FBTrace.sysout("runUntil "+lineNo+" @"+compilationUnit); 548 549 if (!context.stoppedFrame || !context.stoppedFrame.isValid) 550 return; 551 552 var sourceFile = compilationUnit.sourceFile; 553 FBS.runUntil(compilationUnit.sourceFile, lineNo, context.stoppedFrame, this); 554 this.resume(context); 555 }, 556 557 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 558 // moz 559 560 freeze: function(context) 561 { 562 var executionContext = context.stoppedFrame.executionContext; 563 try { 564 executionContext.scriptsEnabled = false; 565 this.suppressEventHandling(context); 566 context.isFrozen = true; 567 568 // https://developer.mozilla.org/en/XUL_Tutorial/Focus_and_Selection#Getting_the_currently_focused_element 569 if (context.window && context.window.document.commandDispatcher) 570 { 571 context.saveFocus = context.window.document.commandDispatcher.focusedElement; 572 if (context.saveFocus && !context.discardBlurEvents) 573 { 574 context.discardBlurEvents = function blurDiscarder(event) 575 { 576 if (!context.saveFocus) 577 { 578 Events.removeEventListener(context.window, "blur", 579 context.discardBlurEvents, true); 580 delete context.discardBlurEvents; 581 } 582 583 if (FBTrace.DBG_UI_LOOP) 584 { 585 FBTrace.sysout("debugger.freeze discard blur event " + 586 context.saveFocus + " while focus is " + 587 context.window.document.commandDispatcher.focusedElement, 588 event); 589 } 590 591 event.preventDefault(); 592 event.stopPropagation(); 593 }, 594 595 Events.addEventListener(context.window, "blur", 596 context.discardBlurEvents, true); 597 } 598 } 599 600 if (FBTrace.DBG_UI_LOOP) 601 { 602 FBTrace.sysout("debugger.freeze context.saveFocus "+context.saveFocus, 603 context.saveFocus); 604 605 FBTrace.sysout("debugger.freeze try to disable scripts "+ 606 (context.eventSuppressor?"and events":"but not events")+" in "+ 607 context.getName()+" executionContext.tag "+executionContext.tag+ 608 ".scriptsEnabled: "+executionContext.scriptsEnabled); 609 } 610 } 611 catch (exc) 612 { 613 // This attribute is only valid for contexts which implement nsIScriptContext. 614 if (FBTrace.DBG_UI_LOOP) 615 FBTrace.sysout("debugger.freeze, freeze exception " + exc + " in " + 616 context.getName(), exc); 617 } 618 }, 619 620 suppressEventHandling: function(context) 621 { 622 if (context.window instanceof Ci.nsIInterfaceRequestor) 623 { 624 context.eventSuppressor = context.window.getInterface(Ci.nsIDOMWindowUtils); 625 if (context.eventSuppressor) 626 context.eventSuppressor.suppressEventHandling(true); 627 } 628 }, 629 630 thaw: function(context) 631 { 632 try { 633 if (context.isFrozen) 634 delete context.isFrozen; 635 else 636 return; // bail, we did not freeze this context 637 638 var executionContext = context.stoppedFrame.executionContext; 639 if (executionContext.isValid) 640 { 641 this.unsuppressEventHandling(context); 642 643 // Before we release JS, put the focus back 644 if (context.saveFocus) 645 { 646 context.window.focus(); 647 context.saveFocus.focus(); 648 delete context.saveFocus; 649 650 if (FBTrace.DBG_UI_LOOP) 651 { 652 var nowFocused = context.window.document.commandDispatcher ? 653 context.window.document.commandDispatcher.focusedElement : null; 654 FBTrace.sysout("debugger.thaw context.saveFocus "+context.saveFocus+ 655 " vs "+nowFocused, context.saveFocus); 656 } 657 } 658 659 executionContext.scriptsEnabled = true; 660 } 661 else 662 { 663 if (FBTrace.DBG_UI_LOOP) 664 FBTrace.sysout("debugger.thaw "+executionContext.tag+ 665 " executionContext is not valid"); 666 } 667 668 if (FBTrace.DBG_UI_LOOP) 669 FBTrace.sysout("debugger.thaw try to enable scripts " + 670 (context.eventSuppressor?"with events suppressed":"events enabled")+ 671 " in "+context.getName()+" executionContext.tag "+executionContext.tag+ 672 ".scriptsEnabled: "+executionContext.scriptsEnabled); 673 } 674 catch (exc) 675 { 676 if (FBTrace.DBG_UI_LOOP) 677 FBTrace.sysout("debugger.stop, scriptsEnabled = true exception:", exc); 678 } 679 }, 680 681 unsuppressEventHandling: function(context) 682 { 683 if (context.eventSuppressor) 684 { 685 context.eventSuppressor.suppressEventHandling(false); 686 delete context.eventSuppressor; 687 } 688 }, 689 690 // on bti 691 toggleFreezeWindow: function(context) 692 { 693 // then we need to break into debugger to get the executionContext 694 if (!context.stopped) 695 { 696 Firebug.Debugger.halt(function grabContext(frame) 697 { 698 context.stoppedFrame = frame; 699 Firebug.Debugger.doToggleFreezeWindow(context); 700 delete context.stoppedFrame; 701 }); 702 703 Firebug.Debugger.suspend(context); 704 } 705 else 706 { 707 Firebug.Debugger.doToggleFreezeWindow(context); 708 } 709 }, 710 711 // moz 712 doToggleFreezeWindow: function(context) 713 { 714 if (context.isFrozen) 715 Firebug.Debugger.unsuppressEventHandling(context); 716 else 717 Firebug.Debugger.suppressEventHandling(context); 718 }, 719 720 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 721 // Breakpoints 722 723 setBreakpoint: function(sourceFile, lineNo) // TODO: arg should be url 724 { 725 if (sourceFile instanceof CompilationUnit) 726 sourceFile = sourceFile.sourceFile; // see HACK in tabContext 727 FBS.setBreakpoint(sourceFile, lineNo, null, Firebug.Debugger); 728 }, 729 730 clearBreakpoint: function(sourceFile, lineNo) 731 { 732 if (sourceFile instanceof CompilationUnit) 733 sourceFile = sourceFile.sourceFile; // see HACK in tabContext 734 FBS.clearBreakpoint(sourceFile.href, lineNo); 735 }, 736 737 setErrorBreakpoint: function(compilationUnit, line) 738 { 739 FBS.setErrorBreakpoint(compilationUnit.sourceFile, line, Firebug.Debugger); 740 }, 741 742 clearErrorBreakpoint: function(compilationUnit, line) 743 { 744 FBS.clearErrorBreakpoint(compilationUnit.getURL(), line, Firebug.Debugger); 745 }, 746 747 // Called by bti browser.clearAllBreakpoints 748 clearAllBreakpoints: function(context) 749 { 750 if (context) 751 { 752 var units = context.getAllCompilationUnits(); 753 FBS.clearAllBreakpoints(units, Firebug.Debugger); 754 FBS.clearErrorBreakpoints(units, Firebug.Debugger); 755 } 756 else 757 { 758 // null means all urls 759 FBS.enumerateBreakpoints(null, {call: function(url, lineNo, bp) 760 { 761 // skip breakpoints of other debuggers. 762 if (bp.debuggerName !== Firebug.Debugger.debuggerName) 763 return; 764 765 FBS.clearBreakpoint(url, lineNo); 766 }}); 767 768 // and also error breakpoints 769 FBS.enumerateErrorBreakpoints(null, {call: function(url, lineNo) 770 { 771 FBS.clearErrorBreakpoint(url, lineNo, Firebug.Debugger); 772 }}); 773 } 774 }, 775 776 enableAllBreakpoints: function(context) 777 { 778 if (FBTrace.DBG_BP) 779 FBTrace.sysout("enableAllBreakpoints sourceFileMap:", context.sourceFileMap); 780 781 for (var url in context.sourceFileMap) 782 { 783 FBS.enumerateBreakpoints(url, {call: function(url, lineNo) 784 { 785 FBS.enableBreakpoint(url, lineNo); 786 }}); 787 } 788 }, 789 790 disableAllBreakpoints: function(context) 791 { 792 for (var url in context.sourceFileMap) 793 { 794 FBS.enumerateBreakpoints(url, {call: function(url, lineNo) 795 { 796 FBS.disableBreakpoint(url, lineNo); 797 }}); 798 } 799 }, 800 801 getBreakpointCount: function(context) 802 { 803 var count = 0; 804 for (var url in context.sourceFileMap) 805 { 806 FBS.enumerateBreakpoints(url, 807 { 808 call: function(url, lineNo) 809 { 810 ++count; 811 } 812 }); 813 814 FBS.enumerateErrorBreakpoints(url, 815 { 816 call: function(url, lineNo) 817 { 818 ++count; 819 } 820 }); 821 } 822 return count; 823 }, 824 825 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 826 // Debugging and monitoring 827 828 traceAll: function(context) 829 { 830 FBS.traceAll(Firebug.SourceFile.sourceURLsAsArray(context), this); 831 }, 832 833 untraceAll: function(context) 834 { 835 FBS.untraceAll(this); 836 }, 837 838 monitorFunction: function(fn, mode) 839 { 840 if (typeof(fn) == "function" || fn instanceof Function) 841 { 842 var script = Firebug.SourceFile.findScriptForFunctionInContext( 843 Firebug.currentContext, fn); 844 845 if (script) 846 { 847 this.monitorScript(fn, script, mode); 848 } 849 else 850 { 851 Firebug.Console.logFormatted( 852 ["Firebug unable to locate jsdIScript for function", fn], 853 Firebug.currentContext, "info"); 854 } 855 } 856 else 857 { 858 Firebug.Console.logFormatted( 859 ["Firebug.Debugger.monitorFunction requires a function", fn], 860 Firebug.currentContext, "info"); 861 } 862 }, 863 864 unmonitorFunction: function(fn, mode) 865 { 866 if (typeof(fn) == "function" || fn instanceof Function) 867 { 868 var script = Firebug.SourceFile.findScriptForFunctionInContext( 869 Firebug.currentContext, fn); 870 871 if (script) 872 this.unmonitorScript(fn, script, mode); 873 } 874 }, 875 876 monitorScript: function(fn, script, mode) 877 { 878 var scriptInfo = Firebug.SourceFile.getSourceFileAndLineByScript( 879 Firebug.currentContext, script); 880 881 if (scriptInfo) 882 { 883 if (mode == "debug") 884 Firebug.Debugger.setBreakpoint(scriptInfo.sourceFile, scriptInfo.lineNo); 885 else if (mode == "monitor") 886 FBS.monitor(scriptInfo.sourceFile, scriptInfo.lineNo, Firebug.Debugger); 887 } 888 }, 889 890 unmonitorScript: function(fn, script, mode) 891 { 892 var scriptInfo = Firebug.SourceFile.getSourceFileAndLineByScript( 893 Firebug.currentContext, script); 894 895 if (scriptInfo) 896 { 897 if (mode == "debug") 898 this.clearBreakpoint(scriptInfo.sourceFile, scriptInfo.lineNo); 899 else if (mode == "monitor") 900 FBS.unmonitor(scriptInfo.sourceFile.href, scriptInfo.lineNo); 901 } 902 }, 903 904 traceCalls: function(context, fn) 905 { 906 if (typeof(fn) == "function" || fn instanceof Function) 907 { 908 var script = Firebug.SourceFile.findScriptForFunctionInContext(context, fn); 909 if (script) 910 this.traceScriptCalls(context, script); 911 else 912 { 913 if (FBTrace.DBG_ERRORS) 914 FBTrace.sysout("debugger.traceCalls no script found for "+fn, fn); 915 } 916 } 917 }, 918 919 untraceCalls: function(context, fn) 920 { 921 if (typeof(fn) == "function" || fn instanceof Function) 922 { 923 var script = Firebug.SourceFile.findScriptForFunctionInContext(context, fn); 924 if (script) 925 this.untraceScriptCalls(context, script); 926 } 927 }, 928 929 traceScriptCalls: function(context, script) 930 { 931 var scriptInfo = Firebug.SourceFile.getSourceFileAndLineByScript(context, script); 932 if (scriptInfo) 933 FBS.traceCalls(scriptInfo.sourceFile, scriptInfo.lineNo, Firebug.Debugger); 934 }, 935 936 untraceScriptCalls: function(context, script) 937 { 938 var scriptInfo = Firebug.SourceFile.getSourceFileAndLineByScript(context, script); 939 if (scriptInfo) 940 FBS.untraceCalls(scriptInfo.sourceFile, scriptInfo.lineNo, Firebug.Debugger); 941 }, 942 943 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 944 // UI Stuff 945 946 /* 947 * Called when a nestedEventLoop begins 948 */ 949 startDebugging: function(context) 950 { 951 if (FBTrace.DBG_UI_LOOP) 952 FBTrace.sysout("Firebug.Debugger startDebugging enter context.stopped:" + 953 context.stopped + " for context: " + context.getName()); 954 955 try 956 { 957 FBS.lockDebugger(); 958 959 context.executingSourceFile = 960 Firebug.SourceFile.getSourceFileByScript(context, context.stoppedFrame.script); 961 962 // bail out, we don't want the user stuck in debug with out source. 963 if (!context.executingSourceFile) 964 { 965 if (FBTrace.DBG_UI_LOOP) 966 FBTrace.sysout("startDebugging resuming, no sourceFile for "+ 967 context.stoppedFrame.script.fileName, 968 context.stoppedFrame.script.functionSource); 969 970 this.resume(context); 971 return; 972 } 973 974 // Make Firebug.currentContext = context and sync the UI 975 if (context != Firebug.currentContext) 976 Firebug.selectContext(context); 977 978 } 979 catch(exc) 980 { 981 if (FBTrace.DBG_ERRORS) 982 FBTrace.sysout("Resuming debugger: error during debugging loop: "+exc, exc); 983 984 Firebug.Console.log("Resuming debugger: error during debugging loop: "+exc); 985 986 this.resume(context); 987 } 988 989 var frame = StackFrame.getStackFrame(context.stoppedFrame, context); 990 Firebug.connection.dispatch( "onStartDebugging", [context, frame]); 991 992 if (FBTrace.DBG_UI_LOOP) 993 FBTrace.sysout("startDebugging exit context.stopped:" + context.stopped + 994 " for context: " + context.getName()); 995 }, 996 997 /** 998 * Called in the main event loop, from jsd, after we have exited the nested event loop 999 */ 1000 stopDebugging: function(context) 1001 { 1002 if (FBTrace.DBG_UI_LOOP) 1003 FBTrace.sysout("stopDebugging enter context: " + context.getName()); 1004 1005 try 1006 { 1007 FBS.unlockDebugger(); 1008 1009 // If the user reloads the page while the debugger is stopped, then 1010 // the current context will be destroyed just before 1011 if (context && !context.aborted) 1012 { 1013 delete context.stopped; 1014 delete context.stoppedFrame; 1015 delete context.currentFrame; 1016 context.executingSourceFile = null; 1017 delete context.breakLineNumber; 1018 1019 Firebug.connection.dispatch( "onStopDebugging", [context]); 1020 1021 } 1022 else 1023 { 1024 if (FBTrace.DBG_UI_LOOP) 1025 FBTrace.sysout("debugger.stopDebugging else "+context.getName()+" "+ 1026 Win.safeGetWindowLocation(context.window)); 1027 } 1028 } 1029 catch (exc) 1030 { 1031 if (FBTrace.DBG_UI_LOOP) 1032 FBTrace.sysout("debugger.stopDebugging FAILS", exc); 1033 1034 // If the window is closed while the debugger is stopped, 1035 // then all hell will break loose here 1036 Debug.ERROR(exc); 1037 } 1038 }, 1039 1040 suspendFirebug: function() 1041 { 1042 Firebug.suspendFirebug(); 1043 }, 1044 1045 resumeFirebug: function() 1046 { 1047 Firebug.resumeFirebug(); 1048 }, 1049 1050 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 1051 1052 supportsWindow: function(win) 1053 { 1054 if (!this.isAlwaysEnabled()) 1055 return false; 1056 1057 var context = ((win && Firebug.TabWatcher) ? 1058 Firebug.TabWatcher.getContextByWindow(win) : null); 1059 1060 this.breakContext = context; 1061 return !!context; 1062 }, 1063 1064 // This is called from fbs for almost all fbs operations 1065 supportsGlobal: function(frameWin) 1066 { 1067 var context = ( (frameWin && Firebug.TabWatcher) ? 1068 Firebug.TabWatcher.getContextByWindow(frameWin) : null); 1069 1070 if (!context) 1071 return false; 1072 1073 // otherwise we cannot be called. 1074 context.jsDebuggerCalledUs = true; 1075 1076 if (!Firebug.Console.injector.isAttached(context, frameWin)) 1077 { 1078 this.injectConsole(context, frameWin); 1079 } 1080 else 1081 { 1082 if (FBTrace.DBG_CONSOLE) 1083 FBTrace.sysout("debugger.supportsGlobal console isAttached to "+ 1084 Win.safeGetWindowLocation(frameWin)+" in "+context.getName()); 1085 } 1086 1087 this.breakContext = context; 1088 //FBTrace.sysout("debugger.js this.breakContext "+this.breakContext.getName()); 1089 return true; 1090 }, 1091 1092 injectConsole: function(context, frameWin) 1093 { 1094 if (Firebug.Console.isAlwaysEnabled()) 1095 { 1096 // This is how the console is injected ahead of JS running on the page 1097 FBS.filterConsoleInjections = true; 1098 try 1099 { 1100 var consoleReady = Firebug.Console.isReadyElsePreparing(context, frameWin); 1101 } 1102 catch(exc) 1103 { 1104 if (FBTrace.DBG_ERRORS) 1105 FBTrace.sysout("debugger.supportsGlobal injectConsole FAILS: "+exc, exc); 1106 } 1107 finally 1108 { 1109 FBS.filterConsoleInjections = false; 1110 } 1111 1112 if (FBTrace.DBG_CONSOLE) 1113 FBTrace.sysout("debugger.supportsGlobal injectConsole consoleReady:"+consoleReady+ 1114 " jsDebuggerCalledUs: "+context.jsDebuggerCalledUs, frameWin); 1115 } 1116 else 1117 { 1118 if (FBTrace.DBG_CONSOLE) 1119 FBTrace.sysout("debugger.supportsGlobal injectConsole console NOT enabled ", 1120 frameWin); 1121 } 1122 }, 1123 1124 onLock: function(state) 1125 { 1126 // XXXjoe For now, trying to see if it's ok to have multiple contexts 1127 // debugging simultaneously - otherwise we need this 1128 //if (this.context != this.debugContext) 1129 { 1130 // XXXjoe Disable step/continue buttons 1131 } 1132 }, 1133 1134 onBreak: function(frame, type) 1135 { 1136 try 1137 { 1138 var context = this.breakContext; 1139 1140 // If the script panel is disabled, Firebug can't break (issue 4290). 1141 if (!Firebug.PanelActivation.isPanelEnabled("script")) 1142 return RETURN_CONTINUE; 1143 1144 if (FBTrace.DBG_BP || (!context && FBTrace.DBG_FBS_ERRORS)) 1145 FBTrace.sysout("debugger.onBreak breakContext: " + 1146 (context ? context.getName() : " none!"), StackFrame.getJSDStackDump(frame)); 1147 1148 delete this.breakContext; 1149 1150 if (!context) 1151 return RETURN_CONTINUE; 1152 1153 if (type == TYPE_DEBUGGER_KEYWORD) 1154 { 1155 var trace = Wrapper.getContentView(context.window)._firebugStackTrace; 1156 if (trace == "console-tracer") 1157 return this.debuggerTracer(context, frame); 1158 else 1159 this.setDebuggerKeywordCause(context, frame); 1160 } 1161 1162 return this.stop(context, frame, type); 1163 } 1164 catch (exc) 1165 { 1166 if (FBTrace.DBG_ERRORS && FBTrace.DBG_BP) 1167 FBTrace.sysout("debugger.onBreak FAILS", exc); 1168 throw exc; 1169 } 1170 }, 1171 1172 debuggerTracer: function(context, frame) 1173 { 1174 var trace = StackFrame.getCorrectedStackTrace(frame, context); 1175 if (FBTrace.DBG_ERRORLOG) 1176 FBTrace.sysout("debugger.firebugDebuggerTracer corrected trace.frames "+ 1177 trace.frames.length, trace.frames); 1178 1179 if (trace) 1180 { 1181 // drop the firebugDebuggerTracer and reorder 1182 //trace.frames = trace.frames.slice(1); 1183 1184 if (FBTrace.DBG_ERRORLOG) 1185 FBTrace.sysout("debugger.firebugDebuggerTracer dropped tracer trace.frames "+ 1186 trace.frames.length, trace.frames); 1187 1188 // drop one frame see attachConsoleInjector 1189 //trace.frames = trace.frames.slice(1); 1190 Firebug.Console.log(trace, context, "stackTrace"); 1191 } 1192 1193 if (FBTrace.DBG_BP) 1194 FBTrace.sysout("debugger.onBreak "+(trace?"debugger trace":" debugger no trace!")); 1195 1196 return RETURN_CONTINUE; 1197 }, 1198 1199 /** 1200 * for |debugger;| keyword offer the skip/continue dialog (optionally?) 1201 */ 1202 setDebuggerKeywordCause: function(context, frame) 1203 { 1204 var sourceFile = Firebug.SourceFile.getSourceFileByScript(context, frame.script); 1205 if (!sourceFile) 1206 { 1207 if (FBTrace.DBG_ERRORS) 1208 FBTrace.sysout("debugger.setDebuggerKeywordCause FAILS, no sourceFile for "+ 1209 frame.script.tag+"@"+frame.script.fileName+" in "+context.getName()); 1210 return; 1211 } 1212 1213 var analyzer = sourceFile.getScriptAnalyzer(frame.script); 1214 var lineNo = analyzer.getSourceLineFromFrame(context, frame); 1215 1216 context.breakingCause = 1217 { 1218 title: Locale.$STR("debugger keyword"), 1219 skipActionTooltip: Locale.$STR("firebug.bon.tooltip.disableDebuggerKeyword2"), 1220 message: Locale.$STR("firebug.bon.cause.disableDebuggerKeyword2"), 1221 skipAction: function addSkipperAndGo() 1222 { 1223 // a breakpoint that never hits, but prevents debugger keyword 1224 // (see FBS.onDebugger as well) 1225 var bp = Firebug.Debugger.setBreakpoint(sourceFile, lineNo); 1226 FBS.disableBreakpoint(sourceFile.href, lineNo); 1227 1228 if (FBTrace.DBG_BP) 1229 FBTrace.sysout("debugger.onBreak converted to disabled bp "+sourceFile.href+ 1230 "@"+lineNo+" tag: "+frame.script.tag, bp); 1231 1232 Firebug.Debugger.resume(context); 1233 }, 1234 }; 1235 }, 1236 1237 onThrow: function(frame, rv) 1238 { 1239 // onThrow is called for throw, for catches that do not succeed, 1240 // and for functions that exceptions pass through. 1241 var context = this.breakContext; 1242 delete this.breakContext; 1243 1244 if (!context) 1245 { 1246 if (FBTrace.DBG_BP) 1247 FBTrace.sysout("debugger.onThrow, no context, try to get from frame\n"); 1248 context = this.getContextByFrame(frame); 1249 } 1250 1251 if (FBTrace.DBG_ERRORLOG) 1252 { 1253 var lines = []; 1254 var frames = StackFrame.getCorrectedStackTrace(frame, context).frames; 1255 for (var i=0; i<frames.length; i++) 1256 lines.push(frames[i].line + ", " + frames[i].fn); 1257 1258 FBTrace.sysout("debugger.onThrow context:" + (context ? context.getName() : 1259 "undefined") + ", " + lines.join("; "), frames); 1260 } 1261 1262 if (!context) 1263 return RETURN_CONTINUE_THROW; 1264 1265 if (!FBS.showStackTrace) 1266 return RETURN_CONTINUE_THROW; 1267 1268 try 1269 { 1270 var realThrow = this.isRealThrow(frame, context); 1271 if (realThrow) 1272 { 1273 context.thrownStackTrace = StackFrame.getCorrectedStackTrace(frame, context); 1274 1275 if (FBTrace.DBG_BP) 1276 FBTrace.sysout("debugger.onThrow reset context.thrownStackTrace", 1277 context.thrownStackTrace.frames); 1278 1279 // xxxHonza: this could fix Issue 3276: Track Throw/Catch is not working 1280 /*if (FBS.trackThrowCatch) 1281 { 1282 var object = { 1283 errorMessage: errorObject.value.stringValue, 1284 message: errorObject.value.stringValue, 1285 sourceName: "", 1286 lineNumber: -1, 1287 sourceLine: "", 1288 category: "javascript", 1289 flags: 2, 1290 exceptionFlag: 2, 1291 }; 1292 1293 Firebug.Errors.logScriptError(context, object, false); 1294 1295 context.thrownStackTrace = StackFrame.getCorrectedStackTrace(frame, context); 1296 }*/ 1297 } 1298 else 1299 { 1300 if (FBTrace.DBG_BP) 1301 FBTrace.sysout("debugger.onThrow not a real throw"); 1302 } 1303 } 1304 catch (exc) 1305 { 1306 if (FBTrace.DBG_ERRORS) 1307 FBTrace.sysout("onThrow FAILS: " + exc, exc); 1308 } 1309 1310 if (Firebug.connection.dispatch("onThrow",[context, frame, rv])) 1311 return this.stop(context, frame, TYPE_THROW, rv); 1312 1313 return RETURN_CONTINUE_THROW; 1314 }, 1315 1316 isRealThrow: function(mozFrame, context) 1317 { 1318 // Determine whether the throw was a real one, or just a rethrow of the 1319 // last exception (probably automatically inserted - which it seems 1320 // happens for every function an exception passes through - but it 1321 // could also be manual because there is no simple way to tell them 1322 // apart). A rethrow is detected when the current stack exists at the 1323 // end of the previous exception's, except that the current top-most 1324 // stack frame only has to be in the same function to match. 1325 if (!context.thrownStackTrace) 1326 return true; 1327 1328 var trace = context.thrownStackTrace.frames; 1329 var findMozFrame = mozFrame.callingFrame, againstFrame = null; 1330 if (findMozFrame) 1331 { 1332 // Verify that the previous exception includes this frame's call 1333 // site somewhere. 1334 var findFrameSig = findMozFrame.script.tag + "." + findMozFrame.pc; 1335 for (var i=1; i<trace.length; i++) 1336 { 1337 var preFrameSig = trace[i].signature(); 1338 1339 if (FBTrace.DBG_ERRORS && FBTrace.DBG_STACK) 1340 { 1341 FBTrace.sysout("debugger.isRealThrow " + findFrameSig + "==" + 1342 preFrameSig); 1343 } 1344 1345 if (findFrameSig === preFrameSig) 1346 { 1347 againstFrame = trace[i-1]; 1348 break; 1349 } 1350 } 1351 1352 if (!againstFrame) 1353 return true; 1354 } 1355 else 1356 { 1357 againstFrame = trace[trace.length-1]; 1358 } 1359 1360 // Verify that the current frame's function location matches what the 1361 // exception has above the matched frame. 1362 if (mozFrame.script !== againstFrame.script) 1363 return true; 1364 1365 return false; 1366 }, 1367 1368 onMonitorScript: function(frame) 1369 { 1370 var context = this.breakContext; 1371 delete this.breakContext; 1372 1373 if (!context) 1374 context = this.getContextByFrame(frame); 1375 if (!context) 1376 return RETURN_CONTINUE; 1377 1378 frame = StackFrame.getStackFrame(frame, context); 1379 1380 Firebug.connection.dispatch("onMonitorScript",[context, frame]); 1381 }, 1382 1383 onFunctionCall: function(context, frame, depth, calling) 1384 { 1385 if (!context) 1386 context = this.getContextByFrame(frame); 1387 1388 if (!context) 1389 return RETURN_CONTINUE; 1390 1391 frame = StackFrame.getStackFrame(frame, context); 1392 1393 Firebug.connection.dispatch("onFunctionCall",[context, frame, depth, calling]); 1394 1395 return context; // returned as first arg on next call from same trace 1396 }, 1397 1398 onError: function(frame, error, hitErrorBreakpoint) 1399 { 1400 var context = this.breakContext; 1401 delete this.breakContext; 1402 1403 // If the script panel is disabled, Firebug can't break on error. 1404 if (!Firebug.PanelActivation.isPanelEnabled("script")) 1405 return 0; 1406 1407 try 1408 { 1409 if (FBTrace.DBG_ERRORLOG) 1410 FBTrace.sysout("debugger.onError: "+error.errorMessage+" in "+ 1411 (context?context.getName():"no context"), error); 1412 1413 if (reTooMuchRecursion.test(error.errorMessage)) 1414 frame = FBS.discardRecursionFrames(frame); 1415 1416 Firebug.errorStackTrace = StackFrame.getCorrectedStackTrace(frame, context); 1417 1418 if (FBTrace.DBG_ERRORLOG) 1419 FBTrace.sysout("debugger.onError; break=" + Firebug.breakOnErrors + 1420 ", errorStackTrace:", Firebug.errorStackTrace); 1421 1422 delete context.breakingCause; 1423 1424 if (Firebug.breakOnErrors || hitErrorBreakpoint) 1425 { 1426 var eventOrigin = Wrapper.unwrapIValue(frame.executionContext.globalObject); 1427 if (!eventOrigin) 1428 return 0; 1429 1430 // Check if the eventOrigin (window) comes from this context. 1431 var eventOriginIndex = -1; 1432 for (var i=0; i<context.windows.length; i++) 1433 { 1434 if (Wrapper.getContentView(context.windows[i]) == eventOrigin) { 1435 eventOriginIndex = i; 1436 break; 1437 } 1438 } 1439 1440 // Bail out if the event that lead the error is not cause by code in this context. 1441 if (eventOriginIndex < 0) 1442 { 1443 if (FBTrace.DBG_ERRORS) 1444 FBTrace.sysout("debugger.onError; error is not from this context: (" + 1445 eventOriginIndex + ") " + frame.script.tag+"@"+frame.script.fileName); 1446 return 0; 1447 } 1448 1449 var sourceFile = Firebug.SourceFile.getSourceFileByScript(context, frame.script); 1450 if (!sourceFile) 1451 { 1452 if (FBTrace.DBG_ERRORS) 1453 FBTrace.sysout("debugger.breakon Errors no sourceFile for "+ 1454 frame.script.tag+"@"+frame.script.fileName); 1455 return; 1456 } 1457 1458 var analyzer = sourceFile.getScriptAnalyzer(frame.script); 1459 var lineNo = analyzer.getSourceLineFromFrame(context, frame); 1460 1461 var doBreak = true; 1462 FBS.enumerateBreakpoints(sourceFile.href, {call: function(url, line, props, scripts) 1463 { 1464 if (FBTrace.DBG_FBS_BP) 1465 FBTrace.sysout("debugger.breakon Errors bp "+url+"@"+line+" scripts "+ 1466 (scripts?scripts.length:"none")); 1467 1468 if (line === lineNo) 1469 doBreak = false; 1470 }}); 1471 1472 if (FBTrace.DBG_BP) 1473 FBTrace.sysout("debugger.breakon Errors " + doBreak + " for " + 1474 sourceFile.href + "@" + lineNo); 1475 1476 if (doBreak) 1477 { 1478 context.breakingCause = 1479 { 1480 title: Locale.$STR("Break on Error"), 1481 message: error.message, 1482 copyAction: Obj.bindFixed(FirebugReps.ErrorMessage.copyError, 1483 FirebugReps.ErrorMessage, error), 1484 1485 skipAction: function addSkipperAndGo() 1486 { 1487 // a breakpoint that never hits, but prevents BON for errors 1488 var bp = Firebug.Debugger.setBreakpoint(sourceFile, lineNo); 1489 FBS.disableBreakpoint(sourceFile.href, lineNo); 1490 1491 if (FBTrace.DBG_BP) 1492 FBTrace.sysout("debugger.breakon Errors set "+sourceFile.href+"@"+ 1493 lineNo+" tag: "+frame.script.tag, bp); 1494 1495 Firebug.Debugger.resume(context); 1496 }, 1497 }; 1498 } 1499 } 1500 } 1501 catch (exc) 1502 { 1503 if (FBTrace.DBG_ERRORS) 1504 FBTrace.sysout("debugger.onError getCorrectedStackTrace FAILED: "+exc, exc); 1505 } 1506 1507 var hookReturn = Firebug.connection.dispatch("onError",[context, frame, error]); 1508 1509 if (!context.breakingCause) 1510 return 0; 1511 1512 if (Firebug.breakOnErrors) 1513 { 1514 // Switch of Break on Next tab lightning. 1515 var panel = context.getPanel("console", true); 1516 //Firebug.Breakpoint.updatePanelTab(panel, false); 1517 1518 return -1; // break 1519 } 1520 1521 if (hookReturn) 1522 return hookReturn; 1523 1524 return -2; /* let firebug service decide to break or not */ 1525 }, 1526 1527 onUncaughtException: function(errorInfo) 1528 { 1529 var context = this.breakContext; 1530 delete this.breakContext; 1531 1532 Firebug.Errors.logScriptError(context, errorInfo, false); 1533 return -2; 1534 }, 1535 1536 onXULScriptCreated: function(frame, outerScript, innerScriptEnumerator) 1537 { 1538 try 1539 { 1540 var context = this.breakContext; 1541 delete this.breakContext; 1542 1543 var sourceFile = context.sourceFileMap[outerScript.fileName]; 1544 if (sourceFile) 1545 { 1546 if (FBTrace.DBG_SOURCEFILES) 1547 FBTrace.sysout("debugger.onXULScriptCreated reuse sourcefile="+ 1548 sourceFile.toString()+" -> "+context.getName()+" ("+context.uid+")"); 1549 1550 Firebug.SourceFile.addScriptsToSourceFile(sourceFile, null, innerScriptEnumerator); 1551 } 1552 else 1553 { 1554 sourceFile = new Firebug.XULSourceFile(outerScript.fileName, outerScript, 1555 innerScriptEnumerator); 1556 } 1557 1558 this.watchSourceFile(context, sourceFile); 1559 1560 if (FBTrace.DBG_SOURCEFILES) 1561 FBTrace.sysout("debugger.onXULScriptCreated script.fileName="+outerScript.fileName+ 1562 " in "+context.getName()+" "+sourceFile); 1563 1564 Firebug.connection.dispatch("onXULScriptCreated",[context, frame, sourceFile.href]); 1565 return sourceFile; 1566 } 1567 catch (e) 1568 { 1569 if (FBTrace.DBG_TOPLEVEL || FBTrace.DBG_ERRORS) 1570 FBTrace.sysout("onXULScriptCreated FaILS "+e, e); 1571 } 1572 }, 1573 1574 onEvalScriptCreated: function(frame, outerScript, innerScripts) 1575 { 1576 try 1577 { 1578 if (FBTrace.DBG_EVAL) 1579 FBTrace.sysout("debugger.onEvalLevelScript script.fileName=" + 1580 outerScript.fileName); 1581 1582 var context = this.breakContext; 1583 delete this.breakContext; 1584 1585 var sourceFile = this.getEvalLevelSourceFile(frame, context, innerScripts); 1586 1587 if (FBTrace.DBG_EVAL) 1588 FBTrace.sysout("debugger.onEvalScriptCreated url="+sourceFile.href, 1589 StackFrame.getCorrectedStackTrace(frame, context)); 1590 1591 Firebug.connection.dispatch("onEvalScriptCreated",[context, frame, sourceFile.href]); 1592 return sourceFile; 1593 } 1594 catch (e) 1595 { 1596 if (FBTrace.DBG_EVAL || FBTrace.DBG_ERRORS) 1597 FBTrace.sysout("onEvalScriptCreated FaILS "+e, e); 1598 } 1599 }, 1600 1601 onEventScriptCreated: function(frame, outerScript, innerScripts) 1602 { 1603 if (FBTrace.DBG_EVENTS) 1604 FBTrace.sysout("debugger.onEventScriptCreated script.fileName=" + 1605 outerScript.fileName, {outerScript: outerScript, script: frame.script}); 1606 1607 var context = this.breakContext; 1608 delete this.breakContext; 1609 1610 var script = frame.script; 1611 var creatorURL = Url.normalizeURL(frame.script.fileName); 1612 var innerScriptArray = []; 1613 1614 try 1615 { 1616 var source = script.functionSource; 1617 1618 while (innerScripts.hasMoreElements()) 1619 { 1620 var inner = innerScripts.getNext(); 1621 source += "\n"+inner.functionSource; 1622 innerScriptArray.push(inner); 1623 } 1624 } 1625 catch (exc) 1626 { 1627 /*Bug 426692 */ 1628 var source = creatorURL + "/"+Obj.getUniqueId(); 1629 } 1630 1631 var lines = Str.splitLines(source); 1632 1633 var urlDescribed = this.getDynamicURL(context, 1634 Url.normalizeURL(frame.script.fileName), lines, "event"); 1635 1636 var handlerName = outerScript.functionName; 1637 if (handlerName) 1638 var url = urlDescribed.href + '/' + handlerName; 1639 else 1640 var url = urlDescribed.href; 1641 1642 context.sourceCache.invalidate(url); 1643 context.sourceCache.storeSplitLines(url, lines); 1644 1645 var sourceFile = new Firebug.EventSourceFile(url, frame.script, "event:"+ 1646 script.functionName+"."+script.tag, lines, new ArrayEnumerator(innerScriptArray)); 1647 1648 this.watchSourceFile(context, sourceFile); 1649 1650 if (FBTrace.DBG_EVENTS) 1651 FBTrace.sysout("debugger.onEventScriptCreated url="+sourceFile.href+"\n"); 1652 1653 if (FBTrace.DBG_EVENTS) 1654 FBTrace.sysout("debugger.onEventScriptCreated sourceFileMap:", context.sourceFileMap); 1655 1656 if (FBTrace.DBG_SOURCEFILES) 1657 FBTrace.sysout("debugger.onEventScriptCreated sourcefile="+sourceFile.toString()+ 1658 " -> "+context.getName()+"\n"); 1659 1660 Firebug.connection.dispatch("onEventScriptCreated",[context, frame, url]); 1661 return sourceFile; 1662 }, 1663 1664 // We just compiled a bunch of JS, eg a script tag in HTML. We are about to run the outerScript. 1665 onTopLevelScriptCreated: function(frame, outerScript, innerScripts) 1666 { 1667 if (FBTrace.DBG_TOPLEVEL) 1668 FBTrace.sysout("debugger("+this.debuggerName+").onTopLevelScriptCreated script.fileName="+ 1669 outerScript.fileName+"\n"); 1670 1671 var context = this.breakContext; 1672 delete this.breakContext; 1673 1674 // This is our only chance to get the linetable for the outerScript 1675 // since it will run and be GC next. 1676 var script = frame.script; 1677 var url = Url.normalizeURL(script.fileName); 1678 1679 if (FBTrace.DBG_TOPLEVEL) 1680 FBTrace.sysout("debugger.onTopLevelScriptCreated frame.script.tag="+frame.script.tag+ 1681 " has url="+url); 1682 1683 var isInline = false; 1684 1685 /* The primary purpose here was to deal with http://code.google.com/p/fbug/issues/detail?id=2912 1686 * This approach could be applied to inline scripts, so I'll leave the code here until we decide. 1687 Win.iterateWindows(context.window, function isInlineScriptTag(win) 1688 { 1689 var location = Win.safeGetWindowLocation(win); 1690 if (location === url) 1691 { 1692 isInline = true; 1693 return isInline; 1694 } 1695 }); 1696 */ 1697 1698 if (FBTrace.DBG_TOPLEVEL) 1699 FBTrace.sysout("debugger.onTopLevelScriptCreated has inLine:"+isInline+" url="+url); 1700 1701 if (isInline) // never true see above 1702 { 1703 var href = url +"/"+context.dynamicURLIndex++; 1704 sourceFile = new Firebug.ScriptTagAppendSourceFile(href, script, 1705 script.lineExtent, innerScripts); 1706 this.watchSourceFile(context, sourceFile); 1707 context.pendingScriptTagSourceFile = sourceFile; 1708 } 1709 else 1710 { 1711 var sourceFile = context.sourceFileMap[url]; 1712 1713 // Multiple script tags in HTML or duplicate .js file names. 1714 if (sourceFile && (sourceFile instanceof Firebug.TopLevelSourceFile)) 1715 { 1716 if (FBTrace.DBG_SOURCEFILES) 1717 FBTrace.sysout("debugger.onTopLevelScriptCreated reuse sourcefile="+ 1718 sourceFile.toString()+" -> "+context.getName()+" ("+context.uid+")"); 1719 1720 if (!sourceFile.outerScript || !sourceFile.outerScript.isValid) 1721 sourceFile.outerScript = outerScript; 1722 1723 Firebug.SourceFile.addScriptsToSourceFile(sourceFile, outerScript, 1724 innerScripts); 1725 } 1726 else 1727 { 1728 sourceFile = new Firebug.TopLevelSourceFile(url, script, script.lineExtent, 1729 innerScripts); 1730 1731 if (FBTrace.DBG_SOURCEFILES) 1732 FBTrace.sysout("debugger.onTopLevelScriptCreated create sourcefile="+ 1733 sourceFile.toString()+" -> "+context.getName()+" ("+context.uid+")"); 1734 } 1735 1736 // If a script is inserted multiple times in HTML, we still need to make 1737 // sure that meta info is updated (e.g. sourceFileByTag in the context) 1738 // (see issue 4880) 1739 this.watchSourceFile(context, sourceFile); 1740 } 1741 1742 Firebug.connection.dispatch("onTopLevelScriptCreated",[context, frame, sourceFile.href]); 1743 return sourceFile; 1744 }, 1745 1746 getContextByFrame: function(frame) 1747 { 1748 if (FBTrace.DBG_BP) 1749 FBTrace.sysout("debugger.getContextByFrame"); 1750 1751 var win = FBS.getOutermostScope(frame); 1752 return win ? Firebug.TabWatcher.getContextByWindow(win) : null; 1753 }, 1754 1755 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 1756 1757 watchSourceFile: function(context, sourceFile) 1758 { 1759 context.addSourceFile(sourceFile); // store in the context and notify listeners 1760 //FBS.watchSourceFile(sourceFile); // tell the service to watch this file 1761 1762 // Update the Script panel, this script could have been loaded asynchronously 1763 // and perhaps is the only one that should be displayed (otherwise the panel 1764 // would show: No Javascript on this page). See issue 4932 1765 var panel = context.getPanel("script", true); 1766 if (panel) 1767 panel.context.invalidatePanels("script"); 1768 }, 1769 1770 unwatchSourceFile: function(context, sourceFile) 1771 { 1772 //FBS.unwatchSourceFile(sourceFile); 1773 context.removeSourceFile(sourceFile); 1774 }, 1775 1776 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 1777 1778 onToggleBreakpoint: function(url, lineNo, isSet, props) 1779 { 1780 if (props.debuggerName != this.debuggerName) // then not for us 1781 { 1782 if (FBTrace.DBG_BP) 1783 FBTrace.sysout("debugger(" + this.debuggerName + 1784 ").onToggleBreakpoint ignoring toggle for " + 1785 props.debuggerName + " target " + lineNo + "@" + url); 1786 return; 1787 } 1788 1789 var found = false; 1790 for (var i = 0; i < Firebug.TabWatcher.contexts.length; ++i) 1791 { 1792 var context = Firebug.TabWatcher.contexts[i]; 1793 var sourceFile = context.sourceFileMap[url]; 1794 if (sourceFile) 1795 { 1796 if (FBTrace.DBG_BP) 1797 FBTrace.sysout("debugger(" + this.debuggerName + 1798 ").onToggleBreakpoint found context " + 1799 context.getName()); 1800 1801 if (!isSet && context.dynamicURLhasBP) 1802 this.checkDynamicURLhasBP(context); 1803 1804 var panel = context.getPanel("script", true); 1805 if (!panel) 1806 continue; 1807 1808 panel.context.invalidatePanels("breakpoints"); 1809 1810 var sourceBox = panel.getSourceBoxByURL(url); 1811 if (!sourceBox) 1812 { 1813 if (FBTrace.DBG_BP) 1814 FBTrace.sysout("debugger("+this.debuggerName+").onToggleBreakpoint context "+ 1815 i+" script panel no sourcebox for url: "+url, panel.sourceBoxes); 1816 continue; 1817 } 1818 1819 var row = sourceBox.getLineNode(lineNo); 1820 if (FBTrace.DBG_BP) 1821 FBTrace.sysout(i+") onToggleBreakpoint getLineNode="+row+" lineNo="+lineNo+ 1822 " context:"+context.getName()); 1823 1824 if (!row) 1825 continue; // we *should* only be called for lines in the viewport... 1826 1827 row.setAttribute("breakpoint", isSet); 1828 if (isSet && props) 1829 { 1830 row.setAttribute("condition", props.condition ? "true" : "false"); 1831 row.breakpointCondition = props.condition ? props.condition : null; 1832 1833 if (props.condition) // issue 1371 1834 { 1835 var watchPanel = this.ableWatchSidePanel(context); 1836 1837 if (watchPanel) 1838 { 1839 watchPanel.addWatch(props.condition); 1840 } 1841 else 1842 { 1843 if (FBTrace.DBG_ERRORS) 1844 FBTrace.sysout("onToggleBreakpoint no watch panel in context "+ 1845 context.getName()); 1846 } 1847 } 1848 row.setAttribute("disabledBreakpoint", new Boolean(props.disabled).toString()); 1849 } 1850 else 1851 { 1852 row.removeAttribute("condition"); 1853 if (props.condition) 1854 { 1855 var watchPanel = this.ableWatchSidePanel(context); 1856 watchPanel.removeWatch(props.condition); 1857 watchPanel.rebuild(); 1858 } 1859 row.removeAttribute("disabledBreakpoint"); 1860 } 1861 Firebug.connection.dispatch( "onToggleBreakpoint", [context, url, lineNo, isSet]); 1862 found = true; 1863 continue; 1864 } 1865 } 1866 1867 if (FBTrace.DBG_BP && !found) 1868 FBTrace.sysout("debugger("+this.debuggerName+").onToggleBreakpoint no find context"); 1869 }, 1870 1871 // xxxHonza, xxxjjb: duplicated in script.js, does it belong here? 1872 // But onToggleBreakpoint needs it. 1873 ableWatchSidePanel: function(context) 1874 { 1875 // TODO if (commandline is not active, then we should not show the new watch feature) 1876 var watchPanel = context.getPanel("watches", true); 1877 if (watchPanel) 1878 return watchPanel; 1879 }, 1880 1881 onToggleErrorBreakpoint: function(url, lineNo, isSet) 1882 { 1883 for (var i = 0; i < Firebug.TabWatcher.contexts.length; ++i) 1884 { 1885 var context = Firebug.TabWatcher.contexts[i]; 1886 var panel = context.getPanel("console", true); 1887 if (panel) 1888 { 1889 panel.context.invalidatePanels("breakpoints"); 1890 1891 for (var row = panel.panelNode.firstChild; row; row = row.nextSibling) 1892 { 1893 var error = row.firstChild.repObject; 1894 if (error instanceof FirebugReps.ErrorMessageObj && error.href == url && 1895 error.lineNo == lineNo) 1896 { 1897 if (isSet) 1898 Css.setClass(row.firstChild, "breakForError"); 1899 else 1900 Css.removeClass(row.firstChild, "breakForError"); 1901 1902 Firebug.connection.dispatch( "onToggleErrorBreakpoint", 1903 [context, url, lineNo, isSet]); 1904 } 1905 } 1906 } 1907 } 1908 }, 1909 1910 onToggleMonitor: function(url, lineNo, isSet) 1911 { 1912 for (var i = 0; i < Firebug.TabWatcher.contexts.length; ++i) 1913 { 1914 var panel = Firebug.TabWatcher.contexts[i].getPanel("console", true); 1915 if (panel) 1916 panel.context.invalidatePanels("breakpoints"); 1917 } 1918 }, 1919 1920 checkDynamicURLhasBP: function (context) 1921 { 1922 context.dynamicURLhasBP = false; 1923 for (var url in context.sourceFileMap) 1924 { 1925 var sourceFile = context.sourceFileMap[url]; 1926 if (sourceFile.isEval() || sourceFile.isEvent()) 1927 { 1928 FBS.enumerateBreakpoints(url, {call: function setDynamicIfSet(url, lineNo) 1929 { 1930 context.dynamicURLhasBP = true; 1931 }}); 1932 } 1933 if (context.dynamicURLhasBP) 1934 break; 1935 } 1936 if (FBTrace.DBG_SOURCEFILES || FBTrace.DBG_BP) 1937 FBTrace.sysout("debugger.checkDynamicURLhasBP "+context.dynamicURLhasBP); 1938 }, 1939 1940 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 1941 // XXXjjb this code is not called, because I found the scheme for detecting Function 1942 // too complex. I'm leaving it here to remind us that we need to support new Function(). 1943 onFunctionConstructor: function(frame, ctor_script) 1944 { 1945 try 1946 { 1947 var context = this.breakContext; 1948 delete this.breakContext; 1949 1950 var sourceFile = this.createSourceFileForFunctionConstructor(frame, ctor_script, context); 1951 1952 if (FBTrace.DBG_EVAL) 1953 { 1954 FBTrace.sysout("debugger.onFunctionConstructor tag=" + ctor_script.tag + 1955 " url=" + sourceFile.href); 1956 1957 FBTrace.sysout(StackFrame.traceToString( 1958 StackFrame.getCorrectedStackTrace(frame, context))); 1959 } 1960 1961 Firebug.connection.dispatch("onFunctionConstructor", 1962 [context, frame, ctor_script, sourceFile.href]); 1963 1964 return sourceFile.href; 1965 } 1966 catch(exc) 1967 { 1968 Debug.ERROR("debugger.onFunctionConstructor failed: "+exc); 1969 1970 if (FBTrace.DBG_EVAL) 1971 FBTrace.sysout("debugger.onFunctionConstructor failed: ",exc); 1972 1973 return null; 1974 } 1975 1976 }, 1977 1978 createSourceFileForFunctionConstructor: function(caller_frame, ctor_script, context) 1979 { 1980 var ctor_expr = null; // this.getConstructorExpression(caller_frame, context); 1981 if (FBTrace.DBG_EVAL) 1982 FBTrace.sysout("createSourceFileForFunctionConstructor ctor_expr:"+ctor_expr+"\n"); 1983 1984 var source; 1985 if (ctor_expr) 1986 { 1987 source = this.getEvalBody(caller_frame, 1988 "lib.createSourceFileForFunctionConstructor ctor_expr", 1, ctor_expr); 1989 } 1990 else 1991 { 1992 source = " bah createSourceFileForFunctionConstructor"; //ctor_script.functionSource; 1993 } 1994 1995 if (FBTrace.DBG_EVAL) 1996 FBTrace.sysout("createSourceFileForFunctionConstructor source:"+source); 1997 1998 var url = this.getDynamicURL(context, Url.normalizeURL(caller_frame.script.fileName), 1999 source, "Function"); 2000 2001 var lines = context.sourceCache.store(url.href, source); 2002 var sourceFile = new Firebug.FunctionConstructorSourceFile(url, caller_frame.script, 2003 ctor_expr, lines.length); 2004 2005 this.watchSourceFile(context, sourceFile); 2006 2007 if (FBTrace.DBG_SOURCEFILES) 2008 FBTrace.sysout("debugger.onNewFunction sourcefile="+sourceFile.toString()+" -> "+ 2009 context.getName()+"\n"); 2010 2011 return sourceFile; 2012 }, 2013 2014 getConstructorExpression: function(caller_frame, context) 2015 { 2016 // We believe we are just after the ctor call. 2017 var decompiled_lineno = getLineAtPC(caller_frame, context); 2018 if (FBTrace.DBG_EVAL) 2019 FBTrace.sysout("debugger.getConstructoreExpression decompiled_lineno:"+ 2020 decompiled_lineno+"\n"); 2021 2022 // TODO place in sourceCache? 2023 var decompiled_lines = Str.splitLines(caller_frame.script.functionSource); 2024 if (FBTrace.DBG_EVAL) 2025 FBTrace.sysout("debugger.getConstructoreExpression decompiled_lines:",decompiled_lines); 2026 2027 var candidate_line = decompiled_lines[decompiled_lineno - 1]; // zero origin 2028 if (FBTrace.DBG_EVAL) 2029 FBTrace.sysout("debugger.getConstructoreExpression candidate_line:" + candidate_line); 2030 2031 if (candidate_line && candidate_line != null) 2032 { 2033 var m = reFunction.exec(candidate_line); 2034 if (m) 2035 var arguments = m[1]; // TODO Lame: need to count parens, with escapes and quotes 2036 } 2037 2038 if (FBTrace.DBG_EVAL) 2039 FBTrace.sysout("debugger.getConstructoreExpression arguments:"+arguments+"\n"); 2040 2041 if (arguments) // need to break down commas and get last arg. 2042 { 2043 var lastComma = arguments.lastIndexOf(','); 2044 return arguments.substring(lastComma+1); // if -1 then 0 2045 } 2046 2047 return null; 2048 }, 2049 2050 // end of guilt trip 2051 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 2052 2053 // Called by debugger.onEval() to store eval() source. 2054 // The frame has the blank-function-name script and it is not the top frame. 2055 // The frame.script.fileName is given by spidermonkey as file of the first eval(). 2056 // The frame.script.baseLineNumber is given by spidermonkey as the line of the first eval() call 2057 // The source that contains the eval() call is the source of our caller. 2058 // If our caller is a file, the source of our caller is at frame.script.baseLineNumber 2059 // If our caller is an eval, the source of our caller is TODO Check Test Case 2060 getEvalLevelSourceFile: function(frame, context, innerScripts) 2061 { 2062 var eval_expr = this.getEvalExpression(frame, context); 2063 2064 if (FBTrace.DBG_EVAL) 2065 FBTrace.sysout("getEvalLevelSourceFile eval_expr:"+eval_expr); 2066 2067 if (eval_expr) 2068 { 2069 var source = this.getEvalBody(frame, "lib.getEvalLevelSourceFile.getEvalBody", 2070 1, eval_expr); 2071 var mapType = PCMAP_SOURCETEXT; 2072 } 2073 else 2074 { 2075 var source = frame.script.functionSource; // XXXms - possible crash on OSX FF2 2076 var mapType = PCMAP_PRETTYPRINT; 2077 } 2078 2079 var lines = Str.splitLines(source); 2080 2081 if (FBTrace.DBG_EVAL) 2082 FBTrace.sysout("getEvalLevelSourceFile "+lines.length+ "lines, mapType:"+ 2083 ((mapType==PCMAP_SOURCETEXT)?"SOURCE":"PRETTY")+" source:"+source); 2084 2085 var url = this.getDynamicURL(context, Url.normalizeURL(frame.script.fileName), 2086 lines, "eval"); 2087 2088 context.sourceCache.invalidate(url.href); 2089 context.sourceCache.storeSplitLines(url.href, lines); 2090 2091 var sourceFile = new Firebug.EvalLevelSourceFile(url, frame.script, eval_expr, lines, 2092 mapType, innerScripts); 2093 2094 this.watchSourceFile(context, sourceFile); 2095 2096 if (FBTrace.DBG_SOURCEFILES) 2097 FBTrace.sysout("debugger.getEvalLevelSourceFile sourcefile="+sourceFile.toString()+ 2098 " -> "+context.getName()+"\n"); 2099 2100 return sourceFile; 2101 }, 2102 2103 getDynamicURL: function(context, callerURL, lines, kind) 2104 { 2105 var url = this.getURLFromLastLine(context, lines); 2106 if (url) 2107 return url; 2108 2109 var url = this.getSequentialURL(context, callerURL, kind); 2110 if (url) 2111 return url; 2112 2113 var url = this.getURLFromMD5(callerURL, lines, kind); 2114 if (url) 2115 return url; 2116 2117 var url = this.getDataURLForScript(callerURL, lines); 2118 if (url) 2119 return url; 2120 2121 return url; 2122 }, 2123 2124 getURLFromLastLine: function(context, lines) 2125 { 2126 var url = null; 2127 // Ignores any trailing whitespace in |source| 2128 const reURIinComment = /\/\/@\ssourceURL=\s*(\S*?)\s*$/m; 2129 var m = reURIinComment.exec(lines[lines.length - 1]); 2130 2131 if (m) 2132 { 2133 // add context info to the sourceURL so eval'd sources are grouped 2134 // correctly in the source file list 2135 if (m[1] && m[1].indexOf('://') == -1) 2136 { 2137 var loc = context.window.location; 2138 if (m[1].charAt(0) != '/') m[1] = '/'+m[1]; // prepend leading slash if necessary 2139 m[1] = loc.protocol + '//' + loc.host + m[1]; // prepend protocol and host 2140 } 2141 2142 var href = new String(m[1]); 2143 href = Url.normalizeURL(href); 2144 2145 url = {href: href, kind: "source"}; 2146 if (FBTrace.DBG_SOURCEFILES) 2147 FBTrace.sysout("debugger.getURLFromLastLine "+url.href, url); 2148 } 2149 else 2150 { 2151 if (FBTrace.DBG_SOURCEFILES) 2152 FBTrace.sysout("debugger.getURLFromLastLine no match"+lines[lines.length - 1]); 2153 } 2154 2155 return url; 2156 }, 2157 2158 getSequentialURL: function(context, callerURL, kind) 2159 { 2160 var url = null; 2161 if (!context.dynamicURLhasBP) 2162 { 2163 // If no breakpoints live in dynamic code then we don't need to compare 2164 // the previous and reloaded source. In that case let's use a cheap Url. 2165 var href = new String(callerURL + (kind ? "/"+kind+"/" : "/nokind/")+"seq/" 2166 +(context.dynamicURLIndex++)); 2167 url = {href: href, kind: "seq"}; 2168 2169 if (FBTrace.DBG_SOURCEFILES || isNaN(context.dynamicURLIndex)) 2170 FBTrace.sysout("debugger.getSequentialURL context:"+context.getName()+ 2171 " url:"+url.href+" index: "+context.dynamicURLIndex, url); 2172 } 2173 return url; 2174 }, 2175 2176 getURLFromMD5: function(callerURL, lines, kind) 2177 { 2178 this.hash_service.init(this.nsICryptoHash.MD5); 2179 var source = lines.join('\n'); // we could double loop, would that be any faster? 2180 byteArray = []; 2181 for (var j = 0; j < source.length; j++) 2182 { 2183 byteArray.push( source.charCodeAt(j) ); 2184 } 2185 this.hash_service.update(byteArray, byteArray.length); 2186 var hash = this.hash_service.finish(true); 2187 2188 // encoding the hash should be ok, it should be information-preserving? 2189 // Or at least reversable? 2190 var href= new String(callerURL + (kind ? "/"+kind+"/" : "/nokind/")+"MD5/" + 2191 encodeURIComponent(hash)); 2192 url = {href: href, kind: "MD5"}; 2193 2194 if (FBTrace.DBG_SOURCEFILES) 2195 FBTrace.sysout("debugger.getURLFromMD5 "+url.href, url); 2196 2197 return url; 2198 }, 2199 2200 getDataURLForScript: function(callerURL, lines) 2201 { 2202 var url = null; 2203 var href = null; 2204 if (!source) 2205 { 2206 href = "eval."+script.tag; 2207 } 2208 else 2209 { 2210 // data:text/javascript;fileName=x%2Cy.js;baseLineNumber=10,<the-url-encoded-data> 2211 href = new String("data:text/javascript;"); 2212 href += "fileName="+encodeURIComponent(callerURL); 2213 var source = lines.join('\n'); 2214 //url += ";"+ "baseLineNumber="+encodeURIComponent(script.baseLineNumber) + 2215 href +="," + encodeURIComponent(source); 2216 } 2217 2218 url = {href:href, kind:"data"}; 2219 if (FBTrace.DBG_SOURCEFILES) 2220 FBTrace.sysout("debugger.getDataURLForScript "+url.href, url); 2221 2222 return url; 2223 }, 2224 2225 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 2226 2227 getEvalExpression: function(frame, context) 2228 { 2229 var expr = this.getEvalExpressionFromEval(frame, context); // eval in eval 2230 2231 return (expr) ? expr : this.getEvalExpressionFromFile(Url.normalizeURL(frame.script.fileName), 2232 frame.script.baseLineNumber, context); 2233 }, 2234 2235 getEvalExpressionFromFile: function(url, lineNo, context) 2236 { 2237 if (context && context.sourceCache) 2238 { 2239 var in_url = Url.reJavascript.exec(url); 2240 if (in_url) 2241 { 2242 var m = reEval.exec(in_url[1]); 2243 if (m) 2244 return m[1]; 2245 else 2246 return null; 2247 } 2248 2249 var htm = reHTM.exec(url); 2250 if (htm) 2251 lineNo = lineNo + 1; // embedded scripts seem to be off by one? XXXjjb heuristic 2252 2253 // Walk backwards from the first line in the function until we find the line which 2254 // matches the pattern above, which is the eval call 2255 var line = ""; 2256 for (var i = 0; i < 3; ++i) 2257 { 2258 line = context.sourceCache.getLine(url, lineNo-i) + line; 2259 if (line && line != null) 2260 { 2261 var m = reEval.exec(line); 2262 if (m) 2263 return m[1]; 2264 } 2265 } 2266 } 2267 return null; 2268 }, 2269 2270 getEvalExpressionFromEval: function(frame, context) 2271 { 2272 var callingFrame = frame.callingFrame; 2273 var sourceFile = Firebug.SourceFile.getSourceFileByScript(context, callingFrame.script); 2274 if (sourceFile) 2275 { 2276 if (FBTrace.DBG_EVAL) 2277 { 2278 FBTrace.sysout("debugger.getEvalExpressionFromEval sourceFile.href=" + 2279 sourceFile.href); 2280 2281 FBTrace.sysout("debugger.getEvalExpressionFromEval callingFrame.pc=" + 2282 callingFrame.pc + " callingFrame.script.baseLineNumber=" + 2283 callingFrame.script.baseLineNumber); 2284 } 2285 2286 var lineNo = callingFrame.script.pcToLine(callingFrame.pc, PCMAP_SOURCETEXT); 2287 lineNo = lineNo - callingFrame.script.baseLineNumber + 1; 2288 var url = sourceFile.href; 2289 2290 if (FBTrace.DBG_EVAL && !context.sourceCache) 2291 FBTrace.sysout("debugger.getEvalExpressionFromEval context.sourceCache null??\n"); 2292 2293 // Walk backwards from the first line in the function until we find the line which 2294 // matches the pattern above, which is the eval call 2295 var line = ""; 2296 for (var i = 0; i < 3; ++i) 2297 { 2298 line = context.sourceCache.getLine(url, lineNo-i) + line; 2299 if (FBTrace.DBG_EVAL) 2300 FBTrace.sysout("debugger.getEvalExpressionFromEval lineNo-i="+lineNo+"-"+i+"="+ 2301 (lineNo-i)+" line:"+line+"\n"); 2302 2303 if (line && line != null) 2304 { 2305 var m = reEval.exec(line); 2306 if (m) 2307 return m[1]; // TODO Lame: need to count parens, with escapes and quotes 2308 } 2309 } 2310 } 2311 return null; 2312 }, 2313 2314 getEvalBody: function(frame, asName, asLine, evalExpr) 2315 { 2316 if (evalExpr) 2317 { 2318 var result_src = {}; 2319 var evalThis = "new String("+evalExpr+");"; 2320 var evaled = frame.eval(evalThis, asName, asLine, result_src); 2321 2322 if (evaled) 2323 { 2324 var src = Wrapper.unwrapIValue(result_src.value); 2325 return src+""; 2326 } 2327 else 2328 { 2329 // Either we failed, or this was a window.eval() call 2330 var source; 2331 2332 if (frame.callingFrame && !this.avoidRecursing) 2333 { 2334 // Try the caller, in case we are in window.eval() where the scope is global 2335 this.avoidRecursing = true; 2336 source = this.getEvalBody(frame.callingFrame, asName, asLine, evalExpr); 2337 delete this.avoidRecursing; 2338 2339 if (FBTrace.DBG_EVAL) 2340 FBTrace.sysout("callingFrame used to get source ", source); 2341 2342 return source; 2343 } 2344 2345 if(evalExpr == "function(p,a,c,k,e,r") 2346 source = "/packer/ JS compressor detected"; 2347 else 2348 source = frame.script.functionSource; 2349 2350 return source+" /* !eval("+evalThis+")) */"; 2351 } 2352 } 2353 else 2354 { 2355 return frame.script.functionSource; // XXXms - possible crash on OSX FF2 2356 } 2357 }, 2358 2359 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 2360 // extends Module 2361 2362 initialize: function() 2363 { 2364 Firebug.clientID = this.registerClient(Firebug.JSDebugClient); 2365 this.nsICryptoHash = Components.interfaces["nsICryptoHash"]; 2366 2367 this.debuggerName = window.location.href +"-@-"+Obj.getUniqueId(); 2368 this.toString = function() { return this.debuggerName; } 2369 2370 if (FBTrace.DBG_INITIALIZE) 2371 FBTrace.sysout("debugger.initialize "+ this.debuggerName+" Firebug.clientID "+ 2372 Firebug.clientID); 2373 2374 this.hash_service = Xpcom.CCSV("@mozilla.org/security/hash;1", "nsICryptoHash"); 2375 2376 2377 this.wrappedJSObject = this; // how we communicate with fbs 2378 2379 this.onFunctionCall = Obj.bind(this.onFunctionCall, this); 2380 2381 Firebug.ActivableModule.initialize.apply(this, arguments); 2382 }, 2383 2384 shutdown: function() 2385 { 2386 //Firebug.connection.unregisterTool(this.asTool); 2387 2388 Firebug.ActivableModule.destroy.apply(this, arguments); 2389 }, 2390 2391 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 2392 // BTI 2393 2394 toolName: "script", 2395 2396 addListener: function(listener) 2397 { 2398 Firebug.connection.addListener(listener); 2399 }, 2400 2401 removeListener: function(listener) 2402 { 2403 Firebug.connection.removeListener(listener); 2404 }, 2405 2406 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 2407 2408 /** 2409 * per-XUL window registration; this method just allows us to keep fbs in this file. 2410 * @param clientAPI an object that implements functions called by fbs for clients. 2411 */ 2412 registerClient: function(clientAPI) 2413 { 2414 return FBS.registerClient(clientAPI); 2415 }, 2416 2417 unregisterClient: function(clientAPI) 2418 { 2419 FBS.unregisterClient(clientAPI); 2420 }, 2421 2422 enable: function() 2423 { 2424 if (FBTrace.DBG_ACTIVATION) 2425 FBTrace.sysout("debugger.Firebug.Debugger.enable; " + this.enabled); 2426 }, 2427 2428 disable: function() 2429 { 2430 if (FBTrace.DBG_ACTIVATION) 2431 FBTrace.sysout("debugger.Firebug.Debugger.disable; " + this.enabled); 2432 }, 2433 2434 selfObserver: {}, // empty listener, registered as observer while Script panel is enabled. 2435 2436 initializeUI: function() 2437 { 2438 Firebug.ActivableModule.initializeUI.apply(this, arguments); 2439 this.obeyPrefs(); 2440 this.filterButton = Firebug.chrome.$("fbScriptFilterMenu"); // TODO move to script.js 2441 this.filterMenuUpdate(); 2442 if (FBS.isJSDActive()) // notify frontend of current state 2443 Firebug.JSDebugClient.onJSDActivate(true, 'Firebug.Debugger.initializeUI'); 2444 }, 2445 2446 obeyPrefs: function() 2447 { 2448 var name = "script.enableSites"; 2449 var value = Firebug.Options.get("script.enableSites"); 2450 this.updateOption(name, value); 2451 }, 2452 2453 initContext: function(context, persistedState) 2454 { 2455 if (persistedState) 2456 context.dynamicURLhasBP = persistedState.dynamicURLhasBP; 2457 2458 context.dynamicURLIndex = 1; // any dynamic urls need to be unique to the context. 2459 2460 context.jsDebuggerCalledUs = false; 2461 2462 Firebug.ActivableModule.initContext.apply(this, arguments); 2463 }, 2464 2465 showContext: function(browser, context) 2466 { 2467 // then context was not active during load 2468 if (context && context.loaded && !context.onLoadWindowContent) 2469 this.updateScriptFiles(context); 2470 }, 2471 2472 // scan windows for 'script' tags (only if debugger is not enabled) 2473 updateScriptFiles: function(context) 2474 { 2475 function addFile(url, scriptTagNumber, dependentURL) 2476 { 2477 var sourceFile = new Firebug.ScriptTagSourceFile(context, url, scriptTagNumber); 2478 sourceFile.dependentURL = dependentURL; 2479 context.addSourceFile(sourceFile); 2480 return true; 2481 } 2482 2483 Win.iterateWindows(context.window, function updateEachWin(win) 2484 { 2485 if (FBTrace.DBG_SOURCEFILES) 2486 FBTrace.sysout("updateScriptFiles Win.iterateWindows: "+win.location.href+ 2487 " documentElement: "+win.document.documentElement); 2488 2489 if (!win.document.documentElement) 2490 return; 2491 2492 var url = Url.normalizeURL(win.location.href); 2493 2494 if (url) 2495 { 2496 if (!context.sourceFileMap.hasOwnProperty(url)) 2497 { 2498 var URLOnly = new Firebug.NoScriptSourceFile(context, url); 2499 context.addSourceFile(URLOnly); 2500 2501 if (FBTrace.DBG_SOURCEFILES) 2502 FBTrace.sysout("updateScriptFiles created NoScriptSourceFile for URL:" + 2503 url, URLOnly); 2504 } 2505 } 2506 2507 var baseUrl = win.location.href; 2508 var bases = win.document.documentElement.getElementsByTagName("base"); 2509 if (bases && bases[0]) 2510 { 2511 baseUrl = bases[0].href; 2512 } 2513 2514 var scripts = win.document.documentElement.getElementsByTagName("script"); 2515 for (var i = 0; i < scripts.length; ++i) 2516 { 2517 var scriptSrc = scripts[i].getAttribute('src'); // for XUL use attribute 2518 var url = scriptSrc ? Url.absoluteURL(scriptSrc, baseUrl) : win.location.href; 2519 url = Url.normalizeURL(url ? url : win.location.href); 2520 var added = addFile(url, i, (scriptSrc?win.location.href:null)); 2521 2522 if (FBTrace.DBG_SOURCEFILES) 2523 FBTrace.sysout("updateScriptFiles "+(scriptSrc?"inclusion":"inline")+ 2524 " script #"+i+"/"+scripts.length+(added?" adding ":" readded ")+url+ 2525 " to context="+context.getName()+"\n"); 2526 } 2527 }); 2528 2529 if (FBTrace.DBG_SOURCEFILES) 2530 { 2531 FBTrace.sysout("updateScriptFiles sourcefiles:", 2532 Firebug.SourceFile.sourceFilesAsArray(context.sourceFileMap)); 2533 } 2534 }, 2535 2536 loadedContext: function(context) 2537 { 2538 if (FBTrace.DBG_ACTIVATION) 2539 FBTrace.sysout("loadedContext needs to trigger watchpanel updates"); 2540 2541 /* 2542 var watchPanel = this.ableWatchSidePanel(context); 2543 var needNow = watchPanel && watchPanel.watches; 2544 var watchPanelState = Firebug.getPanelState({name: "watches", context: context}); 2545 var needPersistent = watchPanelState && watchPanelState.watches; 2546 if (needNow || needPersistent) 2547 { 2548 Firebug.CommandLine.isReadyElsePreparing(context); 2549 if (watchPanel) 2550 { 2551 context.setTimeout(function refreshWatchesAfterCommandLineReady() 2552 { 2553 watchPanel.refresh(); 2554 }); 2555 } 2556 } 2557 */ 2558 2559 if (FBTrace.DBG_SOURCEFILES) 2560 FBTrace.sysout("debugger("+this.debuggerName+").loadedContext enabled on load: "+ 2561 context.onLoadWindowContent+" context.sourceFileMap", context.sourceFileMap); 2562 }, 2563 2564 // clean up the source file map in case the frame is being reloaded. 2565 unwatchWindow: function(context, win) 2566 { 2567 var scriptTags = win.document.getElementsByTagName("script"); 2568 for (var i = 0; i < scriptTags.length; i++) 2569 { 2570 var src = scriptTags[i].getAttribute("src"); 2571 src = src ? src : Win.safeGetWindowLocation(win); 2572 2573 // If the src is not in the source map, try to use absolute url. 2574 if (!context.sourceFileMap[src]) 2575 src = Url.absoluteURL(src, win.location.href); 2576 2577 delete context.sourceFileMap[src]; 2578 2579 if (FBTrace.DBG_SOURCEFILES) 2580 FBTrace.sysout("debugger.unWatchWindow; delete sourceFileMap entry for " + src); 2581 } 2582 if (scriptTags.length > 0) 2583 context.invalidatePanels('script'); 2584 }, 2585 2586 destroyContext: function(context, persistedState) 2587 { 2588 Firebug.ActivableModule.destroyContext.apply(this, arguments); 2589 2590 if (context.stopped) 2591 { 2592 // the abort will call resume, but the nestedEventLoop would continue the load... 2593 this.abort(context); 2594 } 2595 2596 if (persistedState) 2597 { 2598 if (context.dynamicURLhasBP) 2599 persistedState.dynamicURLhasBP = context.dynamicURLhasBP; 2600 else 2601 delete persistedState.dynamicURLhasBP; 2602 } 2603 }, 2604 2605 updateOption: function(name, value) 2606 { 2607 if (name == "breakOnErrors") 2608 Firebug.chrome.getElementById("cmd_firebug_breakOnErrors").setAttribute("checked", value); 2609 }, 2610 2611 getObjectByURL: function(context, url) 2612 { 2613 var sourceFile = Firebug.SourceFile.getSourceFileByHref(url, context); 2614 if (sourceFile) 2615 return new SourceLink.SourceLink(sourceFile.href, 0, "js"); 2616 }, 2617 2618 shutdown: function() 2619 { 2620 this.unregisterClient(Firebug.JSDebugClient); 2621 FBS.unregisterDebugger(this); 2622 }, 2623 2624 /** 2625 * 1.3.1 safe for multiple calls 2626 */ 2627 registerDebugger: function() 2628 { 2629 // Do not activate JSD on shutdown. 2630 // https://bugzilla.mozilla.org/show_bug.cgi?id=756267#c12 2631 if (Firebug.isShutdown) 2632 return; 2633 2634 if (FBTrace.DBG_ACTIVATION) 2635 FBTrace.sysout("registerDebugger"); 2636 2637 // this will eventually set 'jsd' on the statusIcon 2638 var check = FBS.registerDebugger(this); 2639 2640 if (FBTrace.DBG_ACTIVATION) 2641 FBTrace.sysout("debugger.registerDebugger "+check+" debuggers"); 2642 }, 2643 2644 unregisterDebugger: function() // 1.3.1 safe for multiple calls 2645 { 2646 if (FBTrace.DBG_ACTIVATION) 2647 FBTrace.sysout("debugger.unregisterDebugger"); 2648 2649 // stay registered if we are profiling across a reload. 2650 if (Firebug.Profiler && Firebug.Profiler.isProfiling()) 2651 return; 2652 2653 var check = FBS.unregisterDebugger(this); 2654 2655 if (FBTrace.DBG_ACTIVATION) 2656 FBTrace.sysout("debugger.unregisterDebugger: "+check+" debuggers"); 2657 }, 2658 2659 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 2660 // extends ActivableModule 2661 2662 onObserverChange: function(observer) 2663 { 2664 if (FBTrace.DBG_ACTIVATION) 2665 { 2666 var names = []; 2667 this.observers.forEach(function(ob){names.push(ob.name || ob.dispatchName || ob.toolName);}); 2668 2669 FBTrace.sysout("debugger.onObserverChange "+this.hasObservers()+" "+ 2670 this.observers.length+": "+names.join(','), this.observers); 2671 } 2672 2673 if (this.hasObservers()) 2674 { 2675 this.activateDebugger(); 2676 if (Firebug.currentContext) 2677 { 2678 var name = observer.name || observer.dispatchName || observer.toolName; 2679 Firebug.Console.log("enabling javascript debugger "+(name?"to support "+name:"")); 2680 } 2681 } 2682 else 2683 { 2684 this.deactivateDebugger(); 2685 } 2686 }, 2687 2688 activateDebugger: function() 2689 { 2690 this.registerDebugger(); 2691 2692 // If jsd is already active, we'll notify true; else we'll get another event 2693 var isActive = FBS.isJSDActive(); 2694 if (isActive) 2695 Firebug.JSDebugClient.onJSDActivate(true, 'activated already'); 2696 2697 if (FBTrace.DBG_PANELS || FBTrace.DBG_ACTIVATION) 2698 FBTrace.sysout("debugger.activateDebugger requested; activated already? "+isActive); 2699 }, 2700 2701 deactivateDebugger: function() 2702 { 2703 this.unregisterDebugger(); 2704 2705 var isActive = FBS.isJSDActive(); 2706 if (!isActive) 2707 Firebug.JSDebugClient.onJSDDeactivate(false, 'deactivate'); 2708 2709 if (FBTrace.DBG_PANELS || FBTrace.DBG_ACTIVATION) 2710 FBTrace.sysout("debugger.deactivate"); 2711 }, 2712 2713 onSuspendingFirebug: function() 2714 { 2715 var anyStopped = Firebug.TabWatcher.iterateContexts(function isAnyStopped(context) 2716 { 2717 return context.stopped; 2718 }); 2719 2720 return anyStopped; 2721 }, 2722 2723 onSuspendFirebug: function() 2724 { 2725 if (!Firebug.Debugger.isAlwaysEnabled()) 2726 return; 2727 2728 // can be called multiple times. 2729 var paused = FBS.pause(this.debuggerName); 2730 2731 if (FBTrace.DBG_ACTIVATION) 2732 FBTrace.sysout("debugger.onSuspendFirebug paused: "+paused+" isAlwaysEnabled " + 2733 Firebug.Debugger.isAlwaysEnabled()); 2734 2735 // JSD activation is not per browser-tab, so FBS.pause can return 'not-paused' when 2736 // Firebug is activated on another tab. 2737 // The start-button should somehow reflect that JSD can be still active (even if 2738 // Firebug is suspended for the current tab). 2739 if (!paused) // then we failed to suspend, undo 2740 return true; 2741 2742 return false; 2743 }, 2744 2745 onResumeFirebug: function() 2746 { 2747 if (!Firebug.Debugger.isAlwaysEnabled()) 2748 return; 2749 2750 var unpaused = FBS.unPause(); 2751 2752 if (FBTrace.DBG_ACTIVATION) 2753 FBTrace.sysout("debugger.onResumeFirebug unpaused: "+unpaused+" isAlwaysEnabled " + 2754 Firebug.Debugger.isAlwaysEnabled()); 2755 }, 2756 2757 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 2758 // Menu in toolbar. 2759 2760 onScriptFilterMenuTooltipShowing: function(tooltip, context) 2761 { 2762 if (FBTrace.DBG_OPTIONS) 2763 FBTrace.sysout("onScriptFilterMenuTooltipShowing not implemented"); 2764 }, 2765 2766 onScriptFilterMenuCommand: function(event, context) 2767 { 2768 var menu = event.target; 2769 Firebug.Options.set("scriptsFilter", menu.value); 2770 Firebug.Debugger.filterMenuUpdate(); 2771 }, 2772 2773 menuFullLabel: 2774 { 2775 "static": Locale.$STR("ScriptsFilterStatic"), 2776 evals: Locale.$STR("ScriptsFilterEval"), 2777 events: Locale.$STR("ScriptsFilterEvent"), 2778 all: Locale.$STR("ScriptsFilterAll"), 2779 }, 2780 2781 menuShortLabel: 2782 { 2783 "static": Locale.$STR("ScriptsFilterStaticShort"), 2784 evals: Locale.$STR("ScriptsFilterEvalShort"), 2785 events: Locale.$STR("ScriptsFilterEventShort"), 2786 all: Locale.$STR("ScriptsFilterAllShort"), 2787 }, 2788 2789 onScriptFilterMenuPopupShowing: function(menu, context) 2790 { 2791 if (this.menuTooltip) 2792 this.menuTooltip.fbEnabled = false; 2793 2794 var items = menu.getElementsByTagName("menuitem"); 2795 var value = this.filterButton.value; 2796 2797 for (var i=0; i<items.length; i++) 2798 { 2799 var option = items[i].value; 2800 if (!option) 2801 continue; 2802 2803 if (option == value) 2804 items[i].setAttribute("checked", "true"); 2805 2806 items[i].label = Firebug.Debugger.menuFullLabel[option]; 2807 } 2808 2809 return true; 2810 }, 2811 2812 onScriptFilterMenuPopupHiding: function(tooltip, context) 2813 { 2814 if (this.menuTooltip) 2815 this.menuTooltip.fbEnabled = true; 2816 2817 return true; 2818 }, 2819 2820 filterMenuUpdate: function() 2821 { 2822 var value = Firebug.Options.get("scriptsFilter"); 2823 this.filterButton.value = value; 2824 this.filterButton.label = this.menuShortLabel[value]; 2825 this.filterButton.removeAttribute("disabled"); 2826 this.filterButton.setAttribute("value", value); 2827 if (FBTrace.DBG_OPTIONS) 2828 FBTrace.sysout("debugger.filterMenuUpdate value: "+value+" label:"+ 2829 this.filterButton.label+'\n'); 2830 }, 2831 2832 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 2833 // Options 2834 2835 resetAllOptions: function() 2836 { 2837 Firebug.TabWatcher.iterateContexts( function clearBPs(context) 2838 { 2839 Firebug.Debugger.clearAllBreakpoints(context); 2840 }); 2841 2842 var breakpointStore = FBS.getBreakpointStore(); 2843 breakpointStore.clear(true); 2844 } 2845 }); 2846 2847 // ********************************************************************************************* // 2848 2849 Firebug.Debugger.Breakpoint = function(name, href, lineNumber, checked, sourceLine, isFuture) 2850 { 2851 this.name = name; 2852 this.href = href; 2853 this.lineNumber = lineNumber; 2854 this.checked = checked; 2855 this.sourceLine = sourceLine; 2856 this.isFuture = isFuture; 2857 } 2858 2859 // ********************************************************************************************* // 2860 2861 Firebug.DebuggerListener = 2862 { 2863 onStop: function(context, frame, type, rv) 2864 { 2865 }, 2866 2867 onResume: function(context) 2868 { 2869 }, 2870 2871 onThrow: function(context, frame, rv) 2872 { 2873 return false; /* continue throw */ 2874 }, 2875 2876 onError: function(context, frame, error) 2877 { 2878 }, 2879 2880 onEventScriptCreated: function(context, frame, url, sourceFile) 2881 { 2882 }, 2883 2884 onTopLevelScriptCreated: function(context, frame, url, sourceFile) 2885 { 2886 }, 2887 2888 onEvalScriptCreated: function(context, frame, url, sourceFile) 2889 { 2890 }, 2891 2892 onFunctionConstructor: function(context, frame, ctor_script, url, sourceFile) 2893 { 2894 }, 2895 }; 2896 2897 // ********************************************************************************************* // 2898 // Signals from fbs, passed along to our listeners 2899 2900 Firebug.JSDebugClient = 2901 { 2902 onJSDActivate: function(active, fromMsg) 2903 { 2904 if (FBTrace.DBG_ACTIVATION) 2905 FBTrace.sysout("Firebug.JSDebugClient onJSDActivate "+active+" "+fromMsg); 2906 Firebug.connection.dispatch("onActivateTool", ["script", active]); 2907 }, 2908 2909 onJSDDeactivate: function(active, fromMsg) 2910 { 2911 if (FBTrace.DBG_ACTIVATION) 2912 FBTrace.sysout("Firebug.JSDebugClient onJSDDeactivate "+active+" "+fromMsg); 2913 Firebug.connection.dispatch("onActivateTool", ["script", active]); 2914 }, 2915 2916 onPauseJSDRequested: function(rejection, debuggerName) 2917 { 2918 // A debugger client (an instance of Firebug.JSDebugClient) asked to pause JSD. 2919 // Note that there is one instance of Firebug.JSDebugClient per browser (XUL) window. 2920 // If the debugger is 'on' in this browser window JSD and the request comes from 2921 // another window (debugger) JSD should *not* be paused (see issue 4609). 2922 // So, reject only if the request comes from another browser window and Firebug 2923 // is resumed in this window. 2924 if (debuggerName != Firebug.Debugger.debuggerName && !Firebug.getSuspended()) 2925 { 2926 rejection.push(true); 2927 2928 if (FBTrace.DBG_ERRORS) 2929 FBTrace.sysout("Firebug.JSDebugClient onPauseJSDRequested rejected to suspend; " + 2930 "Current debugger: " + Firebug.Debugger.debuggerName + ", requestor debugger: " + 2931 debuggerName); 2932 } 2933 2934 //Firebug.connection.dispatch("onPauseJSDRequested", arguments); 2935 2936 if (FBTrace.DBG_ACTIVATION) 2937 FBTrace.sysout("Firebug.JSDebugClient onPauseJSDRequested rejection: " + 2938 rejection.length + ", jsDebuggerOn: " + Firebug.jsDebuggerOn); 2939 }, 2940 } 2941 2942 // Recursively look for obj in container using array of visited objects 2943 function findObjectPropertyPath(containerName, container, obj, visited) 2944 { 2945 if (!container || !obj || !visited) 2946 return false; 2947 2948 var referents = []; 2949 visited.push(container); 2950 for (var p in container) 2951 { 2952 if (container.hasOwnProperty(p)) 2953 { 2954 var candidate = null; 2955 try 2956 { 2957 candidate = container[p]; 2958 } 2959 catch(exc) 2960 { 2961 // eg sessionStorage 2962 } 2963 2964 if (candidate === obj) // then we found a property pointing to our obj 2965 { 2966 referents.push(new Referent(containerName, container, p, obj)); 2967 } 2968 else // recurse 2969 { 2970 var candidateType = typeof (candidate); 2971 if (candidateType === 'object' || candidateType === 'function') 2972 { 2973 if (visited.indexOf(candidate) === -1) 2974 { 2975 var refsInChildren = findObjectPropertyPath(p, candidate, obj, visited); 2976 if (refsInChildren.length) 2977 { 2978 // As we unwind the recursion we tack on layers of the path. 2979 for (var i = 0; i < refsInChildren.length; i++) 2980 { 2981 var refInChildren = refsInChildren[i]; 2982 refInChildren.prependPath(containerName, container); 2983 referents.push(refInChildren); 2984 2985 FBTrace.sysout(" Did prependPath with p "+p+" gave "+ 2986 referents[referents.length - 1].getObjectPathExpression(), 2987 referents[referents.length - 1]); 2988 } 2989 } 2990 } 2991 //else we already looked at that object. 2992 } // else the object has no properties 2993 } 2994 } 2995 } 2996 2997 FBTrace.sysout(" Returning "+referents.length+ " referents", referents); 2998 2999 return referents; 3000 } 3001 3002 // ********************************************************************************************* // 3003 3004 function getFrameWindow(frame) 3005 { 3006 var result = {}; 3007 if (frame.eval("window", "", 1, result)) 3008 { 3009 var win = Wrapper.unwrapIValue(result.value, Firebug.viewChrome); 3010 return Win.getRootWindow(win); 3011 } 3012 } 3013 3014 function ArrayEnumerator(array) 3015 { 3016 this.index = 0; 3017 this.array = array; 3018 this.hasMoreElements = function() 3019 { 3020 return (this.index < array.length); 3021 } 3022 this.getNext = function() 3023 { 3024 return this.array[++this.index]; 3025 } 3026 } 3027 3028 // ********************************************************************************************* // 3029 // Registration 3030 3031 Firebug.registerActivableModule(Firebug.Debugger); 3032 3033 return Firebug.Debugger; 3034 3035 // ********************************************************************************************* // 3036 }); 3037