1 /* See license.txt for terms of usage */ 2 3 /** 4 * Firebug module can depend only on modules that don't use the 'Firebug' namespace. 5 * So, be careful before you create a new dependency. 6 */ 7 define([ 8 "firebug/lib/lib", 9 "firebug/lib/object", 10 "firebug/chrome/firefox", 11 "firebug/chrome/chrome", 12 "firebug/lib/domplate", 13 "firebug/lib/options", 14 "firebug/lib/locale", 15 "firebug/lib/events", 16 "firebug/lib/wrapper", 17 "firebug/lib/url", 18 "firebug/lib/css", 19 "firebug/chrome/window", 20 "firebug/lib/string", 21 "firebug/lib/array", 22 "firebug/lib/dom", 23 "firebug/lib/http", 24 "firebug/trace/traceListener", 25 "firebug/console/commandLineExposed", 26 ], 27 function(FBL, Obj, Firefox, ChromeFactory, Domplate, Options, Locale, Events, 28 Wrapper, Url, Css, Win, Str, Arr, Dom, Http, TraceListener, CommandLineExposed) { 29 30 // ********************************************************************************************* // 31 // Constants 32 33 const Cc = Components.classes; 34 const Ci = Components.interfaces; 35 36 const nsISupports = Ci.nsISupports; 37 38 const observerService = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService); 39 const categoryManager = Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager); 40 const promptService = Cc["@mozilla.org/embedcomp/prompt-service;1"].getService(Ci.nsIPromptService); 41 42 const versionURL = "chrome://firebug/content/branch.properties"; 43 44 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 45 46 const scriptBlockSize = 20; 47 48 const PLACEMENT_NONE = 0; 49 const PLACEMENT_INBROWSER = 1; 50 const PLACEMENT_DETACHED = 2; 51 const PLACEMENT_MINIMIZED = 3; 52 53 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 54 55 var modules = []; 56 var activeContexts = []; 57 var activableModules = []; 58 var panelTypes = []; 59 var earlyRegPanelTypes = []; // See Firebug.registerPanelType for more info 60 var reps = []; 61 var defaultRep = null; 62 var defaultFuncRep = null; 63 var menuItemControllers = []; 64 var panelTypeMap = {}; 65 66 // ********************************************************************************************* // 67 68 //xxxHonza: we should use the existing Firebug object. 69 if (window.Firebug) 70 { 71 // Stow the pre-load properties, add them back at the end 72 var PreFirebug = {}; 73 var preFirebugKeys = Object.keys(Firebug); 74 preFirebugKeys.forEach(function copyProps(key) 75 { 76 PreFirebug[key] = Firebug[key]; 77 }); 78 } 79 80 /** 81 * @class Represents the main Firebug application object. An instance of this object is 82 * created for each browser window (browser.xul). 83 */ 84 window.Firebug = 85 { 86 version: "1.12", 87 88 dispatchName: "Firebug", 89 modules: modules, 90 panelTypes: panelTypes, 91 earlyRegPanelTypes: earlyRegPanelTypes, 92 uiListeners: [], 93 reps: reps, 94 95 stringCropLength: 50, 96 97 isInitialized: false, 98 isLoaded: false, 99 100 migrations: {}, 101 102 // Custom stylesheets registered by extensions. 103 stylesheets: [], 104 105 // xxxHonza: hack, all "Firebug.Options" occurences should be replaced by "Options" 106 Options: Options, 107 108 viewChrome: null, 109 110 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 111 // Initialization 112 113 initialize: function(chrome) 114 { 115 // This says how much time was necessary to load Firebug overlay (+ all script tags). 116 FBTrace.timeEnd("SCRIPTTAG_TIME"); 117 118 // Measure the entire Firebug initialiation time. 119 FBTrace.time("INITIALIZATION_TIME"); 120 121 Firebug.chrome = chrome; 122 Firebug.originalChrome = Firebug.chrome; 123 124 if (FBTrace.sysout && (!FBL || !FBL.initialize)) 125 { 126 FBTrace.sysout("Firebug is broken, FBL incomplete, if the last function is QI, " + 127 "check lib.js:", FBL); 128 } 129 else if (FBTrace.DBG_INITIALIZE) 130 { 131 FBTrace.sysout("firebug.initialize FBL: " + FBL); 132 } 133 134 if (window.FBL.legacyApiPatch) 135 window.FBL.legacyApiPatch(FBL, this, Firefox); 136 137 // Till now all registered panels have been inserted into earlyRegPanelTypes. 138 var tempPanelTypes = earlyRegPanelTypes; 139 earlyRegPanelTypes = null; 140 Firebug.completeInitialize(tempPanelTypes); 141 }, 142 143 completeInitialize: function(tempPanelTypes) 144 { 145 if (FBL) 146 FBL.initialize(); // non require.js modules 147 148 // Append early registered panels at the end. 149 panelTypes.push.apply(panelTypes, tempPanelTypes); 150 151 // Firebug is getting option-updates from the connection so, 152 // do not register it again here (see issue 6035) 153 //Firebug.Options.addListener(this); 154 155 this.isInitialized = true; 156 157 Events.dispatch(modules, "initialize", []); 158 159 // This is the final of Firebug initialization. 160 FBTrace.timeEnd("INITIALIZATION_TIME"); 161 }, 162 163 sendLoadEvent: function() 164 { 165 this.isLoaded = true; 166 167 var event = document.createEvent("Events"); 168 event.initEvent("FirebugLoaded", true, false); 169 170 // Send to the current window/scope (firebugFrame.xul) 171 window.document.dispatchEvent(event); 172 173 // Send to the top window/scope (browser.xul) 174 if (top != window) 175 top.document.dispatchEvent(event); 176 }, 177 178 getVersion: function() 179 { 180 if (!this.fullVersion) 181 this.fullVersion = this.loadVersion(versionURL); 182 183 return this.fullVersion; 184 }, 185 186 loadVersion: function(versionURL) 187 { 188 var content = Http.getResource(versionURL); 189 if (!content) 190 return "no content at "+versionURL; 191 192 var m = /RELEASE=(.*)/.exec(content); 193 if (m) 194 var release = m[1]; 195 else 196 return "no RELEASE in "+versionURL; 197 198 m = /VERSION=(.*)/.exec(content); 199 if (m) 200 var version = m[1]; 201 else 202 return "no VERSION in "+versionURL; 203 204 return version+""+release; 205 }, 206 207 /** 208 * Substitute strings in the UI, with fall back to en-US 209 */ 210 internationalizeUI: function(doc) // TODO chrome.js 211 { 212 if (!doc) 213 return; 214 215 if (FBTrace.DBG_INITIALIZE) 216 FBTrace.sysout("Firebug.internationalizeUI"); 217 218 var elements = doc.getElementsByClassName("fbInternational"); 219 elements = Arr.cloneArray(elements); 220 var attributes = ["label", "tooltiptext", "aria-label"]; 221 for (var i=0; i<elements.length; i++) 222 { 223 var element = elements[i]; 224 Css.removeClass(elements[i], "fbInternational"); 225 for (var j=0; j<attributes.length; j++) 226 { 227 if (element.hasAttribute(attributes[j])) 228 Locale.internationalize(element, attributes[j]); 229 } 230 } 231 232 // Allow other modules to internationalize UI labels (called also for 233 // detached Firebug window). 234 Events.dispatch(modules, "internationalizeUI", [doc]); 235 }, 236 237 /** 238 * Called when the UI is ready to be initialized, once the panel browsers are loaded, 239 * but before any contexts are created. 240 */ 241 initializeUI: function(detachArgs) 242 { 243 if (FBTrace.DBG_INITIALIZE) 244 FBTrace.sysout("firebug.initializeUI detachArgs:", detachArgs); 245 246 Events.dispatch(menuItemControllers, "initialize", []); // TODO chrome.js 247 248 // In the case that the user opens firebug in a new window but then closes Firefox 249 // window, we don't get the quitApplicationGranted event (platform is still running) 250 // and we call shutdown (Firebug isDetached). 251 window.addEventListener('unload', shutdownFirebug, false); 252 253 // Initial activation of registered panel types. All panel -> module dependencies 254 // should be defined now (in onActivationChange). Must be called after 255 // Firebug.TabWatcher is ready. 256 if (Firebug.PanelActivation) 257 Firebug.PanelActivation.activatePanelTypes(panelTypes); 258 259 // Tell the modules the UI is up. 260 Events.dispatch(modules, "initializeUI", [detachArgs]); 261 }, 262 263 /** 264 * called in browser when Firefox closes and in externalMode when fbs gets 265 * quitApplicationGranted. 266 */ 267 shutdown: function() 268 { 269 if (this.isShutdown) 270 return; 271 272 this.isShutdown = true; 273 274 this.shutdownUI(); 275 276 Events.dispatch(modules, "shutdown"); 277 278 this.Options.shutdown(); 279 this.Options.removeListener(this); 280 281 this.connection.disconnect(); 282 283 this.PanelActivation.deactivatePanelTypes(panelTypes); 284 285 // Shutdown all registered extensions. 286 this.unregisterExtensions(); 287 288 if (FBTrace.DBG_OBSERVERS) 289 { 290 // import fbObserverService 291 Components.utils.import("resource://firebug/observer-service.js"); 292 fbObserverService.traceStacksForTrack(); 293 } 294 295 if (FBTrace.DBG_INITIALIZE) 296 FBTrace.sysout("firebug.shutdown exited "); 297 }, 298 299 shutdownUI: function() // TODO chrome.js 300 { 301 window.removeEventListener("unload", shutdownFirebug, false); 302 303 Events.dispatch(modules, "disable", [Firebug.chrome]); 304 }, 305 306 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 307 // TODO this entire section to XULWindow 308 309 // TODO XULWindow 310 getSuspended: function() 311 { 312 return Firebug.StartButton.getSuspended(); 313 }, 314 315 // TODO XULWindow 316 setSuspended: function(value) 317 { 318 Firebug.StartButton.setSuspended(value); 319 }, 320 321 // TODO XULWindow IN detached "Activate Firebug for the current website" 322 toggleSuspend: function() 323 { 324 // getSuspended returns non-null value if Firebug is suspended. 325 if (this.getSuspended() || this.isDetached()) 326 { 327 // Firebug is suspended now. Two possible actions have been executed: 328 // 1) Firebug UI is closed and the user clicked on the status bar icon in order to 329 // show the UI and resume Firebug. 330 // 2) Firebug is detached, but suspended for the current page. The user clicked 331 // either on the status bar icon or on an activation button that is displayed 332 // within detached Firebug window. 333 this.toggleBar(true); 334 } 335 else 336 { 337 // The users wants to suspend Firebug, let's do it and pull down the visible UI. 338 // xxxHonza: the Firebug isn't suspended if detached and the user clicks on the 339 // status bar icon (the detached window should becoma blank displaying only 340 // the activation button). 341 this.suspend(); 342 343 // Close detached Firebug or 344 // show/hide Firebug UI according to the browser.showFirebug flag. 345 if (Firebug.isDetached()) 346 this.toggleDetachBar(false); 347 else 348 this.syncBar(); 349 } 350 }, 351 352 // dispatch suspendFirebug to all windows 353 suspend: function() 354 { 355 if (Firebug.rerun) 356 return; 357 358 Firebug.suspendFirebug(); 359 }, 360 361 // dispatch onSuspendFirebug to all modules 362 suspendFirebug: function() 363 { 364 var cancelSuspend = Events.dispatch2(activableModules, "onSuspendingFirebug", []); 365 if (cancelSuspend) 366 return; 367 368 this.setSuspended("suspending"); 369 370 // TODO no context arg 371 var cancelSuspend = Events.dispatch2(activableModules, "onSuspendFirebug", 372 [Firebug.currentContext]); 373 374 if (cancelSuspend) 375 Firebug.resume(); 376 else 377 this.setSuspended("suspended"); 378 }, 379 380 resume: function() 381 { 382 Firebug.resumeFirebug(); 383 }, 384 385 resumeFirebug: function() // dispatch onResumeFirebug to all modules 386 { 387 this.setSuspended("resuming"); 388 389 // TODO no context arg 390 Events.dispatch(activableModules, 'onResumeFirebug', [Firebug.currentContext]); 391 this.setSuspended(null); 392 }, 393 394 getURLsForAllActiveContexts: function() 395 { 396 var contextURLSet = []; 397 398 // create a list of all unique activeContexts 399 Firebug.connection.eachContext(function createActiveContextList(context) 400 { 401 if (FBTrace.DBG_WINDOWS) 402 FBTrace.sysout("context " + context.getName()); 403 404 try 405 { 406 var cw = context.window; 407 if (cw) 408 { 409 var url; 410 if (cw.closed) 411 { 412 url = "about:closed"; 413 } 414 else 415 { 416 if ("location" in cw) 417 url = cw.location.toString(); 418 else 419 url = context.getName(); 420 } 421 422 if (url) 423 { 424 if (contextURLSet.indexOf(url) == -1) 425 contextURLSet.push(url); 426 } 427 } 428 } 429 catch(e) 430 { 431 if (FBTrace.DBG_ERRORS) 432 FBTrace.sysout("firebug.getURLsForAllActiveContexts could not get " + 433 "window.location for a context", e); 434 } 435 }); 436 437 if (FBTrace.DBG_ACTIVATION) 438 FBTrace.sysout("active contexts urls " + contextURLSet.length); 439 440 return contextURLSet; 441 }, 442 443 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 444 // Registration 445 446 /** 447 * Set a default value for a preference into the firebug preferences list. 448 * 449 * @param name preference name, possibly dot segmented, will be stored under 450 * extensions.firebug.<name> 451 * @param value default value of preference 452 * @return true if default set, else false 453 */ 454 registerPreference: function(name, value) 455 { 456 Firebug.Options.register(name, value); 457 }, 458 459 registerModule: function() 460 { 461 modules.push.apply(modules, arguments); 462 463 // Fire the initialize event for modules that are registered later. 464 if (Firebug.isInitialized) 465 Events.dispatch(arguments, "initialize", []); 466 467 if (FBTrace.DBG_REGISTRATION) 468 { 469 for (var i = 0; i < arguments.length; ++i) 470 FBTrace.sysout("registerModule "+arguments[i].dispatchName); 471 } 472 }, 473 474 unregisterModule: function() 475 { 476 for (var i = 0; i < arguments.length; ++i) 477 Arr.remove(modules, arguments[i]); 478 479 // Fire shutdown if module was unregistered dynamically (not on Firebug shutdown). 480 if (!Firebug.isShutdown) 481 Events.dispatch(arguments, "shutdown", []); 482 }, 483 484 registerActivableModule: function() 485 { 486 activableModules.push.apply(activableModules, arguments); 487 this.registerModule.apply(this, arguments); 488 }, 489 490 registerUIListener: function() 491 { 492 for (var j = 0; j < arguments.length; j++) 493 Firebug.uiListeners.push(arguments[j]); 494 }, 495 496 unregisterUIListener: function() 497 { 498 for (var i = 0; i < arguments.length; ++i) 499 Arr.remove(Firebug.uiListeners, arguments[i]); 500 }, 501 502 registerPanel: function() 503 { 504 for (var i=0; i<arguments.length; ++i) 505 { 506 var panelName = arguments[i].prototype.name; 507 var panel = panelTypeMap[panelName]; 508 if (panel) 509 { 510 if (FBTrace.DBG_ERRORS) 511 { 512 FBTrace.sysout("firebug.registerPanel; ERROR a panel with the same " + 513 "ID already registered! " + panelName); 514 } 515 } 516 } 517 518 // In order to keep built in panels (like Console, Script...) be the first one 519 // and insert all panels coming from extension at the end, catch any early registered 520 // panel (i.e. before FBL.initialize is called, such as YSlow) in a temp array 521 // that is appended at the end as soon as FBL.initialize is called. 522 if (earlyRegPanelTypes) 523 earlyRegPanelTypes.push.apply(earlyRegPanelTypes, arguments); 524 else 525 panelTypes.push.apply(panelTypes, arguments); 526 527 for (var i=0; i<arguments.length; ++i) 528 panelTypeMap[arguments[i].prototype.name] = arguments[i]; 529 530 if (FBTrace.DBG_REGISTRATION) 531 { 532 for (var i=0; i<arguments.length; ++i) 533 FBTrace.sysout("registerPanel " + arguments[i].prototype.name); 534 } 535 536 // If Firebug is not initialized yet the UI will be updated automatically soon. 537 if (!this.isInitialized) 538 return; 539 540 Firebug.chrome.syncMainPanels(); 541 Firebug.chrome.syncSidePanels(); 542 }, 543 544 unregisterPanel: function(panelType) 545 { 546 var panelName = panelType ? panelType.prototype.name : null; 547 548 if (FBTrace.DBG_REGISTRATION) 549 { 550 FBTrace.sysout("firebug.unregisterPanel: " + 551 (panelName ? panelName : "Undefined panelType")); 552 } 553 554 // Remove all instance of the panel. 555 Firebug.connection.eachContext(function (context) 556 { 557 // An empty state can be probably used at this moment since 558 // we are unregistering the panel anyway. 559 var state = {}; //context.browser.persistedState; 560 context.removePanel(panelType, state); 561 }); 562 563 // Now remove panel-type itself. 564 for (var i=0; i<panelTypes.length; i++) 565 { 566 if (panelTypes[i] == panelType) 567 { 568 panelTypes.splice(i, 1); 569 break; 570 } 571 } 572 573 delete panelTypeMap[panelType.prototype.name]; 574 575 // We don't have to update Firebug UI if it's just closing. 576 if (this.isShutdown) 577 return; 578 579 // Make sure another panel is selected if the current one is has been removed. 580 var panel = this.chrome.getSelectedPanel(); 581 if (panel && panel.name == panelName) 582 Firebug.chrome.selectPanel("html"); 583 584 // The panel tab must be removed from the UI. 585 Firebug.chrome.syncMainPanels(); 586 Firebug.chrome.syncSidePanels(); 587 }, 588 589 registerRep: function() 590 { 591 reps.push.apply(reps, arguments); 592 }, 593 594 unregisterRep: function() 595 { 596 for (var i = 0; i < arguments.length; ++i) 597 Arr.remove(reps, arguments[i]); 598 }, 599 600 setDefaultReps: function(funcRep, rep) 601 { 602 defaultRep = rep; 603 defaultFuncRep = funcRep; 604 }, 605 606 registerStringBundle: function(bundleURI) 607 { 608 Locale.registerStringBundle(bundleURI); 609 }, 610 611 unregisterStringBundle: function(bundleURI) 612 { 613 // xxxHonza: TODO: 614 }, 615 616 /** 617 * Allows registering of custom stylesheet coming from extension. The stylesheet is then 618 * used automatially thorough Firebug UI. 619 * @param {Object} styleURI URI of the stylesheet. 620 */ 621 registerStylesheet: function(styleURI) 622 { 623 this.stylesheets.push(styleURI); 624 625 // Append the stylesheet into the UI if Firebug is already loaded 626 if (this.isLoaded) 627 Firebug.chrome.appendStylesheet(styleURI); 628 629 if (FBTrace.DBG_REGISTRATION) 630 FBTrace.sysout("registerStylesheet " + styleURI); 631 }, 632 633 unregisterStylesheet: function(styleURI) 634 { 635 // xxxHonza: TODO 636 }, 637 638 registerMenuItem: function(menuItemController) 639 { 640 FBTrace.sysout("Firebug.registerMenuItem"); 641 menuItemControllers.push(menuItemController); 642 }, 643 644 registerTracePrefix: function(prefix, type, removePrefix, styleURI) 645 { 646 var listener = Firebug.TraceModule.getListenerByPrefix(prefix); 647 if (listener && FBTrace.DBG_ERRORS) 648 { 649 FBTrace.sysout("firebug.registerTracePrefix; ERROR " + 650 "there is already such prefix registered!"); 651 return; 652 } 653 654 listener = new TraceListener(prefix, type, removePrefix, styleURI); 655 Firebug.TraceModule.addListener(listener); 656 }, 657 658 unregisterTracePrefix: function(prefix) 659 { 660 var listener = Firebug.TraceModule.getListenerByPrefix(prefix); 661 if (listener) 662 Firebug.TraceModule.removeListener(listener); 663 }, 664 665 registerCommand: function(name, config) 666 { 667 return CommandLineExposed.registerCommand(name, config); 668 }, 669 670 unregistereCommand: function(name) 671 { 672 return CommandLineExposed.unregisterCommand(name); 673 }, 674 675 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 676 // Options 677 678 getPref: function() 679 { 680 // TODO deprecated 681 return Options.getPref.apply(Firebug.Options, arguments); 682 }, 683 684 setPref: function() 685 { 686 // TODO deprecated 687 return Options.setPref.apply(Firebug.Options, arguments); 688 }, 689 690 clearPref: function() 691 { 692 // TODO deprecated 693 return Options.clearPref.apply(Options, arguments); 694 }, 695 696 prefDomain: "extensions.firebug", 697 698 updateOption: function(name, value) 699 { 700 // fbtest changes options which change prefs which trigger updates in fbtrace 701 if (!Firebug.chrome) 702 return; 703 704 // Distribute to the current chrome. 705 Firebug.chrome.updateOption(name, value); 706 707 // If Firebug is detached distribute also into the in-browser chrome. 708 if (Firebug.chrome != Firebug.originalChrome) 709 Firebug.originalChrome.updateOption(name, value); 710 711 Events.dispatch(Firebug.modules, "updateOption", [name, value]); 712 }, 713 714 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 715 716 shouldIgnore: function(objectChromeView) 717 { 718 if (objectChromeView) 719 { 720 var contentView = Wrapper.unwrapObject(objectChromeView); 721 return (contentView && contentView.firebugIgnore); 722 } 723 // else don't ignore things we don't understand 724 }, 725 726 setIgnored: function(objectChromeView) 727 { 728 if (objectChromeView) 729 { 730 var contentView = Wrapper.unwrapObject(objectChromeView); 731 if (contentView) 732 contentView.firebugIgnore = true; 733 } 734 }, 735 736 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 737 // Browser Bottom Bar 738 739 // TODO XULWindow 740 showBar: function(show) 741 { 742 var browser = Firefox.getCurrentBrowser(); 743 744 if (FBTrace.DBG_WINDOWS || FBTrace.DBG_ACTIVATION) 745 FBTrace.sysout("showBar("+show+") for browser "+browser.currentURI.spec+ 746 " Firebug.currentContext "+Firebug.currentContext); 747 748 Firebug.chrome.toggleOpen(show); 749 750 if (!show) 751 Firebug.Inspector.inspectNode(null); 752 753 //xxxHonza: should be removed. 754 Events.dispatch(Firebug.uiListeners, show ? "showUI" : "hideUI", 755 [browser, Firebug.currentContext]); 756 757 // Sync panel state after the showUI event is dispatched. syncPanel method calls 758 // Panel.show method, which expects the active context to be already registered. 759 if (show) 760 Firebug.chrome.syncPanel(); 761 else 762 Firebug.chrome.selectPanel(); // select null causes hide() on selected 763 764 Firebug.StartButton.resetTooltip(); 765 }, 766 767 closeFirebug: function(userCommands) // this is really deactivate 768 { 769 if (!Firebug.currentContext) 770 return; 771 772 // It looks like FBTest is calling Firebug.Activation.clearAnnotations() 773 // when there is no current context. 774 //throw new Error("closeFirebug ERROR: no Firebug.currentContext "); 775 776 // Focus the browser window again 777 Firebug.currentContext.window.focus(); 778 779 Firebug.connection.closeContext(Firebug.currentContext, userCommands); 780 Firebug.StartButton.resetTooltip(); 781 }, 782 783 /** 784 * Primary function to activate or minimize firebug. Used by 785 * <ol> 786 * <li>the status bar icon click action</li> 787 * <li>the activation button (within Firebug.xul) click action</li> 788 * </ol> 789 * @param forceOpen: don't minimize, stay open if open. 790 * @param panelName: eg 'script', to select a specific panel. 791 */ 792 toggleBar: function(forceOpen, panelName) 793 { 794 if (panelName) 795 Firebug.chrome.selectPanel(panelName); 796 // if is deactivated. 797 if (!Firebug.currentContext) 798 { 799 var context = Firebug.getContext(); 800 // Be sure the UI is open for a newly created context. 801 forceOpen = true; 802 } 803 804 if (Firebug.isDetached()) 805 { 806 //in detached mode, two possibilities exist, the firebug windows is 807 // the active window of the user or no. 808 if ( !Firebug.chrome.hasFocus() || forceOpen) 809 Firebug.chrome.focus(); 810 else 811 Firebug.minimizeBar(); 812 } 813 // toggle minimize 814 else if (Firebug.isMinimized()) 815 { 816 // be careful, unMinimize func always sets placement to 817 // inbrowser first then unminimizes. when we want to 818 // unminimize in detached mode must call detachBar func. 819 if (Firebug.framePosition == "detached") 820 this.detachBar(); 821 else 822 Firebug.unMinimize(); 823 } 824 // else isInBrowser 825 else if (!forceOpen) 826 { 827 Firebug.minimizeBar(); 828 } 829 830 return true; 831 }, 832 833 /** 834 * Get context for the current website 835 */ 836 getContext: function() 837 { 838 var webApp = Firebug.connection.getCurrentSelectedWebApp(); 839 var context = Firebug.connection.getContextByWebApp(webApp); 840 // we are not debugging the selected tab. 841 if (!context) 842 { 843 context = Firebug.connection.getOrCreateContextByWebApp(webApp); 844 } 845 return context; 846 }, 847 848 /** 849 * Primary function to re-show firebug due to visiting active site. 850 * Unlike toggleBar, we are trying to obey the current placement, not change it. 851 */ 852 showContext: function(browser, context) 853 { 854 // user wants detached but we are not yet 855 if (Firebug.framePosition == "detached" && !Firebug.isDetached()) 856 { 857 if (context && !Firebug.isMinimized()) // don't detach if it's minimized 2067 858 this.detachBar(); // the placement will be set once the external window opens 859 else // just make sure we are not showing 860 this.showBar(false); 861 } 862 else if (Firebug.openMinimized() && !Firebug.isMinimized()) 863 this.minimizeBar(); 864 else if (Firebug.isMinimized()) 865 this.showBar(false); // don't show, we are minimized 866 else if (Firebug.isDetached()) 867 Firebug.chrome.syncResumeBox(context); 868 else // inBrowser 869 this.showBar(context?true:false); 870 }, 871 872 minimizeBar: function() // just pull down the UI, but don't deactivate the context 873 { 874 if (Firebug.isDetached()) 875 { 876 // TODO reattach 877 878 // window is closing in detached mode 879 var parent = this.getFirebugFrameParent(); 880 if (parent) 881 { 882 parent.exportFirebug(); 883 parent.close(); 884 } 885 886 Firebug.setPlacement("minimized"); 887 this.showBar(false); 888 Firebug.chrome.focus(); 889 } 890 else // inBrowser -> minimized 891 { 892 Firebug.setPlacement("minimized"); 893 this.showBar(false); 894 895 // Focus the browser window again 896 if (Firebug.currentContext) 897 Firebug.currentContext.window.focus(); 898 } 899 }, 900 901 unMinimize: function() 902 { 903 Firebug.setPlacement("inBrowser"); 904 Firebug.showBar(true); 905 }, 906 907 onShowDetachTooltip: function(tooltip) 908 { 909 tooltip.label = Firebug.isDetached() ? Locale.$STR("firebug.AttachFirebug") : 910 Locale.$STR("firebug.DetachFirebug"); 911 return true; 912 }, 913 914 /** 915 * function to switch between detached and inbrowser modes. 916 * @param forceOpen: should not be closed, stay open if open or open it. 917 * @param reopenInBrowser: switch from detahced to inbrowser mode. 918 */ 919 toggleDetachBar: function(forceOpen, reopenInBrowser) 920 { 921 //detached -> inbrowser 922 if (!forceOpen && Firebug.isDetached()) 923 { 924 var parent = this.getFirebugFrameParent(); 925 parent.exportFirebug(); 926 parent.close(); 927 928 if (reopenInBrowser) 929 { 930 // Is Firebug deactivated ? if yes, should be 931 // activated at first, then unminimize. 932 if (!Firebug.currentContext) 933 { 934 var context = Firebug.getContext(); 935 } 936 Firebug.unMinimize(); 937 } 938 else 939 { 940 Firebug.minimizeBar(); 941 } 942 943 Firebug.chrome.syncPositionPref(); 944 } 945 // is minimized now but the last time that has been closed, was in detached mode, 946 // so it should be returned to in browser mode because the user has pressed CTRL+F12. 947 else if (Firebug.framePosition == "detached" && Firebug.isMinimized()) 948 { 949 Firebug.unMinimize(); 950 Firebug.chrome.syncPositionPref(); 951 } 952 // else is in browser mode, then switch to detached mode. 953 else 954 { 955 this.detachBar(); 956 } 957 }, 958 959 closeDetachedWindow: function(userCommands) 960 { 961 Firebug.showBar(false); 962 963 if (Firebug.currentContext) 964 ToolInterface.browser.closeContext(Firebug.currentContext, userCommands); 965 966 // else the user closed Firebug external window while not looking at 967 // a debugged web page. 968 Firebug.StartButton.resetTooltip(); 969 }, 970 971 detachBar: function() 972 { 973 if (Firebug.isDetached()) // can be set true attachBrowser 974 { 975 Firebug.chrome.focus(); 976 return null; 977 } 978 979 if (Firebug.chrome.waitingForDetach) 980 return null; 981 982 Firebug.chrome.waitingForDetach = true; 983 Firebug.chrome.toggleOpen(false); // don't show in browser.xul now 984 985 if (FBTrace.DBG_ACTIVATION) 986 { 987 FBTrace.sysout("Firebug.detachBar opening firebug.xul for context " + 988 Firebug.currentContext.getName() ); 989 } 990 991 Firebug.chrome.syncPositionPref("detached"); 992 993 return Firefox.openWindow("Firebug", 994 "chrome://firebug/content/firefox/firebug.xul", 995 "", {}); 996 }, 997 998 // show firebug if we should 999 syncBar: function() 1000 { 1001 var browser = Firefox.getCurrentBrowser(); 1002 1003 // implicitly this is operating in the chrome of browser.xul 1004 this.showBar(browser && browser.showFirebug); 1005 }, 1006 1007 toggleCommandLine: function(showCommandEditor) 1008 { 1009 Options.set("commandEditor", showCommandEditor); 1010 }, 1011 1012 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 1013 1014 /** 1015 * Returns parent of the firebugFrame.xul frame. The actual parent depends on whether 1016 * Firebug is attached or detached. 1017 * 1018 * attached -> browser.xul 1019 * detached -> firebug.xul 1020 */ 1021 getFirebugFrameParent: function() 1022 { 1023 // We need firebug.xul in case of detached state. So, don't use 'top' since 1024 // it references browser.xul 1025 return Firebug.chrome.window.parent; 1026 }, 1027 1028 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 1029 // deprecated 1030 1031 resetAllOptions: function(confirm) 1032 { 1033 if (confirm) 1034 { 1035 var promptService = Cc["@mozilla.org/embedcomp/prompt-service;1"]. 1036 getService(Ci.nsIPromptService); 1037 1038 // Do not reset options if the user changed its mind. 1039 if (!promptService.confirm(null, Locale.$STR("Firebug"), 1040 Locale.$STR("confirmation.Reset_All_Firebug_Options"))) 1041 { 1042 return; 1043 } 1044 } 1045 1046 // Dispatch to non-module objects. 1047 Options.resetAllOptions(confirm); 1048 1049 // Dispatch to all modules so that additional settings can be reset. 1050 Events.dispatch(modules, "resetAllOptions", []); 1051 }, 1052 1053 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 1054 // Panels 1055 1056 getPanelType: function(panelName) 1057 { 1058 if (panelTypeMap.hasOwnProperty(panelName)) 1059 return panelTypeMap[panelName]; 1060 else 1061 return null; 1062 }, 1063 1064 getPanelTitle: function(panelType) 1065 { 1066 if (!panelType) 1067 return null; 1068 1069 return panelType.prototype.title ? panelType.prototype.title 1070 : Locale.$STR("Panel-"+panelType.prototype.name); 1071 }, 1072 1073 getPanelTooltip: function(panelType) 1074 { 1075 var tooltip = panelType.prototype.tooltip ? panelType.prototype.tooltip 1076 : Locale.$STR("panel.tip."+panelType.prototype.name); 1077 return tooltip != panelType.prototype.name ? tooltip : this.getPanelTitle(panelType); 1078 }, 1079 1080 getMainPanelTypes: function(context) 1081 { 1082 var resultTypes = []; 1083 1084 for (var i = 0; i < panelTypes.length; ++i) 1085 { 1086 var panelType = panelTypes[i]; 1087 if (!panelType.prototype.parentPanel) 1088 resultTypes.push(panelType); 1089 } 1090 1091 if (context.panelTypes) 1092 { 1093 for (var i = 0; i < context.panelTypes.length; ++i) 1094 { 1095 var panelType = context.panelTypes[i]; 1096 if (!panelType.prototype.parentPanel) 1097 resultTypes.push(panelType); 1098 } 1099 } 1100 1101 resultTypes.sort(function(a, b) 1102 { 1103 return a.prototype.order < b.prototype.order ? -1 : 1; 1104 }); 1105 1106 return resultTypes; 1107 }, 1108 1109 getSidePanelTypes: function(context, mainPanel) 1110 { 1111 if (!mainPanel) 1112 return []; 1113 1114 var resultTypes = []; 1115 1116 for (var i = 0; i < panelTypes.length; ++i) 1117 { 1118 var panelType = panelTypes[i]; 1119 1120 if (panelType.prototype.parentPanel && 1121 (panelType.prototype.parentPanel == mainPanel.name)) 1122 { 1123 resultTypes.push(panelType); 1124 } 1125 } 1126 1127 if (context.panelTypes) 1128 { 1129 for (var i = 0; i < context.panelTypes.length; ++i) 1130 { 1131 var panelType = context.panelTypes[i]; 1132 if (panelType.prototype.parentPanel == mainPanel.name) 1133 resultTypes.push(panelType); 1134 } 1135 } 1136 1137 resultTypes.sort(function(a, b) 1138 { 1139 return a.prototype.order < b.prototype.order ? -1 : 1; 1140 }); 1141 1142 return resultTypes; 1143 }, 1144 1145 /** 1146 * Returns all panel types, whose activation can be toggled 1147 * @returns {Object} Activable panel types 1148 */ 1149 getActivablePanelTypes: function() 1150 { 1151 var activablePanelTypes = []; 1152 for (var i = 0; i < panelTypes.length; ++i) 1153 { 1154 if (this.PanelActivation.isPanelActivable(panelTypes[i])) 1155 activablePanelTypes.push(panelTypes[i]); 1156 } 1157 1158 return activablePanelTypes; 1159 }, 1160 1161 /** 1162 * Gets an object containing the state of the panel from the last time 1163 * it was displayed before one or more page reloads. 1164 * The 'null' return here is a too-subtle signal to the panel code in bindings.xml. 1165 * Note that panel.context may not have a persistedState, but in addition the persisted 1166 * state for panel.name may be null. 1167 */ 1168 getPanelState: function(panel) 1169 { 1170 var persistedState = panel.context.persistedState; 1171 if (!persistedState || !persistedState.panelState) 1172 return null; 1173 1174 return persistedState.panelState[panel.name]; 1175 }, 1176 1177 showPanel: function(browser, panel) 1178 { 1179 // The panel may be null 1180 Events.dispatch(modules, "showPanel", [browser, panel]); 1181 }, 1182 1183 showSidePanel: function(browser, sidePanel) 1184 { 1185 Events.dispatch(modules, "showSidePanel", [browser, sidePanel]); 1186 }, 1187 1188 eachPanel: function(callback) 1189 { 1190 Firebug.connection.eachContext(function iteratePanels(context) 1191 { 1192 var rc = context.eachPanelInContext(callback); 1193 if (rc) 1194 return rc; 1195 }); 1196 }, 1197 1198 dispatchToPanels: function(fName, args) 1199 { 1200 Firebug.eachPanel( function dispatchToPanel(panel) 1201 { 1202 if (panel[fName]) 1203 return panel[fName].apply(panel,args); 1204 }); 1205 }, 1206 1207 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 1208 1209 dispatch: function(listeners, eventId, args) 1210 { 1211 Events.dispatch(listeners, eventId, args); 1212 }, 1213 1214 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 1215 // URL mapping 1216 1217 getObjectByURL: function(context, url) 1218 { 1219 for (var i = 0; i < modules.length; ++i) 1220 { 1221 var object = modules[i].getObjectByURL(context, url); 1222 if (object) 1223 return object; 1224 } 1225 }, 1226 1227 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 1228 // Reps 1229 1230 getRep: function(object, context) 1231 { 1232 var type = typeof(object); 1233 if (type == 'object' && object instanceof String) 1234 type = 'string'; 1235 1236 for (var i = 0; i < reps.length; ++i) 1237 { 1238 var rep = reps[i]; 1239 try 1240 { 1241 if (rep.supportsObject(object, type, (context?context:Firebug.currentContext) )) 1242 { 1243 //if (FBTrace.DBG_DOM) 1244 // FBTrace.sysout("getRep type: "+type+" object: "+object, rep); 1245 return rep; 1246 } 1247 } 1248 catch (exc) 1249 { 1250 if (FBTrace.DBG_ERRORS) 1251 { 1252 FBTrace.sysout("firebug.getRep FAILS: "+ exc, exc); 1253 FBTrace.sysout("firebug.getRep reps["+i+"/"+reps.length+"]: "+ 1254 (typeof(reps[i])), reps[i]); 1255 } 1256 } 1257 } 1258 1259 //if (FBTrace.DBG_DOM) 1260 // FBTrace.sysout("getRep default type: "+type+" object: "+object, rep); 1261 1262 return (type == "function") ? defaultFuncRep : defaultRep; 1263 }, 1264 1265 getRepObject: function(node) 1266 { 1267 var target = null; 1268 for (var child = node; child; child = child.parentNode) 1269 { 1270 if (Css.hasClass(child, "repTarget")) 1271 target = child; 1272 1273 if (child.repObject != null) 1274 { 1275 if (!target && Css.hasClass(child, "repIgnore")) 1276 break; 1277 else 1278 return child.repObject; 1279 } 1280 } 1281 }, 1282 1283 /** 1284 * The child node that has a repObject 1285 */ 1286 getRepNode: function(node) 1287 { 1288 for (var child = node; child; child = child.parentNode) 1289 { 1290 if (child.repObject != null) 1291 return child; 1292 } 1293 }, 1294 1295 getElementByRepObject: function(element, object) 1296 { 1297 for (var child = element.firstChild; child; child = child.nextSibling) 1298 { 1299 if (child.repObject === object) 1300 return child; 1301 } 1302 }, 1303 1304 /** 1305 * Takes an element from a panel document and finds the owning panel. 1306 */ 1307 getElementPanel: function(element) 1308 { 1309 for (; element; element = element.parentNode) 1310 { 1311 if (element.ownerPanel) 1312 return element.ownerPanel; 1313 } 1314 }, 1315 1316 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 1317 1318 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 1319 // nsISupports 1320 1321 QueryInterface : function(iid) 1322 { 1323 if (iid.equals(nsISupports)) 1324 { 1325 return this; 1326 } 1327 1328 throw Components.results.NS_NOINTERFACE; 1329 }, 1330 1331 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 1332 // Placement 1333 1334 isDetached: function() 1335 { 1336 return Firebug.placement == PLACEMENT_DETACHED; 1337 }, 1338 1339 isMinimized: function() 1340 { 1341 return Firebug.placement == PLACEMENT_MINIMIZED; 1342 }, 1343 1344 isInBrowser: function() 1345 { 1346 return Firebug.placement == PLACEMENT_INBROWSER; 1347 }, 1348 1349 placements: ["none", "inBrowser", "detached", "minimized"], 1350 1351 placement: 1, 1352 1353 setPlacement: function(toPlacement) 1354 { 1355 // TODO : This should probably be an event so others can link into this 1356 Firebug.chrome.$("fbSearchBox").hideOptions(); 1357 1358 if (FBTrace.DBG_ACTIVATION) 1359 FBTrace.sysout("Firebug.setPlacement from " + Firebug.getPlacement() + " to " + 1360 toPlacement + " with chrome " + Firebug.chrome.window.location); 1361 1362 for (var i=0; i<Firebug.placements.length; i++) 1363 { 1364 if (toPlacement == Firebug.placements[i]) 1365 { 1366 if (Firebug.placement != i) // then we are changing the value 1367 { 1368 Firebug.placement = i; 1369 delete Firebug.previousPlacement; 1370 Options.set("previousPlacement", Firebug.placement); 1371 Firebug.StartButton.resetTooltip(); 1372 } 1373 return Firebug.placement; 1374 } 1375 } 1376 throw new Error("Firebug.setPlacement cannot match "+toPlacement+" as a placement"); 1377 }, 1378 1379 getPlacement: function() 1380 { 1381 return Firebug.placements[Firebug.placement]; 1382 }, 1383 1384 openMinimized: function() 1385 { 1386 if (!Firebug.previousPlacement) 1387 Firebug.previousPlacement = Options.get("previousPlacement"); 1388 1389 return (Firebug.previousPlacement && (Firebug.previousPlacement == PLACEMENT_MINIMIZED) ) 1390 }, 1391 1392 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 1393 // Firebug.TabWatcher Listener 1394 1395 getContextType: function() 1396 { 1397 return Firebug.TabContext; 1398 }, 1399 1400 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 1401 1402 /** 1403 * This method syncs the UI to a context 1404 * @param context to become the active and visible context 1405 */ 1406 selectContext: function(context) 1407 { 1408 this.showContext(context.browser, context); 1409 }, 1410 1411 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 1412 1413 focusBrowserTab: function(win) // TODO move to FBL 1414 { 1415 Firefox.selectTabByWindow(win); 1416 this.chrome.focus(); 1417 }, 1418 1419 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 1420 // FBTest 1421 1422 // Expose our test list to the FBTest console for automated testing. 1423 onGetTestList: function(testLists) 1424 { 1425 testLists.push({ 1426 extension: "Firebug", 1427 testListURL: "http://getfirebug.com/tests/head/firebug.html" 1428 }); 1429 } 1430 }; 1431 1432 // ********************************************************************************************* // 1433 // API for Greasemonkey, Jetpack and other Firefox extensions 1434 1435 /** 1436 * @param global wrapped up global: outer window or sandbox 1437 * @return a |console| object for the window 1438 */ 1439 Firebug.getConsoleByGlobal = function getConsoleByGlobal(global) 1440 { 1441 try 1442 { 1443 var context = Firebug.connection.getContextByWindow(global); 1444 if (context) 1445 { 1446 var handler = Firebug.Console.injector.getConsoleHandler(context, global); 1447 1448 if (!handler) 1449 handler = Firebug.Console.isReadyElsePreparing(context, global);; 1450 1451 if (handler) 1452 { 1453 FBTrace.sysout("Firebug.getConsoleByGlobal " + handler.console + " for " + 1454 context.getName(), handler); 1455 1456 return handler.console; 1457 } 1458 1459 if (FBTrace.DBG_ERRORS) 1460 FBTrace.sysout("Firebug.getConsoleByGlobal FAILS, no handler for global " + 1461 global + " " + Win.safeGetWindowLocation(global), global); 1462 } 1463 else 1464 { 1465 if (FBTrace.DBG_ERRORS) 1466 FBTrace.sysout("Firebug.getConsoleByGlobal FAILS, no context for global " + 1467 global, global); 1468 } 1469 } 1470 catch (exc) 1471 { 1472 if (FBTrace.DBG_ERRORS) 1473 FBTrace.sysout("Firebug.getConsoleByGlobal FAILS " + exc, exc); 1474 } 1475 } 1476 1477 // ********************************************************************************************* // 1478 1479 /** 1480 * Support for listeners registration. This object is also extended by Firebug.Module, 1481 * so all modules supports listening automatically. Note that an array of listeners is 1482 * created for each intance of a module within the initialize method. Thus all derived 1483 * module classes must ensure that the Firebug.Module.initialize method is called for the 1484 * super class. 1485 */ 1486 Firebug.Listener = function() 1487 { 1488 // The array is created when the first listeners is added. 1489 // It can't be created here since derived objects would share 1490 // the same array. 1491 this.fbListeners = null; 1492 } 1493 Firebug.Listener.prototype = 1494 { 1495 addListener: function(listener) 1496 { 1497 if (!listener) 1498 { 1499 if (FBTrace.DBG_ERRORS) 1500 FBTrace.sysout("firebug.Listener.addListener; ERROR null listener registered."); 1501 return; 1502 } 1503 1504 // Delay the creation until the objects are created so 'this' causes new array 1505 // for this object (e.g. module, panel, etc.) 1506 if (!this.fbListeners) 1507 this.fbListeners = []; 1508 1509 this.fbListeners.push(listener); 1510 }, 1511 1512 removeListener: function(listener) 1513 { 1514 // if this.fbListeners is null, remove is being called with no add 1515 Arr.remove(this.fbListeners, listener); 1516 } 1517 }; 1518 1519 // ********************************************************************************************* // 1520 1521 /** 1522 * @module Base class for all modules. Every derived module object must be registered using 1523 * <code>Firebug.registerModule</code> method. There is always one instance of a module object 1524 * per browser window. 1525 */ 1526 Firebug.Module = Obj.extend(new Firebug.Listener(), 1527 /** @lends Firebug.Module */ 1528 { 1529 /** 1530 * Called by Firebug when Firefox window is opened. 1531 */ 1532 initialize: function() 1533 { 1534 }, 1535 1536 /** 1537 * Called when the UI is ready for context creation. 1538 * Used by chromebug; normally FrameProgressListener events trigger UI synchronization, 1539 * this event allows sync without progress events. 1540 */ 1541 initializeUI: function(detachArgs) 1542 { 1543 }, 1544 1545 /** 1546 * Called by Firebug when Firefox window is closed. 1547 */ 1548 shutdown: function() 1549 { 1550 }, 1551 1552 /** 1553 * Called when a new context is created but before the page is loaded. 1554 */ 1555 initContext: function(context, persistedState) 1556 { 1557 }, 1558 1559 /** 1560 * Called when a context is destroyed. Module may store info on persistedState 1561 * for reloaded pages. 1562 */ 1563 destroyContext: function(context, persistedState) 1564 { 1565 }, 1566 1567 /** 1568 * Called when attaching to a window (top-level or frame). 1569 */ 1570 watchWindow: function(context, win) 1571 { 1572 }, 1573 1574 /** 1575 * Called when unwatching a window (top-level or frame). 1576 */ 1577 unwatchWindow: function(context, win) 1578 { 1579 }, 1580 1581 // Called when a FF tab is create or activated (user changes FF tab) 1582 // Called after context is created or with context == null (to abort?) 1583 showContext: function(browser, context) 1584 { 1585 }, 1586 1587 /** 1588 * Called after a context's page gets DOMContentLoaded 1589 */ 1590 loadedContext: function(context) 1591 { 1592 }, 1593 1594 /* 1595 * After "onSelectingPanel", a panel has been selected but is not yet visible 1596 * @param browser a tab's browser element 1597 * @param panel selectet panel OR null 1598 */ 1599 showPanel: function(browser, panel) 1600 { 1601 }, 1602 1603 showSidePanel: function(browser, sidePanel) 1604 { 1605 }, 1606 1607 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 1608 1609 updateOption: function(name, value) 1610 { 1611 }, 1612 1613 getObjectByURL: function(context, url) 1614 { 1615 }, 1616 1617 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 1618 // intermodule dependency 1619 1620 // caller needs module. win maybe context.window or iframe in context.window. 1621 // true means module is ready now, else getting ready 1622 isReadyElsePreparing: function(context, win) 1623 { 1624 }, 1625 }); 1626 1627 // ********************************************************************************************* // 1628 1629 Firebug.Extension = 1630 { 1631 acceptContext: function(win,uri) 1632 { 1633 return false; 1634 }, 1635 1636 declineContext: function(win,uri) 1637 { 1638 return false; 1639 } 1640 }; 1641 1642 // ********************************************************************************************* // 1643 1644 /** 1645 * @panel Base class for all panels. Every derived panel must define a constructor and 1646 * register with <code>Firebug.registerPanel</code> method. An instance of the panel 1647 * object is created by the framework for each browser tab where Firebug is activated. 1648 */ 1649 Firebug.Panel = Obj.extend(new Firebug.Listener(), 1650 /** @lends Firebug.Panel */ 1651 { 1652 searchable: false, // supports search 1653 editable: true, // clicking on contents in the panel will invoke the inline editor, eg the CSS Style panel or HTML panel. 1654 breakable: false, // if true, supports break-on-next (the pause button functionality) 1655 order: 2147483647, // relative position of the panel (or a side panel) 1656 statusSeparator: "<", // the character used to separate items on the panel status (aka breadcrumbs) in the tool bar, eg ">" in the DOM panel 1657 enableA11y: false, // true if the panel wants to participate in A11y accessibility support. 1658 deriveA11yFrom: null, // Name of the panel that uses the same a11y logic. 1659 inspectable: false, // true to support inspecting elements inside this panel 1660 1661 initialize: function(context, doc) 1662 { 1663 if (!context.browser) 1664 { 1665 if (FBTrace.DBG_ERRORS) 1666 FBTrace.sysout("attempt to create panel with dud context!"); 1667 return false; 1668 } 1669 1670 this.context = context; 1671 this.document = doc; 1672 1673 this.panelNode = doc.createElement("div"); 1674 this.panelNode.ownerPanel = this; 1675 1676 Css.setClass(this.panelNode, "panelNode panelNode-" + this.name + " contextUID=" + 1677 context.uid); 1678 1679 // Load persistent content if any. 1680 var persistedState = Firebug.getPanelState(this); 1681 if (persistedState) 1682 { 1683 this.persistContent = persistedState.persistContent; 1684 if (this.persistContent && persistedState.panelNode) 1685 this.loadPersistedContent(persistedState); 1686 } 1687 1688 doc.body.appendChild(this.panelNode); 1689 1690 // Update panel's tab in case the break-on-next (BON) is active. 1691 var shouldBreak = this.shouldBreakOnNext(); 1692 Firebug.Breakpoint.updatePanelTab(this, shouldBreak); 1693 1694 if (FBTrace.DBG_INITIALIZE) 1695 FBTrace.sysout("firebug.initialize panelNode for " + this.name); 1696 1697 this.initializeNode(this.panelNode); 1698 }, 1699 1700 destroy: function(state) // Panel may store info on state 1701 { 1702 if (FBTrace.DBG_INITIALIZE) 1703 FBTrace.sysout("firebug.destroy panelNode for " + this.name); 1704 1705 if (this.panelNode) 1706 { 1707 if (this.persistContent) 1708 this.savePersistedContent(state); 1709 else 1710 delete state.persistContent; 1711 1712 delete this.panelNode.ownerPanel; 1713 } 1714 1715 this.destroyNode(); 1716 1717 // xxxHonza: not exactly sure why, but it helps when testing memory-leask. 1718 // Note the the selection can point to a document (in case of the HTML panel). 1719 // Perhaps it breaks a cycle (page -> firebug -> page)? 1720 delete this.selection; 1721 delete this.panelBrowser; 1722 }, 1723 1724 savePersistedContent: function(state) 1725 { 1726 state.panelNode = this.panelNode; 1727 state.persistContent = this.persistContent; 1728 }, 1729 1730 loadPersistedContent: function(persistedState) 1731 { 1732 // move the nodes from the persistedState to the panel 1733 while (persistedState.panelNode.firstChild) 1734 this.panelNode.appendChild(persistedState.panelNode.firstChild); 1735 1736 Dom.scrollToBottom(this.panelNode); 1737 }, 1738 1739 // called when a panel in one XUL window is about to disappear to later reappear 1740 // another XUL window. 1741 detach: function(oldChrome, newChrome) 1742 { 1743 }, 1744 1745 // this is how a panel in one window reappears in another window; lazy called 1746 reattach: function(doc) 1747 { 1748 this.document = doc; 1749 1750 if (this.panelNode) 1751 { 1752 var scrollTop = this.panelNode.scrollTop; 1753 this.panelNode = doc.adoptNode(this.panelNode, true); 1754 this.panelNode.ownerPanel = this; 1755 doc.body.appendChild(this.panelNode); 1756 this.panelNode.scrollTop = scrollTop; 1757 } 1758 }, 1759 1760 // Called at the end of module.initialize; addEventListener-s here 1761 initializeNode: function(panelNode) 1762 { 1763 Events.dispatch(this.fbListeners, "onInitializeNode", [this]); 1764 }, 1765 1766 // removeEventListener-s here. 1767 destroyNode: function() 1768 { 1769 Events.dispatch(this.fbListeners, "onDestroyNode", [this]); 1770 }, 1771 1772 show: function(state) // persistedPanelState plus non-persisted hide() values 1773 { 1774 }, 1775 1776 hide: function(state) // store info on state for next show. 1777 { 1778 }, 1779 1780 watchWindow: function(context, win) 1781 { 1782 }, 1783 1784 unwatchWindow: function(context, win) 1785 { 1786 }, 1787 1788 updateOption: function(name, value) 1789 { 1790 }, 1791 1792 /** 1793 * Called after chrome.applyTextSize 1794 * @param zoom: ratio of current size to normal size, eg 1.5 1795 */ 1796 onTextSizeChange: function(zoom) 1797 { 1798 1799 }, 1800 1801 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 1802 // Toolbar 1803 1804 showToolbarButtons: function(buttonsId, show) 1805 { 1806 try 1807 { 1808 var buttons = Firebug.chrome.$(buttonsId); 1809 Dom.collapse(buttons, !show); 1810 } 1811 catch (exc) 1812 { 1813 if (FBTrace.DBG_ERRORS) 1814 FBTrace.sysout("firebug.Panel showToolbarButtons FAILS "+exc, exc); 1815 } 1816 }, 1817 1818 onGetPanelToolbarButtons: function(panel, items) 1819 { 1820 return []; 1821 }, 1822 1823 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 1824 1825 /** 1826 * Returns a number indicating the view's ability to inspect the object. 1827 * 1828 * Zero means not supported, and higher numbers indicate specificity. 1829 */ 1830 supportsObject: function(object, type) 1831 { 1832 return 0; 1833 }, 1834 1835 hasObject: function(object) // beyond type testing, is this object selectable? 1836 { 1837 return false; 1838 }, 1839 1840 navigate: function(object) 1841 { 1842 if (!object) 1843 object = this.getDefaultLocation(); 1844 if (!object) 1845 object = null; // not undefined. 1846 1847 // if this.location undefined, may set to null 1848 if (!this.location || (object != this.location)) 1849 { 1850 if (FBTrace.DBG_PANELS) 1851 FBTrace.sysout("navigate "+this.name+" to location "+object, object); 1852 1853 this.location = object; 1854 this.updateLocation(object); 1855 1856 Events.dispatch(Firebug.uiListeners, "onPanelNavigate", [object, this]); 1857 } 1858 else 1859 { 1860 if (FBTrace.DBG_PANELS) 1861 { 1862 FBTrace.sysout("navigate skipped for panel " + this.name + " when object " + 1863 object + " vs this.location=" + this.location, 1864 {object: object, location: this.location}); 1865 } 1866 } 1867 }, 1868 1869 /** 1870 * The location object has been changed, the panel should update it view 1871 * @param object a location, must be one of getLocationList() returns 1872 * if getDefaultLocation() can return null, then updateLocation must handle it here. 1873 */ 1874 updateLocation: function(object) 1875 { 1876 }, 1877 1878 select: function(object, forceUpdate) 1879 { 1880 if (!object) 1881 object = this.getDefaultSelection(); 1882 1883 if (FBTrace.DBG_PANELS) 1884 FBTrace.sysout("firebug.select "+this.name+" forceUpdate: "+forceUpdate+" "+ 1885 object+((object==this.selection)?"==":"!=")+this.selection); 1886 1887 if (forceUpdate || object != this.selection) 1888 { 1889 this.selection = object; 1890 this.updateSelection(object); 1891 1892 Events.dispatch(Firebug.uiListeners, "onObjectSelected", [object, this]); 1893 } 1894 }, 1895 1896 /** 1897 * Firebug wants to show an object to the user and this panel has the best supportsObject() 1898 * result for the object. If the panel displays a container for objects of this type, 1899 * it should set this.selectedObject = object 1900 */ 1901 updateSelection: function(object) 1902 { 1903 }, 1904 1905 /** 1906 * Redisplay the panel based on the current location and selection 1907 */ 1908 refresh: function() 1909 { 1910 if (this.location) 1911 this.updateLocation(this.location); 1912 else if (this.selection) 1913 this.updateSelection(this.selection); 1914 }, 1915 1916 markChange: function(skipSelf) 1917 { 1918 if (this.dependents) 1919 { 1920 if (skipSelf) 1921 { 1922 for (var i = 0; i < this.dependents.length; ++i) 1923 { 1924 var panelName = this.dependents[i]; 1925 if (panelName != this.name) 1926 this.context.invalidatePanels(panelName); 1927 } 1928 } 1929 else 1930 this.context.invalidatePanels.apply(this.context, this.dependents); 1931 } 1932 }, 1933 1934 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 1935 // Inspector 1936 1937 /** 1938 * Called by the framework when the user starts inspecting. Inspecting must be enabled 1939 * for the panel (panel.inspectable == true) 1940 */ 1941 startInspecting: function() 1942 { 1943 }, 1944 1945 /** 1946 * Called by the framework when inspecting is in progress and the user moves mouse over 1947 * a new page element. Inspecting must be enabled for the panel (panel.inspectable == true). 1948 * This method is called in a timeout to avoid performance penalties when the user moves 1949 * the mouse over the page elements too fast. 1950 * @param {Element} node The page element being inspected 1951 * @returns {Boolean} Returns true if the node should be selected within the panel using 1952 * the default panel selection mechanism (i.e. by calling panel.select(node) method). 1953 */ 1954 inspectNode: function(node) 1955 { 1956 return true; 1957 }, 1958 1959 /** 1960 * Called by the framework when the user stops inspecting. Inspecting must be enabled 1961 * for the panel (panel.inspectable == true) 1962 * @param {Element} node The last page element inspected 1963 * @param {Boolean} canceled Set to true if inspecing has been canceled 1964 * by pressing the escape key. 1965 */ 1966 stopInspecting: function(node, canceled) 1967 { 1968 }, 1969 1970 /** 1971 * Called by the framework when inspecting is in progress. Allows to inspect 1972 * only nodes that are supported by the panel. Derived panels can provide effective 1973 * algorithms to provide these nodes. 1974 * @param {Element} node Currently inspected page element. 1975 */ 1976 getInspectNode: function(node) 1977 { 1978 while (node) 1979 { 1980 if (this.supportsObject(node, typeof node)) 1981 return node; 1982 node = node.parentNode; 1983 } 1984 return null; 1985 }, 1986 1987 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 1988 1989 /* 1990 * Called by search in the case something was found. 1991 * This will highlight the given node for a specific timespan. There's only one node 1992 * highlighted at a time. 1993 * @param {Node} Node to highlight 1994 */ 1995 highlightNode: function(node) 1996 { 1997 if (this.highlightedNode) 1998 Css.cancelClassTimed(this.highlightedNode, "jumpHighlight", this.context); 1999 2000 this.highlightedNode = node; 2001 2002 if (node) 2003 Css.setClassTimed(node, "jumpHighlight", this.context); 2004 }, 2005 2006 /* 2007 * Called by the framework when panel search is used. 2008 * This is responsible for finding and highlighting search matches. 2009 * @param {String} text String to search for 2010 * @param {Boolean} reverse Indicates, if search is reversed 2011 * @return true, if search matched, otherwise false 2012 */ 2013 search: function(text, reverse) 2014 { 2015 }, 2016 2017 /** 2018 * Retrieves the search options that this modules supports. 2019 * This is used by the search UI to present the proper options. 2020 */ 2021 getSearchOptionsMenuItems: function() 2022 { 2023 return [ 2024 Firebug.Search.searchOptionMenu("search.Case Sensitive", "searchCaseSensitive", 2025 "search.tip.Case_Sensitive") 2026 ]; 2027 }, 2028 2029 /** 2030 * Navigates to the next document whose match parameter returns true. 2031 */ 2032 navigateToNextDocument: function(match, reverse) 2033 { 2034 // This is an approximation of the UI that is displayed by the location 2035 // selector. This should be close enough, although it may be better 2036 // to simply generate the sorted list within the module, rather than 2037 // sorting within the UI. 2038 var self = this; 2039 function compare(a, b) 2040 { 2041 var locA = self.getObjectDescription(a); 2042 var locB = self.getObjectDescription(b); 2043 if (locA.path > locB.path) 2044 return 1; 2045 if (locA.path < locB.path) 2046 return -1; 2047 if (locA.name > locB.name) 2048 return 1; 2049 if (locA.name < locB.name) 2050 return -1; 2051 return 0; 2052 } 2053 2054 var allLocs = this.getLocationList().sort(compare); 2055 for (var curPos = 0; curPos < allLocs.length && allLocs[curPos] != this.location; curPos++); 2056 2057 function transformIndex(index) 2058 { 2059 if (reverse) 2060 { 2061 // For the reverse case we need to implement wrap around. 2062 var intermediate = curPos - index - 1; 2063 return (intermediate < 0 ? allLocs.length : 0) + intermediate; 2064 } 2065 else 2066 { 2067 return (curPos + index + 1) % allLocs.length; 2068 } 2069 }; 2070 2071 for (var next = 0; next < allLocs.length - 1; next++) 2072 { 2073 var object = allLocs[transformIndex(next)]; 2074 2075 if (match(object)) 2076 { 2077 this.navigate(object); 2078 return object; 2079 } 2080 } 2081 }, 2082 2083 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 2084 2085 // Called when "Options" clicked. Return array of 2086 // {label: 'name', nol10n: true, type: "checkbox", checked: <value>, 2087 // command:function to set <value>} 2088 getOptionsMenuItems: function() 2089 { 2090 return null; 2091 }, 2092 2093 /** 2094 * Called by chrome.onContextMenu to build the context menu when this panel has focus. 2095 * See also FirebugRep for a similar function also called by onContextMenu 2096 * Extensions may monkey patch and chain off this call 2097 * @param object: the 'realObject', a model value, eg a DOM property 2098 * @param target: the HTML element clicked on. 2099 * @return an array of menu items. 2100 */ 2101 getContextMenuItems: function(object, target) 2102 { 2103 return []; 2104 }, 2105 2106 getBreakOnMenuItems: function() 2107 { 2108 return []; 2109 }, 2110 2111 getEditor: function(target, value) 2112 { 2113 }, 2114 2115 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 2116 2117 getDefaultSelection: function() 2118 { 2119 return null; 2120 }, 2121 2122 browseObject: function(object) 2123 { 2124 }, 2125 2126 getPopupObject: function(target) 2127 { 2128 return Firebug.getRepObject(target); 2129 }, 2130 2131 getTooltipObject: function(target) 2132 { 2133 return Firebug.getRepObject(target); 2134 }, 2135 2136 showInfoTip: function(infoTip, x, y) 2137 { 2138 2139 }, 2140 2141 getObjectPath: function(object) 2142 { 2143 return null; 2144 }, 2145 2146 // An array of objects that can be passed to getObjectLocation. 2147 // The list of things a panel can show, eg sourceFiles. 2148 // Only shown if panel.location defined and supportsObject true 2149 getLocationList: function() 2150 { 2151 return null; 2152 }, 2153 2154 getDefaultLocation: function() 2155 { 2156 return null; 2157 }, 2158 2159 getObjectLocation: function(object) 2160 { 2161 return ""; 2162 }, 2163 2164 // Text for the location list menu eg script panel source file list 2165 // return.path: group/category label, return.name: item label 2166 getObjectDescription: function(object) 2167 { 2168 var url = this.getObjectLocation(object); 2169 return Url.splitURLBase(url); 2170 }, 2171 2172 /** 2173 * UI signal that a tab needs attention, eg Script panel is currently stopped on a breakpoint 2174 * @param: show boolean, true turns on. 2175 */ 2176 highlight: function(show) 2177 { 2178 var tab = this.getTab(); 2179 if (!tab) 2180 return; 2181 2182 if (show) 2183 tab.setAttribute("highlight", "true"); 2184 else 2185 tab.removeAttribute("highlight"); 2186 }, 2187 2188 getTab: function() 2189 { 2190 var chrome = Firebug.chrome; 2191 2192 var tab = chrome.$("fbPanelBar2").getTab(this.name); 2193 if (!tab) 2194 tab = chrome.$("fbPanelBar1").getTab(this.name); 2195 return tab; 2196 }, 2197 2198 /** 2199 * If the panel supports source viewing, then return a SourceLink, else null 2200 * @param target an element from the panel under the mouse 2201 * @param object the realObject under the mouse 2202 */ 2203 getSourceLink: function(target, object) 2204 { 2205 }, 2206 2207 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 2208 // Support for Break On Next 2209 2210 /** 2211 * Called by the framework to see if the panel currently supports BON 2212 */ 2213 supportsBreakOnNext: function() 2214 { 2215 return this.breakable; // most panels just use this flag 2216 }, 2217 2218 /** 2219 * Called by the framework when the user clicks on the Break On Next button. 2220 * @param {Boolean} armed Set to true if the Break On Next feature is 2221 * to be armed for action and set to false if the Break On Next should be disarmed. 2222 * If 'armed' is true, then the next call to shouldBreakOnNext should be |true|. 2223 */ 2224 breakOnNext: function(armed) 2225 { 2226 }, 2227 2228 /** 2229 * Called when a panel is selected/displayed. The method should return true 2230 * if the Break On Next feature is currently armed for this panel. 2231 */ 2232 shouldBreakOnNext: function() 2233 { 2234 return false; 2235 }, 2236 2237 /** 2238 * Returns labels for Break On Next tooltip (one for enabled and one for disabled state). 2239 * @param {Boolean} enabled Set to true if the Break On Next feature is 2240 * currently activated for this panel. 2241 */ 2242 getBreakOnNextTooltip: function(enabled) 2243 { 2244 return null; 2245 }, 2246 }); 2247 2248 // ********************************************************************************************* // 2249 2250 /** 2251 * @panel This object represents a panel with two states: enabled/disabled. Such support 2252 * is important for panel that represents performance penalties and it's useful for the 2253 * user to have the option to disable them. 2254 * 2255 * All methods in this object are used on the prototype object (they reprent class methods) 2256 * and so, |this| points to the panel's prototype and *not* to the panel instance. 2257 */ 2258 Firebug.ActivablePanel = Obj.extend(Firebug.Panel, 2259 { 2260 activable: true, 2261 2262 isActivable: function() 2263 { 2264 return this.activable; 2265 }, 2266 2267 isEnabled: function() 2268 { 2269 if (!this.isActivable()) 2270 return true; 2271 2272 if (!this.name) 2273 return false; 2274 2275 return Options.get(this.name+".enableSites"); 2276 }, 2277 2278 setEnabled: function(enable) 2279 { 2280 if (!this.name || !this.activable) 2281 return; 2282 2283 Options.set(this.name+".enableSites", enable); 2284 }, 2285 2286 /** 2287 * Called when an instance of this panel type is enabled or disabled. Again notice that 2288 * this is a class method and so, panel instance variables (like e.g. context) are 2289 * not accessible from this method. 2290 * @param {Object} enable Set to true if this panel type is now enabled. 2291 */ 2292 onActivationChanged: function(enable) 2293 { 2294 // TODO: Use Firebug.ActivableModule.addObserver to express dependencies on modules. 2295 }, 2296 }); 2297 2298 // ********************************************************************************************* // 2299 2300 /** 2301 * @module Should be used by modules (Firebug specific task controllers) that supports 2302 * activation. An example of such 'activable' module can be the debugger module 2303 * {@link Firebug.Debugger}, which can be disabled in order to avoid performance 2304 * penalties (in cases where the user doesn't need a debugger for the moment). 2305 */ 2306 Firebug.ActivableModule = Obj.extend(Firebug.Module, 2307 /** @lends Firebug.ActivableModule */ 2308 { 2309 /** 2310 * Every activable module is disabled by default waiting for on a panel 2311 * that wants to have it enabled (and display provided data). The rule is 2312 * if there is no panel (view) the module is disabled. 2313 */ 2314 enabled: false, 2315 2316 /** 2317 * List of observers (typically panels). If there is at least one observer registered 2318 * The module becomes active. 2319 */ 2320 observers: null, 2321 2322 /** 2323 * List of dependent modules. 2324 */ 2325 dependents: null, 2326 2327 initialize: function() 2328 { 2329 Firebug.Module.initialize.apply(this, arguments); 2330 }, 2331 2332 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 2333 // Observers (dependencies) 2334 2335 hasObservers: function() 2336 { 2337 return this.observers ? this.observers.length > 0 : false; 2338 }, 2339 2340 addObserver: function(observer) 2341 { 2342 if (!this.observers) 2343 this.observers = []; 2344 2345 if (this.observers.indexOf(observer) === -1) 2346 { 2347 this.observers.push(observer); 2348 this.onObserverChange(observer); // targeted, not dispatched. 2349 } 2350 // else no-op 2351 }, 2352 2353 removeObserver: function(observer) 2354 { 2355 if (!this.observers) 2356 return; 2357 2358 if (this.observers.indexOf(observer) !== -1) 2359 { 2360 Arr.remove(this.observers, observer); 2361 this.onObserverChange(observer); // targeted, not dispatched 2362 } 2363 // else no-op 2364 }, 2365 2366 /** 2367 * This method is called if an observer (e.g. {@link Firebug.Panel}) is added or removed. 2368 * The module should decide about activation/deactivation upon existence of at least one 2369 * observer. 2370 */ 2371 onObserverChange: function(observer) 2372 { 2373 if (FBTrace.DBG_WINDOWS) 2374 FBTrace.sysout("firebug.ActivableModule.onObserverChange;"); 2375 }, 2376 2377 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 2378 // Firebug Activation 2379 2380 onSuspendingFirebug: function() 2381 { 2382 // Called before any suspend actions. First caller to return true aborts suspend. 2383 }, 2384 2385 onSuspendFirebug: function() 2386 { 2387 // When the number of activeContexts decreases to zero. Modules should remove 2388 // listeners, disable function that takes resources 2389 }, 2390 2391 onResumeFirebug: function() 2392 { 2393 // When the number of activeContexts increases from zero. Modules should undo the 2394 // work done in onSuspendFirebug 2395 }, 2396 2397 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 2398 // Module enable/disable APIs. 2399 2400 setDefaultState: function(enable) 2401 { 2402 //@deprecated 2403 Firebug.Console.log("Deprecated: don't use ActivableModule.setDefaultState!", 2404 Firebug.currentContext); 2405 }, 2406 2407 isEnabled: function() 2408 { 2409 return this.hasObservers(); 2410 }, 2411 2412 isAlwaysEnabled: function() 2413 { 2414 return this.hasObservers(); 2415 } 2416 }); 2417 2418 // ********************************************************************************************* // 2419 2420 /** 2421 * MeasureBox 2422 * To get pixels size.width and size.height: 2423 * <ul><li> this.startMeasuring(view); </li> 2424 * <li> var size = this.measureText(lineNoCharsSpacer); </li> 2425 * <li> this.stopMeasuring(); </li> 2426 * </ul> 2427 */ 2428 Firebug.MeasureBox = 2429 { 2430 startMeasuring: function(target) 2431 { 2432 if (!this.measureBox) 2433 { 2434 this.measureBox = target.ownerDocument.createElement("span"); 2435 this.measureBox.className = "measureBox"; 2436 } 2437 2438 Css.copyTextStyles(target, this.measureBox); 2439 target.ownerDocument.body.appendChild(this.measureBox); 2440 }, 2441 2442 getMeasuringElement: function() 2443 { 2444 return this.measureBox; 2445 }, 2446 2447 measureText: function(value) 2448 { 2449 this.measureBox.innerHTML = value ? Str.escapeForSourceLine(value) : "m"; 2450 return {width: this.measureBox.offsetWidth, height: this.measureBox.offsetHeight-1}; 2451 }, 2452 2453 measureInputText: function(value) 2454 { 2455 value = value ? Str.escapeForTextNode(value) : "m"; 2456 if (!Firebug.showTextNodesWithWhitespace) 2457 value = value.replace(/\t/g,'mmmmmm').replace(/\ /g,'m'); 2458 2459 this.measureBox.innerHTML = value; 2460 return {width: this.measureBox.offsetWidth, height: this.measureBox.offsetHeight-1}; 2461 }, 2462 2463 getBox: function(target) 2464 { 2465 var style = this.measureBox.ownerDocument.defaultView.getComputedStyle(this.measureBox, ""); 2466 var box = Css.getBoxFromStyles(style, this.measureBox); 2467 return box; 2468 }, 2469 2470 stopMeasuring: function() 2471 { 2472 this.measureBox.parentNode.removeChild(this.measureBox); 2473 } 2474 }; 2475 2476 // ********************************************************************************************* // 2477 2478 with (Domplate) { 2479 Firebug.Rep = domplate( 2480 { 2481 className: "", 2482 inspectable: true, 2483 2484 supportsObject: function(object, type) 2485 { 2486 return false; 2487 }, 2488 2489 highlightObject: function(object, context) 2490 { 2491 var realObject = this.getRealObject(object, context); 2492 if (realObject) 2493 Firebug.Inspector.highlightObject(realObject, context); 2494 }, 2495 2496 unhighlightObject: function(object, context) 2497 { 2498 Firebug.Inspector.highlightObject(null); 2499 }, 2500 2501 inspectObject: function(object, context) 2502 { 2503 Firebug.chrome.select(object); 2504 }, 2505 2506 browseObject: function(object, context) 2507 { 2508 }, 2509 2510 persistObject: function(object, context) 2511 { 2512 }, 2513 2514 getRealObject: function(object, context) 2515 { 2516 return object; 2517 }, 2518 2519 getTitle: function(object) 2520 { 2521 if (object.constructor && typeof(object.constructor) == 'function') 2522 { 2523 var ctorName = object.constructor.name; 2524 if (ctorName && ctorName != "Object") 2525 return ctorName; 2526 } 2527 2528 var label = FBL.safeToString(object); // eg [object XPCWrappedNative [object foo]] 2529 2530 const re =/\[object ([^\]]*)/; 2531 var m = re.exec(label); 2532 var n = null; 2533 if (m) 2534 n = re.exec(m[1]); // eg XPCWrappedNative [object foo 2535 2536 if (n) 2537 return n[1]; // eg foo 2538 else 2539 return m ? m[1] : label; 2540 }, 2541 2542 showInfoTip: function(infoTip, target, x, y) 2543 { 2544 return false; 2545 }, 2546 2547 getTooltip: function(object) 2548 { 2549 return null; 2550 }, 2551 2552 /** 2553 * Called by chrome.onContextMenu to build the context menu when the underlying object 2554 * has this rep. See also Panel for a similar function also called by onContextMenu 2555 * Extensions may monkey patch and chain off this call 2556 * 2557 * @param object: the 'realObject', a model value, eg a DOM property 2558 * @param target: the HTML element clicked on. 2559 * @param context: the context, probably Firebug.currentContext 2560 * @return an array of menu items. 2561 */ 2562 getContextMenuItems: function(object, target, context) 2563 { 2564 return []; 2565 }, 2566 2567 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 2568 // Convenience for domplates 2569 2570 STR: function(name) 2571 { 2572 return Locale.$STR(name); 2573 }, 2574 2575 cropString: function(text) 2576 { 2577 return Str.cropString(text); 2578 }, 2579 2580 cropMultipleLines: function(text, limit) 2581 { 2582 return Str.cropMultipleLines(text, limit); 2583 }, 2584 2585 toLowerCase: function(text) 2586 { 2587 return text ? text.toLowerCase() : text; 2588 }, 2589 2590 plural: function(n) 2591 { 2592 return n == 1 ? "" : "s"; 2593 } 2594 })}; 2595 2596 // ********************************************************************************************* // 2597 2598 Firebug.Migrator = 2599 { 2600 /* 2601 * UI Update from element oldButton to element newButton. 2602 * On first call, Arrow points from old to new, button OK required 2603 * After OK, the oldButton is removed and not shown again unless preference is erased. 2604 */ 2605 migrateButton: function(oldButton, newButton) 2606 { 2607 if (Firebug.Migrator.getMigrated(oldButton)) 2608 { 2609 oldButton.parentNode.removeChild(oldButton); 2610 return; 2611 } 2612 2613 function showMigration(event) 2614 { 2615 oldButton.removeEventListener('mouseover', showMigration, false); 2616 2617 var endPoint = newButton.getBoundingClientRect(); 2618 var origin = oldButton.getBoundingClientRect(); 2619 2620 // A box surrounding both buttons 2621 var left = Math.min(origin.left, endPoint.left); 2622 var right = Math.max(origin.right, endPoint.right); 2623 var top = Math.min(origin.top, endPoint.top); 2624 var bottom = Math.max(origin.bottom, endPoint.bottom); 2625 2626 var width = right - left; 2627 var height = bottom - top; 2628 2629 var migrationPanel = Firebug.chrome.$("fbMigrator"); 2630 var panelWidth = Math.max(width, 150); 2631 var panelHeight = Math.max(height, 150); 2632 migrationPanel.sizeTo(panelWidth, panelHeight); 2633 2634 // x, y are offsets from the upper left corner of the oldButton, that 2635 // is the reference point of the 'overlap' position of the popup 2636 // (Hint, think about all the x values then all the y values.) 2637 if (left == origin.left) 2638 { 2639 var x = 0; 2640 var x1 = origin.width; 2641 } 2642 else 2643 { 2644 var x = origin.width - width; 2645 var x1 = width - origin.width; 2646 } 2647 if (top == origin.top) 2648 { 2649 var y = 0; 2650 var y1 = origin.height; 2651 } 2652 else 2653 { 2654 var y = origin.height - origin; 2655 var y1 = height - origin.height; 2656 } 2657 2658 if (left == endPoint.left) 2659 var x2 = endPoint.width; 2660 else 2661 var x2 = width - endPoint.width; 2662 2663 if (top == endPoint.top) 2664 var y2 = endPoint.height; 2665 else 2666 var y2 = height - endPoint.height; 2667 2668 migrationPanel.openPopup(oldButton, 'overlap', x, y, false, true); 2669 2670 Firebug.Migrator.drawMigrationLine(x1, y1, x2, y2); 2671 2672 Firebug.Migrator.removeButtonOnOk(oldButton, migrationPanel); 2673 } 2674 oldButton.addEventListener('mouseover', showMigration, false); 2675 }, 2676 2677 drawMigrationLine: function(x1, y1, x2, y2) 2678 { 2679 var migrationFrame = Firebug.chrome.$('fbMigrationFrame'); 2680 2681 var line = migrationFrame.contentDocument.getElementById("migrationPath"); 2682 line.setAttribute("x1", x1); 2683 line.setAttribute("x2", x1); 2684 line.setAttribute("y1", y1); 2685 line.setAttribute("y2", y1); 2686 2687 var progress = 0; 2688 var steps = 100; 2689 var stepStep = 1; 2690 var xStep = (x2 - x1)/steps; 2691 var yStep = (y2 - y1)/steps; 2692 var xCur = x1; 2693 var yCur = y1; 2694 Firebug.Migrator.animate = setInterval(function growLine() 2695 { 2696 xCur += stepStep*xStep; 2697 yCur += stepStep*yStep; 2698 steps -= stepStep; 2699 if (steps > 50) 2700 stepStep++; 2701 else 2702 stepStep--; 2703 //FBTrace.sysout("animate steps "+steps+" stepStep "+stepStep+" x "+xCur+" y "+yCur); 2704 line.setAttribute("x2", xCur); 2705 line.setAttribute("y2", yCur); 2706 if (steps < 0) 2707 clearInterval(animate); 2708 }, 50); 2709 }, 2710 2711 removeButtonOnOk: function(oldButton, migrationPanel) 2712 { 2713 var migrationOk = Firebug.chrome.$('fbMigrationOk'); 2714 migrationOk.addEventListener('click', function migrationComplete(event) 2715 { 2716 // xxxHonza, XXXjjb: I have seen an exception saying that oldButton.parentNode is null. 2717 oldButton.parentNode.removeChild(oldButton); 2718 Firebug.Migrator.setMigrated(oldButton); 2719 clearInterval(Firebug.Migrator.animate); 2720 migrationPanel.hidePopup(); 2721 migrationOk.removeEventListener('click', migrationComplete, true); 2722 }, true); 2723 }, 2724 2725 getMigrated: function(elt) 2726 { 2727 var id = elt.getAttribute('id'); 2728 return Options.get("migrated_"+id); 2729 }, 2730 2731 setMigrated: function(elt) 2732 { 2733 var id = elt.getAttribute('id'); 2734 Options.set( "migrated_"+id, true, typeof(true)); 2735 }, 2736 2737 } 2738 2739 // ********************************************************************************************* // 2740 2741 /** 2742 * If we are detached and the main Firefox window closes, also close the matching Firebug window. 2743 */ 2744 function shutdownFirebug() 2745 { 2746 try 2747 { 2748 if (Firebug.isDetached()) 2749 Firebug.chrome.close(); 2750 } 2751 catch (exc) 2752 { 2753 window.dump("shutdownFirebug FAILS: "+exc+"\n"); 2754 } 2755 2756 Firebug.shutdown(); 2757 } 2758 2759 if (preFirebugKeys) 2760 { 2761 // Add back the preLoad properties 2762 preFirebugKeys.forEach(function copyProps(key) 2763 { 2764 Firebug[key] = PreFirebug[key]; 2765 }); 2766 } 2767 2768 // ********************************************************************************************* // 2769 // Registration 2770 2771 Firebug.Firefox = Firefox; 2772 Firebug.Domplate = Domplate; 2773 Firebug.ChromeFactory = ChromeFactory; 2774 2775 return Firebug; 2776 2777 // ********************************************************************************************* // 2778 }); 2779