a09ac0875a4a6cc95d27bba49e0ee2d20a3f434b
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Views / BreakpointTreeElement.js
1 /*
2  * Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 WebInspector.BreakpointTreeElement = function(breakpoint, className, title)
27 {
28     console.assert(breakpoint instanceof WebInspector.Breakpoint);
29
30     if (!className)
31         className = WebInspector.BreakpointTreeElement.GenericLineIconStyleClassName;
32
33     WebInspector.GeneralTreeElement.call(this, [WebInspector.BreakpointTreeElement.StyleClassName, className], title, null, breakpoint, false);
34
35     this._breakpoint = breakpoint;
36
37     this._listeners = new WebInspector.EventListenerSet(this, "BreakpointTreeElement listeners");
38     if (!title)
39         this._listeners.register(breakpoint, WebInspector.Breakpoint.Event.LocationDidChange, this._breakpointLocationDidChange);
40     this._listeners.register(breakpoint, WebInspector.Breakpoint.Event.DisabledStateDidChange, this._updateStatus);
41     this._listeners.register(breakpoint, WebInspector.Breakpoint.Event.AutoContinueDidChange, this._updateStatus);
42     this._listeners.register(breakpoint, WebInspector.Breakpoint.Event.ResolvedStateDidChange, this._updateStatus);
43     this._listeners.register(WebInspector.debuggerManager, WebInspector.DebuggerManager.Event.BreakpointsEnabledDidChange, this._updateStatus);
44
45     this._listeners.register(WebInspector.probeManager, WebInspector.ProbeManager.Event.ProbeSetAdded, this._probeSetAdded);
46     this._listeners.register(WebInspector.probeManager, WebInspector.ProbeManager.Event.ProbeSetRemoved, this._probeSetRemoved);
47
48     this._statusImageElement = document.createElement("img");
49     this._statusImageElement.className = WebInspector.BreakpointTreeElement.StatusImageElementStyleClassName;
50     this._listeners.register(this._statusImageElement, "mousedown", this._statusImageElementMouseDown);
51     this._listeners.register(this._statusImageElement, "click", this._statusImageElementClicked);
52
53     if (!title)
54         this._updateTitles();
55     this._updateStatus();
56
57     this.status = this._statusImageElement;
58     this.small = true;
59
60     this._iconAnimationLayerElement = document.createElement("span");
61     this.iconElement.appendChild(this._iconAnimationLayerElement);
62 };
63
64 WebInspector.BreakpointTreeElement.GenericLineIconStyleClassName = "breakpoint-generic-line-icon";
65 WebInspector.BreakpointTreeElement.StyleClassName = "breakpoint";
66 WebInspector.BreakpointTreeElement.StatusImageElementStyleClassName = "status-image";
67 WebInspector.BreakpointTreeElement.StatusImageResolvedStyleClassName = "resolved";
68 WebInspector.BreakpointTreeElement.StatusImageAutoContinueStyleClassName = "auto-continue";
69 WebInspector.BreakpointTreeElement.StatusImageDisabledStyleClassName = "disabled";
70 WebInspector.BreakpointTreeElement.FormattedLocationStyleClassName = "formatted-location";
71 WebInspector.BreakpointTreeElement.ProbeDataUpdatedStyleClassName = "data-updated";
72
73 WebInspector.BreakpointTreeElement.ProbeDataUpdatedAnimationDuration = 400; // milliseconds
74
75
76 WebInspector.BreakpointTreeElement.prototype = {
77     constructor: WebInspector.BreakpointTreeElement,
78
79     // Public
80
81     get breakpoint()
82     {
83         return this._breakpoint;
84     },
85
86     get filterableData()
87     {
88         return {text: this.breakpoint.url};
89     },
90
91     ondelete: function()
92     {
93         if (!WebInspector.debuggerManager.isBreakpointRemovable(this._breakpoint))
94             return false;
95
96         WebInspector.debuggerManager.removeBreakpoint(this._breakpoint);
97         return true;
98     },
99
100     onenter: function()
101     {
102         this._breakpoint.cycleToNextMode();
103         return true;
104     },
105
106     onspace: function()
107     {
108         this._breakpoint.cycleToNextMode();
109         return true;
110     },
111
112     oncontextmenu: function(event)
113     {
114         var contextMenu = new WebInspector.ContextMenu(event);
115         this._breakpoint.appendContextMenuItems(contextMenu, this._statusImageElement);
116         contextMenu.show();
117     },
118
119     onattach: function()
120     {
121         WebInspector.GeneralTreeElement.prototype.onattach.call(this);
122
123         this._listeners.install();
124
125         for (var probeSet of WebInspector.probeManager.probeSets)
126             if (probeSet.breakpoint === this._breakpoint)
127                 this._addProbeSet(probeSet);
128     },
129
130     ondetach: function()
131     {
132         WebInspector.GeneralTreeElement.prototype.ondetach.call(this);
133
134         this._listeners.uninstall();
135
136         if (this._probeSet)
137             this._removeProbeSet(this._probeSet);
138     },
139
140     removeStatusImage: function()
141     {
142         this._statusImageElement.remove();
143         this._statusImageElement = null;
144     },
145
146     // Private
147
148     _updateTitles: function()
149     {
150         var sourceCodeLocation = this._breakpoint.sourceCodeLocation;
151
152         var displayLineNumber = sourceCodeLocation.displayLineNumber;
153         var displayColumnNumber = sourceCodeLocation.displayColumnNumber;
154         if (displayColumnNumber > 0)
155             this.mainTitle = WebInspector.UIString("Line %d:%d").format(displayLineNumber + 1, displayColumnNumber + 1); // The user visible line and column numbers are 1-based.
156         else
157             this.mainTitle = WebInspector.UIString("Line %d").format(displayLineNumber + 1); // The user visible line number is 1-based.
158
159         if (sourceCodeLocation.hasMappedLocation()) {
160             this.subtitle = sourceCodeLocation.formattedLocationString();
161
162             if (sourceCodeLocation.hasFormattedLocation())
163                 this.subtitleElement.classList.add(WebInspector.BreakpointTreeElement.FormattedLocationStyleClassName);
164             else
165                 this.subtitleElement.classList.remove(WebInspector.BreakpointTreeElement.FormattedLocationStyleClassName);
166
167             this.tooltip = this.mainTitle + " \u2014 " + WebInspector.UIString("originally %s").format(sourceCodeLocation.originalLocationString());
168         }
169     },
170
171     _updateStatus: function()
172     {
173         if (!this._statusImageElement)
174             return;
175
176         if (this._breakpoint.disabled)
177             this._statusImageElement.classList.add(WebInspector.BreakpointTreeElement.StatusImageDisabledStyleClassName);
178         else
179             this._statusImageElement.classList.remove(WebInspector.BreakpointTreeElement.StatusImageDisabledStyleClassName);
180
181         if (this._breakpoint.autoContinue)
182             this._statusImageElement.classList.add(WebInspector.BreakpointTreeElement.StatusImageAutoContinueStyleClassName);
183         else
184             this._statusImageElement.classList.remove(WebInspector.BreakpointTreeElement.StatusImageAutoContinueStyleClassName);
185
186         if (this._breakpoint.resolved && WebInspector.debuggerManager.breakpointsEnabled)
187             this._statusImageElement.classList.add(WebInspector.BreakpointTreeElement.StatusImageResolvedStyleClassName);
188         else
189             this._statusImageElement.classList.remove(WebInspector.BreakpointTreeElement.StatusImageResolvedStyleClassName);
190     },
191
192     _addProbeSet: function(probeSet)
193     {
194         console.assert(probeSet instanceof WebInspector.ProbeSet);
195         console.assert(probeSet.breakpoint === this._breakpoint);
196         console.assert(probeSet !== this._probeSet);
197
198         this._probeSet = probeSet;
199         probeSet.addEventListener(WebInspector.ProbeSet.Event.SamplesCleared, this._samplesCleared, this);
200         probeSet.dataTable.addEventListener(WebInspector.ProbeSetDataTable.Event.FrameInserted, this._dataUpdated, this);
201     },
202
203     _removeProbeSet: function(probeSet)
204     {
205         console.assert(probeSet instanceof WebInspector.ProbeSet);
206         console.assert(probeSet === this._probeSet);
207
208         probeSet.removeEventListener(WebInspector.ProbeSet.Event.SamplesCleared, this._samplesCleared, this);
209         probeSet.dataTable.removeEventListener(WebInspector.ProbeSetDataTable.Event.FrameInserted, this._dataUpdated, this);
210         delete this._probeSet;
211     },
212
213     _probeSetAdded: function(event)
214     {
215         var probeSet = event.data.probeSet;
216         if (probeSet.breakpoint === this._breakpoint)
217             this._addProbeSet(probeSet);
218     },
219
220     _probeSetRemoved: function(event)
221     {
222         var probeSet = event.data.probeSet;
223         if (probeSet.breakpoint === this._breakpoint)
224             this._removeProbeSet(probeSet);
225     },
226
227     _samplesCleared: function(event)
228     {
229         console.assert(this._probeSet);
230
231         var oldTable = event.data.oldTable;
232         oldTable.removeEventListener(WebInspector.ProbeSetDataTable.Event.FrameInserted, this._dataUpdated, this);
233         this._probeSet.dataTable.addEventListener(WebInspector.ProbeSetDataTable.Event.FrameInserted, this._dataUpdated, this);
234     },
235
236     _dataUpdated: function()
237     {
238         if (this.element.classList.contains(WebInspector.BreakpointTreeElement.ProbeDataUpdatedStyleClassName)) {
239             clearTimeout(this._removeIconAnimationTimeoutIdentifier);
240             this.element.classList.remove(WebInspector.BreakpointTreeElement.ProbeDataUpdatedStyleClassName);
241             // We want to restart the animation, which can only be done by removing the class,
242             // performing layout, and re-adding the class. Try adding class back on next run loop.
243             window.requestAnimationFrame(this._dataUpdated.bind(this));
244             return;
245         }
246
247         this.element.classList.add(WebInspector.BreakpointTreeElement.ProbeDataUpdatedStyleClassName);
248         this._removeIconAnimationTimeoutIdentifier = setTimeout(function() {
249             this.element.classList.remove(WebInspector.BreakpointTreeElement.ProbeDataUpdatedStyleClassName);
250         }.bind(this), WebInspector.BreakpointTreeElement.ProbeDataUpdatedAnimationDuration);
251     },
252
253
254     _breakpointLocationDidChange: function(event)
255     {
256         console.assert(event.target === this._breakpoint);
257
258         // The Breakpoint has a new display SourceCode. The sidebar will remove us, and ondetach() will clear listeners.
259         if (event.data.oldDisplaySourceCode === this._breakpoint.displaySourceCode)
260             return;
261
262         this._updateTitles();
263     },
264
265     _statusImageElementMouseDown: function(event)
266     {
267         // To prevent the tree element from selecting.
268         event.stopPropagation();
269     },
270
271     _statusImageElementClicked: function(event)
272     {
273         this._breakpoint.cycleToNextMode();
274     }
275 };
276
277 WebInspector.BreakpointTreeElement.prototype.__proto__ = WebInspector.GeneralTreeElement.prototype;