Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / 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     if (WebInspector.experimentsSettings.asyncStackTraces.isEnabled()) {
37         var asyncCheckbox = this.titleElement.appendChild(WebInspector.SettingsUI.createSettingCheckbox(WebInspector.UIString("Async"), WebInspector.settings.enableAsyncStackTraces, true, undefined, WebInspector.UIString("Capture async stack traces")));
38         asyncCheckbox.classList.add("scripts-callstack-async");
39         asyncCheckbox.addEventListener("click", consumeEvent, false);
40         WebInspector.settings.enableAsyncStackTraces.addChangeListener(this._asyncStackTracesStateChanged, this);
41     }
42 }
43
44 WebInspector.CallStackSidebarPane.Events = {
45     CallFrameRestarted: "CallFrameRestarted",
46     CallFrameSelected: "CallFrameSelected"
47 }
48
49 WebInspector.CallStackSidebarPane.prototype = {
50     /**
51      * @param {?Array.<!WebInspector.DebuggerModel.CallFrame>} callFrames
52      * @param {?WebInspector.DebuggerModel.StackTrace} asyncStackTrace
53      */
54     update: function(callFrames, asyncStackTrace)
55     {
56         this.bodyElement.removeChildren();
57         delete this._statusMessageElement;
58         /** @type {!Array.<!WebInspector.CallStackSidebarPane.Placard>} */
59         this.placards = [];
60
61         if (!callFrames) {
62             var infoElement = this.bodyElement.createChild("div", "info");
63             infoElement.textContent = WebInspector.UIString("Not Paused");
64             return;
65         }
66
67         this._appendSidebarPlacards(callFrames);
68
69         while (asyncStackTrace) {
70             var title = asyncStackTrace.description;
71             if (title)
72                 title += " " + WebInspector.UIString("(async)");
73             else
74                 title = WebInspector.UIString("Async Call");
75             var asyncPlacard = new WebInspector.Placard(title, "");
76             asyncPlacard.element.classList.add("placard-label");
77             this.bodyElement.appendChild(asyncPlacard.element);
78             this._appendSidebarPlacards(asyncStackTrace.callFrames, asyncPlacard);
79             asyncStackTrace = asyncStackTrace.asyncStackTrace;
80         }
81     },
82
83     /**
84      * @param {!Array.<!WebInspector.DebuggerModel.CallFrame>} callFrames
85      * @param {!WebInspector.Placard=} asyncPlacard
86      */
87     _appendSidebarPlacards: function(callFrames, asyncPlacard)
88     {
89         for (var i = 0, n = callFrames.length; i < n; ++i) {
90             var placard = new WebInspector.CallStackSidebarPane.Placard(callFrames[i], asyncPlacard);
91             placard.element.addEventListener("click", this._placardSelected.bind(this, placard), false);
92             placard.element.addEventListener("contextmenu", this._placardContextMenu.bind(this, placard), true);
93             if (!i && asyncPlacard) {
94                 asyncPlacard.element.addEventListener("click", this._placardSelected.bind(this, placard), false);
95                 asyncPlacard.element.addEventListener("contextmenu", this._placardContextMenu.bind(this, placard), true);
96             }
97             this.placards.push(placard);
98             this.bodyElement.appendChild(placard.element);
99         }
100     },
101
102     /**
103      * @param {!WebInspector.CallStackSidebarPane.Placard} placard
104      */
105     _placardContextMenu: function(placard, event)
106     {
107         var contextMenu = new WebInspector.ContextMenu(event);
108
109         if (!placard._callFrame.isAsync())
110             contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Restart frame" : "Restart Frame"), this._restartFrame.bind(this, placard));
111
112         contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Copy stack trace" : "Copy Stack Trace"), this._copyStackTrace.bind(this));
113         contextMenu.show();
114     },
115
116     /**
117      * @param {!WebInspector.CallStackSidebarPane.Placard} placard
118      */
119     _restartFrame: function(placard)
120     {
121         placard._callFrame.restart();
122         this.dispatchEventToListeners(WebInspector.CallStackSidebarPane.Events.CallFrameRestarted, placard._callFrame);
123     },
124
125     _asyncStackTracesStateChanged: function()
126     {
127         var enabled = WebInspector.settings.enableAsyncStackTraces.get();
128         if (!enabled && this.placards)
129             this._removeAsyncPlacards();
130     },
131
132     _removeAsyncPlacards: function()
133     {
134         var shouldSelectTopFrame = false;
135         var lastSyncPlacardIndex = -1;
136         for (var i = 0; i < this.placards.length; ++i) {
137             var placard = this.placards[i];
138             if (placard._asyncPlacard) {
139                 if (placard.selected)
140                     shouldSelectTopFrame = true;
141                 placard._asyncPlacard.element.remove();
142                 placard.element.remove();
143             } else {
144                 lastSyncPlacardIndex = i;
145             }
146         }
147         this.placards.length = lastSyncPlacardIndex + 1;
148         if (shouldSelectTopFrame)
149             this._selectPlacardByIndex(0);
150     },
151
152     /**
153      * @param {!WebInspector.DebuggerModel.CallFrame} x
154      */
155     setSelectedCallFrame: function(x)
156     {
157         for (var i = 0; i < this.placards.length; ++i) {
158             var placard = this.placards[i];
159             placard.selected = (placard._callFrame === x);
160         }
161     },
162
163     /**
164      * @return {boolean}
165      */
166     _selectNextCallFrameOnStack: function()
167     {
168         var index = this._selectedCallFrameIndex();
169         if (index === -1)
170             return false;
171         return this._selectPlacardByIndex(index + 1);
172     },
173
174     /**
175      * @return {boolean}
176      */
177     _selectPreviousCallFrameOnStack: function()
178     {
179         var index = this._selectedCallFrameIndex();
180         if (index === -1)
181             return false;
182         return this._selectPlacardByIndex(index - 1);
183     },
184
185     /**
186      * @param {number} index
187      * @return {boolean}
188      */
189     _selectPlacardByIndex: function(index)
190     {
191         if (index < 0 || index >= this.placards.length)
192             return false;
193         this._placardSelected(this.placards[index]);
194         return true;
195     },
196
197     /**
198      * @return {number}
199      */
200     _selectedCallFrameIndex: function()
201     {
202         var selectedCallFrame = WebInspector.debuggerModel.selectedCallFrame();
203         if (!selectedCallFrame)
204             return -1;
205         for (var i = 0; i < this.placards.length; ++i) {
206             var placard = this.placards[i];
207             if (placard._callFrame === selectedCallFrame)
208                 return i;
209         }
210         return -1;
211     },
212
213     /**
214      * @param {!WebInspector.CallStackSidebarPane.Placard} placard
215      */
216     _placardSelected: function(placard)
217     {
218         placard.element.scrollIntoViewIfNeeded();
219         this.dispatchEventToListeners(WebInspector.CallStackSidebarPane.Events.CallFrameSelected, placard._callFrame);
220     },
221
222     _copyStackTrace: function()
223     {
224         var text = "";
225         for (var i = 0; i < this.placards.length; ++i) {
226             if (i && this.placards[i]._asyncPlacard !== this.placards[i - 1]._asyncPlacard)
227                 text += this.placards[i]._asyncPlacard.title + "\n";
228             text += this.placards[i].title + " (" + this.placards[i].subtitle + ")\n";
229         }
230         InspectorFrontendHost.copyText(text);
231     },
232
233     /**
234      * @param {function(!Array.<!WebInspector.KeyboardShortcut.Descriptor>, function(?Event=):boolean)} registerShortcutDelegate
235      */
236     registerShortcuts: function(registerShortcutDelegate)
237     {
238         registerShortcutDelegate(WebInspector.ShortcutsScreen.SourcesPanelShortcuts.NextCallFrame, this._selectNextCallFrameOnStack.bind(this));
239         registerShortcutDelegate(WebInspector.ShortcutsScreen.SourcesPanelShortcuts.PrevCallFrame, this._selectPreviousCallFrameOnStack.bind(this));
240     },
241
242     /**
243      * @param {!Element|string} status
244      */
245     setStatus: function(status)
246     {
247         if (!this._statusMessageElement)
248             this._statusMessageElement = this.bodyElement.createChild("div", "info");
249         if (typeof status === "string") {
250             this._statusMessageElement.textContent = status;
251         } else {
252             this._statusMessageElement.removeChildren();
253             this._statusMessageElement.appendChild(status);
254         }
255     },
256
257     _keyDown: function(event)
258     {
259         if (event.altKey || event.shiftKey || event.metaKey || event.ctrlKey)
260             return;
261         if (event.keyIdentifier === "Up" && this._selectPreviousCallFrameOnStack() || event.keyIdentifier === "Down" && this._selectNextCallFrameOnStack())
262             event.consume(true);
263     },
264
265     __proto__: WebInspector.SidebarPane.prototype
266 }
267
268 /**
269  * @constructor
270  * @extends {WebInspector.Placard}
271  * @param {!WebInspector.DebuggerModel.CallFrame} callFrame
272  * @param {!WebInspector.Placard=} asyncPlacard
273  */
274 WebInspector.CallStackSidebarPane.Placard = function(callFrame, asyncPlacard)
275 {
276     WebInspector.Placard.call(this, callFrame.functionName || WebInspector.UIString("(anonymous function)"), "");
277     callFrame.createLiveLocation(this._update.bind(this));
278     this._callFrame = callFrame;
279     this._asyncPlacard = asyncPlacard;
280 }
281
282 WebInspector.CallStackSidebarPane.Placard.prototype = {
283     /**
284      * @param {!WebInspector.UILocation} uiLocation
285      */
286     _update: function(uiLocation)
287     {
288         this.subtitle = uiLocation.linkText().trimMiddle(100);
289     },
290
291     __proto__: WebInspector.Placard.prototype
292 }