Skip to content

Instantly share code, notes, and snippets.

@teramako
Created June 20, 2012 14:36
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save teramako/2960196 to your computer and use it in GitHub Desktop.
Save teramako/2960196 to your computer and use it in GitHub Desktop.
add Listeners Panel to DOM Inspector

DOM Inspector にイベントリスナのパネルを追加

cap

  • プロファイルディレクトリ/extensions/inspector@mozilla.org/chrome/inspector.jar を unzip
  • mkdir content/inspector/viewers/eventListeners/
  • eventListeners.xuleventListeners.jscontent/inspector/viewers/eventListeners/ に配置
  • content/inspector/res/viewer-registry.rdf にパッチを当てる
  • zip inspector.jar -r ... でアーカイブ作成
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*****************************************************************************
* DOMNodeViewer --------------------------------------------------------------
* The default viewer for DOM Nodes
*****************************************************************************/
//////////////////////////////////////////////////////////////////////////////
//// Global Constants
const Cc = Components.classes,
Ci = Components.interfaces;
XPCOMUtils.defineLazyModuleGetter(this, "Services", "resource://gre/modules/Services.jsm");
//////////////////////////////////////////////////////////////////////////////
//// Global Variables
var viewer;
var gPromptService;
//////////////////////////////////////////////////////////////////////////////
window.addEventListener("load", DOMNodeViewer_initialize, false);
function DOMNodeViewer_initialize()
{
viewer = new DOMNodeViewer();
viewer.initialize(parent.FrameExchange.receiveData(window));
}
//////////////////////////////////////////////////////////////////////////////
//// DOMNodeViewer Class
function DOMNodeViewer() // implements inIViewer
{
this.mObsMan = new ObserverManager(this);
this.mURL = window.location;
this.mAttrTree = document.getElementById("olListeners");
this.mAttrGroupBox = document.getElementById("grpAttr");
this.mDOMView = new EventListenerView();
this.mAttrTree.treeBoxObject.view = this.mDOMView;
}
DOMNodeViewer.prototype =
{
////////////////////////////////////////////////////////////////////////////
//// Initialization
mDOMView: null,
mSubject: null,
mPanel: null,
get selectedIndex()
{
return this.mAttrTree.currentIndex;
},
/**
* Returns an array of the selected indices
*/
get selectedIndices()
{
var indices = [];
var rangeCount = this.mAttrTree.view.selection.getRangeCount();
for (var i = 0; i < rangeCount; ++i) {
var start = {};
var end = {};
this.mAttrTree.view.selection.getRangeAt(i, start, end);
for (var c = start.value; c <= end.value; ++c) {
indices.push(c);
}
}
return indices;
},
/**
* Returns a DOMAttribute from the selected index
*/
get selectedAttribute()
{
var index = this.selectedIndex;
return index >= 0 ?
new DOMAttribute(this.mDOMView.getNodeFromRowIndex(index)) : null;
},
/**
* Returns an array of DOMAttributes from the selected indices
*/
get selectedAttributes()
{
var indices = this.selectedIndices;
var attrs = [];
for (var i = 0; i < indices.length; ++i) {
var idx = this.mDOMView.getNodeFromRowIndex(indices[i]);
attrs.push(new DOMAttribute(idx));
}
return attrs;
},
////////////////////////////////////////////////////////////////////////////
//// interface inIViewer
//// attributes
get uid()
{
return "eventListeners"
},
get pane()
{
return this.mPanel
},
get selection()
{
return null
},
get subject()
{
return this.mSubject
},
set subject(aObject)
{
// the node value's textbox won't fire onchange when we change subjects, so
// let's fire it. this won't do anything if it wasn't actually changed
this.mSubject = aObject;
switch (aObject.nodeType) {
// things with useful nodeValues
case Node.TEXT_NODE:
case Node.CDATA_SECTION_NODE:
case Node.COMMENT_NODE:
case Node.PROCESSING_INSTRUCTION_NODE:
this.mDOMView.setTarget(null);
break;
//XXX this view is designed for elements, write a more useful one for
// document nodes, etc.
default:
this.mDOMView.setTarget(aObject);
var bundle = this.pane.panelset.stringBundle;
this.setTextValue("localName", aObject.localName);
this.setTextValue("nodeType", bundle.getString(aObject.nodeType));
this.setTextValue("namespace", aObject.namespaceURI);
}
var hideAttributes = aObject.nodeType != Node.ELEMENT_NODE;
this.mAttrGroupBox.hidden = hideAttributes;
if (!hideAttributes && aObject != this.mDOMView.rootNode) {
this.mDOMView.rootNode = aObject;
this.mAttrTree.view.selection.select(-1);
}
this.mObsMan.dispatchEvent("subjectChange", { subject: aObject });
},
// methods
initialize: function DNVr_Initialize(aPane)
{
this.mPanel = aPane;
aPane.notifyViewerReady(this);
},
destroy: function DNVr_Destroy()
{
// the node value's textbox won't fire onchange when we change views, so
// let's fire it. this won't do anything if it wasn't actually changed
},
isCommandEnabled: function DNVr_IsCommandEnabled(aCommand)
{
// NB: This function can be fired before the subject is set.
switch (aCommand) {
case "cmdEditCopy":
return this.selectedAttribute != null;
}
return false;
},
getCommand: function DNVr_GetCommand(aCommand)
{
switch (aCommand) {
case "cmdEditCopy":
return new cmdEditCopy(this.selectedAttributes);
}
return null;
},
////////////////////////////////////////////////////////////////////////////
//// Event Dispatching
addObserver: function DNVr_AddObserver(aEvent, aObserver)
{
this.mObsMan.addObserver(aEvent, aObserver);
},
removeObserver: function DNVr_RemoveObserver(aEvent, aObserver)
{
this.mObsMan.removeObserver(aEvent, aObserver);
},
////////////////////////////////////////////////////////////////////////////
//// Uncategorized
setTextValue: function DNVr_SetTextValue(aName, aText)
{
var field = document.getElementById("tx_" + aName);
if (field) {
field.value = aText;
}
}
};
function EventListenerView () {
this.rows = [];
this.target = null;
this.treeBox = null;
}
EventListenerView.prototype = {
setTarget: function (aTarget) {
this.target = aTarget;
this.rebuild();
},
rebuild: function() {
var oldLength = this.rows.length;
if (!this.target || !this.treeBox) {
if (oldLength > 0 && this.treeBox) {
this.treeBox.rowCountChanged(oldLength, -oldLength);
}
return;
}
const listenerService = Cc["@mozilla.org/eventlistenerservice;1"].getService(Ci.nsIEventListenerService);
this.rows = [info for ([, info] in Iterator(listenerService.getListenerInfoFor(this.target)))];
this.treeBox.rowCountChanged(this.rows.length, this.rows.length - oldLength)
this.treeBox.invalidate();
},
// implement nsITreeView
setTree: function (treeBox) {
this.treeBox = treeBox;
this.rebuild();
},
get rowCount() this.rows.length,
canDrop: function () false,
getLevel: function (aRow) 0,
getCellText: function (aRow, aCol) {
if (!(aRow in this.rows))
return;
switch (aCol.element.id) {
case "colType":
return this.rows[aRow].type;
case "colSource":
return this.rows[aRow].toSource();
case "colCapturing":
return this.rows[aRow].capturing;
}
return "";
},
getCellValue: function (aRow, aCol) {
if (!(aRow in this.rows))
return;
if (aCol.element.id === "colCapturing") {
return this.rows[aRow].capturing;
}
return "";
},
isContainer: function (aRow) false,
isSeparator: function (aRow) false,
isSorted: function (aRow) false,
getImageSrc: function (aRow, aCol) "",
getRowProperties: function (aRow, aProps) {},
getCellProperties: function (aRow, aCol, aProps) {},
getColumnProperties: function (aId, aCol, aProps) {},
};
<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!DOCTYPE page [
<!ENTITY % dtd1 SYSTEM "chrome://inspector/locale/inspector.dtd"> %dtd1;
<!ENTITY % dtd2 SYSTEM "chrome://inspector/locale/viewers/domNode.dtd"> %dtd2;
]>
<?xul-overlay href="chrome://inspector/content/editingOverlay.xul"?>
<?xml-stylesheet href="chrome://inspector/skin/viewers/domNode/domNode.css"?>
<page id="winNodeDefault"
style="padding: 10px"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<!--============================= SCRIPTS ============================= -->
<script type="application/javascript"
src="chrome://inspector/content/utils.js"/>
<script type="application/javascript"
src="chrome://inspector/content/jsutil/xpcom/XPCU.js"/>
<script type="application/javascript"
src="chrome://inspector/content/jsutil/events/ObserverManager.js"/>
<script type="application/javascript"
src="chrome://inspector/content/jsutil/commands/baseCommands.js"/>
<script type="application/javascript"
src="chrome://inspector/content/jsutil/system/clipboardFlavors.js"/>
<script type="application/javascript"
src="chrome://inspector/content/viewers/eventListeners/eventListeners.js"/>
<!--============================= CONTENT ============================== -->
<commandset id="cmdsEditing"/>
<popupset id="psPopups">
<menupopup id="ppAttrContext">
<menuitem id="mnEditCut"/>
<menuitem id="mnEditCopy"/>
<menuitem id="mnEditPaste"/>
<menuseparator/>
<menuitem id="mnEditEdit"/>
<menuitem id="mnEditInsert"/>
<menuitem id="mnEditDelete"/>
</menupopup>
</popupset>
<deck id="dkContent" flex="1">
<vbox id="bxElement">
<grid id="olNodeInfo">
<columns>
<column/>
<column flex="1"/>
</columns>
<rows>
<row>
<label value="&localName.label;"
control="tx_localName"
class="olNodeInfoLabel"/>
<textbox id="tx_localName" readonly="true" class="plain"/>
</row>
<row>
<label value="&namespaceURI.label;"
control="tx_namespace"
class="olNodeInfoLabel"/>
<textbox id="tx_namespace" readonly="true" class="plain"/>
</row>
<row>
<label value="&nodeType.label;"
control="tx_nodeType"
class="olNodeInfoLabel"/>
<textbox id="tx_nodeType" readonly="true" class="plain"/>
</row>
</rows>
</grid>
<groupbox id="grpAttr" flex="1">
<caption label="&grpAttr.label;"/>
<tree id="olListeners"
class="plain"
flex="1"
enableColumnDrag="true"
contextmenu="ppAttrContext"
onselect="viewer.pane.panelset.updateAllCommands()">
<treecols>
<!-- These labels don't need to be localized since they are defined
by DOM APIs. For column headers labeled as |name| and |value|,
even though we're really accessing the |nodeName| and |nodeValue|
properties, it's fine because they're guaranteed to match
the attributes' |name| and |value|, respectively. -->
<treecol id="colType"
label="type"
persist="width,hidden,ordinal"
flex="1"/>
<splitter class="tree-splitter"/>
<treecol id="colSource"
label="source"
persist="width,hidden,ordinal"
flex="1"/>
<splitter class="tree-splitter"/>
<treecol id="colCapturing"
label="capturing"
persist="width,hidden,ordinal"
flex="1"/>
<splitter class="tree-splitter"/>
</treecols>
<treechildren id="olListenersBody"
alternatingbackground="true"/>
</tree>
</groupbox>
</vbox>
</deck>
</page>
--- content/inspector/res/viewer-registry.rdf.bk 2012-06-20 23:34:34.203125000 +0900
+++ content/inspector/res/viewer-registry.rdf 2012-06-20 23:31:22.468750000 +0900
@@ -283,6 +283,16 @@
]]></ins:filter>
</rdf:Description>
</rdf:li>
+ <rdf:li>
+ <rdf:Description ins:uid="eventListeners"
+ ins:panels="bxObjectPanel bxObjPanel"
+ ins:description="EventListener">
+ <ins:filter><![CDATA[
+ return object instanceof Components.interfaces.nsIDOMElement ||
+ object instanceof Components.interfaces.nsIDOMDocument
+ ]]></ins:filter>
+ </rdf:Description>
+ </rdf:li>
</rdf:Seq>
</rdf:RDF>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment