Bug 547453: create a web page inspector for Firefox, r=gavin

This commit is contained in:
Rob Campbell 2010-05-13 16:38:21 -04:00
parent cfc66f7a7c
commit 3350ea7bff
12 changed files with 1141 additions and 2 deletions

View File

@ -21,6 +21,7 @@
#
# Contributor(s):
# Ehsan Akhgari <ehsan.akhgari@gmail.com>
# Rob Campbell <rcampbell@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
@ -598,6 +599,12 @@
accesskey="&addons.accesskey;"
command="Tools:Addons"/>
<menuseparator id="devToolsSeparator"/>
<menuitem id="menu_pageinspect"
type="checkbox"
label="&inspectMenu.label;"
accesskey="&inspectMenu.accesskey;"
key="&inspectMenu.commandkey;"
command="Tools:Inspect"/>
<menuitem id="javascriptConsole"
label="&errorConsoleCmd.label;"
accesskey="&errorConsoleCmd.accesskey;"

View File

@ -24,6 +24,7 @@
# Blake Ross <blakeross@telocity.com>
# Shawn Wilsher <me@shawnwilsher.com>
# Ehsan Akhgari <ehsan.akhgari@gmail.com>
# Rob Campbell <rcampbell@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
@ -124,6 +125,7 @@
<command id="Tools:Search" oncommand="BrowserSearch.webSearch();"/>
<command id="Tools:Downloads" oncommand="BrowserDownloadsUI();"/>
<command id="Tools:Inspect" oncommand="InspectorUI.toggleInspectorUI();"/>
<command id="Tools:Addons" oncommand="BrowserOpenAddonsMgr();"/>
<command id="Tools:Sanitize"
oncommand="Cc['@mozilla.org/browser/browserglue;1'].getService(Ci.nsIBrowserGlue).sanitize(window);"/>
@ -205,8 +207,6 @@
# system setting to use emacs emulation, and we should respect it. Focus-Search-Box
# is a fundamental keybinding and we are maintaining a XP binding so that it is easy
# for people to switch to Linux.
#
# Do *not* tamper with these values without talking to ben@mozilla.org
#
<key id="key_search" key="&searchFocus.commandkey;" command="Tools:Search" modifiers="accel"/>
#ifdef XP_MACOSX
@ -222,6 +222,7 @@
<key id="key_openDownloads" key="&downloads.commandkey;" command="Tools:Downloads" modifiers="accel"/>
#endif
<key id="key_errorConsole" key="&errorConsoleCmd.commandkey;" oncommand="toJavaScriptConsole();" modifiers="accel,shift"/>
<key id="key_inspect" key="&inspectMenu.commandkey;" command="Tools:Inspect" modifiers="accel,shift"/>
<key id="openFileKb" key="&openFileCmd.commandkey;" command="Browser:OpenFile" modifiers="accel"/>
<key id="key_savePage" key="&savePageCmd.commandkey;" command="Browser:SavePage" modifiers="accel"/>
<key id="printKb" key="&printCmd.commandkey;" command="cmd_print" modifiers="accel"/>

View File

@ -256,3 +256,10 @@ window[chromehidden~="toolbar"] toolbar:not(.toolbar-primary):not(.chromeclass-m
-moz-binding: url("chrome://global/content/bindings/toolbarbutton.xml#toolbarbutton-image");
margin: 0;
}
/* Inspector / Highlighter */
#highlighter-panel {
-moz-appearance: none;
-moz-window-shadow: none;
}

View File

@ -51,6 +51,7 @@
# Dietrich Ayala <dietrich@mozilla.com>
# Gavin Sharp <gavin@gavinsharp.com>
# Justin Dolske <dolske@mozilla.com>
# Rob Campbell <rcampbell@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
@ -128,6 +129,7 @@ let gInitialPages = [
];
#include browser-fullZoom.js
#include inspector.js
#include browser-places.js
#include browser-tabPreviews.js

View File

@ -33,6 +33,7 @@
# Dão Gottwald <dao@mozilla.com>
# Ehsan Akhgari <ehsan.akhgari@gmail.com>
# Robert Strong <robert.bugzilla@gmail.com>
# Rob Campbell <rcampbell@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
@ -218,6 +219,35 @@
</hbox>
</panel>
<panel id="highlighter-panel"
hidden="true"
ignorekeys="true"
noautofocus="true"
noautohide="true"
onclick="InspectorUI.stopInspecting();"
onmousemove="InspectorUI.highlighter.handleMouseMove(event);"/>
<panel id="inspector-panel"
orient="vertical"
hidden="true"
ignorekeys="true"
noautofocus="true"
noautohide="true"
level="top"
aria-labelledby="inspectPagePanelTitle">
<tree id="inspector-tree" class="plain" seltype="single" treelines="true"
onselect="InspectorUI.onTreeSelected()" flex="1">
<treecols>
<treecol id="colNodeName" label="nodeName" primary="true"
persist="width,hidden,ordinal" flex="1"/>
<splitter class="tree-splitter"/>
<treecol id="colNodeValue" label="nodeValue"
persist="width,hidden,ordinal" flex="1"/>
</treecols>
<treechildren id="inspector-tree-body"/>
</tree>
</panel>
<popup id="toolbar-context-menu"
onpopupshowing="onViewToolbarsPopupShowing(event);">
<menuseparator/>
@ -691,11 +721,13 @@
label="&pasteCmd.label;"
command="cmd_paste"
tooltiptext="&pasteButton.tooltip;"/>
<toolbarbutton id="fullscreen-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
observes="View:FullScreen"
type="checkbox"
label="&fullScreenCmd.label;"
tooltiptext="&fullScreenButton.tooltip;"/>
</toolbarpalette>
</toolbox>

View File

@ -0,0 +1,758 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
#ifdef 0
/* ***** 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 Inspector 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)
*
* 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 ***** */
#endif
const INSPECTOR_INVISIBLE_ELEMENTS = {
"head": true,
"base": true,
"basefont": true,
"isindex": true,
"link": true,
"meta": true,
"script": true,
"style": true,
"title": true,
};
///////////////////////////////////////////////////////////////////////////
//// PanelHighlighter
/**
* A highlighter mechanism using xul panels.
*
* @param aBrowser
* The XUL browser object for the content window being highlighted.
* @param aColor
* A string containing an RGB color for the panel background.
* @param aBorderSize
* A number representing the border thickness of the panel.
* @param anOpacity
* A number representing the alpha value of the panel background.
*/
function PanelHighlighter(aBrowser, aColor, aBorderSize, anOpacity)
{
this.panel = document.getElementById("highlighter-panel");
this.panel.hidden = false;
this.browser = aBrowser;
this.win = this.browser.contentWindow;
this.backgroundColor = aColor;
this.border = aBorderSize;
this.opacity = anOpacity;
this.updatePanelStyles();
}
PanelHighlighter.prototype = {
/**
* Update the panel's style object with current settings.
* TODO see bugXXXXXX, https://wiki.mozilla.org/Firefox/Projects/Inspector#0.7
* and, https://wiki.mozilla.org/Firefox/Projects/Inspector#1.0.
*/
updatePanelStyles: function PanelHighlighter_updatePanelStyles()
{
let style = this.panel.style;
style.backgroundColor = this.backgroundColor;
style.border = "solid blue " + this.border + "px";
style.MozBorderRadius = "4px";
style.opacity = this.opacity;
},
/**
* Highlight this.node, unhilighting first if necessary.
*
* @param scroll
* Boolean determining whether to scroll or not.
*/
highlight: function PanelHighlighter_highlight(scroll)
{
// node is not set or node is not highlightable, bail
if (!this.isNodeHighlightable()) {
return;
}
this.unhighlight();
let rect = this.node.getBoundingClientRect();
if (scroll) {
this.node.scrollIntoView();
}
if (this.viewContainsRect(rect)) {
// TODO check for offscreen boundaries, bug565301
this.panel.openPopup(this.node, "overlap", 0, 0, false, false);
this.panel.sizeTo(rect.width, rect.height);
} else {
this.highlightVisibleRegion(rect);
}
},
/**
* Highlight the given node.
*
* @param element
* a DOM element to be highlighted
* @param params
* extra parameters object
*/
highlightNode: function PanelHighlighter_highlightNode(element, params)
{
this.node = element;
this.highlight(params && params.scroll);
},
/**
* Highlight the visible region of the region described by aRect, if any.
*
* @param aRect
* @returns boolean
* was a region highlighted?
*/
highlightVisibleRegion: function PanelHighlighter_highlightVisibleRegion(aRect)
{
let offsetX = 0;
let offsetY = 0;
let width = 0;
let height = 0;
let visibleWidth = this.win.innerWidth;
let visibleHeight = this.win.innerHeight;
// If any of these edges are out-of-bounds, the node's rectangle is
// completely out-of-view and we can return.
if (aRect.top > visibleHeight || aRect.left > visibleWidth ||
aRect.bottom < 0 || aRect.right < 0) {
return false;
}
// Calculate node offsets, if values are negative, then start the offsets
// at their absolute values from node origin. The delta should be the edge
// of view.
offsetX = aRect.left < 0 ? Math.abs(aRect.left) : 0;
offsetY = aRect.top < 0 ? Math.abs(aRect.top) : 0;
// Calculate actual node width, taking into account the available visible
// width and then subtracting the offset for the final dimension.
width = aRect.right > visibleWidth ? visibleWidth - aRect.left :
aRect.width;
width -= offsetX;
// Calculate actual node height using the same formula as above for width.
height = aRect.bottom > visibleHeight ? visibleHeight - aRect.top :
aRect.height;
height -= offsetY;
// If width and height are non-negative, open the highlighter popup over the
// node and sizeTo width and height.
if (width > 0 && height > 0) {
this.panel.openPopup(this.node, "overlap", offsetX, offsetY, false,
false);
this.panel.sizeTo(width, height);
return true;
}
return false;
},
/**
* Close the highlighter panel.
*/
unhighlight: function PanelHighlighter_unhighlight()
{
if (this.isHighlighting) {
this.panel.hidePopup();
}
},
/**
* Is the highlighter panel open?
*
* @returns boolean
*/
get isHighlighting()
{
return this.panel.state == "open";
},
/**
* Return the midpoint of a line from pointA to pointB.
*
* @param pointA
* An object with x and y properties.
* @param pointB
* An object with x and y properties.
* @returns aPoint
* An object with x and y properties.
*/
midPoint: function PanelHighlighter_midPoint(pointA, pointB)
{
let pointC = { };
pointC.x = (pointB.x - pointA.x) / 2 + pointA.x;
pointC.y = (pointB.y - pointA.y) / 2 + pointA.y;
return pointC;
},
/**
* Return the node under the highlighter rectangle. Useful for testing.
* Calculation based on midpoint of diagonal from top left to bottom right
* of panel.
*
* @returns a DOM node or null if none
*/
get highlitNode()
{
// No highlighter panel? Bail.
if (!this.isHighlighting) {
return null;
}
let browserRect = this.browser.getBoundingClientRect();
let clientRect = this.panel.getBoundingClientRect();
// Calculate top left point offset minus browser chrome.
let a = {
x: clientRect.left - browserRect.left,
y: clientRect.top - browserRect.top
};
// Calculate bottom right point minus browser chrome.
let b = {
x: clientRect.right - browserRect.left,
y: clientRect.bottom - browserRect.top
};
// Get midpoint of diagonal line.
let midpoint = this.midPoint(a, b);
return this.win.document.elementFromPoint(midpoint.x, midpoint.y);
},
/**
* Is this.node highlightable?
*
* @returns boolean
*/
isNodeHighlightable: function PanelHighlighter_isNodeHighlightable()
{
if (!this.node) {
return false;
}
let nodeName = this.node.nodeName.toLowerCase();
if (nodeName[0] == '#') {
return false;
}
return !INSPECTOR_INVISIBLE_ELEMENTS[nodeName];
},
/**
* Returns true if the given viewport-relative rect is within the visible area
* of the window.
*
* @param aRect
* a CSS rectangle object
* @returns boolean
*/
viewContainsRect: function PanelHighlighter_viewContainsRect(aRect)
{
let visibleWidth = this.win.innerWidth;
let visibleHeight = this.win.innerHeight;
return ((0 <= aRect.left) && (aRect.right <= visibleWidth) &&
(0 <= aRect.top) && (aRect.bottom <= visibleHeight))
},
/////////////////////////////////////////////////////////////////////////
//// Event Handling
/**
* Handle mousemoves in panel when InspectorUI.inspecting is true.
*
* @param event
* The MouseEvent triggering the method.
*/
handleMouseMove: function PanelHighlighter_handleMouseMove(event)
{
if (!InspectorUI.inspecting) {
return;
}
let browserRect = this.browser.getBoundingClientRect();
let element = this.win.document.elementFromPoint(event.clientX -
browserRect.left, event.clientY - browserRect.top);
if (element && element != this.node) {
InspectorUI.inspectNode(element);
}
},
};
///////////////////////////////////////////////////////////////////////////
//// InspectorTreeView
/**
* TreeView object to manage the view of the DOM tree. Wraps and provides an
* interface to an inIDOMView object
*
* @param aWindow
* a top-level window object
*/
function InspectorTreeView(aWindow)
{
this.tree = document.getElementById("inspector-tree");
this.treeBody = document.getElementById("inspector-tree-body");
this.view = Cc["@mozilla.org/inspector/dom-view;1"]
.createInstance(Ci.inIDOMView);
this.view.showSubDocuments = true;
this.view.whatToShow = NodeFilter.SHOW_ALL;
this.tree.view = this.view;
this.contentWindow = aWindow;
this.view.rootNode = aWindow.document;
this.view.rebuild();
}
InspectorTreeView.prototype = {
get editable() { return false; },
get selection() { return this.view.selection; },
/**
* Destroy the view.
*/
destroy: function ITV_destroy()
{
this.tree.view = null;
this.view = null;
this.tree = null;
},
/**
* Get the cell text at a given row and column.
*
* @param aRow
* The row index of the desired cell.
* @param aCol
* The column index of the desired cell.
* @returns string
*/
getCellText: function ITV_getCellText(aRow, aCol)
{
return this.view.getCellText(aRow, aCol);
},
/**
* Get the index of the selected row.
*
* @returns number
*/
get selectionIndex()
{
return this.selection.currentIndex;
},
/**
* Get the corresponding node for the currently-selected row in the tree.
*
* @returns DOMNode
*/
get selectedNode()
{
let rowIndex = this.selectionIndex;
return this.view.getNodeFromRowIndex(rowIndex);
},
/**
* Set the selected row in the table to the specified index.
*
* @param anIndex
* The index to set the selection to.
*/
set selectedRow(anIndex)
{
this.view.selection.select(anIndex);
this.tree.treeBoxObject.ensureRowIsVisible(anIndex);
},
/**
* Set the selected node to the specified document node.
*
* @param aNode
* The document node to select in the tree.
*/
set selectedNode(aNode)
{
let rowIndex = this.view.getRowIndexFromNode(aNode);
if (rowIndex > -1) {
this.selectedRow = rowIndex;
} else {
this.selectElementInTree(aNode);
}
},
/**
* Select the given node in the tree, searching for and expanding rows
* as-needed.
*
* @param aNode
* The document node to select in the three.
* @returns boolean
* Whether a node was selected or not if not found.
*/
selectElementInTree: function ITV_selectElementInTree(aNode)
{
if (!aNode) {
this.view.selection.select(null);
return false;
}
// Keep searching until a pre-created ancestor is found, then
// open each ancestor until the found element is created.
let domUtils = Cc["@mozilla.org/inspector/dom-utils;1"].
getService(Ci.inIDOMUtils);
let line = [];
let parent = aNode;
let index = null;
while (parent) {
index = this.view.getRowIndexFromNode(parent);
line.push(parent);
if (index < 0) {
// Row for this node hasn't been created yet.
parent = domUtils.getParentForNode(parent,
this.view.showAnonymousContent);
} else {
break;
}
}
// We have all the ancestors, now open them one-by-one from the top
// to bottom.
let lastIndex;
let view = this.tree.treeBoxObject.view;
for (let i = line.length - 1; i >= 0; i--) {
index = this.view.getRowIndexFromNode(line[i]);
if (index < 0) {
// Can't find the row, so stop trying to descend.
break;
}
if (i > 0 && !view.isContainerOpen(index)) {
view.toggleOpenState(index);
}
lastIndex = index;
}
if (lastIndex >= 0) {
this.selectedRow = lastIndex;
return true;
}
return false;
},
};
///////////////////////////////////////////////////////////////////////////
//// InspectorUI
/**
* Main controller class for the Inspector.
*/
var InspectorUI = {
browser: null,
_showTreePanel: true,
_showStylePanel: false,
_showDOMPanel: false,
highlightColor: "#EEEE66",
highlightThickness: 4,
highlightOpacity: 0.4,
selectEventsSuppressed: false,
inspecting: false,
/**
* Toggle the inspector interface elements on or off.
*
* @param event
* The event that requested the UI change. Toolbar button or menu.
*/
toggleInspectorUI: function InspectorUI_toggleInspectorUI()
{
let toolsInspectCmd = document.getElementById("Tools:Inspect");
if (this.isPanelOpen) {
this.closeInspectorUI();
toolsInspectCmd.setAttribute("checked", "false");
} else {
this.openInspectorUI();
toolsInspectCmd.setAttribute("checked", "true");
}
},
/**
* Is the tree panel open?
*
* @returns boolean
*/
get isPanelOpen()
{
return this.treePanel && this.treePanel.state == "open";
},
/**
* Open the inspector's tree panel and initialize it.
*/
openTreePanel: function InspectorUI_openTreePanel()
{
if (!this.treePanel) {
this.treePanel = document.getElementById("inspector-panel");
this.treePanel.hidden = false;
}
if (!this.isPanelOpen) {
const panelWidthRatio = 7 / 8;
const panelHeightRatio = 1 / 5;
let bar = document.getElementById("status-bar");
this.treePanel.openPopup(bar, "overlap", 120, -120, false, false);
this.treePanel.sizeTo(this.win.outerWidth * panelWidthRatio,
this.win.outerHeight * panelHeightRatio);
this.tree = document.getElementById("inspector-tree");
this.createDocumentModel();
}
},
openStylePanel: function InspectorUI_openStylePanel()
{
// # todo
},
openDOMPanel: function InspectorUI_openDOMPanel()
{
// # todo
},
/**
* Open inspector UI. tree, style and DOM panels if enabled. Add listeners for
* document scrolling and tabContainer.TabSelect.
*/
openInspectorUI: function InspectorUI_openInspectorUI()
{
// initialization
this.browser = gBrowser.selectedBrowser;
this.win = this.browser.contentWindow;
// open inspector UI
if (this._showTreePanel) {
this.openTreePanel();
}
if (this._showStylePanel) {
this.openStylePanel();
}
if (this._showDOMPanel) {
this.openDOMPanel();
}
this.initializeHighlighter();
this.startInspecting();
this.win.document.addEventListener("scroll", this, false);
gBrowser.tabContainer.addEventListener("TabSelect", this, false);
},
/**
* Initialize highlighter.
*/
initializeHighlighter: function InspectorUI_initializeHighlighter()
{
this.highlighter = new PanelHighlighter(this.browser, this.highlightColor,
this.highlightThickness, this.highlightOpacity);
},
/**
* Close inspector UI and associated panels. Unhighlight and stop inspecting.
* Remove event listeners for document scrolling and
* tabContainer.TabSelect.
*/
closeInspectorUI: function InspectorUI_closeInspectorUI()
{
this.win.document.removeEventListener("scroll", this, false);
gBrowser.tabContainer.removeEventListener("TabSelect", this, false);
this.stopInspecting();
if (this.highlighter && this.highlighter.isHighlighting) {
this.highlighter.unhighlight();
}
if (this.isPanelOpen) {
this.treePanel.hidePopup();
this.treeView.destroy();
}
this.browser = this.win = null; // null out references to browser and window
},
/**
* Begin inspecting webpage, attach page event listeners, activate
* highlighter event listeners.
*/
startInspecting: function InspectorUI_startInspecting()
{
this.attachPageListeners();
this.inspecting = true;
},
/**
* Stop inspecting webpage, detach page listeners, disable highlighter
* event listeners.
*/
stopInspecting: function InspectorUI_stopInspecting()
{
if (!this.inspecting)
return;
this.detachPageListeners();
this.inspecting = false;
},
/////////////////////////////////////////////////////////////////////////
//// Model Creation Methods
/**
* Create treeView object from content window.
*/
createDocumentModel: function InspectorUI_createDocumentModel()
{
this.treeView = new InspectorTreeView(this.win);
},
/////////////////////////////////////////////////////////////////////////
//// Event Handling
/**
* Main callback handler for events.
*
* @param event
* The event to be handled.
*/
handleEvent: function InspectorUI_handleEvent(event)
{
switch (event.type) {
case "TabSelect":
this.closeInspectorUI();
break;
case "keypress":
switch (event.keyCode) {
case KeyEvent.DOM_VK_RETURN:
case KeyEvent.DOM_VK_ESCAPE:
this.stopInspecting();
break;
}
break;
case "mousemove":
let element = this.win.document.elementFromPoint(event.clientX,
event.clientY);
if (element && element != this.node) {
this.inspectNode(element);
}
break;
case "click":
this.stopInspecting();
break;
case "scroll":
this.highlighter.highlight();
break;
}
},
/**
* Event fired when a tree row is selected in the tree panel.
*/
onTreeSelected: function InspectorUI_onTreeSelected()
{
if (this.selectEventsSuppressed) {
return false;
}
let treeView = this.treeView;
let node = treeView.selectedNode;
this.highlighter.highlightNode(node); // # todo scrolling causes issues
this.stopInspecting();
return true;
},
/**
* Attach event listeners to content window and child windows to enable
* highlighting and click to stop inspection.
*/
attachPageListeners: function InspectorUI_attachPageListeners()
{
this.win.addEventListener("keypress", this, true);
this.browser.addEventListener("mousemove", this, true);
this.browser.addEventListener("click", this, true);
},
/**
* Detach event listeners from content window and child windows
* to disable highlighting.
*/
detachPageListeners: function InspectorUI_detachPageListeners()
{
this.win.removeEventListener("keypress", this, true);
this.browser.removeEventListener("mousemove", this, true);
this.browser.removeEventListener("click", this, true);
},
/////////////////////////////////////////////////////////////////////////
//// Utility Methods
/**
* inspect the given node, highlighting it on the page and selecting the
* correct row in the tree panel
*
* @param element
* the element in the document to inspect
*/
inspectNode: function InspectorUI_inspectNode(element)
{
this.highlighter.highlightNode(element);
this.selectEventsSuppressed = true;
this.treeView.selectedNode = element;
this.selectEventsSuppressed = false;
},
///////////////////////////////////////////////////////////////////////////
//// Utility functions
/**
* debug logging facility
* @param msg
* text message to send to the log
*/
_log: function LOG(msg)
{
Services.console.logStringMessage(msg);
},
}

View File

@ -125,6 +125,9 @@ _BROWSER_FILES = \
browser_drag.js \
browser_gestureSupport.js \
browser_getshortcutoruri.js \
browser_inspector_initialization.js \
browser_inspector_treeSelection.js \
browser_inspector_highlighter.js \
browser_overflowScroll.js \
browser_pageInfo.js \
browser_page_style_menu.js \

View File

@ -0,0 +1,123 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set 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 Inspector Highlighter Tests.
*
* 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>
*
* 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 ***** */
let doc;
let h1;
function createDocument()
{
let div = doc.createElement("div");
let h1 = doc.createElement("h1");
let p1 = doc.createElement("p");
let p2 = doc.createElement("p");
let div2 = doc.createElement("div");
let p3 = doc.createElement("p");
doc.title = "Inspector Tree Selection Test";
h1.textContent = "Inspector Tree Selection Test";
p1.textContent = "This is some example text";
p2.textContent = "Lorem ipsum dolor sit amet, consectetur adipisicing " +
"elit, sed do eiusmod tempor incididunt ut labore et dolore magna " +
"aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco " +
"laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure " +
"dolor in reprehenderit in voluptate velit esse cillum dolore eu " +
"fugiat nulla pariatur. Excepteur sint occaecat cupidatat non " +
"proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
p3.textContent = "Lorem ipsum dolor sit amet, consectetur adipisicing " +
"elit, sed do eiusmod tempor incididunt ut labore et dolore magna " +
"aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco " +
"laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure " +
"dolor in reprehenderit in voluptate velit esse cillum dolore eu " +
"fugiat nulla pariatur. Excepteur sint occaecat cupidatat non " +
"proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
div.appendChild(h1);
div.appendChild(p1);
div.appendChild(p2);
div2.appendChild(p3);
doc.body.appendChild(div);
doc.body.appendChild(div2);
setupHighlighterTests();
}
function setupHighlighterTests()
{
h1 = doc.querySelectorAll("h1")[0];
ok(h1, "we have the header node");
document.addEventListener("popupshown", runSelectionTests, false);
InspectorUI.toggleInspectorUI();
}
function runSelectionTests(evt)
{
if (evt.target.id != "inspector-panel")
return true;
document.removeEventListener("popupshown", runSelectionTests, false);
document.addEventListener("popupshown", performTestComparisons, false);
EventUtils.synthesizeMouse(h1, 2, 2, {type: "mousemove"}, content);
}
function performTestComparisons(evt)
{
if (evt.target.id != "highlighter-panel")
return true;
document.removeEventListener("popupshown", performTestComparisons, false);
is(h1, InspectorUI.treeView.selectedNode, "selection matches node");
ok(InspectorUI.highlighter.isHighlighting, "panel is highlighting");
is(InspectorUI.highlighter.highlitNode, h1, "highlighter matches selection");
executeSoon(finishUp);
}
function finishUp() {
InspectorUI.closeInspectorUI();
gBrowser.removeCurrentTab();
finish();
}
function test()
{
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function() {
gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
doc = content.document;
waitForFocus(createDocument, content);
}, true);
content.location = "data:text/html,basic tests for inspector";
}

View File

@ -0,0 +1,87 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set 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 Inspector Initializationa and Shutdown Tests.
*
* 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>
*
* 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 ***** */
let doc;
function startInspectorTests()
{
ok(InspectorUI, "InspectorUI variable exists");
document.addEventListener("popupshown", runInspectorTests, false);
InspectorUI.toggleInspectorUI();
}
function runInspectorTests(evt)
{
if (evt.target.id != "inspector-panel")
return true;
document.removeEventListener("popupshown", runInspectorTests, false);
document.addEventListener("popuphidden", finishInspectorTests, false);
ok(InspectorUI.inspecting, "Inspector is highlighting");
ok(InspectorUI.isPanelOpen, "Inspector Tree Panel is open");
todo(InspectorUI.isStylePanelOpen, "Inspector Style Panel is open");
todo(InspectorUI.isDOMPanelOpen, "Inspector DOM Panel is open");
InspectorUI.toggleInspectorUI();
}
function finishInspectorTests(evt)
{
if (evt.target.id != "inspector-panel")
return true;
document.removeEventListener("popuphidden", finishInspectorTests, false);
ok(!InspectorUI.isDOMPanelOpen, "Inspector DOM Panel is closed");
ok(!InspectorUI.isStylePanelOpen, "Inspector Style Panel is closed");
ok(!InspectorUI.isPanelOpen, "Inspector Tree Panel is closed");
ok(!InspectorUI.inspecting, "Inspector is not highlighting");
gBrowser.removeCurrentTab();
finish();
}
function test()
{
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function() {
gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
doc = content.document;
waitForFocus(startInspectorTests, content);
}, true);
content.location = "data:text/html,basic tests for inspector";
}

View File

@ -0,0 +1,114 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set 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 Inspector Tree Selection Tests.
*
* 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>
*
* 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 ***** */
let doc;
let h1;
function createDocument()
{
let div = doc.createElement("div");
let h1 = doc.createElement("h1");
let p1 = doc.createElement("p");
let p2 = doc.createElement("p");
doc.title = "Inspector Tree Selection Test";
h1.textContent = "Inspector Tree Selection Test";
p1.textContent = "This is some example text";
p2.textContent = "Lorem ipsum dolor sit amet, consectetur adipisicing " +
"elit, sed do eiusmod tempor incididunt ut labore et dolore magna " +
"aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco " +
"laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure " +
"dolor in reprehenderit in voluptate velit esse cillum dolore eu " +
"fugiat nulla pariatur. Excepteur sint occaecat cupidatat non " +
"proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
div.appendChild(h1);
div.appendChild(p1);
div.appendChild(p2);
// doc.body.addEventListener("DOMSubtreeModified", , false);
doc.body.appendChild(div);
setupSelectionTests();
}
function setupSelectionTests()
{
h1 = doc.querySelectorAll("h1")[0];
ok(h1, "we have the header node");
document.addEventListener("popupshown", runSelectionTests, false);
InspectorUI.openInspectorUI();
}
function runSelectionTests(evt)
{
if (evt.target.id != "inspector-panel")
return true;
document.removeEventListener("popupshown", runSelectionTests, false);
InspectorUI.stopInspecting();
document.addEventListener("popupshown", performTestComparisons, false);
InspectorUI.treeView.selectedNode = h1;
}
function performTestComparisons(evt)
{
if (evt.target.id != "highlighter-panel")
return true;
document.removeEventListener("popupshown", performTestComparisons, false);
is(h1, InspectorUI.treeView.selectedNode, "selection matches node");
ok(InspectorUI.highlighter.isHighlighting, "panel is highlighting");
is(h1, InspectorUI.highlighter.highlitNode, "highlighter highlighting correct node");
finishUp();
}
function finishUp() {
InspectorUI.closeInspectorUI();
gBrowser.removeCurrentTab();
finish();
}
function test()
{
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function() {
gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
doc = content.document;
waitForFocus(createDocument, content);
}, true);
content.location = "data:text/html,basic tests for inspector";
}

View File

@ -159,6 +159,10 @@
<!ENTITY errorConsoleCmd.accesskey "C">
<!ENTITY errorConsoleCmd.commandkey "j">
<!ENTITY inspectMenu.label "Inspect">
<!ENTITY inspectMenu.accesskey "I">
<!ENTITY inspectMenu.commandkey "I">
<!ENTITY fileMenu.label "File">
<!ENTITY fileMenu.accesskey "F">
<!ENTITY newNavigatorCmd.label "New Window">

View File

@ -2044,3 +2044,4 @@ toolbarbutton.chevron > .toolbarbutton-menu-dropmarker {
.allTabs-preview:focus > * > .allTabs-preview-inner {
-moz-box-shadow: @focusRingShadow@;
}