Bug 561782 - Create DOM Panel for inspecting DOM nodes and their properties r=gavin, r=l10n, a=blocking

This commit is contained in:
Rob Campbell 2010-08-11 20:38:02 -03:00
parent 7dcb1d1fce
commit fc739222d5
12 changed files with 306 additions and 77 deletions

View File

@ -150,8 +150,6 @@
<command id="Inspector:Next"
oncommand="InspectorUI.inspectNext();"
disabled="true"/>
<command id="Inspector:Style"
oncommand="InspectorUI.toggleStylePanel();"/>
</commandset>
<broadcasterset id="mainBroadcasterSet">

View File

@ -255,7 +255,12 @@
label="&inspectStyleButton.label;"
accesskey="&inspectStyleButton.accesskey;"
class="toolbarbutton-text"
command="Inspector:Style"/>
oncommand="InspectorUI.toggleStylePanel();'"/>
<toolbarbutton id="inspector-dom-toolbutton"
label="&inspectDOMButton.label;"
accesskey="&inspectDOMButton.accesskey;"
class="toolbarbutton-text"
oncommand="InspectorUI.toggleDOMPanel();"/>
</toolbar>
<tree id="inspector-tree" class="plain"
seltype="single"
@ -289,7 +294,7 @@
<listbox id="inspector-style-listbox" flex="1"/>
<hbox align="end">
<spacer flex="1" />
<resizer dir="bottomend" />
<resizer dir="bottomend"/>
</hbox>
</panel>

View File

@ -61,40 +61,16 @@ const INSPECTOR_INVISIBLE_ELEMENTS = {
*
* @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)
function PanelHighlighter(aBrowser)
{
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.
@ -517,12 +493,6 @@ InspectorTreeView.prototype = {
*/
var InspectorUI = {
browser: null,
_showTreePanel: true,
_showStylePanel: true,
_showDOMPanel: false,
highlightColor: "#EEEE66",
highlightThickness: 4,
highlightOpacity: 0.4,
selectEventsSuppressed: false,
inspecting: false,
@ -559,7 +529,7 @@ var InspectorUI = {
*/
toggleStylePanel: function IUI_toggleStylePanel()
{
if (this._showStylePanel) {
if (this.isStylePanelOpen) {
this.stylePanel.hidePopup();
} else {
this.openStylePanel();
@ -567,7 +537,21 @@ var InspectorUI = {
this.updateStylePanel(this.treeView.selectedNode);
}
}
this._showStylePanel = !this._showStylePanel;
},
/**
* Toggle the DOM panel. Invoked from the toolbar's DOM button.
*/
toggleDOMPanel: function IUI_toggleDOMPanel()
{
if (this.isDOMPanelOpen) {
this.domPanel.hidePopup();
} else {
this.openDOMPanel();
if (this.treeView.selectedNode) {
this.updateDOMPanel(this.treeView.selectedNode);
}
}
},
/**
@ -590,6 +574,16 @@ var InspectorUI = {
return this.stylePanel && this.stylePanel.state == "open";
},
/**
* Is the DOM panel open?
*
* @returns boolean
*/
get isDOMPanelOpen()
{
return this.domPanel && this.domPanel.state == "open";
},
/**
* Open the inspector's tree panel and initialize it.
*/
@ -603,7 +597,8 @@ var InspectorUI = {
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.openPopupAtScreen(this.win.screenX + 80,
this.win.outerHeight + this.win.screenY);
this.treePanel.sizeTo(this.win.outerWidth * panelWidthRatio,
this.win.outerHeight * panelHeightRatio);
this.tree = document.getElementById("inspector-tree");
@ -616,11 +611,10 @@ var InspectorUI = {
*/
openStylePanel: function IUI_openStylePanel()
{
if (!this.stylePanel) {
if (!this.stylePanel)
this.stylePanel = document.getElementById("inspector-style-panel");
this.stylePanel.hidden = false;
}
if (!this.isStylePanelOpen) {
this.stylePanel.hidden = false;
// open at top right of browser panel, offset by 20px from top.
this.stylePanel.openPopup(this.browser, "end_before", 0, 20, false, false);
// size panel to 200px wide by half browser height - 60.
@ -628,6 +622,20 @@ var InspectorUI = {
}
},
/**
* Open the DOM panel if not already onscreen.
*/
openDOMPanel: function IUI_openDOMPanel()
{
if (!this.isDOMPanelOpen) {
// open at middle right of browser panel, offset by 20px from middle.
this.domPanel.openPopup(this.browser, "end_before", 0,
this.win.outerHeight / 2 - 20, false, false);
// size panel to 200px wide by half browser height - 60.
this.domPanel.sizeTo(200, this.win.outerHeight / 2 - 60);
}
},
/**
* Toggle the dimmed (semi-transparent) state for a panel by setting or
* removing a dimmed attribute.
@ -644,11 +652,6 @@ var InspectorUI = {
}
},
openDOMPanel: function IUI_openDOMPanel()
{
// # todo bug 561782
},
/**
* Open inspector UI. tree, style and DOM panels if enabled. Add listeners for
* document scrolling, resize and tabContainer.TabSelect.
@ -658,24 +661,31 @@ var InspectorUI = {
// initialization
this.browser = gBrowser.selectedBrowser;
this.win = this.browser.contentWindow;
if (!this.style) {
Cu.import("resource:///modules/stylePanel.jsm", this);
this.style.initialize();
}
// DOM panel initialization and loading (via PropertyPanel.jsm)
let domPanelTitle = this.strings.GetStringFromName("dom.domPanelTitle");
let parent = document.getElementById("inspector-style-panel").parentNode;
let propPanel = new (this.PropertyPanel)(parent, document, domPanelTitle, {});
// additional DOM panel setup needed for unittest identification and use
this.domPanel = propPanel.panel;
this.domPanel.setAttribute("id", "inspector-dom-panel");
this.domBox = propPanel.tree;
this.domTreeView = propPanel.treeView;
// open inspector UI
if (this._showTreePanel) {
this.openTreePanel();
}
if (this._showStylePanel) {
this.styleBox = document.getElementById("inspector-style-listbox");
this.clearStylePanel();
this.openStylePanel();
}
if (this._showDOMPanel) {
this.openDOMPanel();
}
this.inspectorBundle = Services.strings.createBundle("chrome://browser/locale/inspector.properties");
this.openTreePanel();
// style panel setup and activation
this.styleBox = document.getElementById("inspector-style-listbox");
this.clearStylePanel();
this.openStylePanel();
// DOM panel setup and activation
this.clearDOMPanel();
this.openDOMPanel();
// setup highlighter and start inspecting
this.initializeHighlighter();
this.startInspecting();
this.win.document.addEventListener("scroll", this, false);
@ -689,8 +699,7 @@ var InspectorUI = {
*/
initializeHighlighter: function IUI_initializeHighlighter()
{
this.highlighter = new PanelHighlighter(this.browser, this.highlightColor,
this.highlightThickness, this.highlightOpacity);
this.highlighter = new PanelHighlighter(this.browser);
},
/**
@ -714,6 +723,9 @@ var InspectorUI = {
if (this.isStylePanelOpen) {
this.stylePanel.hidePopup();
}
if (this.isDOMPanelOpen) {
this.domPanel.hidePopup();
}
this.inspectCmd.setAttribute("checked", false);
this.browser = this.win = null; // null out references to browser and window
},
@ -727,6 +739,7 @@ var InspectorUI = {
this.attachPageListeners();
this.inspecting = true;
this.toggleDimForPanel(this.stylePanel);
this.toggleDimForPanel(this.domPanel);
},
/**
@ -740,8 +753,10 @@ var InspectorUI = {
this.detachPageListeners();
this.inspecting = false;
this.toggleDimForPanel(this.stylePanel);
this.toggleDimForPanel(this.domPanel);
if (this.treeView.selection) {
this.updateStylePanel(this.treeView.selectedNode);
this.updateDOMPanel(this.treeView.selectedNode);
}
},
@ -757,7 +772,7 @@ var InspectorUI = {
},
/**
* add a new item to the listbox
* Add a new item to the style panel listbox.
*
* @param aLabel
* A bit of text to put in the listitem's label attribute.
@ -768,7 +783,7 @@ var InspectorUI = {
*/
addStyleItem: function IUI_addStyleItem(aLabel, aType, aContent)
{
let itemLabelString = this.inspectorBundle.GetStringFromName("style.styleItemLabel");
let itemLabelString = this.strings.GetStringFromName("style.styleItemLabel");
let item = document.createElement("listitem");
// Do not localize these strings
@ -783,6 +798,19 @@ var InspectorUI = {
this.styleBox.appendChild(item);
},
/**
* Add a new item to the DOM panel listbox
*
* @param aPair
* an object with name and value properties.
*/
addDOMItem: function IUI_addDOMItem(aPair)
{
let item = document.createElement("listitem");
item.setAttribute("label", aPair.name + ": " + aPair.value);
this.domBox.appendChild(item);
},
/**
* Create items for each rule included in the given array.
*
@ -791,7 +819,7 @@ var InspectorUI = {
*/
createStyleRuleItems: function IUI_createStyleRuleItems(aRules)
{
let selectorLabel = this.inspectorBundle.GetStringFromName("style.selectorLabel");
let selectorLabel = this.strings.GetStringFromName("style.selectorLabel");
aRules.forEach(function(rule) {
this.addStyleItem(selectorLabel, "selector", rule.id);
@ -821,7 +849,7 @@ var InspectorUI = {
{
this.createStyleRuleItems(aRules);
let inheritedString =
this.inspectorBundle.GetStringFromName("style.inheritedFrom");
this.strings.GetStringFromName("style.inheritedFrom");
aSections.forEach(function(section) {
let sectionTitle = section.element.tagName;
if (section.element.id)
@ -841,6 +869,14 @@ var InspectorUI = {
this.styleBox.removeItemAt(i);
},
/**
* Remove all items from the DOM Panel's listbox.
*/
clearDOMPanel: function IUI_clearStylePanel()
{
this.domTreeView.data = {};
},
/**
* Update the contents of the style panel with styles for the currently
* inspected node.
@ -850,8 +886,10 @@ var InspectorUI = {
*/
updateStylePanel: function IUI_updateStylePanel(aNode)
{
if (this.inspecting || !this.isStylePanelOpen)
if (this.inspecting || !this.isStylePanelOpen) {
return;
}
let rules = [], styleSections = [], usedProperties = {};
this.style.getInheritedRules(aNode, styleSections, usedProperties);
this.style.getElementRules(aNode, rules, usedProperties);
@ -859,6 +897,19 @@ var InspectorUI = {
this.createStyleItems(rules, styleSections);
},
/**
* Update the contents of the DOM panel with name/value pairs for the
* currently-inspected node.
*/
updateDOMPanel: function IUI_updateDOMPanel(aNode)
{
if (this.inspecting || !this.isDOMPanelOpen) {
return;
}
this.domTreeView.data = aNode;
},
/////////////////////////////////////////////////////////////////////////
//// Event Handling
@ -908,11 +959,11 @@ var InspectorUI = {
return false;
}
let treeView = this.treeView;
let node = treeView.selectedNode;
let node = this.treeView.selectedNode;
this.highlighter.highlightNode(node);
this.stopInspecting();
this.updateStylePanel(node);
this.updateDOMPanel(node);
return true;
},
@ -955,6 +1006,7 @@ var InspectorUI = {
this.treeView.selectedNode = aNode;
this.selectEventsSuppressed = false;
this.updateStylePanel(aNode);
this.updateDOMPanel(aNode);
},
/**
@ -1000,7 +1052,32 @@ var InspectorUI = {
},
}
/////////////////////////////////////////////////////////////////////////
//// Initializors
XPCOMUtils.defineLazyGetter(InspectorUI, "inspectCmd", function () {
return document.getElementById("Tools:Inspect");
});
XPCOMUtils.defineLazyGetter(InspectorUI, "strings", function () {
return Services.strings.createBundle("chrome://browser/locale/inspector.properties");
});
XPCOMUtils.defineLazyGetter(InspectorUI, "PropertyTreeView", function () {
var obj = {};
Cu.import("resource://gre/modules/PropertyPanel.jsm", obj);
return obj.PropertyTreeView;
});
XPCOMUtils.defineLazyGetter(InspectorUI, "PropertyPanel", function () {
var obj = {};
Cu.import("resource://gre/modules/PropertyPanel.jsm", obj);
return obj.PropertyPanel;
});
XPCOMUtils.defineLazyGetter(InspectorUI, "style", function () {
var obj = {};
Cu.import("resource:///modules/stylePanel.jsm", obj);
obj.style.initialize();
return obj.style;
});

View File

@ -150,6 +150,7 @@ _BROWSER_FILES = \
browser_inspector_treeSelection.js \
browser_inspector_highlighter.js \
browser_inspector_stylePanel.js \
browser_inspector_domPanel.js \
browser_inspector_iframeTest.js \
browser_inspector_scrolling.js \
browser_pageInfo.js \

View File

@ -0,0 +1,128 @@
/* -*- 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 DOM Panel 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 testGen;
function createDocument()
{
doc.body.innerHTML = '<div id="first" style="{ margin: 10em; ' +
'font-size: 14pt; font-family: helvetica, sans-serif; color: #AAA}">\n' +
'<h1>Some header text</h1>\n' +
'<p id="salutation" style="{font-size: 12pt}">hi.</p>\n' +
'<p id="body" style="{font-size: 12pt}">I am a test-case. This text exists ' +
'solely to provide some things to <span style="{color: yellow}">' +
'highlight</span> and <span style="{font-weight: bold}">count</span> ' +
'DOM list-items in the box at right. If you are reading this, ' +
'you should go do something else instead. Maybe read a book. Or better ' +
'yet, write some test-cases for another bit of code. ' +
'<span style="{font-style: italic}">Maybe more inspector test-cases!</span></p>\n' +
'<p id="closing">end transmission</p>\n' +
'</div>';
doc.title = "Inspector DOM Test";
document.addEventListener("popupshown", runDOMTests, false);
InspectorUI.openInspectorUI();
}
function nodeGenerator()
{
let body = doc.body;
InspectorUI.inspectNode(body);
yield;
let h1 = doc.querySelector("h1");
InspectorUI.inspectNode(h1);
yield;
let first = doc.getElementById("first");
InspectorUI.inspectNode(first);
yield;
let closing = doc.getElementById("#closing");
InspectorUI.inspectNode(closing);
yield;
}
function runDOMTests(evt)
{
if (evt.target.id != "inspector-dom-panel")
return true;
InspectorUI._log("runDOMtests");
document.removeEventListener("popupshown", runDOMTests, false);
InspectorUI.stopInspecting();
document.addEventListener("popupshown", performTestComparisons, false);
testGen = nodeGenerator();
testGen.next();
}
function performTestComparisons(evt)
{
InspectorUI._log("performTestComparisons");
if (evt.target.id != "highlighter-panel")
return true;
ok(InspectorUI.treeView.selectedNode, "selection");
ok(InspectorUI.isDOMPanelOpen, "DOM panel is open?");
ok(InspectorUI.highlighter.isHighlighting, "panel is highlighting");
ok(InspectorUI.domTreeView.rowCount > 0, "domBox has items");
try {
testGen.next();
} catch(StopIteration) {
document.removeEventListener("popupshown", performTestComparisons, false);
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

@ -48,20 +48,20 @@ function startInspectorTests()
function runInspectorTests(evt)
{
if (evt.target.id != "inspector-panel")
if (evt.target.id != "inspector-dom-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");
ok(InspectorUI.isStylePanelOpen, "Inspector Style Panel is open");
todo(InspectorUI.isDOMPanelOpen, "Inspector DOM Panel is open");
ok(InspectorUI.isDOMPanelOpen, "Inspector DOM Panel is open");
InspectorUI.toggleInspectorUI();
}
function finishInspectorTests(evt)
{
if (evt.target.id != "inspector-style-panel")
if (evt.target.id != "inspector-dom-panel")
return true;
document.removeEventListener("popuphidden", finishInspectorTests, false);
ok(!InspectorUI.isDOMPanelOpen, "Inspector DOM Panel is closed");

View File

@ -93,8 +93,7 @@ function performTestComparisons(evt)
return true;
ok(InspectorUI.treeView.selectedNode, "selection");
ok(InspectorUI._showStylePanel, "_showStylePanel");
is(InspectorUI.isStylePanelOpen, InspectorUI._showStylePanel, "style panel matches _showStylePanel?");
ok(InspectorUI.isStylePanelOpen, "style panel is open?");
ok(InspectorUI.highlighter.isHighlighting, "panel is highlighting");
ok(InspectorUI.styleBox.itemCount > 0, "styleBox has items");

View File

@ -186,6 +186,12 @@
<!ENTITY inspectStyleButton.label "Style">
<!ENTITY inspectStyleButton.accesskey "S">
<!ENTITY inspectStylePanelTitle.label "Style">
<!-- LOCALIZATION NOTE (inspectDOMButton.label): This button label
- stands for Document Object Model and appears on the inspector's toolbar.
- It is used to open and closed the DOM panel. There is also a DOM label in
- inspector.properties for the panel titlebar. -->
<!ENTITY inspectDOMButton.label "DOM">
<!ENTITY inspectDOMButton.accesskey "D">
<!ENTITY fileMenu.label "File">
<!ENTITY fileMenu.accesskey "F">

View File

@ -6,6 +6,12 @@ style.selectorLabel=Selector
# inspector. Describes which tagname[#id] the properties are inherited from.
style.inheritedFrom=Inherited from: #1
# LOCALIZATION NOTE (style.styleItemLabel: used in Style panel in inspector.
# LOCALIZATION NOTE (style.styleItemLabel): used in Style panel in inspector.
# Used for construction of list items, #1 = label, #2 = content.
style.styleItemLabel=#1: #2
# LOCALIZATION NOTE (dom.domPanelTitle): used in DOM Panel in inspector.
# Stands for "Document Object Model". Also referenced in in browser.dtd
# and used as a button title.
# Unsure if this localizes well, but including just in case
dom.domPanelTitle=DOM

View File

@ -1506,6 +1506,9 @@ toolbar[mode="text"] toolbarbutton.chevron > .toolbarbutton-icon {
#highlighter-panel {
-moz-appearance: none;
-moz-window-shadow: none;
background: -moz-linear-gradient(top -1deg, #ffdd88, #ffeeaa);
border: none;
opacity: 0.35;
}
listitem.style-selector {

View File

@ -2086,6 +2086,9 @@ toolbarbutton.chevron > .toolbarbutton-menu-dropmarker {
#highlighter-panel {
-moz-appearance: none;
-moz-window-shadow: none;
background: -moz-linear-gradient(top -1deg, #ffdd88, #ffeeaa);
border: none;
opacity: 0.35;
}
listitem.style-selector {

View File

@ -1795,6 +1795,9 @@ toolbarbutton.bookmark-item[dragover="true"][open="true"] {
#highlighter-panel {
-moz-appearance: none;
-moz-window-shadow: none;
background: -moz-linear-gradient(top -1deg, #ffdd88, #ffeeaa);
border: none;
opacity: 0.35;
}
listitem.style-selector {