Upstream version 10.39.225.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.isBlackboxed(callFrame.script.sourceURL, callFrame.script.isContentScript())) {
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, script.isContentScript());
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      * @param {boolean} isContentScript
187      */
188     appendBlackboxURLContextMenuItems: function(contextMenu, url, isContentScript)
189     {
190         var blackboxed = WebInspector.BlackboxSupport.isBlackboxed(url, isContentScript);
191         if (blackboxed) {
192             contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Stop blackboxing" : "Stop Blackboxing"), this._handleContextMenuBlackboxURL.bind(this, url, isContentScript, false));
193         } else {
194             if (WebInspector.BlackboxSupport.canBlackboxURL(url))
195                 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Blackbox script" : "Blackbox Script"), this._handleContextMenuBlackboxURL.bind(this, url, false, true));
196             if (isContentScript)
197                 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Blackbox all content scripts" : "Blackbox All Content Scripts"), this._handleContextMenuBlackboxURL.bind(this, url, true, true));
198         }
199     },
200
201     /**
202      * @param {string} url
203      * @param {boolean} isContentScript
204      * @param {boolean} blackbox
205      */
206     _handleContextMenuBlackboxURL: function(url, isContentScript, blackbox)
207     {
208         if (blackbox) {
209             if (isContentScript)
210                 WebInspector.settings.skipContentScripts.set(true);
211             else
212                 WebInspector.BlackboxSupport.blackboxURL(url);
213         } else {
214             WebInspector.BlackboxSupport.unblackbox(url, isContentScript);
215         }
216     },
217
218     _blackboxingStateChanged: function()
219     {
220         if (!this._target)
221             return;
222         var details = this._target.debuggerModel.debuggerPausedDetails();
223         if (!details)
224             return;
225         this.update(details);
226         var selectedCallFrame = this._target.debuggerModel.selectedCallFrame();
227         if (selectedCallFrame)
228             this.setSelectedCallFrame(selectedCallFrame);
229     },
230
231     /**
232      * @param {!WebInspector.CallStackSidebarPane.Placard} placard
233      */
234     _restartFrame: function(placard)
235     {
236         placard._callFrame.restart();
237     },
238
239     _asyncStackTracesStateChanged: function()
240     {
241         var enabled = WebInspector.settings.enableAsyncStackTraces.get();
242         if (!enabled && this.placards)
243             this._removeAsyncPlacards();
244     },
245
246     _removeAsyncPlacards: function()
247     {
248         var shouldSelectTopFrame = false;
249         var lastSyncPlacardIndex = -1;
250         for (var i = 0; i < this.placards.length; ++i) {
251             var placard = this.placards[i];
252             if (placard._asyncPlacard) {
253                 if (placard.selected)
254                     shouldSelectTopFrame = true;
255                 placard._asyncPlacard.element.remove();
256                 placard.element.remove();
257             } else {
258                 lastSyncPlacardIndex = i;
259             }
260         }
261         this.placards.length = lastSyncPlacardIndex + 1;
262         if (shouldSelectTopFrame)
263             this._selectNextVisiblePlacard(0);
264     },
265
266     /**
267      * @param {!WebInspector.DebuggerModel.CallFrame} x
268      */
269     setSelectedCallFrame: function(x)
270     {
271         for (var i = 0; i < this.placards.length; ++i) {
272             var placard = this.placards[i];
273             placard.selected = (placard._callFrame === x);
274             if (placard.selected && placard.isHidden())
275                 this._revealHiddenPlacards();
276         }
277     },
278
279     /**
280      * @return {boolean}
281      */
282     _selectNextCallFrameOnStack: function()
283     {
284         var index = this._selectedCallFrameIndex();
285         if (index === -1)
286             return false;
287         return this._selectNextVisiblePlacard(index + 1);
288     },
289
290     /**
291      * @return {boolean}
292      */
293     _selectPreviousCallFrameOnStack: function()
294     {
295         var index = this._selectedCallFrameIndex();
296         if (index === -1)
297             return false;
298         return this._selectNextVisiblePlacard(index - 1, true);
299     },
300
301     /**
302      * @param {number} index
303      * @param {boolean=} backward
304      * @return {boolean}
305      */
306     _selectNextVisiblePlacard: function(index, backward)
307     {
308         while (0 <= index && index < this.placards.length) {
309             var placard = this.placards[index];
310             if (!placard.isHidden()) {
311                 this._placardSelected(placard);
312                 return true;
313             }
314             index += backward ? -1 : 1;
315         }
316         return false;
317     },
318
319     /**
320      * @return {number}
321      */
322     _selectedCallFrameIndex: function()
323     {
324         var selectedCallFrame = this._target.debuggerModel.selectedCallFrame();
325         if (!selectedCallFrame)
326             return -1;
327         for (var i = 0; i < this.placards.length; ++i) {
328             var placard = this.placards[i];
329             if (placard._callFrame === selectedCallFrame)
330                 return i;
331         }
332         return -1;
333     },
334
335     /**
336      * @param {!WebInspector.CallStackSidebarPane.Placard} placard
337      */
338     _placardSelected: function(placard)
339     {
340         placard.element.scrollIntoViewIfNeeded();
341         this.dispatchEventToListeners(WebInspector.CallStackSidebarPane.Events.CallFrameSelected, placard._callFrame);
342     },
343
344     _copyStackTrace: function()
345     {
346         var text = "";
347         var lastPlacard = null;
348         for (var i = 0; i < this.placards.length; ++i) {
349             var placard = this.placards[i];
350             if (placard.isHidden())
351                 continue;
352             if (lastPlacard && placard._asyncPlacard !== lastPlacard._asyncPlacard)
353                 text += placard._asyncPlacard.title + "\n";
354             text += placard.title + " (" + placard.subtitle + ")\n";
355             lastPlacard = placard;
356         }
357         InspectorFrontendHost.copyText(text);
358     },
359
360     /**
361      * @param {function(!Array.<!WebInspector.KeyboardShortcut.Descriptor>, function(!Event=):boolean)} registerShortcutDelegate
362      */
363     registerShortcuts: function(registerShortcutDelegate)
364     {
365         registerShortcutDelegate(WebInspector.ShortcutsScreen.SourcesPanelShortcuts.NextCallFrame, this._selectNextCallFrameOnStack.bind(this));
366         registerShortcutDelegate(WebInspector.ShortcutsScreen.SourcesPanelShortcuts.PrevCallFrame, this._selectPreviousCallFrameOnStack.bind(this));
367     },
368
369     /**
370      * @param {!Element|string} status
371      */
372     setStatus: function(status)
373     {
374         if (!this._statusMessageElement)
375             this._statusMessageElement = this.bodyElement.createChild("div", "info");
376         if (typeof status === "string") {
377             this._statusMessageElement.textContent = status;
378         } else {
379             this._statusMessageElement.removeChildren();
380             this._statusMessageElement.appendChild(status);
381         }
382     },
383
384     _keyDown: function(event)
385     {
386         if (event.altKey || event.shiftKey || event.metaKey || event.ctrlKey)
387             return;
388         if (event.keyIdentifier === "Up" && this._selectPreviousCallFrameOnStack() || event.keyIdentifier === "Down" && this._selectNextCallFrameOnStack())
389             event.consume(true);
390     },
391
392     __proto__: WebInspector.SidebarPane.prototype
393 }
394
395 /**
396  * @constructor
397  * @extends {WebInspector.Placard}
398  * @param {!WebInspector.DebuggerModel.CallFrame} callFrame
399  * @param {!WebInspector.Placard=} asyncPlacard
400  */
401 WebInspector.CallStackSidebarPane.Placard = function(callFrame, asyncPlacard)
402 {
403     WebInspector.Placard.call(this, callFrame.functionName || WebInspector.UIString("(anonymous function)"), "");
404     WebInspector.debuggerWorkspaceBinding.createCallFrameLiveLocation(callFrame, this._update.bind(this));
405     this._callFrame = callFrame;
406     this._asyncPlacard = asyncPlacard;
407 }
408
409 WebInspector.CallStackSidebarPane.Placard.prototype = {
410     /**
411      * @param {!WebInspector.UILocation} uiLocation
412      */
413     _update: function(uiLocation)
414     {
415         var text = uiLocation.linkText();
416         this.subtitle = text.trimMiddle(30);
417         this.subtitleElement.title = text;
418     },
419
420     __proto__: WebInspector.Placard.prototype
421 }