зеркало из https://github.com/mozilla/pjs.git
Bug 703031 - [highlighter] Refactor the highlighter code. Create highlighter.jsm - Patch C; r=rcampbell
This commit is contained in:
Родитель
aee37c71de
Коммит
f8119fe33a
|
@ -361,7 +361,7 @@ TreePanel.prototype = {
|
||||||
this.IUI.stopInspecting(true);
|
this.IUI.stopInspecting(true);
|
||||||
} else {
|
} else {
|
||||||
this.IUI.select(node, true, false);
|
this.IUI.select(node, true, false);
|
||||||
this.IUI.highlighter.highlightNode(node);
|
this.IUI.highlighter.highlight(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,29 +1,144 @@
|
||||||
//// Highlighter
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||||
|
/* ***** BEGIN LICENSE BLOCK *****
|
||||||
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||||
|
*
|
||||||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||||||
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
* http://www.mozilla.org/MPL/
|
||||||
|
*
|
||||||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||||
|
* for the specific language governing rights and limitations under the
|
||||||
|
* License.
|
||||||
|
*
|
||||||
|
* The Original Code is the Mozilla Highlighter Module.
|
||||||
|
*
|
||||||
|
* The Initial Developer of the Original Code is
|
||||||
|
* The Mozilla Foundation.
|
||||||
|
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||||
|
* the Initial Developer. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Contributor(s):
|
||||||
|
* Rob Campbell <rcampbell@mozilla.com> (original author)
|
||||||
|
* Mihai Șucan <mihai.sucan@gmail.com>
|
||||||
|
* Julian Viereck <jviereck@mozilla.com>
|
||||||
|
* Paul Rouget <paul@mozilla.com>
|
||||||
|
* Kyle Simpson <ksimpson@mozilla.com>
|
||||||
|
* Johan Charlez <johan.charlez@gmail.com>
|
||||||
|
*
|
||||||
|
* Alternatively, the contents of this file may be used under the terms of
|
||||||
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||||
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||||
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||||
|
* of those above. If you wish to allow use of your version of this file only
|
||||||
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||||
|
* use your version of this file under the terms of the MPL, indicate your
|
||||||
|
* decision by deleting the provisions above and replace them with the notice
|
||||||
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||||
|
* the provisions above, a recipient may use your version of this file under
|
||||||
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||||
|
*
|
||||||
|
* ***** END LICENSE BLOCK ***** */
|
||||||
|
|
||||||
|
const Cu = Components.utils;
|
||||||
|
Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
|
||||||
|
|
||||||
|
var EXPORTED_SYMBOLS = ["Highlighter"];
|
||||||
|
|
||||||
|
const INSPECTOR_INVISIBLE_ELEMENTS = {
|
||||||
|
"head": true,
|
||||||
|
"base": true,
|
||||||
|
"basefont": true,
|
||||||
|
"isindex": true,
|
||||||
|
"link": true,
|
||||||
|
"meta": true,
|
||||||
|
"script": true,
|
||||||
|
"style": true,
|
||||||
|
"title": true,
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A highlighter mechanism.
|
* A highlighter mechanism.
|
||||||
*
|
*
|
||||||
* The highlighter is built dynamically once the Inspector is invoked:
|
* The highlighter is built dynamically into the browser element.
|
||||||
* <stack id="highlighter-container">
|
* The caller is in charge of destroying the highlighter (ie, the highlighter
|
||||||
* <vbox id="highlighter-veil-container">...</vbox>
|
* won't be destroyed if a new tab is selected for example).
|
||||||
* <box id="highlighter-controls>...</vbox>
|
*
|
||||||
* </stack>
|
* API:
|
||||||
|
*
|
||||||
|
* // Constructor and destructor.
|
||||||
|
* // @param aWindow - browser.xul window.
|
||||||
|
* Highlighter(aWindow);
|
||||||
|
* void destroy();
|
||||||
|
*
|
||||||
|
* // Highlight a node.
|
||||||
|
* // @param aNode - node to highlight
|
||||||
|
* // @param aScroll - scroll to ensure the node is visible
|
||||||
|
* void highlight(aNode, aScroll);
|
||||||
|
*
|
||||||
|
* // Get the selected node.
|
||||||
|
* DOMNode getNode();
|
||||||
|
*
|
||||||
|
* // Lock and unlock the select node.
|
||||||
|
* void lock();
|
||||||
|
* void unlock();
|
||||||
|
*
|
||||||
|
* // Show and hide the highlighter
|
||||||
|
* void show();
|
||||||
|
* void hide();
|
||||||
|
* boolean isHidden();
|
||||||
|
*
|
||||||
|
* // Redraw the highlighter if the visible portion of the node has changed.
|
||||||
|
* void invalidateSize(aScroll);
|
||||||
|
*
|
||||||
|
* // Is a node highlightable.
|
||||||
|
* boolean isNodeHighlightable(aNode);
|
||||||
|
*
|
||||||
|
* // Add/Remove lsiteners
|
||||||
|
* // @param aEvent - event name
|
||||||
|
* // @param aListener - function callback
|
||||||
|
* void addListener(aEvent, aListener);
|
||||||
|
* void removeListener(aEvent, aListener);
|
||||||
|
*
|
||||||
|
* Events:
|
||||||
|
*
|
||||||
|
* "closed" - Highlighter is closing
|
||||||
|
* "nodeselected" - A new node has been selected
|
||||||
|
* "highlighting" - Highlighter is highlighting
|
||||||
|
* "locked" - The selected node has been locked
|
||||||
|
* "unlocked" - The selected ndoe has been unlocked
|
||||||
|
*
|
||||||
|
* Structure:
|
||||||
|
*
|
||||||
|
* <stack id="highlighter-container">
|
||||||
|
* <vbox id="highlighter-veil-container">...</vbox>
|
||||||
|
* <box id="highlighter-controls>...</vbox>
|
||||||
|
* </stack>
|
||||||
*
|
*
|
||||||
* @param object aInspector
|
|
||||||
* The InspectorUI instance.
|
|
||||||
*/
|
*/
|
||||||
function Highlighter(aInspector)
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param object aWindow
|
||||||
|
*/
|
||||||
|
function Highlighter(aWindow)
|
||||||
{
|
{
|
||||||
this.IUI = aInspector;
|
this.chromeWin = aWindow;
|
||||||
|
this.tabbrowser = aWindow.gBrowser;
|
||||||
|
this.chromeDoc = aWindow.document;
|
||||||
|
this.browser = aWindow.gBrowser.selectedBrowser;
|
||||||
|
this.events = {};
|
||||||
|
|
||||||
this._init();
|
this._init();
|
||||||
}
|
}
|
||||||
|
|
||||||
Highlighter.prototype = {
|
Highlighter.prototype = {
|
||||||
_init: function Highlighter__init()
|
_init: function Highlighter__init()
|
||||||
{
|
{
|
||||||
this.browser = this.IUI.browser;
|
|
||||||
this.chromeDoc = this.IUI.chromeDoc;
|
|
||||||
|
|
||||||
let stack = this.browser.parentNode;
|
let stack = this.browser.parentNode;
|
||||||
this.win = this.browser.contentWindow;
|
this.win = this.browser.contentWindow;
|
||||||
this._highlighting = false;
|
this._highlighting = false;
|
||||||
|
@ -49,28 +164,23 @@ Highlighter.prototype = {
|
||||||
|
|
||||||
this.buildInfobar(controlsBox);
|
this.buildInfobar(controlsBox);
|
||||||
|
|
||||||
if (!this.IUI.store.getValue(this.winID, "inspecting")) {
|
|
||||||
this.veilContainer.setAttribute("locked", true);
|
|
||||||
this.nodeInfo.container.setAttribute("locked", true);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.browser.addEventListener("resize", this, true);
|
|
||||||
this.browser.addEventListener("scroll", this, true);
|
|
||||||
|
|
||||||
this.transitionDisabler = null;
|
this.transitionDisabler = null;
|
||||||
|
|
||||||
this.computeZoomFactor();
|
this.computeZoomFactor();
|
||||||
this.handleResize();
|
this.unlock();
|
||||||
|
this.hide();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroy the nodes.
|
* Destroy the nodes. Remove listeners.
|
||||||
*/
|
*/
|
||||||
destroy: function Highlighter_destroy()
|
destroy: function Highlighter_destroy()
|
||||||
{
|
{
|
||||||
this.IUI.win.clearTimeout(this.transitionDisabler);
|
this.detachKeysListeners();
|
||||||
this.browser.removeEventListener("scroll", this, true);
|
this.detachMouseListeners();
|
||||||
this.browser.removeEventListener("resize", this, true);
|
this.detachPageListeners();
|
||||||
|
|
||||||
|
this.chromeWin.clearTimeout(this.transitionDisabler);
|
||||||
this.boundCloseEventHandler = null;
|
this.boundCloseEventHandler = null;
|
||||||
this._contentRect = null;
|
this._contentRect = null;
|
||||||
this._highlightRect = null;
|
this._highlightRect = null;
|
||||||
|
@ -87,83 +197,62 @@ Highlighter.prototype = {
|
||||||
this.win = null
|
this.win = null
|
||||||
this.browser = null;
|
this.browser = null;
|
||||||
this.chromeDoc = null;
|
this.chromeDoc = null;
|
||||||
this.IUI = null;
|
this.chromeWin = null;
|
||||||
|
this.tabbrowser = null;
|
||||||
|
|
||||||
|
this.emitEvent("closed");
|
||||||
|
this.removeAllListeners();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Highlight this.node, unhilighting first if necessary.
|
* Show the veil, and select a node.
|
||||||
|
* If no node is specified, the previous selected node is highlighted if any.
|
||||||
|
* If no node was selected, the root element is selected.
|
||||||
*
|
*
|
||||||
* @param boolean aScroll
|
* @param aNode [optional] - The node to be selected.
|
||||||
* Boolean determining whether to scroll or not.
|
* @param aScroll [optional] boolean
|
||||||
|
* Should we scroll to ensure that the selected node is visible.
|
||||||
*/
|
*/
|
||||||
highlight: function Highlighter_highlight(aScroll)
|
highlight: function Highlighter_highlight(aNode, aScroll)
|
||||||
|
{
|
||||||
|
if (this.hidden)
|
||||||
|
this.show();
|
||||||
|
|
||||||
|
let oldNode = this.node;
|
||||||
|
|
||||||
|
if (!aNode) {
|
||||||
|
if (!this.node)
|
||||||
|
this.node = this.win.document.documentElement;
|
||||||
|
} else {
|
||||||
|
this.node = aNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldNode !== this.node) {
|
||||||
|
this.updateInfobar();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.invalidateSize(!!aScroll);
|
||||||
|
|
||||||
|
if (oldNode !== this.node) {
|
||||||
|
this.emitEvent("nodeselected");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the highlighter size and position.
|
||||||
|
*/
|
||||||
|
invalidateSize: function Highlighter_invalidateSize(aScroll)
|
||||||
{
|
{
|
||||||
let rect = null;
|
let rect = null;
|
||||||
|
|
||||||
if (this.node && this.isNodeHighlightable(this.node)) {
|
if (this.node && this.isNodeHighlightable(this.node)) {
|
||||||
|
|
||||||
if (aScroll) {
|
if (aScroll &&
|
||||||
|
this.node.scrollIntoView) { // XUL elements don't have such method
|
||||||
this.node.scrollIntoView();
|
this.node.scrollIntoView();
|
||||||
}
|
}
|
||||||
|
|
||||||
let clientRect = this.node.getBoundingClientRect();
|
let clientRect = this.node.getBoundingClientRect();
|
||||||
|
rect = LayoutHelpers.getDirtyRect(this.node);
|
||||||
// Go up in the tree of frames to determine the correct rectangle.
|
|
||||||
// clientRect is read-only, we need to be able to change properties.
|
|
||||||
rect = {top: clientRect.top,
|
|
||||||
left: clientRect.left,
|
|
||||||
width: clientRect.width,
|
|
||||||
height: clientRect.height};
|
|
||||||
|
|
||||||
let frameWin = this.node.ownerDocument.defaultView;
|
|
||||||
|
|
||||||
// We iterate through all the parent windows.
|
|
||||||
while (true) {
|
|
||||||
|
|
||||||
// Does the selection overflow on the right of its window?
|
|
||||||
let diffx = frameWin.innerWidth - (rect.left + rect.width);
|
|
||||||
if (diffx < 0) {
|
|
||||||
rect.width += diffx;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Does the selection overflow on the bottom of its window?
|
|
||||||
let diffy = frameWin.innerHeight - (rect.top + rect.height);
|
|
||||||
if (diffy < 0) {
|
|
||||||
rect.height += diffy;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Does the selection overflow on the left of its window?
|
|
||||||
if (rect.left < 0) {
|
|
||||||
rect.width += rect.left;
|
|
||||||
rect.left = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Does the selection overflow on the top of its window?
|
|
||||||
if (rect.top < 0) {
|
|
||||||
rect.height += rect.top;
|
|
||||||
rect.top = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Selection has been clipped to fit in its own window.
|
|
||||||
|
|
||||||
// Are we in the top-level window?
|
|
||||||
if (frameWin.parent === frameWin || !frameWin.frameElement) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We are in an iframe.
|
|
||||||
// We take into account the parent iframe position and its
|
|
||||||
// offset (borders and padding).
|
|
||||||
let frameRect = frameWin.frameElement.getBoundingClientRect();
|
|
||||||
|
|
||||||
let [offsetTop, offsetLeft] =
|
|
||||||
this.IUI.getIframeContentOffset(frameWin.frameElement);
|
|
||||||
|
|
||||||
rect.top += frameRect.top + offsetTop;
|
|
||||||
rect.left += frameRect.left + offsetLeft;
|
|
||||||
|
|
||||||
frameWin = frameWin.parent;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.highlightRectangle(rect);
|
this.highlightRectangle(rect);
|
||||||
|
@ -171,11 +260,78 @@ Highlighter.prototype = {
|
||||||
this.moveInfobar();
|
this.moveInfobar();
|
||||||
|
|
||||||
if (this._highlighting) {
|
if (this._highlighting) {
|
||||||
Services.obs.notifyObservers(null,
|
this.emitEvent("highlighting");
|
||||||
INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, null);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the selected node.
|
||||||
|
*
|
||||||
|
* @returns node
|
||||||
|
*/
|
||||||
|
getNode: function() {
|
||||||
|
return this.node;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the highlighter if it has been hidden.
|
||||||
|
*/
|
||||||
|
show: function() {
|
||||||
|
if (!this.hidden) return;
|
||||||
|
this.veilContainer.removeAttribute("hidden");
|
||||||
|
this.nodeInfo.container.removeAttribute("hidden");
|
||||||
|
this.attachKeysListeners();
|
||||||
|
this.attachPageListeners();
|
||||||
|
this.invalidateSize();
|
||||||
|
this.hidden = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide the highlighter, the veil and the infobar.
|
||||||
|
*/
|
||||||
|
hide: function() {
|
||||||
|
if (this.hidden) return;
|
||||||
|
this.veilContainer.setAttribute("hidden", "true");
|
||||||
|
this.nodeInfo.container.setAttribute("hidden", "true");
|
||||||
|
this.detachKeysListeners();
|
||||||
|
this.detachPageListeners();
|
||||||
|
this.hidden = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is the highlighter visible?
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
isHidden: function() {
|
||||||
|
return this.hidden;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lock a node. Stops the inspection.
|
||||||
|
*/
|
||||||
|
lock: function() {
|
||||||
|
if (this.locked === true) return;
|
||||||
|
this.veilContainer.setAttribute("locked", "true");
|
||||||
|
this.nodeInfo.container.setAttribute("locked", "true");
|
||||||
|
this.detachMouseListeners();
|
||||||
|
this.locked = true;
|
||||||
|
this.emitEvent("locked");
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start inspecting.
|
||||||
|
* Unlock the current node (if any), and select any node being hovered.
|
||||||
|
*/
|
||||||
|
unlock: function() {
|
||||||
|
if (this.locked === false) return;
|
||||||
|
this.veilContainer.removeAttribute("locked");
|
||||||
|
this.nodeInfo.container.removeAttribute("locked");
|
||||||
|
this.attachMouseListeners();
|
||||||
|
this.locked = false;
|
||||||
|
this.emitEvent("unlocked");
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Is the specified node highlightable?
|
* Is the specified node highlightable?
|
||||||
*
|
*
|
||||||
|
@ -313,29 +469,6 @@ Highlighter.prototype = {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Is the highlighter highlighting? Public method for querying the state
|
|
||||||
* of the highlighter.
|
|
||||||
*/
|
|
||||||
get isHighlighting() {
|
|
||||||
return this._highlighting;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Highlight the given node.
|
|
||||||
*
|
|
||||||
* @param nsIDOMNode aNode
|
|
||||||
* a DOM element to be highlighted
|
|
||||||
* @param object aParams
|
|
||||||
* extra parameters object
|
|
||||||
*/
|
|
||||||
highlightNode: function Highlighter_highlightNode(aNode, aParams)
|
|
||||||
{
|
|
||||||
this.node = aNode;
|
|
||||||
this.updateInfobar();
|
|
||||||
this.highlight(aParams && aParams.scroll);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Highlight a rectangular region.
|
* Highlight a rectangular region.
|
||||||
*
|
*
|
||||||
|
@ -355,14 +488,10 @@ Highlighter.prototype = {
|
||||||
|
|
||||||
if (oldRect && aRect.top == oldRect.top && aRect.left == oldRect.left &&
|
if (oldRect && aRect.top == oldRect.top && aRect.left == oldRect.left &&
|
||||||
aRect.width == oldRect.width && aRect.height == oldRect.height) {
|
aRect.width == oldRect.width && aRect.height == oldRect.height) {
|
||||||
return this._highlighting; // same rectangle
|
return; // same rectangle
|
||||||
}
|
}
|
||||||
|
|
||||||
// adjust rect for zoom scaling
|
let aRectScaled = LayoutHelpers.getZoomedRect(this.win, aRect);
|
||||||
let aRectScaled = {};
|
|
||||||
for (let prop in aRect) {
|
|
||||||
aRectScaled[prop] = aRect[prop] * this.zoom;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (aRectScaled.left >= 0 && aRectScaled.top >= 0 &&
|
if (aRectScaled.left >= 0 && aRectScaled.top >= 0 &&
|
||||||
aRectScaled.width > 0 && aRectScaled.height > 0) {
|
aRectScaled.width > 0 && aRectScaled.height > 0) {
|
||||||
|
@ -384,7 +513,7 @@ Highlighter.prototype = {
|
||||||
this._contentRect = aRect; // save orig (non-scaled) rect
|
this._contentRect = aRect; // save orig (non-scaled) rect
|
||||||
this._highlightRect = aRectScaled; // and save the scaled rect.
|
this._highlightRect = aRectScaled; // and save the scaled rect.
|
||||||
|
|
||||||
return this._highlighting;
|
return;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -396,8 +525,6 @@ Highlighter.prototype = {
|
||||||
this.veilMiddleBox.style.height = 0;
|
this.veilMiddleBox.style.height = 0;
|
||||||
this.veilTransparentBox.style.width = 0;
|
this.veilTransparentBox.style.width = 0;
|
||||||
this.veilTransparentBox.style.visibility = "hidden";
|
this.veilTransparentBox.style.visibility = "hidden";
|
||||||
Services.obs.notifyObservers(null,
|
|
||||||
INSPECTOR_NOTIFICATIONS.UNHIGHLIGHTING, null);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -543,7 +670,7 @@ Highlighter.prototype = {
|
||||||
// Get midpoint of diagonal line.
|
// Get midpoint of diagonal line.
|
||||||
let midpoint = this.midPoint(a, b);
|
let midpoint = this.midPoint(a, b);
|
||||||
|
|
||||||
return this.IUI.elementFromPoint(this.win.document, midpoint.x,
|
return LayoutHelpers.getElementFromPoint(this.win.document, midpoint.x,
|
||||||
midpoint.y);
|
midpoint.y);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -557,10 +684,50 @@ Highlighter.prototype = {
|
||||||
.screenPixelsPerCSSPixel;
|
.screenPixelsPerCSSPixel;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////
|
||||||
|
//// Event Emitter Mechanism
|
||||||
|
|
||||||
|
addListener: function Highlighter_addListener(aEvent, aListener)
|
||||||
|
{
|
||||||
|
if (!(aEvent in this.events))
|
||||||
|
this.events[aEvent] = [];
|
||||||
|
this.events[aEvent].push(aListener);
|
||||||
|
},
|
||||||
|
|
||||||
|
removeListener: function Highlighter_removeListener(aEvent, aListener)
|
||||||
|
{
|
||||||
|
if (!(aEvent in this.events))
|
||||||
|
return;
|
||||||
|
let idx = this.events[aEvent].indexOf(aListener);
|
||||||
|
if (idx > -1)
|
||||||
|
this.events[aEvent].splice(idx, 1);
|
||||||
|
},
|
||||||
|
|
||||||
|
emitEvent: function Highlighter_emitEvent(aEvent, aArgv)
|
||||||
|
{
|
||||||
|
if (!(aEvent in this.events))
|
||||||
|
return;
|
||||||
|
|
||||||
|
let listeners = this.events[aEvent];
|
||||||
|
let highlighter = this;
|
||||||
|
listeners.forEach(function(aListener) {
|
||||||
|
try {
|
||||||
|
aListener.apply(highlighter, aArgv);
|
||||||
|
} catch(e) {}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
removeAllListeners: function Highlighter_removeAllIsteners()
|
||||||
|
{
|
||||||
|
for (let event in this.events) {
|
||||||
|
delete this.events[event];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////
|
||||||
//// Event Handling
|
//// Event Handling
|
||||||
|
|
||||||
attachInspectListeners: function Highlighter_attachInspectListeners()
|
attachMouseListeners: function Highlighter_attachMouseListeners()
|
||||||
{
|
{
|
||||||
this.browser.addEventListener("mousemove", this, true);
|
this.browser.addEventListener("mousemove", this, true);
|
||||||
this.browser.addEventListener("click", this, true);
|
this.browser.addEventListener("click", this, true);
|
||||||
|
@ -569,7 +736,7 @@ Highlighter.prototype = {
|
||||||
this.browser.addEventListener("mouseup", this, true);
|
this.browser.addEventListener("mouseup", this, true);
|
||||||
},
|
},
|
||||||
|
|
||||||
detachInspectListeners: function Highlighter_detachInspectListeners()
|
detachMouseListeners: function Highlighter_detachMouseListeners()
|
||||||
{
|
{
|
||||||
this.browser.removeEventListener("mousemove", this, true);
|
this.browser.removeEventListener("mousemove", this, true);
|
||||||
this.browser.removeEventListener("click", this, true);
|
this.browser.removeEventListener("click", this, true);
|
||||||
|
@ -578,6 +745,29 @@ Highlighter.prototype = {
|
||||||
this.browser.removeEventListener("mouseup", this, true);
|
this.browser.removeEventListener("mouseup", this, true);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
attachPageListeners: function Highlighter_attachPageListeners()
|
||||||
|
{
|
||||||
|
this.browser.addEventListener("resize", this, true);
|
||||||
|
this.browser.addEventListener("scroll", this, true);
|
||||||
|
},
|
||||||
|
|
||||||
|
detachPageListeners: function Highlighter_detachPageListeners()
|
||||||
|
{
|
||||||
|
this.browser.removeEventListener("resize", this, true);
|
||||||
|
this.browser.removeEventListener("scroll", this, true);
|
||||||
|
},
|
||||||
|
|
||||||
|
attachKeysListeners: function Highlighter_attachKeysListeners()
|
||||||
|
{
|
||||||
|
this.browser.addEventListener("keypress", this, true);
|
||||||
|
this.highlighterContainer.addEventListener("keypress", this, true);
|
||||||
|
},
|
||||||
|
|
||||||
|
detachKeysListeners: function Highlighter_detachKeysListeners()
|
||||||
|
{
|
||||||
|
this.browser.removeEventListener("keypress", this, true);
|
||||||
|
this.highlighterContainer.removeEventListener("keypress", this, true);
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generic event handler.
|
* Generic event handler.
|
||||||
|
@ -595,9 +785,10 @@ Highlighter.prototype = {
|
||||||
this.handleMouseMove(aEvent);
|
this.handleMouseMove(aEvent);
|
||||||
break;
|
break;
|
||||||
case "resize":
|
case "resize":
|
||||||
|
case "scroll":
|
||||||
this.computeZoomFactor();
|
this.computeZoomFactor();
|
||||||
this.brieflyDisableTransitions();
|
this.brieflyDisableTransitions();
|
||||||
this.handleResize(aEvent);
|
this.invalidateSize();
|
||||||
break;
|
break;
|
||||||
case "dblclick":
|
case "dblclick":
|
||||||
case "mousedown":
|
case "mousedown":
|
||||||
|
@ -605,10 +796,78 @@ Highlighter.prototype = {
|
||||||
aEvent.stopPropagation();
|
aEvent.stopPropagation();
|
||||||
aEvent.preventDefault();
|
aEvent.preventDefault();
|
||||||
break;
|
break;
|
||||||
case "scroll":
|
|
||||||
this.brieflyDisableTransitions();
|
|
||||||
this.highlight();
|
|
||||||
break;
|
break;
|
||||||
|
case "keypress":
|
||||||
|
switch (aEvent.keyCode) {
|
||||||
|
case this.chromeWin.KeyEvent.DOM_VK_RETURN:
|
||||||
|
this.locked ? this.unlock() : this.lock();
|
||||||
|
aEvent.preventDefault();
|
||||||
|
aEvent.stopPropagation();
|
||||||
|
break;
|
||||||
|
case this.chromeWin.KeyEvent.DOM_VK_LEFT:
|
||||||
|
let node;
|
||||||
|
if (this.node) {
|
||||||
|
node = this.node.parentNode;
|
||||||
|
} else {
|
||||||
|
node = this.defaultSelection;
|
||||||
|
}
|
||||||
|
if (node && this.isNodeHighlightable(node)) {
|
||||||
|
this.highlight(node);
|
||||||
|
}
|
||||||
|
aEvent.preventDefault();
|
||||||
|
aEvent.stopPropagation();
|
||||||
|
break;
|
||||||
|
case this.chromeWin.KeyEvent.DOM_VK_RIGHT:
|
||||||
|
if (this.node) {
|
||||||
|
// Find the first child that is highlightable.
|
||||||
|
for (let i = 0; i < this.node.childNodes.length; i++) {
|
||||||
|
node = this.node.childNodes[i];
|
||||||
|
if (node && this.isNodeHighlightable(node)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
node = this.defaultSelection;
|
||||||
|
}
|
||||||
|
if (node && this.isNodeHighlightable(node)) {
|
||||||
|
this.highlight(node, true);
|
||||||
|
}
|
||||||
|
aEvent.preventDefault();
|
||||||
|
aEvent.stopPropagation();
|
||||||
|
break;
|
||||||
|
case this.chromeWin.KeyEvent.DOM_VK_UP:
|
||||||
|
if (this.node) {
|
||||||
|
// Find a previous sibling that is highlightable.
|
||||||
|
node = this.node.previousSibling;
|
||||||
|
while (node && !this.isNodeHighlightable(node)) {
|
||||||
|
node = node.previousSibling;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
node = this.defaultSelection;
|
||||||
|
}
|
||||||
|
if (node && this.isNodeHighlightable(node)) {
|
||||||
|
this.highlight(node, true);
|
||||||
|
}
|
||||||
|
aEvent.preventDefault();
|
||||||
|
aEvent.stopPropagation();
|
||||||
|
break;
|
||||||
|
case this.chromeWin.KeyEvent.DOM_VK_DOWN:
|
||||||
|
if (this.node) {
|
||||||
|
// Find a next sibling that is highlightable.
|
||||||
|
node = this.node.nextSibling;
|
||||||
|
while (node && !this.isNodeHighlightable(node)) {
|
||||||
|
node = node.nextSibling;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
node = this.defaultSelection;
|
||||||
|
}
|
||||||
|
if (node && this.isNodeHighlightable(node)) {
|
||||||
|
this.highlight(node, true);
|
||||||
|
}
|
||||||
|
aEvent.preventDefault();
|
||||||
|
aEvent.stopPropagation();
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -619,13 +878,13 @@ Highlighter.prototype = {
|
||||||
brieflyDisableTransitions: function Highlighter_brieflyDisableTransitions()
|
brieflyDisableTransitions: function Highlighter_brieflyDisableTransitions()
|
||||||
{
|
{
|
||||||
if (this.transitionDisabler) {
|
if (this.transitionDisabler) {
|
||||||
this.IUI.win.clearTimeout(this.transitionDisabler);
|
this.chromeWin.clearTimeout(this.transitionDisabler);
|
||||||
} else {
|
} else {
|
||||||
this.veilContainer.setAttribute("disable-transitions", "true");
|
this.veilContainer.setAttribute("disable-transitions", "true");
|
||||||
this.nodeInfo.container.setAttribute("disable-transitions", "true");
|
this.nodeInfo.container.setAttribute("disable-transitions", "true");
|
||||||
}
|
}
|
||||||
this.transitionDisabler =
|
this.transitionDisabler =
|
||||||
this.IUI.win.setTimeout(function() {
|
this.chromeWin.setTimeout(function() {
|
||||||
this.veilContainer.removeAttribute("disable-transitions");
|
this.veilContainer.removeAttribute("disable-transitions");
|
||||||
this.nodeInfo.container.removeAttribute("disable-transitions");
|
this.nodeInfo.container.removeAttribute("disable-transitions");
|
||||||
this.transitionDisabler = null;
|
this.transitionDisabler = null;
|
||||||
|
@ -643,7 +902,7 @@ Highlighter.prototype = {
|
||||||
// Stop inspection when the user clicks on a node.
|
// Stop inspection when the user clicks on a node.
|
||||||
if (aEvent.button == 0) {
|
if (aEvent.button == 0) {
|
||||||
let win = aEvent.target.ownerDocument.defaultView;
|
let win = aEvent.target.ownerDocument.defaultView;
|
||||||
this.IUI.stopInspecting();
|
this.lock();
|
||||||
win.focus();
|
win.focus();
|
||||||
}
|
}
|
||||||
aEvent.preventDefault();
|
aEvent.preventDefault();
|
||||||
|
@ -651,27 +910,19 @@ Highlighter.prototype = {
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle mousemoves in panel when InspectorUI.inspecting is true.
|
* Handle mousemoves in panel.
|
||||||
*
|
*
|
||||||
* @param nsiDOMEvent aEvent
|
* @param nsiDOMEvent aEvent
|
||||||
* The MouseEvent triggering the method.
|
* The MouseEvent triggering the method.
|
||||||
*/
|
*/
|
||||||
handleMouseMove: function Highlighter_handleMouseMove(aEvent)
|
handleMouseMove: function Highlighter_handleMouseMove(aEvent)
|
||||||
{
|
{
|
||||||
let element = this.IUI.elementFromPoint(aEvent.target.ownerDocument,
|
let element = LayoutHelpers.getElementFromPoint(aEvent.target.ownerDocument,
|
||||||
aEvent.clientX, aEvent.clientY);
|
aEvent.clientX, aEvent.clientY);
|
||||||
if (element && element != this.node) {
|
if (element && element != this.node) {
|
||||||
this.IUI.inspectNode(element);
|
this.highlight(element);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle window resize events.
|
|
||||||
*/
|
|
||||||
handleResize: function Highlighter_handleResize()
|
|
||||||
{
|
|
||||||
this.highlight();
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -52,27 +52,11 @@ Cu.import("resource://gre/modules/Services.jsm");
|
||||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||||
Cu.import("resource:///modules/TreePanel.jsm");
|
Cu.import("resource:///modules/TreePanel.jsm");
|
||||||
Cu.import("resource:///modules/devtools/CssRuleView.jsm");
|
Cu.import("resource:///modules/devtools/CssRuleView.jsm");
|
||||||
|
Cu.import("resource:///modules/highlighter.jsm");
|
||||||
const INSPECTOR_INVISIBLE_ELEMENTS = {
|
Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
|
||||||
"head": true,
|
|
||||||
"base": true,
|
|
||||||
"basefont": true,
|
|
||||||
"isindex": true,
|
|
||||||
"link": true,
|
|
||||||
"meta": true,
|
|
||||||
"script": true,
|
|
||||||
"style": true,
|
|
||||||
"title": true,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Inspector notifications dispatched through the nsIObserverService.
|
// Inspector notifications dispatched through the nsIObserverService.
|
||||||
const INSPECTOR_NOTIFICATIONS = {
|
const INSPECTOR_NOTIFICATIONS = {
|
||||||
// Fires once the Inspector highlights an element in the page.
|
|
||||||
HIGHLIGHTING: "inspector-highlighting",
|
|
||||||
|
|
||||||
// Fires once the Inspector stops highlighting any element.
|
|
||||||
UNHIGHLIGHTING: "inspector-unhighlighting",
|
|
||||||
|
|
||||||
// Fires once the Inspector completes the initialization and opens up on
|
// Fires once the Inspector completes the initialization and opens up on
|
||||||
// screen.
|
// screen.
|
||||||
OPENED: "inspector-opened",
|
OPENED: "inspector-opened",
|
||||||
|
@ -298,8 +282,11 @@ InspectorUI.prototype = {
|
||||||
|
|
||||||
this.progressListener = new InspectorProgressListener(this);
|
this.progressListener = new InspectorProgressListener(this);
|
||||||
|
|
||||||
|
this.chromeWin.addEventListener("keypress", this, false);
|
||||||
|
|
||||||
// initialize the highlighter
|
// initialize the highlighter
|
||||||
this.initializeHighlighter();
|
this.highlighter = new Highlighter(this.chromeWin);
|
||||||
|
this.highlighterReady();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -335,17 +322,6 @@ InspectorUI.prototype = {
|
||||||
// Extras go here.
|
// Extras go here.
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize highlighter.
|
|
||||||
*/
|
|
||||||
initializeHighlighter: function IUI_initializeHighlighter()
|
|
||||||
{
|
|
||||||
this.highlighter = new Highlighter(this);
|
|
||||||
this.browser.addEventListener("keypress", this, true);
|
|
||||||
this.highlighter.highlighterContainer.addEventListener("keypress", this, true);
|
|
||||||
this.highlighterReady();
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the InspectorStore.
|
* Initialize the InspectorStore.
|
||||||
*/
|
*/
|
||||||
|
@ -419,8 +395,9 @@ InspectorUI.prototype = {
|
||||||
this.tabbrowser.tabContainer.removeEventListener("TabSelect", this, false);
|
this.tabbrowser.tabContainer.removeEventListener("TabSelect", this, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.chromeWin.removeEventListener("keypress", this, false);
|
||||||
|
|
||||||
this.stopInspecting();
|
this.stopInspecting();
|
||||||
this.browser.removeEventListener("keypress", this, true);
|
|
||||||
|
|
||||||
this.saveToolState(this.winID);
|
this.saveToolState(this.winID);
|
||||||
this.toolsDo(function IUI_toolsHide(aTool) {
|
this.toolsDo(function IUI_toolsHide(aTool) {
|
||||||
|
@ -431,9 +408,6 @@ InspectorUI.prototype = {
|
||||||
this.hideSidebar();
|
this.hideSidebar();
|
||||||
|
|
||||||
if (this.highlighter) {
|
if (this.highlighter) {
|
||||||
this.highlighter.highlighterContainer.removeEventListener("keypress",
|
|
||||||
this,
|
|
||||||
true);
|
|
||||||
this.highlighter.destroy();
|
this.highlighter.destroy();
|
||||||
this.highlighter = null;
|
this.highlighter = null;
|
||||||
}
|
}
|
||||||
|
@ -470,12 +444,10 @@ InspectorUI.prototype = {
|
||||||
this.treePanel.closeEditor();
|
this.treePanel.closeEditor();
|
||||||
|
|
||||||
this.inspectToolbutton.checked = true;
|
this.inspectToolbutton.checked = true;
|
||||||
this.highlighter.attachInspectListeners();
|
|
||||||
|
|
||||||
this.inspecting = true;
|
this.inspecting = true;
|
||||||
this.toolsDim(true);
|
this.toolsDim(true);
|
||||||
this.highlighter.veilContainer.removeAttribute("locked");
|
this.highlighter.unlock();
|
||||||
this.highlighter.nodeInfo.container.removeAttribute("locked");
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -491,21 +463,15 @@ InspectorUI.prototype = {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.inspectToolbutton.checked = false;
|
this.inspectToolbutton.checked = false;
|
||||||
// Detach event listeners from content window and child windows to disable
|
|
||||||
// highlighting. We still want to be notified if the user presses "ESCAPE"
|
|
||||||
// to close the inspector, or "RETURN" to unlock the node, so we don't
|
|
||||||
// remove the "keypress" event until the highlighter is removed.
|
|
||||||
this.highlighter.detachInspectListeners();
|
|
||||||
|
|
||||||
this.inspecting = false;
|
this.inspecting = false;
|
||||||
this.toolsDim(false);
|
this.toolsDim(false);
|
||||||
if (this.highlighter.node) {
|
if (this.highlighter.getNode()) {
|
||||||
this.select(this.highlighter.node, true, true, !aPreventScroll);
|
this.select(this.highlighter.getNode(), true, true, !aPreventScroll);
|
||||||
} else {
|
} else {
|
||||||
this.select(null, true, true);
|
this.select(null, true, true);
|
||||||
}
|
}
|
||||||
this.highlighter.veilContainer.setAttribute("locked", true);
|
this.highlighter.lock();
|
||||||
this.highlighter.nodeInfo.container.setAttribute("locked", true);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -530,7 +496,7 @@ InspectorUI.prototype = {
|
||||||
if (forceUpdate || aNode != this.selection) {
|
if (forceUpdate || aNode != this.selection) {
|
||||||
this.selection = aNode;
|
this.selection = aNode;
|
||||||
if (!this.inspecting) {
|
if (!this.inspecting) {
|
||||||
this.highlighter.highlightNode(this.selection);
|
this.highlighter.highlight(this.selection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -549,7 +515,7 @@ InspectorUI.prototype = {
|
||||||
*/
|
*/
|
||||||
nodeChanged: function IUI_nodeChanged(aUpdater)
|
nodeChanged: function IUI_nodeChanged(aUpdater)
|
||||||
{
|
{
|
||||||
this.highlighter.highlight();
|
this.highlighter.invalidateSize();
|
||||||
this.toolsOnChanged(aUpdater);
|
this.toolsOnChanged(aUpdater);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -561,6 +527,20 @@ InspectorUI.prototype = {
|
||||||
// Setup the InspectorStore or restore state
|
// Setup the InspectorStore or restore state
|
||||||
this.initializeStore();
|
this.initializeStore();
|
||||||
|
|
||||||
|
let self = this;
|
||||||
|
|
||||||
|
this.highlighter.addListener("locked", function() {
|
||||||
|
self.stopInspecting();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.highlighter.addListener("unlocked", function() {
|
||||||
|
self.startInspecting();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.highlighter.addListener("nodeselected", function() {
|
||||||
|
self.select(self.highlighter.getNode(), false, false);
|
||||||
|
});
|
||||||
|
|
||||||
if (this.store.getValue(this.winID, "inspecting")) {
|
if (this.store.getValue(this.winID, "inspecting")) {
|
||||||
this.startInspecting();
|
this.startInspecting();
|
||||||
}
|
}
|
||||||
|
@ -570,6 +550,8 @@ InspectorUI.prototype = {
|
||||||
this.win.focus();
|
this.win.focus();
|
||||||
Services.obs.notifyObservers({wrappedJSObject: this},
|
Services.obs.notifyObservers({wrappedJSObject: this},
|
||||||
INSPECTOR_NOTIFICATIONS.OPENED, null);
|
INSPECTOR_NOTIFICATIONS.OPENED, null);
|
||||||
|
|
||||||
|
this.highlighter.highlight();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -636,74 +618,6 @@ InspectorUI.prototype = {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
break;
|
break;
|
||||||
case this.chromeWin.KeyEvent.DOM_VK_RETURN:
|
|
||||||
this.toggleInspection();
|
|
||||||
event.preventDefault();
|
|
||||||
event.stopPropagation();
|
|
||||||
break;
|
|
||||||
case this.chromeWin.KeyEvent.DOM_VK_LEFT:
|
|
||||||
let node;
|
|
||||||
if (this.selection) {
|
|
||||||
node = this.selection.parentNode;
|
|
||||||
} else {
|
|
||||||
node = this.defaultSelection;
|
|
||||||
}
|
|
||||||
if (node && this.highlighter.isNodeHighlightable(node)) {
|
|
||||||
this.inspectNode(node, true);
|
|
||||||
}
|
|
||||||
event.preventDefault();
|
|
||||||
event.stopPropagation();
|
|
||||||
break;
|
|
||||||
case this.chromeWin.KeyEvent.DOM_VK_RIGHT:
|
|
||||||
if (this.selection) {
|
|
||||||
// Find the first child that is highlightable.
|
|
||||||
for (let i = 0; i < this.selection.childNodes.length; i++) {
|
|
||||||
node = this.selection.childNodes[i];
|
|
||||||
if (node && this.highlighter.isNodeHighlightable(node)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
node = this.defaultSelection;
|
|
||||||
}
|
|
||||||
if (node && this.highlighter.isNodeHighlightable(node)) {
|
|
||||||
this.inspectNode(node, true);
|
|
||||||
}
|
|
||||||
event.preventDefault();
|
|
||||||
event.stopPropagation();
|
|
||||||
break;
|
|
||||||
case this.chromeWin.KeyEvent.DOM_VK_UP:
|
|
||||||
if (this.selection) {
|
|
||||||
// Find a previous sibling that is highlightable.
|
|
||||||
node = this.selection.previousSibling;
|
|
||||||
while (node && !this.highlighter.isNodeHighlightable(node)) {
|
|
||||||
node = node.previousSibling;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
node = this.defaultSelection;
|
|
||||||
}
|
|
||||||
if (node && this.highlighter.isNodeHighlightable(node)) {
|
|
||||||
this.inspectNode(node, true);
|
|
||||||
}
|
|
||||||
event.preventDefault();
|
|
||||||
event.stopPropagation();
|
|
||||||
break;
|
|
||||||
case this.chromeWin.KeyEvent.DOM_VK_DOWN:
|
|
||||||
if (this.selection) {
|
|
||||||
// Find a next sibling that is highlightable.
|
|
||||||
node = this.selection.nextSibling;
|
|
||||||
while (node && !this.highlighter.isNodeHighlightable(node)) {
|
|
||||||
node = node.nextSibling;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
node = this.defaultSelection;
|
|
||||||
}
|
|
||||||
if (node && this.highlighter.isNodeHighlightable(node)) {
|
|
||||||
this.inspectNode(node, true);
|
|
||||||
}
|
|
||||||
event.preventDefault();
|
|
||||||
event.stopPropagation();
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -826,77 +740,12 @@ InspectorUI.prototype = {
|
||||||
inspectNode: function IUI_inspectNode(aNode, aScroll)
|
inspectNode: function IUI_inspectNode(aNode, aScroll)
|
||||||
{
|
{
|
||||||
this.select(aNode, true, true);
|
this.select(aNode, true, true);
|
||||||
this.highlighter.highlightNode(aNode, { scroll: aScroll });
|
this.highlighter.highlight(aNode, aScroll);
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find an element from the given coordinates. This method descends through
|
|
||||||
* frames to find the element the user clicked inside frames.
|
|
||||||
*
|
|
||||||
* @param DOMDocument aDocument the document to look into.
|
|
||||||
* @param integer aX
|
|
||||||
* @param integer aY
|
|
||||||
* @returns Node|null the element node found at the given coordinates.
|
|
||||||
*/
|
|
||||||
elementFromPoint: function IUI_elementFromPoint(aDocument, aX, aY)
|
|
||||||
{
|
|
||||||
let node = aDocument.elementFromPoint(aX, aY);
|
|
||||||
if (node && node.contentDocument) {
|
|
||||||
if (node instanceof Ci.nsIDOMHTMLIFrameElement) {
|
|
||||||
let rect = node.getBoundingClientRect();
|
|
||||||
|
|
||||||
// Gap between the iframe and its content window.
|
|
||||||
let [offsetTop, offsetLeft] = this.getIframeContentOffset(node);
|
|
||||||
|
|
||||||
aX -= rect.left + offsetLeft;
|
|
||||||
aY -= rect.top + offsetTop;
|
|
||||||
|
|
||||||
if (aX < 0 || aY < 0) {
|
|
||||||
// Didn't reach the content document, still over the iframe.
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (node instanceof Ci.nsIDOMHTMLIFrameElement ||
|
|
||||||
node instanceof Ci.nsIDOMHTMLFrameElement) {
|
|
||||||
let subnode = this.elementFromPoint(node.contentDocument, aX, aY);
|
|
||||||
if (subnode) {
|
|
||||||
node = subnode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return node;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
//// Utility functions
|
//// Utility functions
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns iframe content offset (iframe border + padding).
|
|
||||||
* Note: this function shouldn't need to exist, had the platform provided a
|
|
||||||
* suitable API for determining the offset between the iframe's content and
|
|
||||||
* its bounding client rect. Bug 626359 should provide us with such an API.
|
|
||||||
*
|
|
||||||
* @param aIframe
|
|
||||||
* The iframe.
|
|
||||||
* @returns array [offsetTop, offsetLeft]
|
|
||||||
* offsetTop is the distance from the top of the iframe and the
|
|
||||||
* top of the content document.
|
|
||||||
* offsetLeft is the distance from the left of the iframe and the
|
|
||||||
* left of the content document.
|
|
||||||
*/
|
|
||||||
getIframeContentOffset: function IUI_getIframeContentOffset(aIframe)
|
|
||||||
{
|
|
||||||
let style = aIframe.contentWindow.getComputedStyle(aIframe, null);
|
|
||||||
|
|
||||||
let paddingTop = parseInt(style.getPropertyValue("padding-top"));
|
|
||||||
let paddingLeft = parseInt(style.getPropertyValue("padding-left"));
|
|
||||||
|
|
||||||
let borderTop = parseInt(style.getPropertyValue("border-top-width"));
|
|
||||||
let borderLeft = parseInt(style.getPropertyValue("border-left-width"));
|
|
||||||
|
|
||||||
return [borderTop + paddingTop, borderLeft + paddingLeft];
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the unique ID of a window object.
|
* Retrieve the unique ID of a window object.
|
||||||
*
|
*
|
||||||
|
@ -1252,6 +1101,12 @@ InspectorUI.prototype = {
|
||||||
this.getToolbarButtonId(tool.id)).removeAttribute("checked");
|
this.getToolbarButtonId(tool.id)).removeAttribute("checked");
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
}
|
}
|
||||||
|
if (this.store.getValue(this.winID, "inspecting")) {
|
||||||
|
this.highlighter.unlock();
|
||||||
|
} else {
|
||||||
|
this.highlighter.lock();
|
||||||
|
}
|
||||||
|
|
||||||
Services.obs.notifyObservers(null, INSPECTOR_NOTIFICATIONS.STATE_RESTORED, null);
|
Services.obs.notifyObservers(null, INSPECTOR_NOTIFICATIONS.STATE_RESTORED, null);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,204 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||||
|
/* ***** BEGIN LICENSE BLOCK *****
|
||||||
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||||
|
*
|
||||||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||||||
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
* http://www.mozilla.org/MPL/
|
||||||
|
*
|
||||||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||||
|
* for the specific language governing rights and limitations under the
|
||||||
|
* License.
|
||||||
|
*
|
||||||
|
* The Original Code is the Mozilla LayoutHelpers Module.
|
||||||
|
*
|
||||||
|
* The Initial Developer of the Original Code is
|
||||||
|
* The Mozilla Foundation.
|
||||||
|
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||||
|
* the Initial Developer. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Contributor(s):
|
||||||
|
* Rob Campbell <rcampbell@mozilla.com> (original author)
|
||||||
|
* Mihai Șucan <mihai.sucan@gmail.com>
|
||||||
|
* Julian Viereck <jviereck@mozilla.com>
|
||||||
|
* Paul Rouget <paul@mozilla.com>
|
||||||
|
* Kyle Simpson <ksimpson@mozilla.com>
|
||||||
|
*
|
||||||
|
* Alternatively, the contents of this file may be used under the terms of
|
||||||
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||||
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||||
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||||
|
* of those above. If you wish to allow use of your version of this file only
|
||||||
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||||
|
* use your version of this file under the terms of the MPL, indicate your
|
||||||
|
* decision by deleting the provisions above and replace them with the notice
|
||||||
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||||
|
* the provisions above, a recipient may use your version of this file under
|
||||||
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||||
|
*
|
||||||
|
* ***** END LICENSE BLOCK ***** */
|
||||||
|
|
||||||
|
const Cu = Components.utils;
|
||||||
|
const Ci = Components.interfaces;
|
||||||
|
const Cr = Components.results;
|
||||||
|
|
||||||
|
var EXPORTED_SYMBOLS = ["LayoutHelpers"];
|
||||||
|
|
||||||
|
LayoutHelpers = {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the position and the dimensions for the visible portion
|
||||||
|
* of a node, relativalely to the root window.
|
||||||
|
*
|
||||||
|
* @param nsIDOMNode aNode
|
||||||
|
* a DOM element to be highlighted
|
||||||
|
*/
|
||||||
|
getDirtyRect: function LH_getDirectyRect(aNode) {
|
||||||
|
let frameWin = aNode.ownerDocument.defaultView;
|
||||||
|
let clientRect = aNode.getBoundingClientRect();
|
||||||
|
|
||||||
|
// Go up in the tree of frames to determine the correct rectangle.
|
||||||
|
// clientRect is read-only, we need to be able to change properties.
|
||||||
|
rect = {top: clientRect.top,
|
||||||
|
left: clientRect.left,
|
||||||
|
width: clientRect.width,
|
||||||
|
height: clientRect.height};
|
||||||
|
|
||||||
|
// We iterate through all the parent windows.
|
||||||
|
while (true) {
|
||||||
|
|
||||||
|
// Does the selection overflow on the right of its window?
|
||||||
|
let diffx = frameWin.innerWidth - (rect.left + rect.width);
|
||||||
|
if (diffx < 0) {
|
||||||
|
rect.width += diffx;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Does the selection overflow on the bottom of its window?
|
||||||
|
let diffy = frameWin.innerHeight - (rect.top + rect.height);
|
||||||
|
if (diffy < 0) {
|
||||||
|
rect.height += diffy;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Does the selection overflow on the left of its window?
|
||||||
|
if (rect.left < 0) {
|
||||||
|
rect.width += rect.left;
|
||||||
|
rect.left = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Does the selection overflow on the top of its window?
|
||||||
|
if (rect.top < 0) {
|
||||||
|
rect.height += rect.top;
|
||||||
|
rect.top = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Selection has been clipped to fit in its own window.
|
||||||
|
|
||||||
|
// Are we in the top-level window?
|
||||||
|
if (frameWin.parent === frameWin || !frameWin.frameElement) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We are in an iframe.
|
||||||
|
// We take into account the parent iframe position and its
|
||||||
|
// offset (borders and padding).
|
||||||
|
let frameRect = frameWin.frameElement.getBoundingClientRect();
|
||||||
|
|
||||||
|
let [offsetTop, offsetLeft] =
|
||||||
|
this.getIframeContentOffset(frameWin.frameElement);
|
||||||
|
|
||||||
|
rect.top += frameRect.top + offsetTop;
|
||||||
|
rect.left += frameRect.left + offsetLeft;
|
||||||
|
|
||||||
|
frameWin = frameWin.parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rect;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns iframe content offset (iframe border + padding).
|
||||||
|
* Note: this function shouldn't need to exist, had the platform provided a
|
||||||
|
* suitable API for determining the offset between the iframe's content and
|
||||||
|
* its bounding client rect. Bug 626359 should provide us with such an API.
|
||||||
|
*
|
||||||
|
* @param aIframe
|
||||||
|
* The iframe.
|
||||||
|
* @returns array [offsetTop, offsetLeft]
|
||||||
|
* offsetTop is the distance from the top of the iframe and the
|
||||||
|
* top of the content document.
|
||||||
|
* offsetLeft is the distance from the left of the iframe and the
|
||||||
|
* left of the content document.
|
||||||
|
*/
|
||||||
|
getIframeContentOffset: function LH_getIframeContentOffset(aIframe) {
|
||||||
|
let style = aIframe.contentWindow.getComputedStyle(aIframe, null);
|
||||||
|
|
||||||
|
let paddingTop = parseInt(style.getPropertyValue("padding-top"));
|
||||||
|
let paddingLeft = parseInt(style.getPropertyValue("padding-left"));
|
||||||
|
|
||||||
|
let borderTop = parseInt(style.getPropertyValue("border-top-width"));
|
||||||
|
let borderLeft = parseInt(style.getPropertyValue("border-left-width"));
|
||||||
|
|
||||||
|
return [borderTop + paddingTop, borderLeft + paddingLeft];
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply the page zoom factor.
|
||||||
|
*/
|
||||||
|
getZoomedRect: function LH_getZoomedRect(aWin, aRect) {
|
||||||
|
// get page zoom factor, if any
|
||||||
|
let zoom =
|
||||||
|
aWin.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||||
|
.getInterface(Components.interfaces.nsIDOMWindowUtils)
|
||||||
|
.screenPixelsPerCSSPixel;
|
||||||
|
|
||||||
|
// adjust rect for zoom scaling
|
||||||
|
let aRectScaled = {};
|
||||||
|
for (let prop in aRect) {
|
||||||
|
aRectScaled[prop] = aRect[prop] * zoom;
|
||||||
|
}
|
||||||
|
|
||||||
|
return aRectScaled;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find an element from the given coordinates. This method descends through
|
||||||
|
* frames to find the element the user clicked inside frames.
|
||||||
|
*
|
||||||
|
* @param DOMDocument aDocument the document to look into.
|
||||||
|
* @param integer aX
|
||||||
|
* @param integer aY
|
||||||
|
* @returns Node|null the element node found at the given coordinates.
|
||||||
|
*/
|
||||||
|
getElementFromPoint: function LH_elementFromPoint(aDocument, aX, aY)
|
||||||
|
{
|
||||||
|
let node = aDocument.elementFromPoint(aX, aY);
|
||||||
|
if (node && node.contentDocument) {
|
||||||
|
if (node instanceof Ci.nsIDOMHTMLIFrameElement) {
|
||||||
|
let rect = node.getBoundingClientRect();
|
||||||
|
|
||||||
|
// Gap between the iframe and its content window.
|
||||||
|
let [offsetTop, offsetLeft] = LayoutHelpers.getIframeContentOffset(node);
|
||||||
|
|
||||||
|
aX -= rect.left + offsetLeft;
|
||||||
|
aY -= rect.top + offsetTop;
|
||||||
|
|
||||||
|
if (aX < 0 || aY < 0) {
|
||||||
|
// Didn't reach the content document, still over the iframe.
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (node instanceof Ci.nsIDOMHTMLIFrameElement ||
|
||||||
|
node instanceof Ci.nsIDOMHTMLFrameElement) {
|
||||||
|
let subnode = this.getElementFromPoint(node.contentDocument, aX, aY);
|
||||||
|
if (subnode) {
|
||||||
|
node = subnode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
},
|
||||||
|
};
|
|
@ -54,3 +54,4 @@ include $(topsrcdir)/config/rules.mk
|
||||||
libs::
|
libs::
|
||||||
$(NSINSTALL) $(srcdir)/Templater.jsm $(FINAL_TARGET)/modules/devtools
|
$(NSINSTALL) $(srcdir)/Templater.jsm $(FINAL_TARGET)/modules/devtools
|
||||||
$(NSINSTALL) $(srcdir)/Promise.jsm $(FINAL_TARGET)/modules/devtools
|
$(NSINSTALL) $(srcdir)/Promise.jsm $(FINAL_TARGET)/modules/devtools
|
||||||
|
$(NSINSTALL) $(srcdir)/LayoutHelpers.jsm $(FINAL_TARGET)/modules/devtools
|
||||||
|
|
Загрузка…
Ссылка в новой задаче