1 /* See license.txt for terms of usage */ 2 3 define([ 4 "firebug/lib/object", 5 "firebug/firebug", 6 "firebug/chrome/firefox", 7 "firebug/chrome/reps", 8 "firebug/lib/locale", 9 "firebug/lib/events", 10 "firebug/lib/wrapper", 11 "firebug/lib/array", 12 "firebug/lib/css", 13 "firebug/lib/dom", 14 "firebug/lib/xml", 15 "firebug/chrome/window", 16 "firebug/lib/system", 17 "firebug/html/highlighterCache" 18 ], 19 function(Obj, Firebug, Firefox, FirebugReps, Locale, Events, Wrapper, Arr, Css, Dom, Xml, 20 Win, System, HighlighterCache) { 21 22 // ********************************************************************************************* // 23 // Constants 24 25 const inspectDelay = 200; 26 const highlightCssUrl = "chrome://firebug/content/html/highlighter.css"; 27 const ident = HighlighterCache.ident; 28 const Cu = Components.utils; 29 30 // ********************************************************************************************* // 31 // Globals 32 33 var boxModelHighlighter = null; 34 var frameHighlighter = null; 35 36 // ********************************************************************************************* // 37 38 /** 39 * @module Implements Firebug Inspector logic. 40 */ 41 Firebug.Inspector = Obj.extend(Firebug.Module, 42 { 43 dispatchName: "inspector", 44 inspecting: false, 45 inspectingPanel: null, 46 47 /** 48 * Main highlighter method. Can be used to highlight elements using the box model, 49 * frame or image map highlighters. Can highlight single or multiple elements. 50 * 51 * Examples: 52 * Firebug.Inspector.highlightObject([window.content.document.getElementById("gbar"), 53 * window.content.document.getElementById("logo")], 54 * window.content, "frame", null, 55 * ["#ff0000",{background:"#0000ff", border:"#ff0000"}]) 56 * or 57 * Firebug.Inspector.highlightObject([window.content.document.getElementById("gbar"), 58 * window.content.document.getElementById("logo")], window.content, "boxModel", null, 59 * [{content: "#ff0000", padding: "#eeeeee", border: "#00ff00", margin: "#0000ff"}, 60 * {content: "#00ff00", padding: "#eeeeee", border: "#00ff00", margin: "#0000ff"}]) 61 * 62 * @param {Array} elementArr Elements to highlight 63 * @param {Window} context Context of the elements to be highlighted 64 * @param {String} [highlightType] Either "frame" or "boxModel". Default is configurable. 65 * @param {String} [boxFrame] Displays the line guides for the box model layout view. 66 * Valid values are: "content", "padding", "border" or "margin" 67 * @param {String | Array} [colorObj] Any valid html color e.g. red, #f00, #ff0000, etc., 68 * a valid color object or any valid highlighter color array. 69 */ 70 highlightObject: function(elementArr, context, highlightType, boxFrame, colorObj) 71 { 72 var i, elt, elementLen, oldContext, usingColorArray; 73 var highlighter = highlightType ? getHighlighter(highlightType) : this.defaultHighlighter; 74 75 if (!elementArr || !Arr.isArrayLike(elementArr)) 76 { 77 // highlight a single element 78 if (!elementArr || !Dom.isElement(elementArr) || 79 (Wrapper.getContentView(elementArr) && 80 !Xml.isVisible(Wrapper.getContentView(elementArr)))) 81 { 82 if (elementArr && Dom.isRange(elementArr)) 83 elementArr = elementArr; 84 else if (elementArr && elementArr.nodeType == Node.TEXT_NODE) 85 elementArr = elementArr.parentNode; 86 else 87 elementArr = null; 88 } 89 90 if (elementArr && context && context.highlightTimeout) 91 { 92 context.clearTimeout(context.highlightTimeout); 93 delete context.highlightTimeout; 94 } 95 96 oldContext = this.highlightedContext; 97 if (oldContext && oldContext.window) 98 this.clearAllHighlights(); 99 100 // Stop multi element highlighting 101 if (!elementArr) 102 this.repaint.element = null; 103 104 this.highlighter = highlighter; 105 this.highlightedContext = context; 106 107 if (elementArr) 108 { 109 if (elementArr.nodeName && !isVisibleElement(elementArr)) 110 highlighter.unhighlight(context); 111 else if (context && context.window && context.window.document) 112 highlighter.highlight(context, elementArr, boxFrame, colorObj, false); 113 } 114 else if (oldContext) 115 { 116 oldContext.highlightTimeout = oldContext.setTimeout(function() 117 { 118 if (FBTrace.DBG_INSPECT) 119 FBTrace.sysout("Removing inspector highlighter due to setTimeout loop"); 120 121 if (!oldContext.highlightTimeout) 122 return; 123 124 delete oldContext.highlightTimeout; 125 126 if (oldContext.window && oldContext.window.document) 127 { 128 highlighter.unhighlight(oldContext); 129 } 130 }, inspectDelay); 131 } 132 } 133 else 134 { 135 // Highlight multiple elements 136 if (context && context.highlightTimeout) 137 { 138 context.clearTimeout(context.highlightTimeout); 139 delete context.highlightTimeout; 140 } 141 142 this.clearAllHighlights(); 143 usingColorArray = Arr.isArray(colorObj); 144 145 if (context && context.window && context.window.document) 146 { 147 for (i=0, elementLen=elementArr.length; i<elementLen; i++) 148 { 149 elt = elementArr[i]; 150 151 if (elt && elt instanceof HTMLElement) 152 { 153 if (elt.nodeType == Node.TEXT_NODE) 154 elt = elt.parentNode; 155 156 var obj = usingColorArray ? colorObj[i] : colorObj; 157 highlighter.highlight(context, elt, null, obj, true); 158 } 159 } 160 } 161 162 storeHighlighterParams(null, context, elementArr, null, colorObj, highlightType, true); 163 } 164 }, 165 166 /** 167 * Clear all highlighted areas on a page. 168 */ 169 clearAllHighlights: function() 170 { 171 HighlighterCache.clear(); 172 }, 173 174 /** 175 * Toggle inspecting on / off 176 * @param {Window} [context] The window to begin inspecting in, necessary to toggle inspecting on. 177 */ 178 toggleInspecting: function(context) 179 { 180 if (this.inspecting) 181 this.stopInspecting(true); 182 else 183 this.startInspecting(context); 184 }, 185 186 /** 187 * Check if the new panel has the inspectable property set. If so set it as the new inspectingPanel. 188 */ 189 onPanelChanged: function() 190 { 191 if (this.inspecting) 192 { 193 var panelBar1 = Firebug.chrome.$("fbPanelBar1"); 194 var panel = panelBar1.selectedPanel; 195 196 if (panel && panel.inspectable) 197 { 198 this.inspectNode(null); 199 this.inspectingPanel = panel; 200 } 201 } 202 }, 203 204 /** 205 * Turn inspecting on. 206 * @param {Window} context The main browser window 207 */ 208 startInspecting: function(context) 209 { 210 if (this.inspecting || !context || !context.loaded) 211 return; 212 213 this.clearAllHighlights(); 214 215 this.inspecting = true; 216 this.inspectingContext = context; 217 218 Firebug.chrome.setGlobalAttribute("cmd_firebug_toggleInspecting", "checked", "true"); 219 this.attachInspectListeners(context); 220 221 var inspectingPanelName = this._resolveInspectingPanelName(context); 222 this.inspectingPanel = Firebug.chrome.switchToPanel(context, inspectingPanelName); 223 224 if (Firebug.isDetached()) 225 context.window.focus(); 226 else if (Firebug.isMinimized()) 227 Firebug.showBar(true); 228 229 this.inspectingPanel.panelNode.focus(); 230 this.inspectingPanel.startInspecting(); 231 232 Events.dispatch(this.fbListeners, "onStartInspecting", [context]); 233 234 if (context.stopped) 235 Firebug.Debugger.thaw(context); 236 237 var hoverNodes = context.window.document.querySelectorAll(":hover"); 238 239 if (hoverNodes.length != 0) 240 this.inspectNode(hoverNodes[hoverNodes.length-1]); 241 }, 242 243 /** 244 * Highlight a node using the frame highlighter. Can only be used after inspecting has already started. 245 * @param {Element} node The element to inspect 246 */ 247 inspectNode: function(node) 248 { 249 if (node && node.nodeType != Node.ELEMENT_NODE) 250 node = node.parentNode; 251 252 if (node && Firebug.shouldIgnore(node) && !node.fbProxyFor) 253 return; 254 255 var context = this.inspectingContext; 256 257 if (this.inspectTimeout) 258 { 259 context.clearTimeout(this.inspectTimeout); 260 delete this.inspectTimeout; 261 } 262 263 if (node && node.fbProxyFor) 264 node = node.fbProxyFor; 265 266 var inspectingPanel = this.inspectingPanel; 267 268 // Some panels may want to only allow inspection of panel-supported objects 269 node = inspectingPanel ? inspectingPanel.getInspectNode(node) : node; 270 271 var highlightColor = inspectingPanel ? inspectingPanel.inspectHighlightColor : ""; 272 this.highlightObject(node, context, "frame", undefined, highlightColor); 273 274 this.inspectingNode = node; 275 276 if (node) 277 { 278 var _this = this; 279 280 this.inspectTimeout = context.setTimeout(function() 281 { 282 var selection = inspectingPanel ? inspectingPanel.inspectNode(node) : null; 283 Events.dispatch(_this.fbListeners, "onInspectNode", [context, node]); 284 if (selection) 285 inspectingPanel.select(node); 286 }, inspectDelay); 287 } 288 }, 289 290 /** 291 * Stop inspecting and clear all highlights. 292 * @param {Boolean} canceled Indicates whether inspect was canceled (usually via the escape key) 293 * @param {Boolean} [waitForClick] Indicates whether the next click will still forward you 294 * to the clicked element in the HTML panel. 295 */ 296 stopInspecting: function(canceled, waitForClick) 297 { 298 if (!this.inspecting) 299 return; 300 301 var context = this.inspectingContext; 302 303 if (context.stopped) 304 Firebug.Debugger.freeze(context); 305 306 if (this.inspectTimeout) 307 { 308 context.clearTimeout(this.inspectTimeout); 309 delete this.inspectTimeout; 310 } 311 312 this.detachInspectListeners(context); 313 if (!waitForClick) 314 this.detachClickInspectListeners(context.window); 315 316 Firebug.chrome.setGlobalAttribute("cmd_firebug_toggleInspecting", "checked", "false"); 317 318 this.inspecting = false; 319 320 if (this.inspectingPanel) 321 { 322 Firebug.chrome.unswitchToPanel(context, this.inspectingPanel.name, canceled); 323 this.inspectingPanel.stopInspecting(this.inspectingNode, canceled); 324 } 325 else 326 { 327 FBTrace.sysout("inspector.stopInspecting; ERROR? inspectingPanel is NULL"); 328 } 329 330 Events.dispatch(this.fbListeners, "onStopInspecting", [context, this.inspectingNode, canceled]); 331 332 this.inspectNode(null); 333 334 // Make sure there are no (indirect) references to the page document. 335 this.inspectingPanel = null; 336 this.inspectingContext = null; 337 338 if (Firebug.isDetached()) 339 window.focus(); 340 }, 341 342 /** 343 * Get the name of the inspectable panel. 344 * @param {Window} context Context of the panel 345 */ 346 _resolveInspectingPanelName: function(context) 347 { 348 var requestingPanel = context && context.getPanel(context.panelName); 349 350 return (requestingPanel && requestingPanel.inspectable) ? requestingPanel.name : "html"; 351 }, 352 353 /** 354 * Inspect from context menu. 355 * @param {Element} elt The element to inspect 356 */ 357 inspectFromContextMenu: function(elt) 358 { 359 var panel; 360 var inspectingPanelName = "html"; 361 362 Firebug.toggleBar(true, inspectingPanelName); 363 Firebug.chrome.select(elt, inspectingPanelName); 364 panel = Firebug.chrome.selectPanel(inspectingPanelName); 365 panel.panelNode.focus(); 366 }, 367 368 /** 369 * Navigate up and down through the DOM and highlight the result. This method is used by 370 * the key handlers for the up and down arrow keys. 371 * 372 * @param {String} dir Direction to navigate the Dom, either "up" or "down" 373 */ 374 inspectNodeBy: function(dir) 375 { 376 var target; 377 var node = this.inspectingNode; 378 379 if (dir == "up") 380 { 381 target = Firebug.chrome.getNextObject(); 382 } 383 else if (dir == "down") 384 { 385 target = Firebug.chrome.getNextObject(true); 386 if (node && !target) 387 { 388 target = node.contentDocument ? 389 node.contentDocument.documentElement : Dom.getNextElement(node.firstChild); 390 } 391 } 392 393 if (target && Dom.isElement(target)) 394 this.inspectNode(target); 395 else 396 System.beep(); 397 }, 398 399 /** 400 * Repaint the highlighter. Called from the window scroll and resize handlers. 401 */ 402 repaint: function() 403 { 404 var rp = this.repaint; 405 var highlighter = rp.highlighter; 406 var context = rp.context; 407 var element = rp.element; 408 var boxFrame = rp.boxFrame; 409 var colorObj = rp.colorObj; 410 var highlightType = rp.highlightType; 411 var isMulti = rp.isMulti; 412 413 if (!context || (!highlighter && !isMulti)) 414 return; 415 416 if (isMulti && element) 417 { 418 this.highlightObject(element, context, highlightType, null, colorObj); 419 } 420 else if (!isMulti) 421 { 422 var highlighterNode = HighlighterCache.get(highlighter.ident); 423 424 if (highlighterNode && highlighter.ident === ident.boxModel) 425 highlighterNode = highlighterNode.offset; 426 427 if (highlighterNode && highlighterNode.parentNode) 428 { 429 this.clearAllHighlights(); 430 highlighter.highlight(context, element, boxFrame, colorObj, isMulti); 431 } 432 } 433 }, 434 435 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 436 437 /** 438 * Attach the scroll and resize handlers to elt's window. Called from every highlight call. 439 * @param {Element} elt Passed in order to reliably obtain context 440 */ 441 attachRepaintInspectListeners: function(context, elt) 442 { 443 if (!elt || !elt.ownerDocument || !elt.ownerDocument.defaultView) 444 return; 445 446 var win = elt.ownerDocument.defaultView; 447 448 if (FBTrace.DBG_INSPECT) 449 FBTrace.sysout("inspector.attachRepaintInspectListeners to " + win.location.href, elt); 450 451 // there is no way to check if the listeners have already been added and we should 452 // avoid adding properties to the users page. 453 // Adding them again will do no harm so lets just do that. 454 455 // xxxHonza: I think that adding them twice could actually do harm, 456 // so make sure they are removed before. 457 context.removeEventListener(win.document, "resize", this.onInspectingResizeWindow, true); 458 context.removeEventListener(win.document, "scroll", this.onInspectingScroll, true); 459 460 // Register again. 461 context.addEventListener(win.document, "resize", this.onInspectingResizeWindow, true); 462 context.addEventListener(win.document, "scroll", this.onInspectingScroll, true); 463 }, 464 465 /** 466 * Attach key and mouse events to windows recursively. 467 * @param {Window} context Context of the main browser window 468 */ 469 attachInspectListeners: function(context) 470 { 471 var win = context.window; 472 if (!win || !win.document) 473 return; 474 475 if (FBTrace.DBG_INSPECT) 476 FBTrace.sysout("inspector.attachInspectListeners to all subWindows of " + win.location); 477 478 var chrome = Firebug.chrome; 479 480 this.keyListeners = 481 [ 482 chrome.keyCodeListen("RETURN", null, Obj.bindFixed(this.stopInspecting, this)), 483 chrome.keyCodeListen("ESCAPE", null, Obj.bindFixed(this.stopInspecting, this, true)), 484 chrome.keyCodeListen("UP", Events.isControl, Obj.bindFixed(this.inspectNodeBy, this, 485 "up"), true), 486 chrome.keyCodeListen("DOWN", Events.isControl, Obj.bindFixed(this.inspectNodeBy, this, 487 "down"), true), 488 ]; 489 490 Win.iterateWindows(win, Obj.bind(function(subWin) 491 { 492 if (FBTrace.DBG_INSPECT) 493 FBTrace.sysout("inspector.attacheInspectListeners to " + subWin.location + 494 " subWindow of " + win.location); 495 496 Events.addEventListener(subWin.document, "mouseover", this.onInspectingMouseOver, 497 true); 498 Events.addEventListener(subWin.document, "mousedown", this.onInspectingMouseDown, 499 true); 500 Events.addEventListener(subWin.document, "mouseup", this.onInspectingMouseUp, true); 501 Events.addEventListener(subWin.document, "click", this.onInspectingClick, true); 502 Events.addEventListener(subWin.document, "keypress", this.onInspectingKeyPress, true); 503 }, this)); 504 }, 505 506 /** 507 * Remove all event listeners except click listener from windows recursively. 508 * @param {Window} context Context of the main browser window 509 */ 510 detachInspectListeners: function(context) 511 { 512 var i, keyListenersLen; 513 var win = context.window; 514 515 if (!win || !win.document) 516 return; 517 518 var chrome = Firebug.chrome; 519 520 if (this.keyListeners) // XXXjjb for some reason this is null sometimes. 521 { 522 keyListenersLen = this.keyListeners.length; 523 for (i = 0; i < keyListenersLen; ++i) 524 chrome.keyIgnore(this.keyListeners[i]); 525 delete this.keyListeners; 526 } 527 528 Win.iterateWindows(win, Obj.bind(function(subWin) 529 { 530 Events.removeEventListener(subWin.document, "mouseover", this.onInspectingMouseOver, 531 true); 532 Events.removeEventListener(subWin.document, "mousedown", this.onInspectingMouseDown, 533 true); 534 Events.removeEventListener(subWin.document, "mouseup", this.onInspectingMouseUp, true); 535 Events.removeEventListener(subWin.document, "keypress", this.onInspectingKeyPress, 536 true); 537 }, this)); 538 }, 539 540 /** 541 * Remove the click listener independently from detachInspectListeners because if we remove 542 * it after mousedown, we won't be able to cancel clicked links. 543 * 544 * @param {Window} context Context of the main browser window 545 */ 546 detachClickInspectListeners: function(context) 547 { 548 Win.iterateWindows(context, Obj.bind(function(subWin) 549 { 550 Events.removeEventListener(subWin.document, "click", this.onInspectingClick, true); 551 }, this)); 552 }, 553 554 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 555 556 /** 557 * Repaint last highlight in the correct position on window resize. 558 * @param {Event} event Passed for tracing 559 */ 560 onInspectingResizeWindow: function(event) 561 { 562 if (FBTrace.DBG_INSPECT) 563 FBTrace.sysout("onInspectingResizeWindow event", event); 564 565 this.repaint(); 566 }, 567 568 /** 569 * Repaint last highlight in the correct position on scroll. 570 * @param {Event} event Passed for tracing 571 */ 572 onInspectingScroll: function(event) 573 { 574 if (FBTrace.DBG_INSPECT) 575 FBTrace.sysout("onInspectingScroll event", event); 576 577 this.repaint(); 578 }, 579 580 /** 581 * Call inspectNode(event.target) highlighting the element that was moused over. 582 * @param {Event} event Passed for tracing and to identify the target of inspection 583 */ 584 onInspectingMouseOver: function(event) 585 { 586 if (FBTrace.DBG_INSPECT) 587 FBTrace.sysout("onInspectingMouseOver event", event); 588 589 this.inspectNode(event.target); 590 }, 591 592 /** 593 * Trap mousedown events to prevent clicking a document from triggering a document's 594 * mousedown event when inspecting. 595 * 596 * @param {Event} event Used for tracing and canceling the event 597 */ 598 onInspectingMouseDown: function(event) 599 { 600 if (FBTrace.DBG_INSPECT) 601 { 602 FBTrace.sysout("onInspectingMouseDown event", {originalTarget: event.originalTarget, 603 tmpRealOriginalTarget:event.tmpRealOriginalTarget, event:event}); 604 } 605 606 // Allow to scroll the document while inspecting 607 if (event.originalTarget && event.originalTarget.tagName == "xul:thumb") 608 return; 609 610 Events.cancelEvent(event); 611 }, 612 613 /** 614 * Trap mouseup events to prevent clicking a document from triggering a document's mouseup 615 * event when inspecting. 616 * 617 * @param {Event} event Used for tracing and canceling the event 618 */ 619 onInspectingMouseUp: function(event) 620 { 621 if (FBTrace.DBG_INSPECT) 622 { 623 FBTrace.sysout("onInspectingMouseUp event", {originalTarget: event.originalTarget, 624 tmpRealOriginalTarget:event.tmpRealOriginalTarget,event:event}); 625 } 626 627 // Allow to release scrollbar while inspecting 628 if (event.originalTarget && event.originalTarget.tagName == "xul:thumb") 629 return; 630 631 this.stopInspecting(false, true); 632 633 Events.cancelEvent(event); 634 }, 635 636 /** 637 * Trap click events to prevent clicking a document from triggering a document's click event 638 * when inspecting and removes the click inspect listener. 639 * 640 * @param {Event} event Used for tracing and canceling the event 641 */ 642 onInspectingClick: function(event) 643 { 644 if (FBTrace.DBG_INSPECT) 645 FBTrace.sysout("onInspectingClick event", event); 646 647 var win = event.currentTarget.defaultView; 648 if (win) 649 { 650 win = Win.getRootWindow(win); 651 this.detachClickInspectListeners(win); 652 } 653 654 Events.cancelEvent(event); 655 }, 656 657 /** 658 * Trap keypress events to allow manipulation of the hovered elements 659 * 660 * @param {Event} event Used for canceling the event 661 */ 662 onInspectingKeyPress: function(event) 663 { 664 if (event.keyCode == KeyEvent.DOM_VK_DELETE) 665 { 666 Events.dispatch(this.fbListeners, "onBeginFirebugChange", [this.inspectingNode, this]); 667 this.inspectingNode.parentNode.removeChild(this.inspectingNode); 668 Events.dispatch(this.fbListeners, "onEndFirebugChange", [this.inspectingNode, this]); 669 Events.cancelEvent(event); 670 } 671 }, 672 673 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 674 // extends Module 675 676 /** 677 * Initialize the inspector 678 */ 679 initialize: function() 680 { 681 Firebug.Module.initialize.apply(this, arguments); 682 683 this.onInspectingResizeWindow = Obj.bind(this.onInspectingResizeWindow, this); 684 this.onInspectingScroll = Obj.bind(this.onInspectingScroll, this); 685 this.onInspectingMouseOver = Obj.bind(this.onInspectingMouseOver, this); 686 this.onInspectingMouseDown = Obj.bind(this.onInspectingMouseDown, this); 687 this.onInspectingMouseUp = Obj.bind(this.onInspectingMouseUp, this); 688 this.onInspectingClick = Obj.bind(this.onInspectingClick, this); 689 this.onInspectingKeyPress = Obj.bind(this.onInspectingKeyPress, this); 690 this.onPanelChanged = Obj.bind(this.onPanelChanged, this); 691 692 this.updateOption("shadeBoxModel", Firebug.shadeBoxModel); 693 this.updateOption("showQuickInfoBox", Firebug.showQuickInfoBox); 694 695 var panelBar1 = Firebug.chrome.$("fbPanelBar1"); 696 Events.addEventListener(panelBar1, "selectPanel", this.onPanelChanged, false); 697 698 if (FBTrace.DBG_INSPECT) 699 FBTrace.sysout("inspector.initialize;"); 700 }, 701 702 shutdown: function() 703 { 704 Firebug.Module.shutdown.apply(this, arguments); 705 706 var panelBar1 = Firebug.chrome.$("fbPanelBar1"); 707 Events.removeEventListener(panelBar1, "selectPanel", this.onPanelChanged, false); 708 }, 709 710 /** 711 * Stop inspecting and delete timers. 712 * @param {Window} context Context of the main window 713 */ 714 destroyContext: function(context) 715 { 716 if (context.highlightTimeout) 717 { 718 context.clearTimeout(context.highlightTimeout); 719 delete context.highlightTimeout; 720 } 721 722 if (this.inspecting) 723 this.stopInspecting(true); 724 }, 725 726 /** 727 */ 728 unwatchWindow: function(context, win) 729 { 730 try 731 { 732 this.hideQuickInfoBox(); 733 } 734 catch (ex) 735 { 736 // Get unfortunate errors here sometimes, so let's just ignore them since the 737 // window is going away anyhow 738 } 739 }, 740 741 /** 742 * Called when a FF tab is created or activated (user changes FF tab). We stop inspecting 743 * in this situation. 744 * 745 * @param {xul:browser} [browser] Browser 746 * @param {Window} [context] The main browser window 747 */ 748 showContext: function(browser, context) 749 { 750 if (this.inspecting) 751 this.stopInspecting(true); 752 }, 753 754 /** 755 * Called when a panel is shown. 756 * @param {xul:browser} [browser] Browser 757 * @param {Panel} [panel] Panel 758 */ 759 showPanel: function(browser, panel) 760 { 761 // Don't disable the cmd_toggleInspecting command. The related shortcut <key> must 762 // be available even if Firebug is not activated for the site. See 4452 763 // The panel can be null (if disabled) so use the global context. 764 // var context = Firebug.currentContext; 765 // var disabled = (context && context.loaded) ? false : true; 766 // Firebug.chrome.setGlobalAttribute("cmd_firebug_toggleInspecting", "disabled", disabled); 767 }, 768 769 /** 770 * Called after a context's page gets DOMContentLoaded. We enable inspection here. 771 * @param {Window} [context] Context of the main window 772 */ 773 loadedContext: function(context) 774 { 775 // See the comment in showPanel. 776 // Firebug.chrome.setGlobalAttribute("cmd_firebug_toggleInspecting", "disabled", "false"); 777 }, 778 779 /** 780 * Update the shadeBoxModel or showQuickInfoBox options 781 * @param {String} name Either "shadeBoxModel" or "showQuickInfoBox" 782 * @param {Boolean} value Enable or Disable the option 783 */ 784 updateOption: function(name, value) 785 { 786 if (name == "shadeBoxModel") 787 { 788 this.highlightObject(null); 789 this.defaultHighlighter = value ? getHighlighter("boxModel") : getHighlighter("frame"); 790 } 791 else if (name == "showQuickInfoBox") 792 { 793 if (quickInfoBox.boxEnabled && !value) 794 quickInfoBox.hide(); 795 796 quickInfoBox.boxEnabled = value; 797 } 798 }, 799 800 /** 801 * Gets stylesheet by Url. 802 * @param {Window} context the main browser window 803 * @param {String} url URL of the stylesheet 804 */ 805 getObjectByURL: function(context, url) 806 { 807 var styleSheet = Css.getStyleSheetByHref(url, context); 808 if (styleSheet) 809 return styleSheet; 810 }, 811 812 /** 813 * Toggle the quick info box. 814 */ 815 toggleQuickInfoBox: function() 816 { 817 var qiBox = Firebug.chrome.$("fbQuickInfoPanel"); 818 819 if (qiBox.state == "open") 820 quickInfoBox.hide(); 821 822 quickInfoBox.boxEnabled = !quickInfoBox.boxEnabled; 823 824 Firebug.Options.set("showQuickInfoBox", quickInfoBox.boxEnabled); 825 }, 826 827 /** 828 * Hide the quick info box. 829 */ 830 hideQuickInfoBox: function() 831 { 832 quickInfoBox.hide(); 833 834 this.inspectNode(null); 835 } 836 }); 837 838 // ********************************************************************************************* // 839 // Local Helpers 840 841 function getHighlighter(type) 842 { 843 switch (type) 844 { 845 case "boxModel": 846 if (!boxModelHighlighter) 847 boxModelHighlighter = new BoxModelHighlighter(); 848 849 return boxModelHighlighter; 850 851 case "frame": 852 if (!frameHighlighter) 853 frameHighlighter = new Firebug.Inspector.FrameHighlighter(); 854 855 return frameHighlighter; 856 } 857 } 858 859 function pad(element, t, r, b, l) 860 { 861 var css = "padding:" + Math.abs(t) + "px " + Math.abs(r) + "px " 862 + Math.abs(b) + "px " + Math.abs(l) + "px !important;"; 863 864 if (element) 865 element.style.cssText = css; 866 else 867 return css; 868 } 869 870 function moveImp(element, x, y) 871 { 872 var css = "left:" + x + "px !important;top:" + y + "px !important;"; 873 874 if (element) 875 element.style.cssText = css; 876 else 877 return css; 878 } 879 880 function resizeImp(element, w, h) 881 { 882 var css = "width:" + w + "px !important;height:" + h + "px !important;"; 883 884 if (element) 885 element.style.cssText = css; 886 else 887 return css; 888 } 889 890 // ********************************************************************************************* // 891 // Imagemap Highlighter 892 893 function getImageMapHighlighter(context) 894 { 895 if (!context) 896 return; 897 898 var canvas, ctx, mx, my; 899 var doc = context.window.document; 900 901 var init = function(elt) 902 { 903 if (elt) 904 doc = elt.ownerDocument; 905 906 canvas = doc.getElementById("firebugCanvas"); 907 908 if (!canvas) 909 { 910 canvas = doc.createElementNS("http://www.w3.org/1999/xhtml", "canvas"); 911 hideElementFromInspection(canvas); 912 canvas.id = "firebugCanvas"; 913 canvas.className = "firebugResetStyles firebugBlockBackgroundColor firebugCanvas"; 914 canvas.width = context.window.innerWidth; 915 canvas.height = context.window.innerHeight; 916 917 Events.addEventListener(context.window, "scroll", function() 918 { 919 context.imageMapHighlighter.show(false); 920 }, true); 921 922 Events.addEventListener(doc, "mousemove", function(event) 923 { 924 mx = event.clientX; 925 my = event.clientY; 926 }, true); 927 928 doc.body.appendChild(canvas); 929 } 930 }; 931 932 if (!context.imageMapHighlighter) 933 { 934 context.imageMapHighlighter = 935 { 936 ident: ident.imageMap, 937 938 show: function(state) 939 { 940 if (!canvas) 941 init(null); 942 943 canvas.style.cssText = "display:"+(state ? "block" : "none")+" !important"; 944 }, 945 946 getImages: function(mapName, multi) 947 { 948 if (!mapName) 949 return; 950 951 var xpe = new XPathEvaluator(); 952 var nsResolver = xpe.createNSResolver(doc.documentElement); 953 954 var elts = xpe.evaluate("//map[@name='" + mapName + "']", doc, 955 nsResolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); 956 957 if (elts.snapshotLength === 0) 958 return; 959 960 elts = xpe.evaluate("(//img | //input)[@usemap='#" + mapName + "']", 961 doc.documentElement, nsResolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); 962 var eltsLen = elts.snapshotLength; 963 964 var images = []; 965 for (var i = 0; i < eltsLen; ++i) 966 { 967 var elt = elts.snapshotItem(i); 968 var rect = Dom.getLTRBWH(elt); 969 970 if (multi) 971 { 972 images.push(elt); 973 } 974 else if (rect.left <= mx && rect.right >= mx && rect.top <= my && 975 rect.bottom >= my) 976 { 977 images[0] = elt; 978 break; 979 } 980 } 981 982 return images; 983 }, 984 985 highlight: function(eltArea, multi) 986 { 987 if (!eltArea || !eltArea.coords) 988 return; 989 990 var images = this.getImages(eltArea.parentNode.name, multi) || []; 991 992 init(eltArea); 993 994 var v = eltArea.coords.split(","); 995 996 if (!ctx) 997 ctx = canvas.getContext("2d"); 998 999 ctx.fillStyle = "rgba(135, 206, 235, 0.7)"; 1000 ctx.strokeStyle = "rgb(44, 167, 220)"; 1001 ctx.lineWidth = 2; 1002 1003 if (images.length == 0) 1004 images[0] = eltArea; 1005 1006 for (var j = 0, imagesLen = images.length; j < imagesLen; ++j) 1007 { 1008 var rect = Dom.getLTRBWH(images[j], context); 1009 1010 ctx.beginPath(); 1011 1012 if (!multi || (multi && j===0)) 1013 ctx.clearRect(0, 0, canvas.width, canvas.height); 1014 1015 var shape = eltArea.shape.toLowerCase(); 1016 1017 if (shape === "rect") 1018 { 1019 ctx.rect(rect.left + parseInt(v[0], 10), rect.top + parseInt(v[1], 10), v[2] - v[0], v[3] - v[1]); 1020 } 1021 else if (shape === "circle") 1022 { 1023 ctx.arc(rect.left + parseInt(v[0], 10) + ctx.lineWidth / 2, rect.top + parseInt(v[1], 10) + ctx.lineWidth / 2, v[2], 0, Math.PI / 180 * 360, false); 1024 } 1025 else 1026 { 1027 var vLen = v.length; 1028 ctx.moveTo(rect.left + parseInt(v[0], 10), rect.top + parseInt(v[1], 10)); 1029 for (var i = 2; i < vLen; i += 2) 1030 ctx.lineTo(rect.left + parseInt(v[i], 10), rect.top + parseInt(v[i + 1], 10)); 1031 ctx.lineTo(rect.left + parseInt(v[0], 10), rect.top + parseInt(v[1], 10)); 1032 } 1033 1034 ctx.fill(); 1035 ctx.stroke(); 1036 ctx.closePath(); 1037 } 1038 1039 this.show(true); 1040 }, 1041 1042 destroy: function() 1043 { 1044 this.show(false); 1045 canvas = null; 1046 ctx = null; 1047 } 1048 } 1049 } 1050 1051 return context.imageMapHighlighter; 1052 } 1053 1054 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 1055 1056 var quickInfoBox = 1057 { 1058 boxEnabled: undefined, 1059 dragging: false, 1060 storedX: null, 1061 storedY: null, 1062 1063 show: function(element) 1064 { 1065 if (!this.boxEnabled || !element) 1066 return; 1067 1068 this.needsToHide = false; 1069 1070 var domAttribs = ["nodeName", "id", "name", "offsetWidth", "offsetHeight"]; 1071 var cssAttribs = ["position"]; 1072 var compAttribs = [ 1073 "width", "height", "zIndex", "position", "top", "right", "bottom", "left", 1074 "margin-top", "margin-right", "margin-bottom", "margin-left", "color", 1075 "backgroundColor", "fontFamily", "cssFloat", "display", "visibility"]; 1076 var qiBox = Firebug.chrome.$("fbQuickInfoPanel"); 1077 1078 if (qiBox.state==="closed") 1079 { 1080 this.storedX = this.storedX || Firefox.getElementById("content").tabContainer.boxObject.screenX + 5; 1081 this.storedY = this.storedY || Firefox.getElementById("content").tabContainer.boxObject.screenY + 35; 1082 1083 // Dynamically set noautohide to avoid mozilla bug 545265. 1084 if (!this.noautohideAdded) 1085 { 1086 this.noautohideAdded = true; 1087 qiBox.addEventListener("popupshowing", function runOnce() 1088 { 1089 qiBox.removeEventListener("popupshowing", runOnce, false); 1090 qiBox.setAttribute("noautohide", true); 1091 }, false); 1092 } 1093 qiBox.openPopupAtScreen(this.storedX, this.storedY, false); 1094 } 1095 1096 qiBox.removeChild(qiBox.firstChild); 1097 var vbox = document.createElement("vbox"); 1098 qiBox.appendChild(vbox); 1099 1100 var needsTitle = this.addRows(element, vbox, domAttribs); 1101 var needsTitle2 = this.addRows(element.style, vbox, cssAttribs); 1102 1103 var lab; 1104 if (needsTitle || needsTitle2) 1105 { 1106 lab = document.createElement("label"); 1107 lab.setAttribute("class", "fbQuickInfoBoxTitle"); 1108 lab.setAttribute("value", Locale.$STR("quickInfo")); 1109 vbox.insertBefore(lab, vbox.firstChild); 1110 } 1111 1112 lab = document.createElement("label"); 1113 lab.setAttribute("class", "fbQuickInfoBoxTitle"); 1114 lab.setAttribute("value", Locale.$STR("computedStyle")); 1115 vbox.appendChild(lab); 1116 1117 this.addRows(element, vbox, compAttribs, true); 1118 }, 1119 1120 hide: function() 1121 { 1122 // if mouse is over panel defer hiding to mouseout to not cause flickering 1123 var qiBox = Firebug.chrome.$("fbQuickInfoPanel"); 1124 if (qiBox.state==="closed") 1125 return; 1126 1127 if (qiBox.mozMatchesSelector(":hover")) 1128 return; 1129 1130 this.storedX = qiBox.boxObject.screenX; 1131 this.storedY = qiBox.boxObject.screenY; 1132 qiBox.hidePopup(); 1133 }, 1134 1135 addRows: function(domBase, vbox, attribs, computedStyle) 1136 { 1137 if (!domBase) 1138 return; 1139 1140 var needsTitle = false; 1141 for (var i = 0; i < attribs.length; i++) 1142 { 1143 var value; 1144 if (computedStyle) 1145 { 1146 var cs = getNonFrameBody(domBase).ownerDocument.defaultView.getComputedStyle(domBase, null); 1147 value = cs.getPropertyValue(attribs[i]); 1148 1149 if (value && /rgb\(\d+,\s\d+,\s\d+\)/.test(value)) 1150 value = rgbToHex(value); 1151 } 1152 else 1153 { 1154 value = domBase[attribs[i]]; 1155 } 1156 1157 if (value) 1158 { 1159 needsTitle = true; 1160 var hbox = document.createElement("hbox"); 1161 var lab = document.createElement("label"); 1162 lab.setAttribute("class", "fbQuickInfoName"); 1163 lab.setAttribute("value", attribs[i]); 1164 hbox.appendChild(lab); 1165 var desc = document.createElement("label"); 1166 desc.setAttribute("class", "fbQuickInfoValue"); 1167 desc.appendChild(document.createTextNode(": " + value)); 1168 hbox.appendChild(desc); 1169 vbox.appendChild(hbox); 1170 } 1171 } 1172 1173 return needsTitle; 1174 } 1175 }; 1176 1177 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 1178 1179 Firebug.Inspector.FrameHighlighter = function() 1180 { 1181 }; 1182 1183 Firebug.Inspector.FrameHighlighter.prototype = 1184 { 1185 ident: ident.frame, 1186 1187 doNotHighlight: function(element) 1188 { 1189 return false; // (element instanceof XULElement); 1190 }, 1191 1192 highlight: function(context, element, extra, colorObj, isMulti) 1193 { 1194 if (this.doNotHighlight(element)) 1195 return; 1196 1197 // if a single color was passed in lets use it as the border color 1198 if (typeof colorObj === "string") 1199 colorObj = {background: "transparent", border: colorObj}; 1200 else 1201 colorObj = colorObj || {background: "transparent", border: "highlight"}; 1202 1203 Firebug.Inspector.attachRepaintInspectListeners(context, element); 1204 storeHighlighterParams(this, context, element, null, colorObj, null, isMulti); 1205 1206 var cs; 1207 var offset = Dom.getLTRBWH(element); 1208 var x = offset.left, y = offset.top; 1209 var w = offset.width, h = offset.height; 1210 1211 if (FBTrace.DBG_INSPECT) 1212 FBTrace.sysout("FrameHighlighter HTML tag:" + element.tagName + " x:" + x + 1213 " y:" + y + " w:" + w + " h:" + h); 1214 1215 var wacked = isNaN(x) || isNaN(y) || isNaN(w) || isNaN(h); 1216 if (wacked) 1217 { 1218 if (FBTrace.DBG_INSPECT) 1219 FBTrace.sysout("FrameHighlighter.highlight has bad boxObject for " + element.tagName); 1220 1221 return; 1222 } 1223 1224 if (element.tagName !== "AREA") 1225 { 1226 if (FBTrace.DBG_INSPECT) 1227 FBTrace.sysout("FrameHighlighter " + element.tagName); 1228 var body = getNonFrameBody(element); 1229 if (!body) 1230 return this.unhighlight(context); 1231 1232 this.ihl && this.ihl.show(false); 1233 1234 quickInfoBox.show(element); 1235 var highlighter = this.getHighlighter(context, isMulti); 1236 var bgDiv = highlighter.firstChild; 1237 var css = moveImp(null, x, y) + resizeImp(null, w, h); 1238 1239 if (Dom.isElement(element)) 1240 { 1241 cs = body.ownerDocument.defaultView.getComputedStyle(element, null); 1242 1243 if (cs.transform && cs.transform != "none") 1244 css += "transform: " + cs.transform + " !important;" + 1245 "transform-origin: " + cs.transformOrigin + " !important;"; 1246 if (cs.borderRadius) 1247 css += "border-radius: " + cs.borderRadius + " !important;"; 1248 if (cs.borderTopLeftRadius) 1249 css += "border-top-left-radius: " + cs.borderTopLeftRadius + " !important;"; 1250 if (cs.borderTopRightRadius) 1251 css += "border-top-right-radius: " + cs.borderTopRightRadius + " !important;"; 1252 if (cs.borderBottomRightRadius) 1253 css += "border-bottom-right-radius: " + cs.borderBottomRightRadius + " !important;"; 1254 if (cs.borderBottomLeftRadius) 1255 css += "border-bottom-left-radius: " + cs.borderBottomLeftRadius + " !important;"; 1256 } 1257 css += "box-shadow: 0 0 2px 2px "+ 1258 (colorObj && colorObj.border ? colorObj.border : "highlight")+"!important;"; 1259 1260 if (colorObj && colorObj.background) 1261 { 1262 bgDiv.style.cssText = "width: 100%!important; height: 100%!important;" + 1263 "background-color: "+colorObj.background+"!important; opacity: 0.6!important;"; 1264 } 1265 else 1266 { 1267 bgDiv.style.cssText = "background-color: transparent!important;"; 1268 } 1269 1270 highlighter.style.cssText = css; 1271 1272 var needsAppend = !highlighter.parentNode || highlighter.ownerDocument != body.ownerDocument; 1273 if (needsAppend) 1274 { 1275 if (FBTrace.DBG_INSPECT) 1276 FBTrace.sysout("FrameHighlighter needsAppend: " + highlighter.ownerDocument.documentURI + 1277 " !?= " + body.ownerDocument.documentURI, highlighter); 1278 1279 attachStyles(context, body.ownerDocument); 1280 1281 try 1282 { 1283 body.appendChild(highlighter); 1284 } 1285 catch(exc) 1286 { 1287 if (FBTrace.DBG_INSPECT) 1288 FBTrace.sysout("inspector.FrameHighlighter.highlight body.appendChild FAILS for body " + 1289 body + " " + exc, exc); 1290 } 1291 1292 // otherwise the proxies take up screen space in browser.xul 1293 if (element.ownerDocument && element.ownerDocument.contentType.indexOf("xul") === -1) 1294 createProxiesForDisabledElements(body); 1295 } 1296 } 1297 else 1298 { 1299 this.ihl = getImageMapHighlighter(context); 1300 this.ihl.highlight(element, false); 1301 } 1302 }, 1303 1304 unhighlight: function(context) 1305 { 1306 if (FBTrace.DBG_INSPECT) 1307 FBTrace.sysout("FrameHighlighter unhighlight", context.window.location); 1308 1309 var highlighter = this.getHighlighter(context); 1310 var body = highlighter.parentNode; 1311 if (body) 1312 { 1313 body.removeChild(highlighter); 1314 quickInfoBox.hide(); 1315 } 1316 1317 this.ihl && this.ihl.destroy(); 1318 this.ihl = null; 1319 }, 1320 1321 getHighlighter: function(context, isMulti) 1322 { 1323 if (!isMulti) 1324 { 1325 var div = HighlighterCache.get(ident.frame); 1326 if (div) 1327 return div; 1328 } 1329 1330 var doc = context.window.document; 1331 div = doc.createElementNS("http://www.w3.org/1999/xhtml", "div"); 1332 var div2 = doc.createElementNS("http://www.w3.org/1999/xhtml", "div"); 1333 1334 hideElementFromInspection(div); 1335 hideElementFromInspection(div2); 1336 1337 div.className = "firebugResetStyles firebugBlockBackgroundColor"; 1338 div2.className = "firebugResetStyles"; 1339 div.appendChild(div2); 1340 div.ident = ident.frame; 1341 HighlighterCache.add(div); 1342 return div; 1343 } 1344 }; 1345 1346 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 1347 1348 function BoxModelHighlighter() 1349 { 1350 } 1351 1352 Firebug.Inspector.BoxModelHighlighter = BoxModelHighlighter; 1353 1354 BoxModelHighlighter.prototype = 1355 { 1356 ident: ident.boxModel, 1357 1358 highlight: function(context, element, boxFrame, colorObj, isMulti) 1359 { 1360 var line, contentCssText, paddingCssText, borderCssText, marginCssText, 1361 nodes = this.getNodes(context, isMulti), 1362 highlightFrame = boxFrame ? nodes[boxFrame] : null; 1363 1364 // if a single color was passed in lets use it as the content box color 1365 if (typeof colorObj === "string") 1366 colorObj = {content: colorObj, padding: "SlateBlue", border: "#444444", margin: "#EDFF64"}; 1367 else 1368 colorObj = colorObj || {content: "SkyBlue", padding: "SlateBlue", border: "#444444", margin: "#EDFF64"}; 1369 1370 Firebug.Inspector.attachRepaintInspectListeners(context, element); 1371 storeHighlighterParams(this, context, element, boxFrame, colorObj, null, isMulti); 1372 1373 if (context.highlightFrame) 1374 Css.removeClass(context.highlightFrame, "firebugHighlightBox"); 1375 1376 if (element.tagName !== "AREA") 1377 { 1378 this.ihl && this.ihl.show(false); 1379 1380 quickInfoBox.show(element); 1381 context.highlightFrame = highlightFrame; 1382 1383 if (highlightFrame) 1384 { 1385 Css.setClass(nodes.offset, "firebugHighlightGroup"); 1386 Css.setClass(highlightFrame, "firebugHighlightBox"); 1387 } 1388 else 1389 Css.removeClass(nodes.offset, "firebugHighlightGroup"); 1390 1391 var win = (element.ownerDocument ? element.ownerDocument.defaultView : null); 1392 if (!win) 1393 return; 1394 1395 var style = win.getComputedStyle(element, ""); 1396 if (!style) 1397 { 1398 if (FBTrace.DBG_INSPECT) 1399 FBTrace.sysout("highlight: no style for element " + element, element); 1400 return; 1401 } 1402 1403 var styles = Css.readBoxStyles(style); 1404 var offset = Dom.getLTRBWH(element); 1405 var x = offset.left - Math.abs(styles.marginLeft); 1406 var y = offset.top - Math.abs(styles.marginTop); 1407 var w = offset.width - (styles.paddingLeft + styles.paddingRight + styles.borderLeft + styles.borderRight); 1408 var h = offset.height - (styles.paddingTop + styles.paddingBottom + styles.borderTop + styles.borderBottom); 1409 1410 moveImp(nodes.offset, x, y); 1411 marginCssText = pad(null, styles.marginTop, styles.marginRight, styles.marginBottom, styles.marginLeft); 1412 borderCssText = pad(null, styles.borderTop, styles.borderRight, styles.borderBottom, styles.borderLeft); 1413 paddingCssText = pad(null, styles.paddingTop, styles.paddingRight, styles.paddingBottom, styles.paddingLeft); 1414 contentCssText = resizeImp(null, w, h); 1415 1416 // element.tagName !== "BODY" for issue 2447. hopefully temporary, robc 1417 var showLines = Firebug.showRulers && boxFrame && element.tagName !== "BODY"; 1418 if (showLines) 1419 { 1420 var offsetParent = element.offsetParent; 1421 1422 if (offsetParent) 1423 this.setNodesByOffsetParent(win, offsetParent, nodes); 1424 1425 var left = x; 1426 var top = y; 1427 var width = w-1; 1428 var height = h-1; 1429 1430 if (boxFrame == "content") 1431 { 1432 left += Math.abs(styles.marginLeft) + Math.abs(styles.borderLeft) 1433 + Math.abs(styles.paddingLeft); 1434 top += Math.abs(styles.marginTop) + Math.abs(styles.borderTop) 1435 + Math.abs(styles.paddingTop); 1436 } 1437 else if (boxFrame == "padding") 1438 { 1439 left += Math.abs(styles.marginLeft) + Math.abs(styles.borderLeft); 1440 top += Math.abs(styles.marginTop) + Math.abs(styles.borderTop); 1441 width += Math.abs(styles.paddingLeft) + Math.abs(styles.paddingRight); 1442 height += Math.abs(styles.paddingTop) + Math.abs(styles.paddingBottom); 1443 } 1444 else if (boxFrame == "border") 1445 { 1446 left += Math.abs(styles.marginLeft); 1447 top += Math.abs(styles.marginTop); 1448 width += Math.abs(styles.paddingLeft) + Math.abs(styles.paddingRight) 1449 + Math.abs(styles.borderLeft) + Math.abs(styles.borderRight); 1450 height += Math.abs(styles.paddingTop) + Math.abs(styles.paddingBottom) 1451 + Math.abs(styles.borderTop) + Math.abs(styles.borderBottom); 1452 } 1453 else if (boxFrame == "margin") 1454 { 1455 width += Math.abs(styles.paddingLeft) + Math.abs(styles.paddingRight) 1456 + Math.abs(styles.borderLeft) + Math.abs(styles.borderRight) 1457 + Math.abs(styles.marginLeft) + Math.abs(styles.marginRight); 1458 height += Math.abs(styles.paddingTop) + Math.abs(styles.paddingBottom) 1459 + Math.abs(styles.borderTop) + Math.abs(styles.borderBottom) 1460 + Math.abs(styles.marginTop) + Math.abs(styles.marginBottom); 1461 } 1462 1463 moveImp(nodes.lines.top, 0, top); 1464 moveImp(nodes.lines.right, left + width, 0); 1465 moveImp(nodes.lines.bottom, 0, top + height); 1466 moveImp(nodes.lines.left, left, 0) 1467 } 1468 1469 var body = getNonFrameBody(element); 1470 if (!body) 1471 return this.unhighlight(context); 1472 1473 if (colorObj.content) 1474 nodes.content.style.cssText = contentCssText + " background-color: " + colorObj.content + " !important;"; 1475 else 1476 nodes.content.style.cssText = contentCssText + " background-color: #87CEEB !important;"; 1477 1478 if (colorObj.padding) 1479 nodes.padding.style.cssText = paddingCssText + " background-color: " + colorObj.padding + " !important;"; 1480 else 1481 nodes.padding.style.cssText = paddingCssText + " background-color: #6A5ACD !important;"; 1482 1483 if (colorObj.border) 1484 nodes.border.style.cssText = borderCssText + " background-color: " + colorObj.border + " !important;"; 1485 else 1486 nodes.border.style.cssText = borderCssText + " background-color: #444444 !important;"; 1487 1488 if (colorObj.margin) 1489 nodes.margin.style.cssText = marginCssText + " background-color: " + colorObj.margin + " !important;"; 1490 else 1491 nodes.margin.style.cssText = marginCssText + " background-color: #EDFF64 !important;"; 1492 1493 var needsAppend = !nodes.offset.parentNode 1494 || nodes.offset.parentNode.ownerDocument != body.ownerDocument; 1495 1496 if (needsAppend) 1497 { 1498 attachStyles(context, body.ownerDocument); 1499 body.appendChild(nodes.offset); 1500 } 1501 1502 if (showLines) 1503 { 1504 if (!nodes.lines.top.parentNode) 1505 { 1506 if (nodes.parent) 1507 body.appendChild(nodes.parent); 1508 1509 for (line in nodes.lines) 1510 body.appendChild(nodes.lines[line]); 1511 } 1512 } 1513 else if (nodes.lines.top.parentNode) 1514 { 1515 if (nodes.parent) 1516 body.removeChild(nodes.parent); 1517 1518 for (line in nodes.lines) 1519 body.removeChild(nodes.lines[line]); 1520 } 1521 } 1522 else 1523 { 1524 this.ihl = getImageMapHighlighter(context); 1525 this.ihl.highlight(element, true); 1526 } 1527 }, 1528 1529 unhighlight: function(context) 1530 { 1531 HighlighterCache.clear(); 1532 quickInfoBox.hide(); 1533 }, 1534 1535 getNodes: function(context, isMulti) 1536 { 1537 if (context.window) 1538 { 1539 var doc = context.window.document; 1540 1541 if (FBTrace.DBG_ERRORS && !doc) 1542 FBTrace.sysout("inspector getNodes no document for window:" + window.location); 1543 if (FBTrace.DBG_INSPECT && doc) 1544 FBTrace.sysout("inspect.getNodes doc: " + doc.location); 1545 1546 if (!isMulti) 1547 { 1548 var nodes = HighlighterCache.get(ident.boxModel); 1549 if (nodes) 1550 return nodes; 1551 } 1552 1553 var Ruler = "firebugResetStyles firebugBlockBackgroundColor firebugRuler firebugRuler"; 1554 var Box = "firebugResetStyles firebugBlockBackgroundColor firebugLayoutBox firebugLayoutBox"; 1555 var CustomizableBox = "firebugResetStyles firebugLayoutBox"; 1556 var Line = "firebugResetStyles firebugBlockBackgroundColor firebugLayoutLine firebugLayoutLine"; 1557 1558 function create(className, name) 1559 { 1560 var div = doc.createElementNS("http://www.w3.org/1999/xhtml", "div"); 1561 hideElementFromInspection(div); 1562 1563 if (className !== CustomizableBox) 1564 div.className = className + name; 1565 else 1566 div.className = className; 1567 1568 return div; 1569 } 1570 1571 nodes = 1572 { 1573 parent: create(Box, "Parent"), 1574 rulerH: create(Ruler, "H"), 1575 rulerV: create(Ruler, "V"), 1576 offset: create(Box, "Offset"), 1577 margin: create(CustomizableBox, "Margin"), 1578 border: create(CustomizableBox, "Border"), 1579 padding: create(CustomizableBox, "Padding"), 1580 content: create(CustomizableBox, "Content"), 1581 lines: { 1582 top: create(Line, "Top"), 1583 right: create(Line, "Right"), 1584 bottom: create(Line, "Bottom"), 1585 left: create(Line, "Left") 1586 } 1587 }; 1588 1589 nodes.parent.appendChild(nodes.rulerH); 1590 nodes.parent.appendChild(nodes.rulerV); 1591 nodes.offset.appendChild(nodes.margin); 1592 nodes.margin.appendChild(nodes.border); 1593 nodes.border.appendChild(nodes.padding); 1594 nodes.padding.appendChild(nodes.content); 1595 } 1596 1597 nodes.ident = ident.boxModel; 1598 HighlighterCache.add(nodes); 1599 return nodes; 1600 }, 1601 1602 setNodesByOffsetParent: function(win, offsetParent, nodes) 1603 { 1604 var parentStyle = win.getComputedStyle(offsetParent, ""); 1605 var parentOffset = Dom.getLTRBWH(offsetParent); 1606 var parentX = parentOffset.left + parseInt(parentStyle.borderLeftWidth, 10); 1607 var parentY = parentOffset.top + parseInt(parentStyle.borderTopWidth, 10); 1608 var parentW = offsetParent.offsetWidth-1; 1609 var parentH = offsetParent.offsetHeight-1; 1610 1611 nodes.parent.style.cssText = moveImp(null, parentX, parentY) + resizeImp(null, parentW, parentH); 1612 1613 if (parentX < 14) 1614 Css.setClass(nodes.parent, "overflowRulerX"); 1615 else 1616 Css.removeClass(nodes.parent, "overflowRulerX"); 1617 1618 if (parentY < 14) 1619 Css.setClass(nodes.parent, "overflowRulerY"); 1620 else 1621 Css.removeClass(nodes.parent, "overflowRulerY"); 1622 } 1623 }; 1624 1625 // ********************************************************************************************* // 1626 1627 function getNonFrameBody(elt) 1628 { 1629 if (Dom.isRange(elt)) 1630 { 1631 elt = elt.commonAncestorContainer; 1632 } 1633 var body = Dom.getBody(elt.ownerDocument); 1634 return (body.localName && body.localName.toUpperCase() === "FRAMESET") ? null : body; 1635 } 1636 1637 function attachStyles(context, doc) 1638 { 1639 if (!context.highlightStyleCache) 1640 context.highlightStyleCache = new WeakMap(); 1641 var highlightStyleCache = context.highlightStyleCache; 1642 1643 var style; 1644 if (highlightStyleCache.has(doc)) 1645 { 1646 style = highlightStyleCache.get(doc); 1647 } 1648 else 1649 { 1650 style = Css.createStyleSheet(doc, highlightCssUrl); 1651 highlightStyleCache.set(doc, style); 1652 } 1653 1654 // Cater for the possiblity that someone might have removed our stylesheet. 1655 if (!style.parentNode) 1656 Css.addStyleSheet(doc, style); 1657 } 1658 1659 function createProxiesForDisabledElements(body) 1660 { 1661 var i, rect, div, node, cs, css, 1662 doc = body.ownerDocument, 1663 xpe = new XPathEvaluator(), 1664 nsResolver = xpe.createNSResolver(doc.documentElement); 1665 1666 var result = xpe.evaluate("//*[@disabled]", doc.documentElement, 1667 nsResolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); 1668 1669 var l = result.snapshotLength; 1670 for (i=0; i<l; i++) 1671 { 1672 node = result.snapshotItem(i); 1673 cs = body.ownerDocument.defaultView.getComputedStyle(node, null); 1674 rect = node.getBoundingClientRect(); 1675 div = doc.createElementNS("http://www.w3.org/1999/xhtml", "div"); 1676 hideElementFromInspection(div); 1677 div.className = "firebugResetStyles fbProxyElement"; 1678 1679 css = moveImp(null, rect.left, rect.top + body.scrollTop) + resizeImp(null, rect.width, rect.height); 1680 if (cs.transform && cs.transform != "none") 1681 css += "transform:" + cs.transform + " !important;" + 1682 "transform-origin:" + cs.transformOrigin + " !important;"; 1683 if (cs.borderRadius) 1684 css += "border-radius:" + cs.borderRadius + " !important;"; 1685 if (cs.borderTopLeftRadius) 1686 css += "border-top-left-radius:" + cs.borderTopLeftRadius + " !important;"; 1687 if (cs.borderTopRightRadius) 1688 css += "border-top-right-radius:" + cs.borderTopRightRadius + " !important;"; 1689 if (cs.borderBottomRightRadius) 1690 css += "border-bottom-right-radius:" + cs.borderBottomRightRadius + " !important;"; 1691 if (cs.borderBottomLeftRadius) 1692 css += "border-bottom-left-radius:" + cs.borderBottomLeftRadius + " !important;"; 1693 1694 div.style.cssText = css; 1695 div.fbProxyFor = node; 1696 1697 body.appendChild(div); 1698 div.ident = ident.proxyElt; 1699 HighlighterCache.add(div); 1700 } 1701 } 1702 1703 function rgbToHex(value) 1704 { 1705 return value.replace(/\brgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)/gi, function(_, r, g, b) 1706 { 1707 return "#"+((1 << 24) + (r << 16) + (g << 8) + (b << 0)).toString(16).substr(-6).toUpperCase(); 1708 }); 1709 } 1710 1711 function isVisibleElement(elt) 1712 { 1713 var invisibleElements = 1714 { 1715 "head": true, 1716 "base": true, 1717 "basefont": true, 1718 "isindex": true, 1719 "link": true, 1720 "meta": true, 1721 "script": true, 1722 "style": true, 1723 "title": true 1724 }; 1725 1726 return !invisibleElements[elt.nodeName.toLowerCase()]; 1727 } 1728 1729 function hideElementFromInspection(elt) 1730 { 1731 if (!FBTrace.DBG_INSPECT) 1732 Firebug.setIgnored(elt); 1733 } 1734 1735 // highlightType is only to be used for multihighlighters 1736 function storeHighlighterParams(highlighter, context, element, boxFrame, colorObj, 1737 highlightType, isMulti) 1738 { 1739 var fir = Firebug.Inspector.repaint; 1740 1741 fir.highlighter = highlighter; 1742 fir.context = context; 1743 fir.element = element; 1744 fir.boxFrame = boxFrame; 1745 fir.colorObj = colorObj; 1746 fir.highlightType = highlightType; 1747 fir.isMulti = isMulti; 1748 1749 Firebug.Inspector.highlightedContext = context; 1750 } 1751 1752 // ********************************************************************************************* // 1753 // Registration 1754 1755 Firebug.registerModule(Firebug.Inspector); 1756 1757 return Firebug.Inspector; 1758 1759 // ********************************************************************************************* // 1760 }); 1761