2011-06-30 Pavel Feldman <pfeldman@chromium.org>
[WebKit-https.git] / Source / WebCore / inspector / front-end / EventListenersSidebarPane.js
1 /*
2  * Copyright (C) 2007 Apple Inc.  All rights reserved.
3  * Copyright (C) 2009 Joseph Pecoraro
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1.  Redistributions of source code must retain the above copyright
10  *     notice, this list of conditions and the following disclaimer.
11  * 2.  Redistributions in binary form must reproduce the above copyright
12  *     notice, this list of conditions and the following disclaimer in the
13  *     documentation and/or other materials provided with the distribution.
14  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
15  *     its contributors may be used to endorse or promote products derived
16  *     from this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 WebInspector.EventListenersSidebarPane = function()
31 {
32     WebInspector.SidebarPane.call(this, WebInspector.UIString("Event Listeners"));
33     this.bodyElement.addStyleClass("events-pane");
34
35     this.sections = [];
36
37     this.settingsSelectElement = document.createElement("select");
38     this.settingsSelectElement.className = "select-filter";
39
40     var option = document.createElement("option");
41     option.value = "all";
42     option.label = WebInspector.UIString("All Nodes");
43     this.settingsSelectElement.appendChild(option);
44
45     option = document.createElement("option");
46     option.value = "selected";
47     option.label = WebInspector.UIString("Selected Node Only");
48     this.settingsSelectElement.appendChild(option);
49
50     var filter = WebInspector.settings.eventListenersFilter;
51     if (filter === "all")
52         this.settingsSelectElement[0].selected = true;
53     else if (filter === "selected")
54         this.settingsSelectElement[1].selected = true;
55     this.settingsSelectElement.addEventListener("click", function(event) { event.stopPropagation() }, false);
56     this.settingsSelectElement.addEventListener("change", this._changeSetting.bind(this), false);
57
58     this.titleElement.appendChild(this.settingsSelectElement);
59 }
60
61 WebInspector.EventListenersSidebarPane._objectGroupName = "event-listeners-sidebar-pane";
62
63 WebInspector.EventListenersSidebarPane.prototype = {
64     update: function(node)
65     {
66         RuntimeAgent.releaseObjectGroup(WebInspector.EventListenersSidebarPane._objectGroupName);
67         var body = this.bodyElement;
68         body.removeChildren();
69         this.sections = [];
70
71         var self = this;
72         function callback(error, eventListeners) {
73             if (error)
74                 return;
75
76             var sectionNames = [];
77             var sectionMap = {};
78             for (var i = 0; i < eventListeners.length; ++i) {
79                 var eventListener = eventListeners[i];
80                 eventListener.node = WebInspector.domAgent.nodeForId(eventListener.nodeId);
81                 delete eventListener.nodeId; // no longer needed
82                 if (/^function _inspectorCommandLineAPI_logEvent\(/.test(eventListener.handlerBody.toString()))
83                     continue; // ignore event listeners generated by monitorEvent
84                 var type = eventListener.type;
85                 var section = sectionMap[type];
86                 if (!section) {
87                     section = new WebInspector.EventListenersSection(type, node.id);
88                     sectionMap[type] = section;
89                     sectionNames.push(type);
90                     self.sections.push(section);
91                 }
92                 section.addListener(eventListener);
93             }
94             
95             if (sectionNames.length === 0) {
96                 var div = document.createElement("div");
97                 div.className = "info";
98                 div.textContent = WebInspector.UIString("No Event Listeners");
99                 body.appendChild(div);
100                 return;
101             }
102
103             sectionNames.sort();
104             for (var i = 0; i < sectionNames.length; ++i) {
105                 var section = sectionMap[sectionNames[i]];
106                 section.update();
107                 body.appendChild(section.element);
108             }
109         }
110
111         if (node)
112             node.eventListeners(callback);
113     },
114
115     _changeSetting: function(event)
116     {
117         var selectedOption = this.settingsSelectElement[this.settingsSelectElement.selectedIndex];
118         WebInspector.settings.eventListenersFilter = selectedOption.value;
119
120         for (var i = 0; i < this.sections.length; ++i)
121             this.sections[i].update();
122     }
123 }
124
125 WebInspector.EventListenersSidebarPane.prototype.__proto__ = WebInspector.SidebarPane.prototype;
126
127 WebInspector.EventListenersSection = function(title, nodeId)
128 {
129     this.eventListeners = [];
130     this._nodeId = nodeId;
131     WebInspector.PropertiesSection.call(this, title);
132
133     // Changed from a Properties List
134     this.propertiesElement.parentNode.removeChild(this.propertiesElement);
135     delete this.propertiesElement;
136     delete this.propertiesTreeOutline;
137
138     this.eventBars = document.createElement("div");
139     this.eventBars.className = "event-bars";
140     this.element.appendChild(this.eventBars);
141 }
142
143 WebInspector.EventListenersSection.prototype = {
144     update: function()
145     {
146         // A Filtered Array simplifies when to create connectors
147         var filteredEventListeners = this.eventListeners;
148         if (WebInspector.settings.eventListenersFilter === "selected") {
149             filteredEventListeners = [];
150             for (var i = 0; i < this.eventListeners.length; ++i) {
151                 var eventListener = this.eventListeners[i];
152                 if (eventListener.node.id === this._nodeId)
153                     filteredEventListeners.push(eventListener);
154             }
155         }
156
157         this.eventBars.removeChildren();
158         var length = filteredEventListeners.length;
159         for (var i = 0; i < length; ++i) {
160             var eventListener = filteredEventListeners[i];
161             var eventListenerBar = new WebInspector.EventListenerBar(eventListener, this._nodeId);
162             this.eventBars.appendChild(eventListenerBar.element);
163         }
164     },
165
166     addListener: function(eventListener)
167     {
168         this.eventListeners.push(eventListener);
169     }
170 }
171
172 WebInspector.EventListenersSection.prototype.__proto__ = WebInspector.PropertiesSection.prototype;
173
174 WebInspector.EventListenerBar = function(eventListener, nodeId)
175 {
176     this.eventListener = eventListener;
177     this._nodeId = nodeId;
178     WebInspector.ObjectPropertiesSection.call(this);
179     this._setNodeTitle();
180     this._setFunctionSubtitle();
181     this.editable = false;
182     this.element.className = "event-bar"; /* Changed from "section" */
183     this.headerElement.addStyleClass("source-code");
184     this.propertiesElement.className = "event-properties properties-tree source-code"; /* Changed from "properties" */
185 }
186
187 WebInspector.EventListenerBar.prototype = {
188     update: function()
189     {
190         function updateWithNodeObject(nodeObject)
191         {
192             var properties = [];
193
194             if (this.eventListener.type)
195                 properties.push(WebInspector.RemoteObjectProperty.fromPrimitiveValue("type", this.eventListener.type));
196             if (typeof this.eventListener.useCapture !== "undefined")
197                 properties.push(WebInspector.RemoteObjectProperty.fromPrimitiveValue("useCapture", this.eventListener.useCapture));
198             if (typeof this.eventListener.isAttribute !== "undefined")
199                 properties.push(WebInspector.RemoteObjectProperty.fromPrimitiveValue("isAttribute", this.eventListener.isAttribute));
200             if (nodeObject)
201                 properties.push(new WebInspector.RemoteObjectProperty("node", nodeObject));
202             if (typeof this.eventListener.handlerBody !== "undefined")
203                 properties.push(WebInspector.RemoteObjectProperty.fromPrimitiveValue("listenerBody", this.eventListener.handlerBody));
204             if (this.eventListener.location) {
205                 properties.push(WebInspector.RemoteObjectProperty.fromPrimitiveValue("sourceName", this.eventListener.location.sourceId));
206                 properties.push(WebInspector.RemoteObjectProperty.fromPrimitiveValue("lineNumber", this.eventListener.location.lineNumber));
207             }
208
209             this.updateProperties(properties);
210         }
211         WebInspector.RemoteObject.resolveNode(this.eventListener.node, WebInspector.EventListenersSidebarPane._objectGroupName, updateWithNodeObject.bind(this));
212     },
213
214     _setNodeTitle: function()
215     {
216         var node = this.eventListener.node;
217         if (!node)
218             return;
219
220         if (node.nodeType() === Node.DOCUMENT_NODE) {
221             this.titleElement.textContent = "document";
222             return;
223         }
224
225         if (node.id === this._nodeId) {
226             this.titleElement.textContent = node.appropriateSelectorFor();
227             return;
228         }
229
230         this.titleElement.removeChildren();
231         this.titleElement.appendChild(WebInspector.panels.elements.linkifyNodeReference(this.eventListener.node));
232     },
233
234     _setFunctionSubtitle: function()
235     {
236         // Requires that Function.toString() return at least the function's signature.
237         if (this.eventListener.location) {
238             this.subtitleElement.removeChildren();
239             this.subtitleElement.appendChild(WebInspector.linkifyResourceAsNode(this.eventListener.location.sourceId, "scripts", this.eventListener.location.lineNumber));
240         } else {
241             var match = this.eventListener.handlerBody.match(/function ([^\(]+?)\(/);
242             if (match)
243                 this.subtitleElement.textContent = match[1];
244             else
245                 this.subtitleElement.textContent = WebInspector.UIString("(anonymous function)");
246         }
247     }
248 }
249
250 WebInspector.EventListenerBar.prototype.__proto__ = WebInspector.ObjectPropertiesSection.prototype;