1 /* See license.txt for terms of usage */ 2 3 define([ 4 "firebug/lib/trace", 5 "firebug/lib/options", 6 "firebug/lib/locale", 7 "firebug/lib/array", 8 "firebug/firefox/browserOverlayLib", 9 "firebug/firefox/browserCommands", 10 "firebug/firefox/browserMenu", 11 "firebug/firefox/browserToolbar", 12 ], 13 function(FBTrace, Options, Locale, Arr, BrowserOverlayLib, BrowserCommands, BrowserMenu, 14 BrowserToolbar) { 15 16 with (BrowserOverlayLib) { 17 18 // ********************************************************************************************* // 19 // Constants 20 21 var Cc = Components.classes; 22 var Ci = Components.interfaces; 23 var Cu = Components.utils; 24 25 Locale.registerStringBundle("chrome://firebug/locale/firebug.properties"); 26 Locale.registerStringBundle("chrome://firebug/locale/cookies.properties"); 27 28 Cu.import("resource://firebug/loader.js"); 29 Cu.import("resource://firebug/fbtrace.js"); 30 31 const firstRunPage = "https://getfirebug.com/firstrun#Firebug "; 32 33 // ********************************************************************************************* // 34 // BrowserOverlay Implementation 35 36 function BrowserOverlay(win) 37 { 38 this.win = win; 39 this.doc = win.document; 40 } 41 42 BrowserOverlay.prototype = 43 { 44 // When Firebug is disabled or unistalled this elements must be removed from 45 // chrome UI (XUL). 46 nodesToRemove: [], 47 48 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 49 // Initialization 50 51 initialize: function(reason) 52 { 53 // Expose BrowserOverlayLib object to extensions. 54 this.win.Firebug.BrowserOverlayLib = BrowserOverlayLib; 55 56 // This element (a broadcaster) is storing Firebug state information. Other elements 57 // (like for example the Firebug start button) can watch it and display the info to 58 // the user. 59 $el(this.doc, "broadcaster", {id: "firebugStatus", suspended: true}, 60 $(this.doc, "mainBroadcasterSet")); 61 62 var node = $stylesheet(this.doc, "chrome://firebug/content/firefox/browserOverlay.css"); 63 this.nodesToRemove.push(node); 64 65 this.loadContextMenuOverlay(); 66 this.loadFirstRunPage(reason); 67 68 var version = this.getVersion(); 69 70 BrowserCommands.overlay(this.doc); 71 BrowserMenu.overlay(this.doc); 72 BrowserToolbar.overlay(this.doc, version); 73 74 this.internationalize(); 75 this.allPagesActivation(); 76 }, 77 78 internationalize: function() 79 { 80 // Internationalize all elements with 'fbInternational' class. Clone 81 // before internationalizing. 82 var elements = Arr.cloneArray(this.doc.getElementsByClassName("fbInternational")); 83 Locale.internationalizeElements(this.doc, elements, ["label", "tooltiptext", "aria-label"]); 84 }, 85 86 allPagesActivation: function() 87 { 88 // Load Firebug by default if activation is on for all pages (see issue 5522) 89 if (Options.get("allPagesActivation") == "on" || !Options.get("delayLoad")) 90 { 91 var self = this; 92 this.startFirebug(function(Firebug) 93 { 94 var browser = Firebug.Firefox.getBrowserForWindow(self.win); 95 var uri = Firebug.Firefox.getCurrentURI(); 96 97 // Open Firebug UI (e.g. if the annotations say so, issue 5623) 98 if (uri && Firebug.TabWatcher.shouldCreateContext(browser, uri.spec, null)) 99 Firebug.toggleBar(true); 100 101 FBTrace.sysout("Firebug loaded by default since 'allPagesActivation' is on " + 102 "or 'delayLoad' is false"); 103 }); 104 } 105 }, 106 107 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 108 // Load Rest of Firebug 109 110 /** 111 * This method is called by the Fremework to load entire Firebug. It's executed when 112 * the user requires Firebug for the first time. 113 * 114 * @param {Object} callback Executed when Firebug is fully loaded 115 */ 116 startFirebug: function(callback) 117 { 118 if (this.win.Firebug.waitingForFirstLoad) 119 return; 120 121 if (this.win.Firebug.isInitialized) 122 return callback && callback(this.win.Firebug); 123 124 if (FBTrace.DBG_INITIALIZE) 125 FBTrace.sysout("overlay; Load Firebug...", (callback ? callback.toString() : "")); 126 127 this.win.Firebug.waitingForFirstLoad = true; 128 129 var container = $(this.doc, "appcontent"); 130 131 // List of Firebug scripts that must be loaded into the global scope (browser.xul) 132 // FBTrace is no longer loaded into the global space. 133 var scriptSources = [ 134 "chrome://firebug/content/legacy.js", 135 "chrome://firebug/content/moduleConfig.js" 136 ] 137 138 // Create script elements. 139 var self = this; 140 scriptSources.forEach(function(url) 141 { 142 $script(self.doc, url); 143 }); 144 145 // Create Firebug splitter element. 146 $el(this.doc, "splitter", {id: "fbContentSplitter", collapsed: "true"}, container); 147 148 // Create Firebug main frame and container. 149 $el(this.doc, "vbox", {id: "fbMainFrame", collapsed: "true", persist: "height,width"}, [ 150 $el(this.doc, "browser", { 151 id: "fbMainContainer", 152 flex: "2", 153 src: "chrome://firebug/content/firefox/firebugFrame.xul", 154 disablehistory: "true" 155 }) 156 ], container); 157 158 // When Firebug is fully loaded and initialized it fires a "FirebugLoaded" 159 // event to the browser document (browser.xul scope). Wait for that to happen. 160 this.doc.addEventListener("FirebugLoaded", function onLoad() 161 { 162 self.doc.removeEventListener("FirebugLoaded", onLoad, false); 163 self.win.Firebug.waitingForFirstLoad = false; 164 165 // xxxHonza: TODO find a better place for notifying extensions 166 FirebugLoader.dispatchToScopes("firebugFrameLoad", [self.win.Firebug]); 167 callback && callback(self.win.Firebug); 168 }, false); 169 }, 170 171 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 172 // Firebug Menu Handlers 173 174 onOptionsShowing: function(popup) 175 { 176 for (var child = popup.firstChild; child; child = child.nextSibling) 177 { 178 if (child.localName == "menuitem") 179 { 180 var option = child.getAttribute("option"); 181 if (option) 182 { 183 var checked = Options.get(option); 184 185 // xxxHonza: I belive that allPagesActivation could be simple boolean option. 186 if (option == "allPagesActivation") 187 checked = (checked == "on") ? true : false; 188 189 child.setAttribute("checked", checked); 190 } 191 } 192 } 193 }, 194 195 onToggleOption: function(menuItem) 196 { 197 var option = menuItem.getAttribute("option"); 198 var checked = menuItem.getAttribute("checked") == "true"; 199 200 Options.set(option, checked); 201 }, 202 203 onMenuShowing: function(popup, event) 204 { 205 // If the event comes from a sub menu, just ignore it. 206 if (popup != event.target) 207 return; 208 209 while (popup.lastChild) 210 popup.removeChild(popup.lastChild); 211 212 // Generate dynamic content. 213 for (var i=0; i<BrowserMenu.firebugMenuContent.length; i++) 214 popup.appendChild(BrowserMenu.firebugMenuContent[i].cloneNode(true)); 215 216 var collapsed = "true"; 217 if (this.win.Firebug.chrome) 218 { 219 var fbContentBox = this.win.Firebug.chrome.$("fbContentBox"); 220 collapsed = fbContentBox.getAttribute("collapsed"); 221 } 222 223 var currPos = Options.get("framePosition"); 224 var placement = this.win.Firebug.getPlacement ? this.win.Firebug.getPlacement() : ""; 225 226 // Switch between "Open Firebug" and "Hide Firebug" label in the popup menu. 227 var toggleFirebug = popup.querySelector("#menu_firebug_toggleFirebug"); 228 if (toggleFirebug) 229 { 230 var hiddenUI = (collapsed == "true" || placement == "minimized"); 231 toggleFirebug.setAttribute("label", (hiddenUI ? 232 Locale.$STR("firebug.ShowFirebug") : Locale.$STR("firebug.HideFirebug"))); 233 234 toggleFirebug.setAttribute("tooltiptext", (hiddenUI ? 235 Locale.$STR("firebug.menu.tip.Open_Firebug") : 236 Locale.$STR("firebug.menu.tip.Minimize_Firebug"))); 237 238 var currentLocation = toggleFirebug.ownerDocument.defaultView.top.location.href; 239 var inDetachedWindow = currentLocation.indexOf("firebug.xul") > 0; 240 241 // If Firebug is detached, use "Focus Firebug Window" label 242 // instead of "Hide Firebug" when the menu isn't opened from 243 // within the detached Firebug window. the 'placement' is used 244 // to ensure Firebug isn't closed with close button of detached window 245 // and 'inDetachedWindow' variable is also used to ensure the menu is 246 // opened from within the detached window. 247 if (currPos == "detached" && this.win.Firebug.currentContext && 248 placement != "minimized" && !inDetachedWindow) 249 { 250 toggleFirebug.setAttribute("label", Locale.$STR("firebug.FocusFirebug")); 251 toggleFirebug.setAttribute("tooltiptext", 252 Locale.$STR("firebug.menu.tip.Focus_Firebug")); 253 } 254 } 255 256 // Hide "Deactivate Firebug" menu if Firebug is not active. 257 var closeFirebug = popup.querySelector("#menu_firebug_closeFirebug"); 258 if (closeFirebug) 259 { 260 closeFirebug.setAttribute("collapsed", 261 (this.win.Firebug.currentContext ? "false" : "true")); 262 } 263 264 // Update About Menu 265 var version = this.getVersion(); 266 if (version) 267 { 268 var node = popup.getElementsByClassName("firebugAbout")[0]; 269 var aboutLabel = node.getAttribute("label"); 270 node.setAttribute("label", aboutLabel + " " + version); 271 node.classList.remove("firebugAbout"); 272 } 273 274 // Allow Firebug menu customization (see FBTest and FBTrace as an example). 275 var event = new this.win.CustomEvent("firebugMenuShowing", {detail: popup}); 276 this.doc.dispatchEvent(event); 277 }, 278 279 onMenuHiding: function(popup, event) 280 { 281 if (popup != event.target) 282 return; 283 284 // xxxHonza: I don't know why the timeout must be here, but if it isn't 285 // the icon menu is broken (see issue 5427) 286 this.win.setTimeout(function() 287 { 288 while (popup.lastChild) 289 popup.removeChild(popup.lastChild); 290 }); 291 }, 292 293 onPositionPopupShowing: function(popup) 294 { 295 while (popup.lastChild) 296 popup.removeChild(popup.lastChild); 297 298 // Load Firebug before the position is changed. 299 var oncommand = "Firebug.browserOverlay.startFirebug(function(){" + 300 "Firebug.chrome.setPosition('%pos%')" + "})"; 301 302 var items = []; 303 var currPos = Options.get("framePosition"); 304 for each (var pos in ["detached", "top", "bottom", "left", "right"]) 305 { 306 var label = pos.charAt(0).toUpperCase() + pos.slice(1); 307 var item = $menuitem(this.doc, { 308 label: Locale.$STR("firebug.menu." + label), 309 tooltiptext: Locale.$STR("firebug.menu.tip." + label), 310 type: "radio", 311 oncommand: oncommand.replace("%pos%", pos), 312 checked: (currPos == pos) 313 }); 314 315 if (pos == "detached") 316 items.key = "key_firebug_detachFirebug"; 317 318 popup.appendChild(item); 319 } 320 321 return true; 322 }, 323 324 openAboutDialog: function() 325 { 326 var self = this; 327 328 // Firefox 4.0+ 329 Cu["import"]("resource://gre/modules/AddonManager.jsm"); 330 this.win.AddonManager.getAddonByID("firebug@software.joehewitt.com", function(addon) 331 { 332 self.win.openDialog("chrome://mozapps/content/extensions/about.xul", "", 333 "chrome,centerscreen,modal", addon); 334 }); 335 }, 336 337 setPosition: function(newPosition) 338 { 339 // todo 340 }, 341 342 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 343 // Firebug Version 344 345 getVersion: function() 346 { 347 var versionURL = "chrome://firebug/content/branch.properties"; 348 var ioService = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); 349 350 var channel = ioService.newChannel(versionURL, null, null); 351 var input = channel.open(); 352 var sis = Cc["@mozilla.org/scriptableinputstream;1"]. 353 createInstance(Ci.nsIScriptableInputStream); 354 sis.init(input); 355 356 var content = sis.readBytes(input.available()); 357 sis.close(); 358 359 var m = /RELEASE=(.*)/.exec(content); 360 if (m) 361 var release = m[1]; 362 else 363 return "no RELEASE in " + versionURL; 364 365 m = /VERSION=(.*)/.exec(content); 366 if (m) 367 var version = m[1]; 368 else 369 return "no VERSION in " + versionURL; 370 371 return version+""+release; 372 }, 373 374 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 375 // External Editors 376 377 onEditorsShowing: function(popup) 378 { 379 var self = this; 380 this.startFirebug(function() 381 { 382 self.win.Firebug.ExternalEditors.onEditorsShowing(popup); 383 }); 384 385 return true; 386 }, 387 388 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 389 // Page Context Menu Overlay 390 391 loadContextMenuOverlay: function() 392 { 393 var contextMenu = this.win.nsContextMenu; 394 if (typeof(contextMenu) == "undefined") 395 return; 396 397 // isTargetAFormControl is removed, see: 398 // https://bugzilla.mozilla.org/show_bug.cgi?id=433168 399 if (typeof(contextMenu.prototype.isTargetAFormControl) != "undefined") 400 { 401 // https://bugzilla.mozilla.org/show_bug.cgi?id=433168 402 var setTargetOriginal = this.setTargetOriginal = contextMenu.prototype.setTarget; 403 contextMenu.prototype.setTarget = function(aNode, aRangeParent, aRangeOffset) 404 { 405 setTargetOriginal.apply(this, arguments); 406 407 if (this.isTargetAFormControl(aNode)) 408 this.shouldDisplay = true; 409 }; 410 } 411 412 // Hide built-in inspector if the pref says so. 413 var initItemsOriginal = this.initItemsOriginal = contextMenu.prototype.initItems; 414 contextMenu.prototype.initItems = function() 415 { 416 initItemsOriginal.apply(this, arguments); 417 418 // Hide built-in inspector menu item if the pref "extensions.firebug.hideDefaultInspector" 419 // says so. Note that there is also built-in preference "devtools.inspector.enable" that 420 // can be used for the same purpose. 421 var hideInspect = Options.get("hideDefaultInspector"); 422 if (hideInspect) 423 { 424 this.showItem("inspect-separator", false); 425 this.showItem("context-inspect", false); 426 } 427 } 428 }, 429 430 unloadContextMenuOverlay: function() 431 { 432 var contextMenu = this.win.nsContextMenu; 433 if (typeof(contextMenu) == "undefined") 434 return; 435 436 contextMenu.prototype.setTarget = this.setTargetOriginal; 437 contextMenu.prototype.initItems = this.initItemsOriginal; 438 }, 439 440 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 441 // First Run Page 442 443 loadFirstRunPage: function(reason) 444 { 445 if (this.checkFirebugVersion(Options.get("currentVersion")) <= 0) 446 return; 447 448 // Do not show the first run page when Firebug is being updated. It'll be displayed 449 // the next time the browser is restarted 450 // # ADDON_UPGRADE == 7 451 if (reason == 7) 452 return; 453 454 // Open the page in the top most window, so the user can see it immediately. 455 var wm = Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator); 456 if (wm.getMostRecentWindow("navigator:browser") == this.win.top) 457 { 458 // Update the preference to make sure the page is not displayed again. 459 // To avoid being annoying when Firefox crashes, forcibly save it, too. 460 var version = this.getVersion(); 461 Options.set("currentVersion", version); 462 463 if (Options.get("showFirstRunPage")) 464 { 465 var self = this; 466 var timeout = this.win.setTimeout(function() 467 { 468 if (self.win.closed) 469 return; 470 471 self.openFirstRunPage(self.win); 472 }, 1000); 473 474 this.win.addEventListener("unload", function() 475 { 476 clearTimeout(timeout); 477 }, false); 478 } 479 } 480 }, 481 482 openFirstRunPage: function(win) 483 { 484 var version = this.getVersion(); 485 var url = firstRunPage + version; 486 487 var browser = win.gBrowser; 488 if (!browser) 489 { 490 FBTrace.sysout("browserOverlay.openFirstRunPage; ERROR there is no gBrowser!"); 491 return; 492 } 493 494 // Open the firstRunPage in background 495 /*gBrowser.selectedTab = */browser.addTab(url, null, null, null); 496 497 // Make sure prefs are stored, otherwise the firstRunPage would be displayed 498 // again if Firefox crashes. 499 this.win.setTimeout(function() 500 { 501 Options.forceSave(); 502 }, 400); 503 }, 504 505 checkFirebugVersion: function(currentVersion) 506 { 507 if (!currentVersion) 508 return 1; 509 510 var version = this.getVersion(); 511 512 // Use Firefox comparator service. 513 var versionChecker = Cc["@mozilla.org/xpcom/version-comparator;1"]. 514 getService(Ci.nsIVersionComparator); 515 516 return versionChecker.compare(version, currentVersion); 517 } 518 } 519 520 // ********************************************************************************************* // 521 // Registration 522 523 return BrowserOverlay; 524 525 // ********************************************************************************************* // 526 }}); 527