Bug 650794 - Disable HTML panel and make it use registerTools API; r=msucan

--HG--
rename : browser/devtools/highlighter/insideOutBox.js => browser/devtools/highlighter/InsideOutBox.jsm
This commit is contained in:
Rob Campbell 2011-09-26 13:59:23 -03:00
Родитель 6874df2014
Коммит 5ec6c6a313
16 изменённых файлов: 942 добавлений и 645 удалений

Просмотреть файл

@ -232,7 +232,6 @@
noautohide="true"
titlebar="normal"
close="true"
onpopuphiding="InspectorUI.closeInspectorUI();"
label="&inspectPanelTitle.label;">
<hbox id="tree-panel-resizer-box" align="end">
<spacer flex="1" />

Просмотреть файл

@ -125,6 +125,9 @@ InsideOutBoxView = {
* The box object containing the InsideOutBox. Required to add/remove
* children during box manipulation (toggling opened or closed).
*/
var EXPORTED_SYMBOLS = ["InsideOutBox"];
function InsideOutBox(aView, aBox)
{
this.view = aView;
@ -450,7 +453,7 @@ InsideOutBox.prototype =
try {
this.box.removeChild(this.rootObjectBox);
} catch (exc) {
InspectorUI._log("this.box.removeChild(this.rootObjectBox) FAILS " +
this.view._log("this.box.removeChild(this.rootObjectBox) FAILS " +
this.box + " must not contain " + this.rootObjectBox);
}
}
@ -643,4 +646,18 @@ InsideOutBox.prototype =
return node;
},
/**
* Clean up our mess.
*/
destroy: function IOBox_destroy()
{
delete this.view;
delete this.box;
delete this.rootObject;
delete this.rootObjectBox;
delete this.selectedObjectBox;
delete this.highlightedObjectBox;
delete this.scrollIntoView;
}
};

Просмотреть файл

@ -46,6 +46,8 @@ include $(DEPTH)/config/autoconf.mk
EXTRA_JS_MODULES = \
domplate.jsm \
InsideOutBox.jsm \
TreePanel.jsm \
$(NULL)
ifdef ENABLE_TESTS

Просмотреть файл

@ -0,0 +1,694 @@
/* -*- 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 the Mozilla Tree Panel.
*
* The Initial Developer of the Original Code is
* The Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* 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 <getify@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;
Cu.import("resource:///modules/domplate.jsm");
Cu.import("resource:///modules/InsideOutBox.jsm");
Cu.import("resource:///modules/Services.jsm");
var EXPORTED_SYMBOLS = ["TreePanel"];
/**
* TreePanel
* A container for the Inspector's HTML Tree Panel widget constructor function.
* @param aContext nsIDOMWindow (xulwindow)
* @param aIUI global InspectorUI object
*/
function TreePanel(aContext, aIUI) {
this._init(aContext, aIUI);
};
TreePanel.prototype = {
showTextNodesWithWhitespace: false,
id: "treepanel", // DO NOT LOCALIZE
/**
* container.
* @returns xul:panel element
*/
get container()
{
return this.document.getElementById("inspector-tree-panel");
},
/**
* Main TreePanel boot-strapping method. Initialize the TreePanel with the
* originating context and the InspectorUI global.
* @param aContext nsIDOMWindow (xulwindow)
* @param aIUI global InspectorUI object
*/
_init: function TP__init(aContext, aIUI)
{
this.IUI = aIUI;
this.window = aContext;
this.document = this.window.document;
domplateUtils.setDOM(this.window);
let isOpen = this.isOpen.bind(this);
this.registrationObject = {
id: this.id,
label: this.IUI.strings.GetStringFromName("htmlPanel.label"),
tooltiptext: this.IUI.strings.GetStringFromName("htmlPanel.tooltiptext"),
accesskey: this.IUI.strings.GetStringFromName("htmlPanel.accesskey"),
context: this,
get isOpen() isOpen(),
show: this.open,
hide: this.close,
onSelect: this.select,
panel: this.container,
unregister: this.destroy,
};
this.editingEvents = {};
this._boundClose = this.close.bind(this);
this.container.addEventListener("popuphiding", this._boundClose, false);
// Register the HTML panel with the highlighter
this.IUI.registerTool(this.registrationObject);
},
/**
* Initialization function for the TreePanel.
*/
initializeIFrame: function TP_initializeIFrame()
{
if (!this.initializingTreePanel || this.treeLoaded) {
return;
}
this.treeBrowserDocument = this.treeIFrame.contentDocument;
this.treePanelDiv = this.treeBrowserDocument.createElement("div");
this.treeBrowserDocument.body.appendChild(this.treePanelDiv);
this.treePanelDiv.ownerPanel = this;
this.ioBox = new InsideOutBox(this, this.treePanelDiv);
this.ioBox.createObjectBox(this.IUI.win.document.documentElement);
this.treeLoaded = true;
this.treeIFrame.addEventListener("click", this.onTreeClick.bind(this), false);
this.treeIFrame.addEventListener("dblclick", this.onTreeDblClick.bind(this), false);
delete this.initializingTreePanel;
Services.obs.notifyObservers(null,
this.window.INSPECTOR_NOTIFICATIONS.TREEPANELREADY, null);
if (this.IUI.selection)
this.select(this.IUI.selection, true);
},
/**
* Open the inspector's tree panel and initialize it.
*/
open: function TP_open()
{
if (this.initializingTreePanel && !this.treeLoaded) {
return;
}
this.initializingTreePanel = true;
this.container.hidden = false;
this.treeIFrame = this.document.getElementById("inspector-tree-iframe");
if (!this.treeIFrame) {
let resizerBox = this.document.getElementById("tree-panel-resizer-box");
this.treeIFrame = this.document.createElement("iframe");
this.treeIFrame.setAttribute("id", "inspector-tree-iframe");
this.treeIFrame.setAttribute("flex", "1");
this.treeIFrame.setAttribute("type", "content");
this.treeIFrame = this.container.insertBefore(this.treeIFrame, resizerBox);
}
let self = this;
this.container.addEventListener("popupshown", function treePanelShown() {
self.container.removeEventListener("popupshown",
treePanelShown, false);
self.treeIFrame.addEventListener("load",
function loadedInitializeTreePanel() {
self.treeIFrame.removeEventListener("load",
loadedInitializeTreePanel, true);
self.initializeIFrame();
}, true);
let src = self.treeIFrame.getAttribute("src");
if (src != "chrome://browser/content/inspector.html") {
self.treeIFrame.setAttribute("src",
"chrome://browser/content/inspector.html");
} else {
self.treeIFrame.contentWindow.location.reload();
}
}, false);
const panelWidthRatio = 7 / 8;
const panelHeightRatio = 1 / 5;
let width = parseInt(this.IUI.win.outerWidth * panelWidthRatio);
let height = parseInt(this.IUI.win.outerHeight * panelHeightRatio);
let y = Math.min(this.document.defaultView.screen.availHeight - height,
this.IUI.win.innerHeight);
this.container.openPopup(this.browser, "overlap", 0, 0,
false, false);
this.container.moveTo(80, y);
this.container.sizeTo(width, height);
},
/**
* Close the TreePanel.
*/
close: function TP_close()
{
if (this.treePanelDiv) {
this.treePanelDiv.ownerPanel = null;
let parent = this.treePanelDiv.parentNode;
parent.removeChild(this.treePanelDiv);
delete this.treePanelDiv;
delete this.treeBrowserDocument;
}
this.treeLoaded = false;
this.container.hidePopup();
},
/**
* Is the TreePanel open?
* @returns boolean
*/
isOpen: function TP_isOpen()
{
return this.treeLoaded && this.container.state == "open";
},
/**
* Create the ObjectBox for the given object.
* @param object nsIDOMNode
* @param isRoot boolean - Is this the root object?
* @returns InsideOutBox
*/
createObjectBox: function TP_createObjectBox(object, isRoot)
{
let tag = domplateUtils.getNodeTag(object);
if (tag)
return tag.replace({object: object}, this.treeBrowserDocument);
},
getParentObject: function TP_getParentObject(node)
{
let parentNode = node ? node.parentNode : null;
if (!parentNode) {
// Documents have no parentNode; Attr, Document, DocumentFragment, Entity,
// and Notation. top level windows have no parentNode
if (node && node == this.window.Node.DOCUMENT_NODE) {
// document type
if (node.defaultView) {
let embeddingFrame = node.defaultView.frameElement;
if (embeddingFrame)
return embeddingFrame.parentNode;
}
}
// a Document object without a parentNode or window
return null; // top level has no parent
}
if (parentNode.nodeType == this.window.Node.DOCUMENT_NODE) {
if (parentNode.defaultView) {
return parentNode.defaultView.frameElement;
}
// parent is document element, but no window at defaultView.
return null;
}
if (!parentNode.localName)
return null;
return parentNode;
},
getChildObject: function TP_getChildObject(node, index, previousSibling)
{
if (!node)
return null;
if (node.contentDocument) {
// then the node is a frame
if (index == 0) {
return node.contentDocument.documentElement; // the node's HTMLElement
}
return null;
}
if (node instanceof this.window.GetSVGDocument) {
let svgDocument = node.getSVGDocument();
if (svgDocument) {
// then the node is a frame
if (index == 0) {
return svgDocument.documentElement; // the node's SVGElement
}
return null;
}
}
let child = null;
if (previousSibling) // then we are walking
child = this.getNextSibling(previousSibling);
else
child = this.getFirstChild(node);
if (this.showTextNodesWithWhitespace)
return child;
for (; child; child = this.getNextSibling(child)) {
if (!domplateUtils.isWhitespaceText(child))
return child;
}
return null; // we have no children worth showing.
},
getFirstChild: function TP_getFirstChild(node)
{
this.treeWalker = node.ownerDocument.createTreeWalker(node,
this.window.NodeFilter.SHOW_ALL, null, false);
return this.treeWalker.firstChild();
},
getNextSibling: function TP_getNextSibling(node)
{
let next = this.treeWalker.nextSibling();
if (!next)
delete this.treeWalker;
return next;
},
/////////////////////////////////////////////////////////////////////
// Event Handling
/**
* Handle click events in the html tree panel.
* @param aEvent
* The mouse event.
*/
onTreeClick: function TP_onTreeClick(aEvent)
{
let node;
let target = aEvent.target;
let hitTwisty = false;
if (this.hasClass(target, "twisty")) {
node = this.getRepObject(aEvent.target.nextSibling);
hitTwisty = true;
} else {
node = this.getRepObject(aEvent.target);
}
if (node) {
if (hitTwisty) {
this.ioBox.toggleObject(node);
} else {
if (this.IUI.inspecting) {
this.IUI.stopInspecting(true);
} else {
this.IUI.select(node, true, false);
this.IUI.highlighter.highlightNode(node);
}
}
}
},
/**
* Handle double-click events in the html tree panel.
* (double-clicking an attribute value allows it to be edited)
* @param aEvent
* The mouse event.
*/
onTreeDblClick: function TP_onTreeDblClick(aEvent)
{
// if already editing an attribute value, double-clicking elsewhere
// in the tree is the same as a click, which dismisses the editor
if (this.editingContext)
this.closeEditor();
let target = aEvent.target;
if (this.hasClass(target, "nodeValue")) {
let repObj = this.getRepObject(target);
let attrName = target.getAttribute("data-attributeName");
let attrVal = target.innerHTML;
this.editAttributeValue(target, repObj, attrName, attrVal);
}
},
/**
* Starts the editor for an attribute value.
* @param aAttrObj
* The DOM object representing the attribute value in the HTML Tree
* @param aRepObj
* The original DOM (target) object being inspected/edited
* @param aAttrName
* The name of the attribute being edited
* @param aAttrVal
* The current value of the attribute being edited
*/
editAttributeValue:
function TP_editAttributeValue(aAttrObj, aRepObj, aAttrName, aAttrVal)
{
let editor = this.treeBrowserDocument.getElementById("attribute-editor");
let editorInput =
this.treeBrowserDocument.getElementById("attribute-editor-input");
let attrDims = aAttrObj.getBoundingClientRect();
// figure out actual viewable viewport dimensions (sans scrollbars)
let viewportWidth = this.treeBrowserDocument.documentElement.clientWidth;
let viewportHeight = this.treeBrowserDocument.documentElement.clientHeight;
// saves the editing context for use when the editor is saved/closed
this.editingContext = {
attrObj: aAttrObj,
repObj: aRepObj,
attrName: aAttrName
};
// highlight attribute-value node in tree while editing
this.addClass(aAttrObj, "editingAttributeValue");
// show the editor
this.addClass(editor, "editing");
// offset the editor below the attribute-value node being edited
let editorVeritcalOffset = 2;
// keep the editor comfortably within the bounds of the viewport
let editorViewportBoundary = 5;
// outer editor is sized based on the <input> box inside it
editorInput.style.width = Math.min(attrDims.width, viewportWidth -
editorViewportBoundary) + "px";
editorInput.style.height = Math.min(attrDims.height, viewportHeight -
editorViewportBoundary) + "px";
let editorDims = editor.getBoundingClientRect();
// calculate position for the editor according to the attribute node
let editorLeft = attrDims.left + this.treeIFrame.contentWindow.scrollX -
// center the editor against the attribute value
((editorDims.width - attrDims.width) / 2);
let editorTop = attrDims.top + this.treeIFrame.contentWindow.scrollY +
attrDims.height + editorVeritcalOffset;
// but, make sure the editor stays within the visible viewport
editorLeft = Math.max(0, Math.min(
(this.treeIFrame.contentWindow.scrollX +
viewportWidth - editorDims.width),
editorLeft)
);
editorTop = Math.max(0, Math.min(
(this.treeIFrame.contentWindow.scrollY +
viewportHeight - editorDims.height),
editorTop)
);
// position the editor
editor.style.left = editorLeft + "px";
editor.style.top = editorTop + "px";
// set and select the text
editorInput.value = aAttrVal;
editorInput.select();
// listen for editor specific events
this.bindEditorEvent(editor, "click", function(aEvent) {
aEvent.stopPropagation();
});
this.bindEditorEvent(editor, "dblclick", function(aEvent) {
aEvent.stopPropagation();
});
this.bindEditorEvent(editor, "keypress",
this.handleEditorKeypress.bind(this));
// event notification
Services.obs.notifyObservers(null, this.window.INSPECTOR_NOTIFICATIONS.EDITOR_OPENED,
null);
},
/**
* Handle binding an event handler for the editor.
* (saves the callback for easier unbinding later)
* @param aEditor
* The DOM object for the editor
* @param aEventName
* The name of the event to listen for
* @param aEventCallback
* The callback to bind to the event (and also to save for later
* unbinding)
*/
bindEditorEvent:
function TP_bindEditorEvent(aEditor, aEventName, aEventCallback)
{
this.editingEvents[aEventName] = aEventCallback;
aEditor.addEventListener(aEventName, aEventCallback, false);
},
/**
* Handle unbinding an event handler from the editor.
* (unbinds the previously bound and saved callback)
* @param aEditor
* The DOM object for the editor
* @param aEventName
* The name of the event being listened for
*/
unbindEditorEvent: function TP_unbindEditorEvent(aEditor, aEventName)
{
aEditor.removeEventListener(aEventName, this.editingEvents[aEventName],
false);
this.editingEvents[aEventName] = null;
},
/**
* Handle keypress events in the editor.
* @param aEvent
* The keyboard event.
*/
handleEditorKeypress: function TP_handleEditorKeypress(aEvent)
{
if (aEvent.which == this.window.KeyEvent.DOM_VK_RETURN) {
this.saveEditor();
} else if (aEvent.keyCode == this.window.KeyEvent.DOM_VK_ESCAPE) {
this.closeEditor();
}
},
/**
* Close the editor and cleanup.
*/
closeEditor: function TP_closeEditor()
{
let editor = this.treeBrowserDocument.getElementById("attribute-editor");
let editorInput =
this.treeBrowserDocument.getElementById("attribute-editor-input");
// remove highlight from attribute-value node in tree
this.removeClass(this.editingContext.attrObj, "editingAttributeValue");
// hide editor
this.removeClass(editor, "editing");
// stop listening for editor specific events
this.unbindEditorEvent(editor, "click");
this.unbindEditorEvent(editor, "dblclick");
this.unbindEditorEvent(editor, "keypress");
// clean up after the editor
editorInput.value = "";
editorInput.blur();
this.editingContext = null;
this.editingEvents = {};
// event notification
Services.obs.notifyObservers(null, this.window.INSPECTOR_NOTIFICATIONS.EDITOR_CLOSED,
null);
},
/**
* Commit the edits made in the editor, then close it.
*/
saveEditor: function TP_saveEditor()
{
let editorInput =
this.treeBrowserDocument.getElementById("attribute-editor-input");
// set the new attribute value on the original target DOM element
this.editingContext.repObj.setAttribute(this.editingContext.attrName,
editorInput.value);
// update the HTML tree attribute value
this.editingContext.attrObj.innerHTML = editorInput.value;
this.IUI.isDirty = true;
// event notification
Services.obs.notifyObservers(null, this.window.INSPECTOR_NOTIFICATIONS.EDITOR_SAVED,
null);
this.closeEditor();
},
/**
* Simple tree select method.
* @param aNode the DOM node in the content document to select.
* @param aScroll boolean scroll to the visible node?
*/
select: function TP_select(aNode, aScroll)
{
if (this.ioBox)
this.ioBox.select(aNode, true, true, aScroll);
},
///////////////////////////////////////////////////////////////////////////
//// Utility functions
/**
* Does the given object have a class attribute?
* @param aNode
* the DOM node.
* @param aClass
* The class string.
* @returns boolean
*/
hasClass: function TP_hasClass(aNode, aClass)
{
if (!(aNode instanceof this.window.Element))
return false;
return aNode.classList.contains(aClass);
},
/**
* Add the class name to the given object.
* @param aNode
* the DOM node.
* @param aClass
* The class string.
*/
addClass: function TP_addClass(aNode, aClass)
{
if (aNode instanceof this.window.Element)
aNode.classList.add(aClass);
},
/**
* Remove the class name from the given object
* @param aNode
* the DOM node.
* @param aClass
* The class string.
*/
removeClass: function TP_removeClass(aNode, aClass)
{
if (aNode instanceof this.window.Element)
aNode.classList.remove(aClass);
},
/**
* Get the "repObject" from the HTML panel's domplate-constructed DOM node.
* In this system, a "repObject" is the Object being Represented by the box
* object. It is the "real" object that we're building our facade around.
*
* @param element
* The element in the HTML panel the user clicked.
* @returns either a real node or null
*/
getRepObject: function TP_getRepObject(element)
{
let target = null;
for (let child = element; child; child = child.parentNode) {
if (this.hasClass(child, "repTarget"))
target = child;
if (child.repObject) {
if (!target && this.hasClass(child.repObject, "repIgnore"))
break;
else
return child.repObject;
}
}
return null;
},
/**
* Destructor function. Cleanup.
*/
destroy: function TP_destroy()
{
if (this.isOpen()) {
this.close();
}
domplateUtils.setDOM(null);
delete this.treeWalker;
if (this.treePanelDiv) {
this.treePanelDiv.ownerPanel = null;
let parent = this.treePanelDiv.parentNode;
parent.removeChild(this.treePanelDiv);
delete this.treePanelDiv;
delete this.treeBrowserDocument;
}
if (this.treeIFrame) {
this.treeIFrame.removeEventListener("dblclick", this.onTreeDblClick, false);
this.treeIFrame.removeEventListener("click", this.onTreeClick, false);
let parent = this.treeIFrame.parentNode;
parent.removeChild(this.treeIFrame);
delete this.treeIFrame;
}
if (this.ioBox) {
this.ioBox.destroy();
delete this.ioBox;
}
this.container.removeEventListener("popuphiding", this._boundClose, false);
delete this._boundClose;
}
};

Просмотреть файл

@ -26,7 +26,7 @@
* Mihai Șucan <mihai.sucan@gmail.com>
* Julian Viereck <jviereck@mozilla.com>
* Paul Rouget <paul@mozilla.com>
* Kyle Simpson <ksimpson@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
@ -43,8 +43,6 @@
* ***** END LICENSE BLOCK ***** */
#endif
#include insideOutBox.js
const INSPECTOR_INVISIBLE_ELEMENTS = {
"head": true,
"base": true,
@ -72,6 +70,9 @@ const INSPECTOR_NOTIFICATIONS = {
// Fires once the Inspector is closed.
CLOSED: "inspector-closed",
// Fires when the Tree Panel is opened and initialized.
TREEPANELREADY: "inspector-treepanel-ready",
// Event notifications for the attribute-value editor
EDITOR_OPENED: "inspector-editor-opened",
EDITOR_CLOSED: "inspector-editor-closed",
@ -554,9 +555,8 @@ var InspectorUI = {
browser: null,
tools: {},
toolEvents: {},
showTextNodesWithWhitespace: false,
inspecting: false,
treeLoaded: false,
treePanelEnabled: true,
get enabled()
{
return gPrefService.getBoolPref("devtools.inspector.enabled");
@ -571,7 +571,7 @@ var InspectorUI = {
*/
toggleInspectorUI: function IUI_toggleInspectorUI(aEvent)
{
if (this.isTreePanelOpen) {
if (this.isInspectorOpen) {
this.closeInspectorUI();
} else {
this.openInspectorUI();
@ -592,13 +592,13 @@ var InspectorUI = {
},
/**
* Is the tree panel open?
* Is the inspector UI open? Simply check if the toolbar is visible or not.
*
* @returns boolean
*/
get isTreePanelOpen()
get isInspectorOpen()
{
return this.treePanel && this.treePanel.state == "open";
return this.toolbar && !this.toolbar.hidden && this.highlighter;
},
/**
@ -610,176 +610,6 @@ var InspectorUI = {
return doc.documentElement ? doc.documentElement.lastElementChild : null;
},
initializeTreePanel: function IUI_initializeTreePanel()
{
this.treeBrowserDocument = this.treeIFrame.contentDocument;
this.treePanelDiv = this.treeBrowserDocument.createElement("div");
this.treeBrowserDocument.body.appendChild(this.treePanelDiv);
this.treePanelDiv.ownerPanel = this;
this.ioBox = new InsideOutBox(this, this.treePanelDiv);
this.ioBox.createObjectBox(this.win.document.documentElement);
this.treeLoaded = true;
this.editingContext = null;
this.editingEvents = {};
// initialize the highlighter
this.initializeHighlighter();
},
/**
* Open the inspector's tree panel and initialize it.
*/
openTreePanel: function IUI_openTreePanel()
{
if (!this.treePanel) {
this.treePanel = document.getElementById("inspector-tree-panel");
this.treePanel.hidden = false;
}
this.treeIFrame = document.getElementById("inspector-tree-iframe");
if (!this.treeIFrame) {
let resizerBox = document.getElementById("tree-panel-resizer-box");
this.treeIFrame = document.createElement("iframe");
this.treeIFrame.setAttribute("id", "inspector-tree-iframe");
this.treeIFrame.setAttribute("flex", "1");
this.treeIFrame.setAttribute("type", "content");
this.treeIFrame.setAttribute("onclick", "InspectorUI.onTreeClick(event)");
this.treeIFrame.setAttribute("ondblclick", "InspectorUI.onTreeDblClick(event);");
this.treeIFrame = this.treePanel.insertBefore(this.treeIFrame, resizerBox);
}
this.treePanel.addEventListener("popupshown", function treePanelShown() {
InspectorUI.treePanel.removeEventListener("popupshown",
treePanelShown, false);
InspectorUI.treeIFrame.addEventListener("load",
function loadedInitializeTreePanel() {
InspectorUI.treeIFrame.removeEventListener("load",
loadedInitializeTreePanel, true);
InspectorUI.initializeTreePanel();
}, true);
let src = InspectorUI.treeIFrame.getAttribute("src");
if (src != "chrome://browser/content/inspector.html") {
InspectorUI.treeIFrame.setAttribute("src",
"chrome://browser/content/inspector.html");
} else {
InspectorUI.treeIFrame.contentWindow.location.reload();
}
}, false);
const panelWidthRatio = 7 / 8;
const panelHeightRatio = 1 / 5;
let width = parseInt(this.win.outerWidth * panelWidthRatio);
let height = parseInt(this.win.outerHeight * panelHeightRatio);
let y = Math.min(window.screen.availHeight - height, this.win.innerHeight);
this.treePanel.openPopup(this.browser, "overlap", 0, 0,
false, false);
this.treePanel.moveTo(80, y);
this.treePanel.sizeTo(width, height);
},
createObjectBox: function IUI_createObjectBox(object, isRoot)
{
let tag = this.domplateUtils.getNodeTag(object);
if (tag)
return tag.replace({object: object}, this.treeBrowserDocument);
},
getParentObject: function IUI_getParentObject(node)
{
let parentNode = node ? node.parentNode : null;
if (!parentNode) {
// Documents have no parentNode; Attr, Document, DocumentFragment, Entity,
// and Notation. top level windows have no parentNode
if (node && node == Node.DOCUMENT_NODE) {
// document type
if (node.defaultView) {
let embeddingFrame = node.defaultView.frameElement;
if (embeddingFrame)
return embeddingFrame.parentNode;
}
}
// a Document object without a parentNode or window
return null; // top level has no parent
}
if (parentNode.nodeType == Node.DOCUMENT_NODE) {
if (parentNode.defaultView) {
return parentNode.defaultView.frameElement;
}
// parent is document element, but no window at defaultView.
return null;
}
if (!parentNode.localName) {
return null;
}
return parentNode;
},
getChildObject: function IUI_getChildObject(node, index, previousSibling)
{
if (!node)
return null;
if (node.contentDocument) {
// then the node is a frame
if (index == 0) {
return node.contentDocument.documentElement; // the node's HTMLElement
}
return null;
}
if (node instanceof GetSVGDocument) {
let svgDocument = node.getSVGDocument();
if (svgDocument) {
// then the node is a frame
if (index == 0) {
return svgDocument.documentElement; // the node's SVGElement
}
return null;
}
}
let child = null;
if (previousSibling) // then we are walking
child = this.getNextSibling(previousSibling);
else
child = this.getFirstChild(node);
if (this.showTextNodesWithWhitespace)
return child;
for (; child; child = this.getNextSibling(child)) {
if (!this.domplateUtils.isWhitespaceText(child))
return child;
}
return null; // we have no children worth showing.
},
getFirstChild: function IUI_getFirstChild(node)
{
this.treeWalker = node.ownerDocument.createTreeWalker(node,
NodeFilter.SHOW_ALL, null, false);
return this.treeWalker.firstChild();
},
getNextSibling: function IUI_getNextSibling(node)
{
let next = this.treeWalker.nextSibling();
if (!next)
delete this.treeWalker;
return next;
},
/**
* Open inspector UI and HTML tree. Add listeners for document scrolling,
* resize, tabContainer.TabSelect and others. If a node is provided, then
@ -791,11 +621,12 @@ var InspectorUI = {
openInspectorUI: function IUI_openInspectorUI(aNode)
{
// InspectorUI is already up and running. Lock a node if asked (via context).
if (this.treeLoaded && this.highlighter && aNode) {
if (this.isInspectorOpen && aNode) {
this.inspectNode(aNode);
this.stopInspecting();
return;
}
// Observer used to inspect the specified element from content after the
// inspector UI has been opened.
function inspectObserver(aElement) {
@ -805,6 +636,7 @@ var InspectorUI = {
this.inspectNode(aElement);
this.stopInspecting();
};
var boundInspectObserver = inspectObserver.bind(this, aNode);
if (aNode) {
@ -821,17 +653,20 @@ var InspectorUI = {
this.initTools();
if (!this.domplate) {
Cu.import("resource:///modules/domplate.jsm", this);
this.domplateUtils.setDOM(window);
if (!this.TreePanel && this.treePanelEnabled) {
Cu.import("resource:///modules/TreePanel.jsm", this);
this.treePanel = new this.TreePanel(window, this);
}
this.openTreePanel();
this.toolbar.hidden = false;
this.inspectCmd.setAttribute("checked", true);
this.inspectMenuitem.setAttribute("checked", true);
this.isDirty = false;
gBrowser.addProgressListener(InspectorProgressListener);
// initialize the highlighter
this.initializeHighlighter();
},
/**
@ -885,11 +720,13 @@ var InspectorUI = {
if (selectedNode) {
this.inspectNode(selectedNode);
}
this.isDirty = InspectorStore.getValue(this.winID, "isDirty");
} else {
// First time inspecting, set state to no selection + live inspection.
InspectorStore.addStore(this.winID);
InspectorStore.setValue(this.winID, "selectedNode", null);
InspectorStore.setValue(this.winID, "inspecting", true);
InspectorStore.setValue(this.winID, "isDirty", this.isDirty);
this.win.addEventListener("pagehide", this, true);
}
},
@ -908,8 +745,8 @@ var InspectorUI = {
{
// if currently editing an attribute value, closing the
// highlighter/HTML panel dismisses the editor
if (this.editingContext)
this.closeEditor();
if (this.treePanel && this.treePanel.editingContext)
this.treePanel.closeEditor();
if (this.closing || !this.win || !this.browser) {
return;
@ -930,6 +767,7 @@ var InspectorUI = {
this.selection);
}
InspectorStore.setValue(this.winID, "inspecting", this.inspecting);
InspectorStore.setValue(this.winID, "isDirty", this.isDirty);
}
if (InspectorStore.isEmpty()) {
@ -948,44 +786,18 @@ var InspectorUI = {
this.highlighter = null;
}
if (this.treePanelDiv) {
this.treePanelDiv.ownerPanel = null;
let parent = this.treePanelDiv.parentNode;
parent.removeChild(this.treePanelDiv);
delete this.treePanelDiv;
delete this.treeBrowserDocument;
}
if (this.treeIFrame) {
let parent = this.treeIFrame.parentNode;
parent.removeChild(this.treeIFrame);
delete this.treeIFrame;
}
delete this.ioBox;
if (this.domplate) {
this.domplateUtils.setDOM(null);
delete this.domplate;
delete this.HTMLTemplates;
delete this.domplateUtils;
}
this.inspectCmd.setAttribute("checked", false);
this.inspectMenuitem.setAttribute("checked", false);
this.browser = this.win = null; // null out references to browser and window
this.winID = null;
this.selection = null;
this.treeLoaded = false;
this.closing = false;
this.isDirty = false;
this.treePanel.addEventListener("popuphidden", function treePanelHidden() {
this.removeEventListener("popuphidden", treePanelHidden, false);
InspectorUI.closing = false;
Services.obs.notifyObservers(null, INSPECTOR_NOTIFICATIONS.CLOSED, null);
}, false);
this.treePanel.hidePopup();
delete this.treePanel;
delete this.stylePanel;
delete this.toolbar;
delete this.TreePanel;
Services.obs.notifyObservers(null, INSPECTOR_NOTIFICATIONS.CLOSED, null);
},
/**
@ -996,10 +808,10 @@ var InspectorUI = {
{
// if currently editing an attribute value, starting
// "live inspection" mode closes the editor
if (this.editingContext)
this.closeEditor();
if (this.treePanel && this.treePanel.editingContext)
this.treePanel.closeEditor();
document.getElementById("inspector-inspect-toolbutton").checked = true;
this.inspectToolbutton.checked = true;
this.attachPageListeners();
this.inspecting = true;
this.toolsDim(true);
@ -1018,7 +830,7 @@ var InspectorUI = {
return;
}
document.getElementById("inspector-inspect-toolbutton").checked = false;
this.inspectToolbutton.checked = false;
this.detachPageListeners();
this.inspecting = false;
this.toolsDim(false);
@ -1036,15 +848,15 @@ var InspectorUI = {
* node to inspect
* @param forceUpdate
* force an update?
* @param aScroll
* force scroll?
* @param aScroll boolean
* scroll the tree panel?
*/
select: function IUI_select(aNode, forceUpdate, aScroll)
{
// if currently editing an attribute value, using the
// highlighter dismisses the editor
if (this.editingContext)
this.closeEditor();
if (this.treePanel && this.treePanel.editingContext)
this.treePanel.closeEditor();
if (!aNode)
aNode = this.defaultSelection;
@ -1054,10 +866,9 @@ var InspectorUI = {
if (!this.inspecting) {
this.highlighter.highlightNode(this.selection);
}
this.ioBox.select(this.selection, true, true, aScroll);
}
this.toolsSelect();
this.toolsSelect(aScroll);
},
/////////////////////////////////////////////////////////////////////////
@ -1093,7 +904,7 @@ var InspectorUI = {
switch (event.type) {
case "TabSelect":
winID = this.getWindowID(gBrowser.selectedBrowser.contentWindow);
if (this.isTreePanelOpen && winID != this.winID) {
if (this.isInspectorOpen && winID != this.winID) {
this.closeInspectorUI(true);
inspectorClosed = true;
}
@ -1148,265 +959,6 @@ var InspectorUI = {
}
},
/**
* Handle click events in the html tree panel.
* @param aEvent
* The mouse event.
*/
onTreeClick: function IUI_onTreeClick(aEvent)
{
// if currently editing an attribute value, clicking outside
// the editor dismisses the editor
if (this.editingContext) {
this.closeEditor();
// clicking outside the editor ONLY closes the editor
// so, cancel the rest of the processing of this event
aEvent.preventDefault();
return;
}
let node;
let target = aEvent.target;
let hitTwisty = false;
if (this.hasClass(target, "twisty")) {
node = this.getRepObject(aEvent.target.nextSibling);
hitTwisty = true;
} else {
node = this.getRepObject(aEvent.target);
}
if (node) {
if (hitTwisty) {
this.ioBox.toggleObject(node);
} else {
if (this.inspecting) {
this.toolsSelect();
this.stopInspecting(true);
} else {
this.select(node, true, false);
this.highlighter.highlightNode(node);
this.toolsSelect();
}
}
}
},
/**
* Handle double-click events in the html tree panel.
* (double-clicking an attribute value allows it to be edited)
* @param aEvent
* The mouse event.
*/
onTreeDblClick: function IUI_onTreeDblClick(aEvent)
{
// if already editing an attribute value, double-clicking elsewhere
// in the tree is the same as a click, which dismisses the editor
if (this.editingContext)
this.closeEditor();
let target = aEvent.target;
if (this.hasClass(target, "nodeValue")) {
let repObj = this.getRepObject(target);
let attrName = target.getAttribute("data-attributeName");
let attrVal = target.innerHTML;
this.editAttributeValue(target, repObj, attrName, attrVal);
}
},
/**
* Starts the editor for an attribute value.
* @param aAttrObj
* The DOM object representing the attribute value in the HTML Tree
* @param aRepObj
* The original DOM (target) object being inspected/edited
* @param aAttrName
* The name of the attribute being edited
* @param aAttrVal
* The current value of the attribute being edited
*/
editAttributeValue:
function IUI_editAttributeValue(aAttrObj, aRepObj, aAttrName, aAttrVal)
{
let editor = this.treeBrowserDocument.getElementById("attribute-editor");
let editorInput =
this.treeBrowserDocument.getElementById("attribute-editor-input");
let attrDims = aAttrObj.getBoundingClientRect();
// figure out actual viewable viewport dimensions (sans scrollbars)
let viewportWidth = this.treeBrowserDocument.documentElement.clientWidth;
let viewportHeight = this.treeBrowserDocument.documentElement.clientHeight;
// saves the editing context for use when the editor is saved/closed
this.editingContext = {
attrObj: aAttrObj,
repObj: aRepObj,
attrName: aAttrName
};
// highlight attribute-value node in tree while editing
this.addClass(aAttrObj, "editingAttributeValue");
// show the editor
this.addClass(editor, "editing");
// offset the editor below the attribute-value node being edited
let editorVeritcalOffset = 2;
// keep the editor comfortably within the bounds of the viewport
let editorViewportBoundary = 5;
// outer editor is sized based on the <input> box inside it
editorInput.style.width = Math.min(attrDims.width, viewportWidth -
editorViewportBoundary) + "px";
editorInput.style.height = Math.min(attrDims.height, viewportHeight -
editorViewportBoundary) + "px";
let editorDims = editor.getBoundingClientRect();
// calculate position for the editor according to the attribute node
let editorLeft = attrDims.left + this.treeIFrame.contentWindow.scrollX -
// center the editor against the attribute value
((editorDims.width - attrDims.width) / 2);
let editorTop = attrDims.top + this.treeIFrame.contentWindow.scrollY +
attrDims.height + editorVeritcalOffset;
// but, make sure the editor stays within the visible viewport
editorLeft = Math.max(0, Math.min(
(this.treeIFrame.contentWindow.scrollX +
viewportWidth - editorDims.width),
editorLeft)
);
editorTop = Math.max(0, Math.min(
(this.treeIFrame.contentWindow.scrollY +
viewportHeight - editorDims.height),
editorTop)
);
// position the editor
editor.style.left = editorLeft + "px";
editor.style.top = editorTop + "px";
// set and select the text
editorInput.value = aAttrVal;
editorInput.select();
// listen for editor specific events
this.bindEditorEvent(editor, "click", function(aEvent) {
aEvent.stopPropagation();
});
this.bindEditorEvent(editor, "dblclick", function(aEvent) {
aEvent.stopPropagation();
});
this.bindEditorEvent(editor, "keypress",
this.handleEditorKeypress.bind(this));
// event notification
Services.obs.notifyObservers(null, INSPECTOR_NOTIFICATIONS.EDITOR_OPENED,
null);
},
/**
* Handle binding an event handler for the editor.
* (saves the callback for easier unbinding later)
* @param aEditor
* The DOM object for the editor
* @param aEventName
* The name of the event to listen for
* @param aEventCallback
* The callback to bind to the event (and also to save for later
* unbinding)
*/
bindEditorEvent:
function IUI_bindEditorEvent(aEditor, aEventName, aEventCallback)
{
this.editingEvents[aEventName] = aEventCallback;
aEditor.addEventListener(aEventName, aEventCallback, false);
},
/**
* Handle unbinding an event handler from the editor.
* (unbinds the previously bound and saved callback)
* @param aEditor
* The DOM object for the editor
* @param aEventName
* The name of the event being listened for
*/
unbindEditorEvent: function IUI_unbindEditorEvent(aEditor, aEventName)
{
aEditor.removeEventListener(aEventName, this.editingEvents[aEventName],
false);
this.editingEvents[aEventName] = null;
},
/**
* Handle keypress events in the editor.
* @param aEvent
* The keyboard event.
*/
handleEditorKeypress: function IUI_handleEditorKeypress(aEvent)
{
if (aEvent.which == KeyEvent.DOM_VK_RETURN) {
this.saveEditor();
} else if (aEvent.keyCode == KeyEvent.DOM_VK_ESCAPE) {
this.closeEditor();
}
},
/**
* Close the editor and cleanup.
*/
closeEditor: function IUI_closeEditor()
{
let editor = this.treeBrowserDocument.getElementById("attribute-editor");
let editorInput =
this.treeBrowserDocument.getElementById("attribute-editor-input");
// remove highlight from attribute-value node in tree
this.removeClass(this.editingContext.attrObj, "editingAttributeValue");
// hide editor
this.removeClass(editor, "editing");
// stop listening for editor specific events
this.unbindEditorEvent(editor, "click");
this.unbindEditorEvent(editor, "dblclick");
this.unbindEditorEvent(editor, "keypress");
// clean up after the editor
editorInput.value = "";
editorInput.blur();
this.editingContext = null;
this.editingEvents = {};
// event notification
Services.obs.notifyObservers(null, INSPECTOR_NOTIFICATIONS.EDITOR_CLOSED,
null);
},
/**
* Commit the edits made in the editor, then close it.
*/
saveEditor: function IUI_saveEditor()
{
let editorInput =
this.treeBrowserDocument.getElementById("attribute-editor-input");
// set the new attribute value on the original target DOM element
this.editingContext.repObj.setAttribute(this.editingContext.attrName,
editorInput.value);
// update the HTML tree attribute value
this.editingContext.attrObj.innerHTML = editorInput.value;
this.isDirty = true;
// event notification
Services.obs.notifyObservers(null, INSPECTOR_NOTIFICATIONS.EDITOR_SAVED,
null);
this.closeEditor();
},
/**
* Attach event listeners to content window and child windows to enable
* highlighting and click to stop inspection.
@ -1511,47 +1063,6 @@ var InspectorUI = {
return [borderTop + paddingTop, borderLeft + paddingLeft];
},
/**
* Does the given object have a class attribute?
* @param aNode
* the DOM node.
* @param aClass
* The class string.
* @returns boolean
*/
hasClass: function IUI_hasClass(aNode, aClass)
{
if (!(aNode instanceof Element))
return false;
return aNode.classList.contains(aClass);
},
/**
* Add the class name to the given object.
* @param aNode
* the DOM node.
* @param aClass
* The class string.
*/
addClass: function IUI_addClass(aNode, aClass)
{
if (aNode instanceof Element)
aNode.classList.add(aClass);
},
/**
* Remove the class name from the given object
* @param aNode
* the DOM node.
* @param aClass
* The class string.
*/
removeClass: function IUI_removeClass(aNode, aClass)
{
if (aNode instanceof Element)
aNode.classList.remove(aClass);
},
/**
* Retrieve the unique ID of a window object.
*
@ -1574,32 +1085,6 @@ var InspectorUI = {
return util.currentInnerWindowID;
},
/**
* Get the "repObject" from the HTML panel's domplate-constructed DOM node.
* In this system, a "repObject" is the Object being Represented by the box
* object. It is the "real" object that we're building our facade around.
*
* @param element
* The element in the HTML panel the user clicked.
* @returns either a real node or null
*/
getRepObject: function IUI_getRepObject(element)
{
let target = null;
for (let child = element; child; child = child.parentNode) {
if (this.hasClass(child, "repTarget"))
target = child;
if (child.repObject) {
if (!target && this.hasClass(child.repObject, "repIgnore"))
break;
else
return child.repObject;
}
}
return null;
},
/**
* @param msg
* text message to send to the log
@ -1808,12 +1293,14 @@ var InspectorUI = {
/**
* For each tool in the tools collection select the current node that is
* selected in the highlighter
* @param aScroll boolean
* Do you want to scroll the treepanel?
*/
toolsSelect: function IUI_toolsSelect()
toolsSelect: function IUI_toolsSelect(aScroll)
{
this.toolsDo(function IUI_toolsOnSelect(aTool) {
if (aTool.isOpen) {
aTool.onSelect.call(aTool.context, InspectorUI.selection);
aTool.onSelect.call(aTool.context, InspectorUI.selection, aScroll);
}
});
},
@ -1991,7 +1478,7 @@ var InspectorProgressListener = {
function IPL_onStateChange(aProgress, aRequest, aFlag, aStatus)
{
// Remove myself if the Inspector is no longer open.
if (!InspectorUI.isTreePanelOpen) {
if (!InspectorUI.isInspectorOpen) {
gBrowser.removeProgressListener(InspectorProgressListener);
return;
}
@ -2089,10 +1576,14 @@ var InspectorProgressListener = {
/////////////////////////////////////////////////////////////////////////
//// Initializers
XPCOMUtils.defineLazyGetter(InspectorUI, "inspectCmd", function () {
XPCOMUtils.defineLazyGetter(InspectorUI, "inspectMenuitem", function () {
return document.getElementById("Tools:Inspect");
});
XPCOMUtils.defineLazyGetter(InspectorUI, "inspectToolbutton", function () {
return document.getElementById("inspector-inspect-toolbutton");
});
XPCOMUtils.defineLazyGetter(InspectorUI, "strings", function () {
return Services.strings.
createBundle("chrome://browser/locale/inspector.properties");

Просмотреть файл

@ -61,5 +61,7 @@ _BROWSER_FILES = \
browser_inspector_bug_566084_location_changed.js \
$(NULL)
# browser_inspector_treePanel_click.js \
libs:: $(_BROWSER_FILES)
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)

Просмотреть файл

@ -17,7 +17,7 @@ function runInspectorTests() {
is(para.textContent, "init", "paragraph content is correct");
ok(InspectorUI.inspecting, "Inspector is highlighting");
ok(InspectorUI.isTreePanelOpen, "Inspector Panel is open");
ok(InspectorUI.isInspectorOpen, "Inspector is open");
InspectorUI.isDirty = true;
@ -56,7 +56,7 @@ function onPageLoad() {
is(para.textContent, "test2", "paragraph content is correct");
ok(!InspectorUI.inspecting, "Inspector is not highlighting");
ok(!InspectorUI.isTreePanelOpen, "Inspector Panel is not open");
ok(!InspectorUI.isInspectorOpen, "Inspector Panel is not open");
testEnd();
}
@ -68,7 +68,7 @@ function locationTest2() {
is(para.textContent, "init", "paragraph content is correct");
ok(InspectorUI.inspecting, "Inspector is highlighting");
ok(InspectorUI.isTreePanelOpen, "Inspector Panel is open");
ok(InspectorUI.isInspectorOpen, "Inspector Panel is open");
notificationBox.addEventListener("AlertActive", alertActive2, false);
@ -102,7 +102,6 @@ function alertActive2() {
function testEnd() {
notificationBox = null;
InspectorUI.isDirty = false;
gBrowser.removeCurrentTab();
executeSoon(finish);
}

Просмотреть файл

@ -3,11 +3,11 @@
/* ***** BEGIN LICENSE BLOCK *****
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*
*
* Contributor(s):
* Rob Campbell <rcampbell@mozilla.com>
* Mihai Sucan <mihai.sucan@gmail.com>
* Kyle Simpson <ksimpson@mozilla.com>
* Kyle Simpson <ksimpson@mozilla.com>
*
* ***** END LICENSE BLOCK ***** */
@ -25,19 +25,26 @@ function setupEditorTests()
div.setAttribute("id", "foobar");
div.setAttribute("class", "barbaz");
doc.body.appendChild(div);
Services.obs.addObserver(runEditorTests, INSPECTOR_NOTIFICATIONS.OPENED, false);
Services.obs.addObserver(setupHTMLPanel, INSPECTOR_NOTIFICATIONS.OPENED, false);
InspectorUI.toggleInspectorUI();
}
function setupHTMLPanel()
{
Services.obs.removeObserver(setupHTMLPanel, INSPECTOR_NOTIFICATIONS.OPENED);
Services.obs.addObserver(runEditorTests, INSPECTOR_NOTIFICATIONS.TREEPANELREADY, false);
InspectorUI.treePanel.open();
}
function runEditorTests()
{
Services.obs.removeObserver(runEditorTests, INSPECTOR_NOTIFICATIONS.OPENED, false);
Services.obs.removeObserver(runEditorTests, INSPECTOR_NOTIFICATIONS.TREEPANELREADY);
InspectorUI.stopInspecting();
// setup generator for async test steps
editorTestSteps = doEditorTestSteps();
// add step listeners
Services.obs.addObserver(doNextStep, INSPECTOR_NOTIFICATIONS.EDITOR_OPENED, false);
Services.obs.addObserver(doNextStep, INSPECTOR_NOTIFICATIONS.EDITOR_CLOSED, false);
@ -49,16 +56,17 @@ function runEditorTests()
function doEditorTestSteps()
{
let editor = InspectorUI.treeBrowserDocument.getElementById("attribute-editor");
let editorInput = InspectorUI.treeBrowserDocument.getElementById("attribute-editor-input");
let treePanel = InspectorUI.treePanel;
let editor = treePanel.treeBrowserDocument.getElementById("attribute-editor");
let editorInput = treePanel.treeBrowserDocument.getElementById("attribute-editor-input");
// Step 1: grab and test the attribute-value nodes in the HTML panel, then open editor
let attrValNode_id = InspectorUI.treeBrowserDocument.querySelectorAll(".nodeValue.editable[data-attributeName='id']")[0];
let attrValNode_class = InspectorUI.treeBrowserDocument.querySelectorAll(".nodeValue.editable[data-attributeName='class']")[0];
let attrValNode_id = treePanel.treeBrowserDocument.querySelectorAll(".nodeValue.editable[data-attributeName='id']")[0];
let attrValNode_class = treePanel.treeBrowserDocument.querySelectorAll(".nodeValue.editable[data-attributeName='class']")[0];
is(attrValNode_id.innerHTML, "foobar", "Step 1: we have the correct `id` attribute-value node in the HTML panel");
is(attrValNode_class.innerHTML, "barbaz", "we have the correct `class` attribute-value node in the HTML panel");
// double-click the `id` attribute-value node to open the editor
executeSoon(function() {
// firing 2 clicks right in a row to simulate a double-click
@ -69,7 +77,7 @@ function doEditorTestSteps()
// Step 2: validate editing session, enter new attribute value into editor, and save input
ok(InspectorUI.editingContext, "Step 2: editor session started");
ok(InspectorUI.treePanel.editingContext, "Step 2: editor session started");
let editorVisible = editor.classList.contains("editing");
ok(editorVisible, "editor popup visible");
@ -88,13 +96,13 @@ function doEditorTestSteps()
let attrValNodeHighlighted = attrValNode_id.classList.contains("editingAttributeValue");
ok(attrValNodeHighlighted, "`id` attribute-value node is editor-highlighted");
is(InspectorUI.editingContext.repObj, div, "editor session has correct reference to div");
is(InspectorUI.editingContext.attrObj, attrValNode_id, "editor session has correct reference to `id` attribute-value node in HTML panel");
is(InspectorUI.editingContext.attrName, "id", "editor session knows correct attribute-name");
is(treePanel.editingContext.repObj, div, "editor session has correct reference to div");
is(treePanel.editingContext.attrObj, attrValNode_id, "editor session has correct reference to `id` attribute-value node in HTML panel");
is(treePanel.editingContext.attrName, "id", "editor session knows correct attribute-name");
editorInput.value = "Hello World";
editorInput.focus();
// hit <enter> to save the inputted value
executeSoon(function() {
EventUtils.synthesizeKey("VK_RETURN", {}, attrValNode_id.ownerDocument.defaultView);
@ -106,7 +114,7 @@ function doEditorTestSteps()
// Step 3: validate that the previous editing session saved correctly, then open editor on `class` attribute value
ok(!InspectorUI.editingContext, "Step 3: editor session ended");
ok(!treePanel.editingContext, "Step 3: editor session ended");
editorVisible = editor.classList.contains("editing");
ok(!editorVisible, "editor popup hidden");
attrValNodeHighlighted = attrValNode_id.classList.contains("editingAttributeValue");
@ -124,16 +132,16 @@ function doEditorTestSteps()
// Step 4: enter value into editor, then hit <escape> to discard it
ok(InspectorUI.editingContext, "Step 4: editor session started");
ok(treePanel.editingContext, "Step 4: editor session started");
editorVisible = editor.classList.contains("editing");
ok(editorVisible, "editor popup visible");
is(InspectorUI.editingContext.attrObj, attrValNode_class, "editor session has correct reference to `class` attribute-value node in HTML panel");
is(InspectorUI.editingContext.attrName, "class", "editor session knows correct attribute-name");
is(treePanel.editingContext.attrObj, attrValNode_class, "editor session has correct reference to `class` attribute-value node in HTML panel");
is(treePanel.editingContext.attrName, "class", "editor session knows correct attribute-name");
editorInput.value = "Hello World";
editorInput.focus();
// hit <escape> to discard the inputted value
executeSoon(function() {
EventUtils.synthesizeKey("VK_ESCAPE", {}, attrValNode_class.ownerDocument.defaultView);
@ -143,7 +151,7 @@ function doEditorTestSteps()
// Step 5: validate that the previous editing session discarded correctly, then open editor on `id` attribute value again
ok(!InspectorUI.editingContext, "Step 5: editor session ended");
ok(!treePanel.editingContext, "Step 5: editor session ended");
editorVisible = editor.classList.contains("editing");
ok(!editorVisible, "editor popup hidden");
is(div.getAttribute("class"), "barbaz", "`class` attribute-value *not* updated");
@ -159,15 +167,15 @@ function doEditorTestSteps()
// Step 6: validate that editor opened again, then test double-click inside of editor (should do nothing)
ok(InspectorUI.editingContext, "Step 6: editor session started");
ok(treePanel.editingContext, "Step 6: editor session started");
editorVisible = editor.classList.contains("editing");
ok(editorVisible, "editor popup visible");
// double-click on the editor input box
executeSoon(function() {
// firing 2 clicks right in a row to simulate a double-click
EventUtils.synthesizeMouse(editorInput, 2, 2, {clickCount: 2}, editorInput.ownerDocument.defaultView);
// since the previous double-click is supposed to do nothing,
// wait a brief moment, then move on to the next step
executeSoon(function() {
@ -178,12 +186,12 @@ function doEditorTestSteps()
yield; // End of Step 6
// Step 7: validate that editing session is still correct, then enter a value and try a click
// Step 7: validate that editing session is still correct, then enter a value and try a click
// outside of editor (should cancel the editing session)
ok(InspectorUI.editingContext, "Step 7: editor session still going");
ok(treePanel.editingContext, "Step 7: editor session still going");
editorVisible = editor.classList.contains("editing");
ok(editorVisible, "editor popup still visible");
editorInput.value = "all your base are belong to us";
// single-click the `class` attribute-value node
@ -195,12 +203,12 @@ function doEditorTestSteps()
// Step 8: validate that the editor was closed and that the editing was not saved
ok(!InspectorUI.editingContext, "Step 8: editor session ended");
ok(!treePanel.editingContext, "Step 8: editor session ended");
editorVisible = editor.classList.contains("editing");
ok(!editorVisible, "editor popup hidden");
is(div.getAttribute("id"), "Hello World", "`id` attribute-value *not* updated");
is(attrValNode_id.innerHTML, "Hello World", "attribute-value node in HTML panel *not* updated");
// End of Step 8
// end of all steps, so clean up

Просмотреть файл

@ -43,7 +43,6 @@ let div1;
let div2;
let iframe1;
let iframe2;
let highlighterFrame;
function createDocument()
{
@ -99,7 +98,6 @@ function runIframeTests()
Services.obs.addObserver(performTestComparisons1,
INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
highlighterFrame = InspectorUI.highlighter.iframe;
executeSoon(moveMouseOver.bind(this, div1));
}

Просмотреть файл

@ -68,16 +68,27 @@ function startInspectorTests()
function runInspectorTests()
{
Services.obs.removeObserver(runInspectorTests,
INSPECTOR_NOTIFICATIONS.OPENED, false);
INSPECTOR_NOTIFICATIONS.OPENED);
Services.obs.addObserver(treePanelTests,
INSPECTOR_NOTIFICATIONS.TREEPANELREADY, false);
ok(InspectorUI.toolbar, "we have the toolbar.");
ok(!InspectorUI.toolbar.hidden, "toolbar is visible");
ok(InspectorUI.inspecting, "Inspector is inspecting");
ok(!InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is not open");
ok(InspectorUI.highlighter, "Highlighter is up");
InspectorUI.treePanel.open();
}
function treePanelTests()
{
Services.obs.removeObserver(treePanelTests,
INSPECTOR_NOTIFICATIONS.TREEPANELREADY);
Services.obs.addObserver(runContextMenuTest,
INSPECTOR_NOTIFICATIONS.CLOSED, false);
ok(!InspectorUI.toolbar.hidden, "toolbar is visible");
let iframe = document.getElementById("inspector-tree-iframe");
is(InspectorUI.treeIFrame, iframe, "Inspector IFrame matches");
ok(InspectorUI.inspecting, "Inspector is inspecting");
ok(InspectorUI.isTreePanelOpen, "Inspector Tree Panel is open");
ok(InspectorUI.highlighter, "Highlighter is up");
ok(InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is open");
executeSoon(function() {
InspectorUI.closeInspectorUI();
@ -110,7 +121,7 @@ function inspectNodesFromContextTest()
Services.obs.addObserver(openInspectorForContextTest, INSPECTOR_NOTIFICATIONS.CLOSED, false);
ok(!InspectorUI.inspecting, "Inspector is not actively highlighting");
is(InspectorUI.selection, salutation, "Inspector is highlighting salutation");
ok(InspectorUI.isTreePanelOpen, "Inspector Tree Panel is open");
ok(!InspectorUI.isTreePanelOpen, "Inspector Tree Panel is closed");
// TODO: These tests depend on the style inspector patches.
todo(InspectorUI.isStylePanelOpen, "Inspector Style Panel is open");
executeSoon(function() {
@ -162,9 +173,9 @@ function finishInspectorTests()
INSPECTOR_NOTIFICATIONS.CLOSED);
ok(!InspectorUI.highlighter, "Highlighter is gone");
ok(!InspectorUI.isTreePanelOpen, "Inspector Tree Panel is closed");
ok(!InspectorUI.treePanel, "Inspector Tree Panel is closed");
ok(!InspectorUI.inspecting, "Inspector is not inspecting");
ok(InspectorUI.toolbar.hidden, "toolbar is hidden");
ok(!InspectorUI.toolbar, "toolbar is hidden");
gBrowser.removeCurrentTab();
finish();

Просмотреть файл

@ -61,7 +61,7 @@ function inspectorUIOpen1()
// Make sure the inspector is open.
ok(InspectorUI.inspecting, "Inspector is highlighting");
ok(InspectorUI.isTreePanelOpen, "Inspector Tree Panel is open");
ok(!InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is not open");
ok(!InspectorStore.isEmpty(), "InspectorStore is not empty");
is(InspectorStore.length, 1, "InspectorStore.length = 1");
@ -87,7 +87,7 @@ function inspectorTabOpen2()
{
// Make sure the inspector is closed.
ok(!InspectorUI.inspecting, "Inspector is not highlighting");
ok(!InspectorUI.isPanelOpen, "Inspector Tree Panel is closed");
ok(!InspectorUI.treePanel, "Inspector Tree Panel is closed");
is(InspectorStore.length, 1, "InspectorStore.length = 1");
// Activate the inspector again.
@ -105,7 +105,7 @@ function inspectorUIOpen2()
// Make sure the inspector is open.
ok(InspectorUI.inspecting, "Inspector is highlighting");
ok(InspectorUI.isTreePanelOpen, "Inspector Tree Panel is open");
ok(!InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is not open");
is(InspectorStore.length, 2, "InspectorStore.length = 2");
// Disable highlighting.
@ -127,7 +127,23 @@ function inspectorFocusTab1()
// Make sure the inspector is still open.
ok(InspectorUI.inspecting, "Inspector is highlighting");
ok(InspectorUI.isTreePanelOpen, "Inspector Tree Panel is open");
ok(!InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is not open");
is(InspectorStore.length, 2, "InspectorStore.length = 2");
is(InspectorUI.selection, div, "selection matches the div element");
Services.obs.addObserver(inspectorOpenTreePanelTab1,
INSPECTOR_NOTIFICATIONS.TREEPANELREADY, false);
InspectorUI.treePanel.open();
}
function inspectorOpenTreePanelTab1()
{
Services.obs.removeObserver(inspectorOpenTreePanelTab1,
INSPECTOR_NOTIFICATIONS.TREEPANELREADY);
ok(InspectorUI.inspecting, "Inspector is highlighting");
ok(InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is open");
is(InspectorStore.length, 2, "InspectorStore.length = 2");
is(InspectorUI.selection, div, "selection matches the div element");
@ -144,7 +160,40 @@ function inspectorFocusTab2()
// Make sure the inspector is still open.
ok(!InspectorUI.inspecting, "Inspector is not highlighting");
ok(InspectorUI.isTreePanelOpen, "Inspector Tree Panel is open");
ok(!InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is not open");
is(InspectorStore.length, 2, "InspectorStore.length = 2");
isnot(InspectorUI.selection, div, "selection does not match the div element");
// Switch back to tab 1.
Services.obs.addObserver(inspectorSecondFocusTab1,
INSPECTOR_NOTIFICATIONS.TREEPANELREADY, false);
gBrowser.selectedTab = tab1;
}
function inspectorSecondFocusTab1()
{
Services.obs.removeObserver(inspectorSecondFocusTab1,
INSPECTOR_NOTIFICATIONS.TREEPANELREADY);
ok(InspectorUI.inspecting, "Inspector is highlighting");
ok(InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is open");
is(InspectorStore.length, 2, "InspectorStore.length = 2");
is(InspectorUI.selection, div, "selection matches the div element");
// Switch back to tab 2.
Services.obs.addObserver(inspectorSecondFocusTab2,
INSPECTOR_NOTIFICATIONS.OPENED, false);
gBrowser.selectedTab = tab2;
}
function inspectorSecondFocusTab2()
{
Services.obs.removeObserver(inspectorSecondFocusTab2,
INSPECTOR_NOTIFICATIONS.OPENED);
// Make sure the inspector is still open.
ok(!InspectorUI.inspecting, "Inspector is not highlighting");
ok(!InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is not open");
is(InspectorStore.length, 2, "InspectorStore.length = 2");
isnot(InspectorUI.selection, div, "selection does not match the div element");
@ -161,7 +210,7 @@ function inspectorTabUnload1(evt)
// Make sure the Inspector is still open and that the state is correct.
ok(!InspectorUI.inspecting, "Inspector is not highlighting");
ok(InspectorUI.isTreePanelOpen, "Inspector Tree Panel is open");
ok(!InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is not open");
is(InspectorStore.length, 1, "InspectorStore.length = 1");
InspectorUI.closeInspectorUI();

Просмотреть файл

@ -17,7 +17,7 @@ function test() {
waitForFocus(setupTest, content);
}, true);
content.location = "data:text/html,<div><p></p></div>";
content.location = 'data:text/html,<div style="width: 200px; height: 200px"><p></p></div>';
function setupTest() {
node1 = doc.querySelector("div");
@ -28,28 +28,32 @@ function test() {
function runTests() {
Services.obs.removeObserver(runTests, INSPECTOR_NOTIFICATIONS.OPENED);
testNode1();
Services.obs.addObserver(testNode1, INSPECTOR_NOTIFICATIONS.TREEPANELREADY, false);
InspectorUI.select(node1, true, true, true);
InspectorUI.openTreePanel();
}
function testNode1() {
let box = InspectorUI.ioBox.createObjectBox(node1);
box.click();
executeSoon(function() {
is(InspectorUI.selection, node1, "selection matches node");
is(InspectorUI.highlighter.node, node1, "selection matches node");
testNode2();
});
dump("testNode1\n");
Services.obs.removeObserver(testNode1, INSPECTOR_NOTIFICATIONS.TREEPANELREADY);
is(InspectorUI.selection, node1, "selection matches node");
is(InspectorUI.highlighter.node, node1, "selection matches node");
testNode2();
}
function testNode2() {
let box = InspectorUI.ioBox.createObjectBox(node2);
box.click();
executeSoon(function() {
is(InspectorUI.selection, node2, "selection matches node");
is(InspectorUI.highlighter.node, node2, "selection matches node");
Services.obs.addObserver(finishUp, INSPECTOR_NOTIFICATIONS.CLOSED, false);
InspectorUI.closeInspectorUI();
});
dump("testNode2\n")
Services.obs.addObserver(testHighlightingNode2, INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
InspectorUI.treePanelSelect("node2");
}
function testHighlightingNode2() {
dump("testHighlightingNode2\n")
Services.obs.removeObserver(testHighlightingNode2, INSPECTOR_NOTIFICATIONS.HIGHLIGHTING);
is(InspectorUI.selection, node2, "selection matches node");
is(InspectorUI.highlighter.node, node2, "selection matches node");
Services.obs.addObserver(finishUp, INSPECTOR_NOTIFICATIONS.CLOSED, false);
InspectorUI.closeInspectorUI();
}
function finishUp() {

Просмотреть файл

@ -71,10 +71,19 @@ function xhr_onReadyStateChange() {
function inspectorOpened()
{
Services.obs.removeObserver(inspectorOpened,
INSPECTOR_NOTIFICATIONS.OPENED, false);
INSPECTOR_NOTIFICATIONS.OPENED);
Services.obs.addObserver(treePanelOpened, INSPECTOR_NOTIFICATIONS.TREEPANELREADY, false);
InspectorUI.treePanel.open();
}
function treePanelOpened()
{
Services.obs.removeObserver(treePanelOpened,
INSPECTOR_NOTIFICATIONS.TREEPANELREADY);
ok(InspectorUI.inspecting, "Inspector is highlighting");
ok(InspectorUI.isTreePanelOpen, "Inspector Tree Panel is open");
ok(InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is open");
InspectorUI.stopInspecting();
ok(!InspectorUI.inspecting, "Inspector is not highlighting");
@ -91,8 +100,8 @@ function inspectorOpened()
ok(iframeDiv, "Found the div element inside the iframe");
InspectorUI.inspectNode(iframeDiv);
ok(InspectorUI.treePanelDiv, "InspectorUI.treePanelDiv is available");
is(InspectorUI.treePanelDiv.innerHTML.replace(/^\s+|\s+$/mg, ''),
ok(InspectorUI.treePanel.treePanelDiv, "InspectorUI.treePanelDiv is available");
is(InspectorUI.treePanel.treePanelDiv.innerHTML.replace(/^\s+|\s+$/mg, ''),
expectedResult, "treePanelDiv.innerHTML is correct");
expectedResult = null;
@ -107,7 +116,7 @@ function inspectorClosed()
INSPECTOR_NOTIFICATIONS.CLOSED, false);
ok(!InspectorUI.inspecting, "Inspector is not highlighting");
ok(!InspectorUI.isTreePanelOpen, "Inspector Tree Panel is not open");
ok(!InspectorUI.treePanel, "Inspector Tree Panel is not open");
gBrowser.removeCurrentTab();
finish();

Просмотреть файл

@ -1,4 +1,5 @@
browser.jar:
* content/browser/inspector.html (highlighter/inspector.html)
content/browser/NetworkPanel.xhtml (webconsole/NetworkPanel.xhtml)
* content/browser/scratchpad.xul (scratchpad/scratchpad.xul)
* content/browser/scratchpad.js (scratchpad/scratchpad.js)

Просмотреть файл

@ -12,11 +12,11 @@
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is HUDService code.
# The Original Code is Scratchpad Build Code.
#
# The Initial Developer of the Original Code is Mozilla Corporation.
#
# Portions created by the Initial Developer are Copyright (C) 2010
# The Initial Developer of the Original Code is The Mozilla Foundation.
#
# Portions created by the Initial Developer are Copyright (C) 2011
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
@ -44,9 +44,7 @@ VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
ifdef ENABLE_TESTS
ifneq (mobile,$(MOZ_BUILD_APP))
DIRS += test
endif
endif
include $(topsrcdir)/config/rules.mk

Просмотреть файл

@ -7,3 +7,18 @@ confirmNavigationAway.buttonLeaveAccesskey=L
confirmNavigationAway.buttonStay=Stay on Page
confirmNavigationAway.buttonStayAccesskey=S
# LOCALIZATION NOTE (htmlPanel): Used in the Inspector tool's openInspectorUI
# method when registering the HTML panel.
# LOCALIZATION NOTE (htmlPanel.label): A button label that appears on the
# InspectorUI's toolbar.
htmlPanel.label=HTML
# LOCALIZATION NOTE (htmlPanel.tooltiptext): The text that appears when a user
# hovers over the HTML panel's toolbar button.
htmlPanel.tooltiptext=HTML panel
# LOCALIZATION NOTE (htmlPanel.accesskey): The key bound to the HTML panel's
# toolbar button.
htmlPanel.accesskey=H