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/**
31 * @constructor
32 * @extends {WebInspector.SidebarPane}
33 */
34WebInspector.EventListenersSidebarPane = function()
35{
36    WebInspector.SidebarPane.call(this, WebInspector.UIString("Event Listeners"));
37    this.bodyElement.addStyleClass("events-pane");
38
39    this.sections = [];
40
41    this.settingsSelectElement = document.createElement("select");
42    this.settingsSelectElement.className = "select-filter";
43
44    var option = document.createElement("option");
45    option.value = "all";
46    option.label = WebInspector.UIString("All Nodes");
47    this.settingsSelectElement.appendChild(option);
48
49    option = document.createElement("option");
50    option.value = "selected";
51    option.label = WebInspector.UIString("Selected Node Only");
52    this.settingsSelectElement.appendChild(option);
53
54    var filter = WebInspector.settings.eventListenersFilter.get();
55    if (filter === "all")
56        this.settingsSelectElement[0].selected = true;
57    else if (filter === "selected")
58        this.settingsSelectElement[1].selected = true;
59    this.settingsSelectElement.addEventListener("click", function(event) { event.consume() }, false);
60    this.settingsSelectElement.addEventListener("change", this._changeSetting.bind(this), false);
61
62    this.titleElement.appendChild(this.settingsSelectElement);
63
64    this._linkifier = new WebInspector.Linkifier();
65}
66
67WebInspector.EventListenersSidebarPane._objectGroupName = "event-listeners-sidebar-pane";
68
69WebInspector.EventListenersSidebarPane.prototype = {
70    update: function(node)
71    {
72        RuntimeAgent.releaseObjectGroup(WebInspector.EventListenersSidebarPane._objectGroupName);
73        this._linkifier.reset();
74
75        var body = this.bodyElement;
76        body.removeChildren();
77        this.sections = [];
78
79        var self = this;
80        function callback(error, eventListeners) {
81            if (error)
82                return;
83
84            var selectedNodeOnly = "selected" === WebInspector.settings.eventListenersFilter.get();
85            var sectionNames = [];
86            var sectionMap = {};
87            for (var i = 0; i < eventListeners.length; ++i) {
88                var eventListener = eventListeners[i];
89                if (selectedNodeOnly && (node.id !== eventListener.nodeId))
90                    continue;
91                eventListener.node = WebInspector.domAgent.nodeForId(eventListener.nodeId);
92                delete eventListener.nodeId; // no longer needed
93                if (/^function _inspectorCommandLineAPI_logEvent\(/.test(eventListener.handlerBody.toString()))
94                    continue; // ignore event listeners generated by monitorEvent
95                var type = eventListener.type;
96                var section = sectionMap[type];
97                if (!section) {
98                    section = new WebInspector.EventListenersSection(type, node.id, self._linkifier);
99                    sectionMap[type] = section;
100                    sectionNames.push(type);
101                    self.sections.push(section);
102                }
103                section.addListener(eventListener);
104            }
105
106            if (sectionNames.length === 0) {
107                var div = document.createElement("div");
108                div.className = "info";
109                div.textContent = WebInspector.UIString("No Event Listeners");
110                body.appendChild(div);
111                return;
112            }
113
114            sectionNames.sort();
115            for (var i = 0; i < sectionNames.length; ++i) {
116                var section = sectionMap[sectionNames[i]];
117                body.appendChild(section.element);
118            }
119        }
120
121        if (node)
122            node.eventListeners(WebInspector.EventListenersSidebarPane._objectGroupName, callback);
123        this._selectedNode = node;
124    },
125
126    willHide: function()
127    {
128        delete this._selectedNode;
129    },
130
131    _changeSetting: function()
132    {
133        var selectedOption = this.settingsSelectElement[this.settingsSelectElement.selectedIndex];
134        WebInspector.settings.eventListenersFilter.set(selectedOption.value);
135        this.update(this._selectedNode);
136    },
137
138    __proto__: WebInspector.SidebarPane.prototype
139}
140
141/**
142 * @constructor
143 * @extends {WebInspector.PropertiesSection}
144 */
145WebInspector.EventListenersSection = function(title, nodeId, linkifier)
146{
147    this.eventListeners = [];
148    this._nodeId = nodeId;
149    this._linkifier = linkifier;
150    WebInspector.PropertiesSection.call(this, title);
151
152    // Changed from a Properties List
153    this.propertiesElement.parentNode.removeChild(this.propertiesElement);
154    delete this.propertiesElement;
155    delete this.propertiesTreeOutline;
156
157    this._eventBars = document.createElement("div");
158    this._eventBars.className = "event-bars";
159    this.element.appendChild(this._eventBars);
160}
161
162WebInspector.EventListenersSection.prototype = {
163    addListener: function(eventListener)
164    {
165        var eventListenerBar = new WebInspector.EventListenerBar(eventListener, this._nodeId, this._linkifier);
166        this._eventBars.appendChild(eventListenerBar.element);
167    },
168
169    __proto__: WebInspector.PropertiesSection.prototype
170}
171
172/**
173 * @constructor
174 * @extends {WebInspector.ObjectPropertiesSection}
175 */
176WebInspector.EventListenerBar = function(eventListener, nodeId, linkifier)
177{
178    WebInspector.ObjectPropertiesSection.call(this, WebInspector.RemoteObject.fromPrimitiveValue(""));
179
180    this.eventListener = eventListener;
181    this._nodeId = nodeId;
182    this._setNodeTitle();
183    this._setFunctionSubtitle(linkifier);
184    this.editable = false;
185    this.element.className = "event-bar"; /* Changed from "section" */
186    this.headerElement.addStyleClass("source-code");
187    this.propertiesElement.className = "event-properties properties-tree source-code"; /* Changed from "properties" */
188}
189
190WebInspector.EventListenerBar.prototype = {
191    update: function()
192    {
193        function updateWithNodeObject(nodeObject)
194        {
195            var properties = [];
196
197            if (this.eventListener.type)
198                properties.push(WebInspector.RemoteObjectProperty.fromPrimitiveValue("type", this.eventListener.type));
199            if (typeof this.eventListener.useCapture !== "undefined")
200                properties.push(WebInspector.RemoteObjectProperty.fromPrimitiveValue("useCapture", this.eventListener.useCapture));
201            if (typeof this.eventListener.isAttribute !== "undefined")
202                properties.push(WebInspector.RemoteObjectProperty.fromPrimitiveValue("isAttribute", this.eventListener.isAttribute));
203            if (nodeObject)
204                properties.push(new WebInspector.RemoteObjectProperty("node", nodeObject));
205            if (typeof this.eventListener.handler !== "undefined") {
206                var remoteObject = WebInspector.RemoteObject.fromPayload(this.eventListener.handler);
207                properties.push(new WebInspector.RemoteObjectProperty("handler", remoteObject));
208            }
209            if (typeof this.eventListener.handlerBody !== "undefined")
210                properties.push(WebInspector.RemoteObjectProperty.fromPrimitiveValue("listenerBody", this.eventListener.handlerBody));
211            if (this.eventListener.sourceName)
212                properties.push(WebInspector.RemoteObjectProperty.fromPrimitiveValue("sourceName", this.eventListener.sourceName));
213            if (this.eventListener.location)
214                properties.push(WebInspector.RemoteObjectProperty.fromPrimitiveValue("lineNumber", this.eventListener.location.lineNumber + 1));
215
216            this.updateProperties(properties);
217        }
218        WebInspector.RemoteObject.resolveNode(this.eventListener.node, WebInspector.EventListenersSidebarPane._objectGroupName, updateWithNodeObject.bind(this));
219    },
220
221    _setNodeTitle: function()
222    {
223        var node = this.eventListener.node;
224        if (!node)
225            return;
226
227        if (node.nodeType() === Node.DOCUMENT_NODE) {
228            this.titleElement.textContent = "document";
229            return;
230        }
231
232        if (node.id === this._nodeId) {
233            this.titleElement.textContent = node.appropriateSelectorFor();
234            return;
235        }
236
237        this.titleElement.removeChildren();
238        this.titleElement.appendChild(WebInspector.DOMPresentationUtils.linkifyNodeReference(this.eventListener.node));
239    },
240
241    _setFunctionSubtitle: function(linkifier)
242    {
243        // Requires that Function.toString() return at least the function's signature.
244        if (this.eventListener.location) {
245            this.subtitleElement.removeChildren();
246            var urlElement;
247            if (this.eventListener.location.scriptId)
248                urlElement = linkifier.linkifyRawLocation(this.eventListener.location);
249            if (!urlElement) {
250                var url = this.eventListener.sourceName;
251                var lineNumber = this.eventListener.location.lineNumber;
252                var columnNumber = 0;
253                urlElement = linkifier.linkifyLocation(url, lineNumber, columnNumber);
254            }
255            this.subtitleElement.appendChild(urlElement);
256        } else {
257            var match = this.eventListener.handlerBody.match(/function ([^\(]+?)\(/);
258            if (match)
259                this.subtitleElement.textContent = match[1];
260            else
261                this.subtitleElement.textContent = WebInspector.UIString("(anonymous function)");
262        }
263    },
264
265    __proto__: WebInspector.ObjectPropertiesSection.prototype
266}
267