Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / sources / CallStackSidebarPane.js
1 /*
2  * Copyright (C) 2008 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. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 /**
27  * @constructor
28  * @extends {WebInspector.SidebarPane}
29  */
30 WebInspector.CallStackSidebarPane = function()
31 {
32     WebInspector.SidebarPane.call(this, WebInspector.UIString("Call Stack"));
33     this.bodyElement.addEventListener("keydown", this._keyDown.bind(this), true);
34     this.bodyElement.tabIndex = 0;
35
36     var asyncCheckbox = this.titleElement.appendChild(WebInspector.SettingsUI.createSettingCheckbox(WebInspector.UIString("Async"), WebInspector.settings.enableAsyncStackTraces, true, undefined, WebInspector.UIString("Capture async stack traces")));
37     asyncCheckbox.classList.add("scripts-callstack-async");
38     asyncCheckbox.addEventListener("click", consumeEvent, false);
39     WebInspector.settings.enableAsyncStackTraces.addChangeListener(this._asyncStackTracesStateChanged, this);
40     WebInspector.settings.skipStackFramesPattern.addChangeListener(this._blackboxingStateChanged, this);
41 }
42
43 WebInspector.CallStackSidebarPane.Events = {
44     CallFrameSelected: "CallFrameSelected"
45 }
46
47 WebInspector.CallStackSidebarPane.prototype = {
48     /**
49      * @param {?WebInspector.DebuggerPausedDetails} details
50      */
51     update: function(details)
52     {
53         this.bodyElement.removeChildren();
54
55         if (!details) {
56             var infoElement = this.bodyElement.createChild("div", "info");
57             infoElement.textContent = WebInspector.UIString("Not Paused");
58             return;
59         }
60
61         this._target = details.target();
62         var callFrames = details.callFrames;
63         var asyncStackTrace = details.asyncStackTrace;
64
65         delete this._statusMessageElement;
66         delete this._hiddenPlacardsMessageElement;
67         /** @type {!Array.<!WebInspector.CallStackSidebarPane.Placard>} */
68         this.placards = [];
69         this._hiddenPlacards = 0;
70
71         this._appendSidebarPlacards(callFrames);
72         var topStackHidden = (this._hiddenPlacards === this.placards.length);
73
74         while (asyncStackTrace) {
75             var title = WebInspector.asyncStackTraceLabel(asyncStackTrace.description);
76             var asyncPlacard = new WebInspector.Placard(title, "");
77             asyncPlacard.element.addEventListener("click", this._selectNextVisiblePlacard.bind(this, this.placards.length, false), false);
78             asyncPlacard.element.addEventListener("contextmenu", this._asyncPlacardContextMenu.bind(this, this.placards.length), true);
79             asyncPlacard.element.classList.add("placard-label");
80             this.bodyElement.appendChild(asyncPlacard.element);
81             this._appendSidebarPlacards(asyncStackTrace.callFrames, asyncPlacard);
82             asyncStackTrace = asyncStackTrace.asyncStackTrace;
83         }
84
85         if (topStackHidden)
86             this._revealHiddenPlacards();
87         if (this._hiddenPlacards) {
88             var element = document.createElementWithClass("div", "hidden-placards-message");
89             if (this._hiddenPlacards === 1)
90                 element.textContent = WebInspector.UIString("1 stack frame is hidden (black-boxed).");
91             else
92                 element.textContent = WebInspector.UIString("%d stack frames are hidden (black-boxed).", this._hiddenPlacards);
93             element.createTextChild(" ");
94             var showAllLink = element.createChild("span", "node-link");
95             showAllLink.textContent = WebInspector.UIString("Show");
96             showAllLink.addEventListener("click", this._revealHiddenPlacards.bind(this), false);
97             this.bodyElement.insertBefore(element, this.bodyElement.firstChild);
98             this._hiddenPlacardsMessageElement = element;
99         }
100     },
101
102     /**
103      * @param {!Array.<!WebInspector.DebuggerModel.CallFrame>} callFrames
104      * @param {!WebInspector.Placard=} asyncPlacard
105      */
106     _appendSidebarPlacards: function(callFrames, asyncPlacard)
107     {
108         var allPlacardsHidden = true;
109         for (var i = 0, n = callFrames.length; i < n; ++i) {
110             var callFrame = callFrames[i];
111             var placard = new WebInspector.CallStackSidebarPane.Placard(callFrame, asyncPlacard);
112             placard.element.addEventListener("click", this._placardSelected.bind(this, placard), false);
113             placard.element.addEventListener("contextmenu", this._placardContextMenu.bind(this, placard), true);
114             this.placards.push(placard);
115             this.bodyElement.appendChild(placard.element);
116
117             if (WebInspector.BlackboxSupport.isBlackboxedURL(callFrame.script.sourceURL)) {
118                 placard.setHidden(true);
119                 placard.element.classList.add("dimmed");
120                 ++this._hiddenPlacards;
121             } else {
122                 allPlacardsHidden = false;
123             }
124         }
125         if (allPlacardsHidden && asyncPlacard)
126             asyncPlacard.setHidden(true);
127     },
128
129     _revealHiddenPlacards: function()
130     {
131         if (!this._hiddenPlacards)
132             return;
133         this._hiddenPlacards = 0;
134         for (var i = 0; i < this.placards.length; ++i) {
135             var placard = this.placards[i];
136             placard.setHidden(false);
137             if (placard._asyncPlacard)
138                 placard._asyncPlacard.setHidden(false);
139         }
140         if (this._hiddenPlacardsMessageElement) {
141             this._hiddenPlacardsMessageElement.remove();
142             delete this._hiddenPlacardsMessageElement;
143         }
144     },
145
146     /**
147      * @param {!WebInspector.CallStackSidebarPane.Placard} placard
148      * @param {!Event} event
149      */
150     _placardContextMenu: function(placard, event)
151     {
152         var contextMenu = new WebInspector.ContextMenu(event);
153
154         if (!placard._callFrame.isAsync())
155             contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Restart frame" : "Restart Frame"), this._restartFrame.bind(this, placard));
156
157         contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Copy stack trace" : "Copy Stack Trace"), this._copyStackTrace.bind(this));
158
159         var script = placard._callFrame.script;
160         if (!script.isSnippet()) {
161             contextMenu.appendSeparator();
162             this.appendBlackboxURLContextMenuItems(contextMenu, script.sourceURL);
163         }
164
165         contextMenu.show();
166     },
167
168     /**
169      * @param {number} index
170      * @param {!Event} event
171      */
172     _asyncPlacardContextMenu: function(index, event)
173     {
174         for (; index < this.placards.length; ++index) {
175             var placard = this.placards[index];
176             if (!placard.isHidden()) {
177                 this._placardContextMenu(placard, event);
178                 break;
179             }
180         }
181     },
182
183     /**
184      * @param {!WebInspector.ContextMenu} contextMenu
185      * @param {string} url
186      */
187     appendBlackboxURLContextMenuItems: function(contextMenu, url)
188     {
189         if (!url)
190             return;
191         var blackboxed = WebInspector.BlackboxSupport.isBlackboxedURL(url);
192         if (blackboxed)
193             contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Stop blackboxing" : "Stop Blackboxing"), this._handleContextMenuBlackboxURL.bind(this, url, false));
194         else
195             contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Blackbox script" : "Blackbox Script"), this._handleContextMenuBlackboxURL.bind(this, url, true));
196     },
197
198     /**
199      * @param {string} url
200      * @param {boolean} blackbox
201      */
202     _handleContextMenuBlackboxURL: function(url, blackbox)
203     {
204         if (blackbox)
205             WebInspector.BlackboxSupport.blackboxURL(url);
206         else
207             WebInspector.BlackboxSupport.unblackboxURL(url);
208     },
209
210     _blackboxingStateChanged: function()
211     {
212         if (!this._target)
213             return;
214         var details = this._target.debuggerModel.debuggerPausedDetails();
215         if (!details)
216             return;
217         this.update(details);
218         var selectedCallFrame = this._target.debuggerModel.selectedCallFrame();
219         if (selectedCallFrame)
220             this.setSelectedCallFrame(selectedCallFrame);
221     },
222
223     /**
224      * @param {!WebInspector.CallStackSidebarPane.Placard} placard
225      */
226     _restartFrame: function(placard)
227     {
228         placard._callFrame.restart();
229     },
230
231     _asyncStackTracesStateChanged: function()
232     {
233         var enabled = WebInspector.settings.enableAsyncStackTraces.get();
234         if (!enabled && this.placards)
235             this._removeAsyncPlacards();
236     },
237
238     _removeAsyncPlacards: function()
239     {
240         var shouldSelectTopFrame = false;
241         var lastSyncPlacardIndex = -1;
242         for (var i = 0; i < this.placards.length; ++i) {
243             var placard = this.placards[i];
244             if (placard._asyncPlacard) {
245                 if (placard.selected)
246                     shouldSelectTopFrame = true;
247                 placard._asyncPlacard.element.remove();
248                 placard.element.remove();
249             } else {
250                 lastSyncPlacardIndex = i;
251             }
252         }
253         this.placards.length = lastSyncPlacardIndex + 1;
254         if (shouldSelectTopFrame)
255             this._selectNextVisiblePlacard(0);
256     },
257
258     /**
259      * @param {!WebInspector.DebuggerModel.CallFrame} x
260      */
261     setSelectedCallFrame: function(x)
262     {
263         for (var i = 0; i < this.placards.length; ++i) {
264             var placard = this.placards[i];
265             placard.selected = (placard._callFrame === x);
266             if (placard.selected && placard.isHidden())
267                 this._revealHiddenPlacards();
268         }
269     },
270
271     /**
272      * @return {boolean}
273      */
274     _selectNextCallFrameOnStack: function()
275     {
276         var index = this._selectedCallFrameIndex();
277         if (index === -1)
278             return false;
279         return this._selectNextVisiblePlacard(index + 1);
280     },
281
282     /**
283      * @return {boolean}
284      */
285     _selectPreviousCallFrameOnStack: function()
286     {
287         var index = this._selectedCallFrameIndex();
288         if (index === -1)
289             return false;
290         return this._selectNextVisiblePlacard(index - 1, true);
291     },
292
293     /**
294      * @param {number} index
295      * @param {boolean=} backward
296      * @return {boolean}
297      */
298     _selectNextVisiblePlacard: function(index, backward)
299     {
300         while (0 <= index && index < this.placards.length) {
301             var placard = this.placards[index];
302             if (!placard.isHidden()) {
303                 this._placardSelected(placard);
304                 return true;
305             }
306             index += backward ? -1 : 1;
307         }
308         return false;
309     },
310
311     /**
312      * @return {number}
313      */
314     _selectedCallFrameIndex: function()
315     {
316         var selectedCallFrame = this._target.debuggerModel.selectedCallFrame();
317         if (!selectedCallFrame)
318             return -1;
319         for (var i = 0; i < this.placards.length; ++i) {
320             var placard = this.placards[i];
321             if (placard._callFrame === selectedCallFrame)
322                 return i;
323         }
324         return -1;
325     },
326
327     /**
328      * @param {!WebInspector.CallStackSidebarPane.Placard} placard
329      */
330     _placardSelected: function(placard)
331     {
332         placard.element.scrollIntoViewIfNeeded();
333         this.dispatchEventToListeners(WebInspector.CallStackSidebarPane.Events.CallFrameSelected, placard._callFrame);
334     },
335
336     _copyStackTrace: function()
337     {
338         var text = "";
339         var lastPlacard = null;
340         for (var i = 0; i < this.placards.length; ++i) {
341             var placard = this.placards[i];
342             if (placard.isHidden())
343                 continue;
344             if (lastPlacard && placard._asyncPlacard !== lastPlacard._asyncPlacard)
345                 text += placard._asyncPlacard.title + "\n";
346             text += placard.title + " (" + placard.subtitle + ")\n";
347             lastPlacard = placard;
348         }
349         InspectorFrontendHost.copyText(text);
350     },
351
352     /**
353      * @param {function(!Array.<!WebInspector.KeyboardShortcut.Descriptor>, function(!Event=):boolean)} registerShortcutDelegate
354      */
355     registerShortcuts: function(registerShortcutDelegate)
356     {
357         registerShortcutDelegate(WebInspector.ShortcutsScreen.SourcesPanelShortcuts.NextCallFrame, this._selectNextCallFrameOnStack.bind(this));
358         registerShortcutDelegate(WebInspector.ShortcutsScreen.SourcesPanelShortcuts.PrevCallFrame, this._selectPreviousCallFrameOnStack.bind(this));
359     },
360
361     /**
362      * @param {!Element|string} status
363      */
364     setStatus: function(status)
365     {
366         if (!this._statusMessageElement)
367             this._statusMessageElement = this.bodyElement.createChild("div", "info");
368         if (typeof status === "string") {
369             this._statusMessageElement.textContent = status;
370         } else {
371             this._statusMessageElement.removeChildren();
372             this._statusMessageElement.appendChild(status);
373         }
374     },
375
376     _keyDown: function(event)
377     {
378         if (event.altKey || event.shiftKey || event.metaKey || event.ctrlKey)
379             return;
380         if (event.keyIdentifier === "Up" && this._selectPreviousCallFrameOnStack() || event.keyIdentifier === "Down" && this._selectNextCallFrameOnStack())
381             event.consume(true);
382     },
383
384     __proto__: WebInspector.SidebarPane.prototype
385 }
386
387 /**
388  * @constructor
389  * @extends {WebInspector.Placard}
390  * @param {!WebInspector.DebuggerModel.CallFrame} callFrame
391  * @param {!WebInspector.Placard=} asyncPlacard
392  */
393 WebInspector.CallStackSidebarPane.Placard = function(callFrame, asyncPlacard)
394 {
395     WebInspector.Placard.call(this, callFrame.functionName || WebInspector.UIString("(anonymous function)"), "");
396     WebInspector.debuggerWorkspaceBinding.createCallFrameLiveLocation(callFrame, this._update.bind(this));
397     this._callFrame = callFrame;
398     this._asyncPlacard = asyncPlacard;
399 }
400
401 WebInspector.CallStackSidebarPane.Placard.prototype = {
402     /**
403      * @param {!WebInspector.UILocation} uiLocation
404      */
405     _update: function(uiLocation)
406     {
407         this.subtitle = uiLocation.linkText().trimMiddle(30);
408     },
409
410     __proto__: WebInspector.Placard.prototype
411 }