2 * Copyright (C) 2008 Apple Inc. All Rights Reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
28 * @extends {WebInspector.SidebarPane}
30 WebInspector
.CallStackSidebarPane = function()
32 WebInspector
.SidebarPane
.call(this, WebInspector
.UIString("Call Stack"));
33 this.element
.addEventListener("keydown", this._keyDown
.bind(this), true);
34 this.element
.tabIndex
= 0;
35 this.callFrameList
= new WebInspector
.UIList();
36 this.callFrameList
.show(this.element
);
38 WebInspector
.moduleSetting("enableAsyncStackTraces").addChangeListener(this._asyncStackTracesStateChanged
, this);
39 WebInspector
.moduleSetting("skipStackFramesPattern").addChangeListener(this._blackboxingStateChanged
, this);
43 WebInspector
.CallStackSidebarPane
.Events
= {
44 CallFrameSelected
: "CallFrameSelected",
45 RevealHiddenCallFrames
: "RevealHiddenCallFrames"
48 WebInspector
.CallStackSidebarPane
.prototype = {
50 * @param {?WebInspector.DebuggerPausedDetails} details
52 update: function(details
)
54 this.callFrameList
.detach();
55 this.callFrameList
.clear();
56 this.element
.removeChildren();
59 var infoElement
= this.element
.createChild("div", "callstack-info");
60 infoElement
.textContent
= WebInspector
.UIString("Not Paused");
64 this.callFrameList
.show(this.element
);
65 this._debuggerModel
= details
.debuggerModel
;
66 var callFrames
= details
.callFrames
;
67 var asyncStackTrace
= details
.asyncStackTrace
;
69 delete this._statusMessageElement
;
70 delete this._hiddenCallFramesMessageElement
;
71 /** @type {!Array.<!WebInspector.CallStackSidebarPane.CallFrame>} */
73 this._hiddenCallFrames
= 0;
75 this._appendSidebarCallFrames(callFrames
);
76 var topStackHidden
= (this._hiddenCallFrames
=== this.callFrames
.length
);
78 while (asyncStackTrace
) {
79 var title
= WebInspector
.asyncStackTraceLabel(asyncStackTrace
.description
);
80 var asyncCallFrame
= new WebInspector
.UIList
.Item(title
, "", true);
81 asyncCallFrame
.element
.addEventListener("click", this._selectNextVisibleCallFrame
.bind(this, this.callFrames
.length
, false), false);
82 asyncCallFrame
.element
.addEventListener("contextmenu", this._asyncCallFrameContextMenu
.bind(this, this.callFrames
.length
), true);
83 this._appendSidebarCallFrames(asyncStackTrace
.callFrames
, asyncCallFrame
);
84 asyncStackTrace
= asyncStackTrace
.asyncStackTrace
;
88 this._revealHiddenCallFrames();
89 if (this._hiddenCallFrames
) {
90 var element
= createElementWithClass("div", "hidden-callframes-message");
91 if (this._hiddenCallFrames
=== 1)
92 element
.textContent
= WebInspector
.UIString("1 stack frame is hidden (black-boxed).");
94 element
.textContent
= WebInspector
.UIString("%d stack frames are hidden (black-boxed).", this._hiddenCallFrames
);
95 element
.createTextChild(" ");
96 var showAllLink
= element
.createChild("span", "link");
97 showAllLink
.textContent
= WebInspector
.UIString("Show");
98 showAllLink
.addEventListener("click", this._revealHiddenCallFrames
.bind(this), false);
99 this.element
.insertBefore(element
, this.element
.firstChild
);
100 this._hiddenCallFramesMessageElement
= element
;
105 * @param {!Array.<!WebInspector.DebuggerModel.CallFrame>} callFrames
106 * @param {!WebInspector.UIList.Item=} asyncCallFrameItem
108 _appendSidebarCallFrames: function(callFrames
, asyncCallFrameItem
)
110 if (asyncCallFrameItem
)
111 this.callFrameList
.addItem(asyncCallFrameItem
);
113 var allCallFramesHidden
= true;
114 for (var i
= 0, n
= callFrames
.length
; i
< n
; ++i
) {
115 var callFrame
= callFrames
[i
];
116 var callFrameItem
= new WebInspector
.CallStackSidebarPane
.CallFrame(callFrame
, asyncCallFrameItem
);
117 callFrameItem
.element
.addEventListener("click", this._callFrameSelected
.bind(this, callFrameItem
), false);
118 callFrameItem
.element
.addEventListener("contextmenu", this._callFrameContextMenu
.bind(this, callFrameItem
), true);
119 this.callFrames
.push(callFrameItem
);
121 if (WebInspector
.BlackboxSupport
.isBlackboxed(callFrame
.script
.sourceURL
, callFrame
.script
.isContentScript())) {
122 callFrameItem
.setHidden(true);
123 callFrameItem
.setDimmed(true);
124 ++this._hiddenCallFrames
;
126 this.callFrameList
.addItem(callFrameItem
);
127 allCallFramesHidden
= false;
130 if (allCallFramesHidden
&& asyncCallFrameItem
) {
131 asyncCallFrameItem
.setHidden(true);
132 asyncCallFrameItem
.element
.remove();
136 _revealHiddenCallFrames: function()
138 if (!this._hiddenCallFrames
)
140 this._hiddenCallFrames
= 0;
141 this.callFrameList
.clear();
142 for (var i
= 0; i
< this.callFrames
.length
; ++i
) {
143 var callFrame
= this.callFrames
[i
];
144 if (callFrame
._asyncCallFrame
) {
145 callFrame
._asyncCallFrame
.setHidden(false);
146 if (i
&& callFrame
._asyncCallFrame
!== this.callFrames
[i
- 1]._asyncCallFrame
)
147 this.callFrameList
.addItem(callFrame
._asyncCallFrame
);
149 callFrame
.setHidden(false);
150 this.callFrameList
.addItem(callFrame
);
152 if (this._hiddenCallFramesMessageElement
) {
153 this._hiddenCallFramesMessageElement
.remove();
154 delete this._hiddenCallFramesMessageElement
;
156 this.dispatchEventToListeners(WebInspector
.CallStackSidebarPane
.Events
.RevealHiddenCallFrames
);
160 * @param {!WebInspector.CallStackSidebarPane.CallFrame} callFrame
161 * @param {!Event} event
163 _callFrameContextMenu: function(callFrame
, event
)
165 var contextMenu
= new WebInspector
.ContextMenu(event
);
167 if (!callFrame
._callFrame
.isAsync())
168 contextMenu
.appendItem(WebInspector
.UIString
.capitalize("Restart ^frame"), this._restartFrame
.bind(this, callFrame
));
170 contextMenu
.appendItem(WebInspector
.UIString
.capitalize("Copy ^stack ^trace"), this._copyStackTrace
.bind(this));
172 var script
= callFrame
._callFrame
.script
;
173 this.appendBlackboxURLContextMenuItems(contextMenu
, script
.sourceURL
, script
.isContentScript());
179 * @param {number} index
180 * @param {!Event} event
182 _asyncCallFrameContextMenu: function(index
, event
)
184 for (; index
< this.callFrames
.length
; ++index
) {
185 var callFrame
= this.callFrames
[index
];
186 if (!callFrame
.isHidden()) {
187 this._callFrameContextMenu(callFrame
, event
);
194 * @param {!WebInspector.ContextMenu} contextMenu
195 * @param {string} url
196 * @param {boolean} isContentScript
198 appendBlackboxURLContextMenuItems: function(contextMenu
, url
, isContentScript
)
200 var blackboxed
= WebInspector
.BlackboxSupport
.isBlackboxed(url
, isContentScript
);
201 var canBlackBox
= WebInspector
.BlackboxSupport
.canBlackboxURL(url
);
202 if (!blackboxed
&& !isContentScript
&& !canBlackBox
)
206 contextMenu
.appendItem(WebInspector
.UIString
.capitalize("Stop ^blackboxing"), this._handleContextMenuBlackboxURL
.bind(this, url
, isContentScript
, false));
209 contextMenu
.appendItem(WebInspector
.UIString
.capitalize("Blackbox ^script"), this._handleContextMenuBlackboxURL
.bind(this, url
, false, true));
211 contextMenu
.appendItem(WebInspector
.UIString
.capitalize("Blackbox ^all ^content ^scripts"), this._handleContextMenuBlackboxURL
.bind(this, url
, true, true));
216 * @param {string} url
217 * @param {boolean} isContentScript
218 * @param {boolean} blackbox
220 _handleContextMenuBlackboxURL: function(url
, isContentScript
, blackbox
)
224 WebInspector
.moduleSetting("skipContentScripts").set(true);
226 WebInspector
.BlackboxSupport
.blackboxURL(url
);
228 WebInspector
.BlackboxSupport
.unblackbox(url
, isContentScript
);
232 _blackboxingStateChanged: function()
234 if (!this._debuggerModel
)
236 var details
= this._debuggerModel
.debuggerPausedDetails();
239 this.update(details
);
240 var selectedCallFrame
= this._debuggerModel
.selectedCallFrame();
241 if (selectedCallFrame
)
242 this.setSelectedCallFrame(selectedCallFrame
);
246 * @param {!WebInspector.CallStackSidebarPane.CallFrame} callFrame
248 _restartFrame: function(callFrame
)
250 callFrame
._callFrame
.restart();
253 _asyncStackTracesStateChanged: function()
255 var enabled
= WebInspector
.moduleSetting("enableAsyncStackTraces").get();
256 if (!enabled
&& this.callFrames
)
257 this._removeAsyncCallFrames();
260 _removeAsyncCallFrames: function()
262 var shouldSelectTopFrame
= false;
263 var lastSyncCallFrameIndex
= -1;
264 for (var i
= 0; i
< this.callFrames
.length
; ++i
) {
265 var callFrame
= this.callFrames
[i
];
266 if (callFrame
._asyncCallFrame
) {
267 if (callFrame
.isSelected())
268 shouldSelectTopFrame
= true;
269 callFrame
._asyncCallFrame
.element
.remove();
270 callFrame
.element
.remove();
272 lastSyncCallFrameIndex
= i
;
275 this.callFrames
.length
= lastSyncCallFrameIndex
+ 1;
276 if (shouldSelectTopFrame
)
277 this._selectNextVisibleCallFrame(0);
281 * @param {!WebInspector.DebuggerModel.CallFrame} x
283 setSelectedCallFrame: function(x
)
285 for (var i
= 0; i
< this.callFrames
.length
; ++i
) {
286 var callFrame
= this.callFrames
[i
];
287 callFrame
.setSelected(callFrame
._callFrame
=== x
);
288 if (callFrame
.isSelected() && callFrame
.isHidden())
289 this._revealHiddenCallFrames();
296 _selectNextCallFrameOnStack: function()
298 var index
= this._selectedCallFrameIndex();
301 return this._selectNextVisibleCallFrame(index
+ 1);
307 _selectPreviousCallFrameOnStack: function()
309 var index
= this._selectedCallFrameIndex();
312 return this._selectNextVisibleCallFrame(index
- 1, true);
316 * @param {number} index
317 * @param {boolean=} backward
320 _selectNextVisibleCallFrame: function(index
, backward
)
322 while (0 <= index
&& index
< this.callFrames
.length
) {
323 var callFrame
= this.callFrames
[index
];
324 if (!callFrame
.isHidden() && !callFrame
.isLabel()) {
325 this._callFrameSelected(callFrame
);
328 index
+= backward
? -1 : 1;
336 _selectedCallFrameIndex: function()
338 if (!this._debuggerModel
)
340 var selectedCallFrame
= this._debuggerModel
.selectedCallFrame();
341 if (!selectedCallFrame
)
343 for (var i
= 0; i
< this.callFrames
.length
; ++i
) {
344 var callFrame
= this.callFrames
[i
];
345 if (callFrame
._callFrame
=== selectedCallFrame
)
352 * @param {!WebInspector.CallStackSidebarPane.CallFrame} callFrame
354 _callFrameSelected: function(callFrame
)
356 callFrame
.element
.scrollIntoViewIfNeeded();
357 this.dispatchEventToListeners(WebInspector
.CallStackSidebarPane
.Events
.CallFrameSelected
, callFrame
._callFrame
);
360 _copyStackTrace: function()
363 var lastCallFrame
= null;
364 for (var i
= 0; i
< this.callFrames
.length
; ++i
) {
365 var callFrame
= this.callFrames
[i
];
366 if (callFrame
.isHidden())
368 if (lastCallFrame
&& callFrame
._asyncCallFrame
!== lastCallFrame
._asyncCallFrame
)
369 text
+= callFrame
._asyncCallFrame
.title() + "\n";
370 text
+= callFrame
.title() + " (" + callFrame
.subtitle() + ")\n";
371 lastCallFrame
= callFrame
;
373 InspectorFrontendHost
.copyText(text
);
377 * @param {function(!Array.<!WebInspector.KeyboardShortcut.Descriptor>, function(!Event=):boolean)} registerShortcutDelegate
379 registerShortcuts: function(registerShortcutDelegate
)
381 registerShortcutDelegate(WebInspector
.ShortcutsScreen
.SourcesPanelShortcuts
.NextCallFrame
, this._selectNextCallFrameOnStack
.bind(this));
382 registerShortcutDelegate(WebInspector
.ShortcutsScreen
.SourcesPanelShortcuts
.PrevCallFrame
, this._selectPreviousCallFrameOnStack
.bind(this));
386 * @param {!Element|string} status
388 setStatus: function(status
)
390 if (!this._statusMessageElement
)
391 this._statusMessageElement
= this.element
.createChild("div", "callstack-info status");
392 if (typeof status
=== "string") {
393 this._statusMessageElement
.textContent
= status
;
395 this._statusMessageElement
.removeChildren();
396 this._statusMessageElement
.appendChild(status
);
400 _keyDown: function(event
)
402 if (event
.altKey
|| event
.shiftKey
|| event
.metaKey
|| event
.ctrlKey
)
404 if (event
.keyIdentifier
=== "Up" && this._selectPreviousCallFrameOnStack() || event
.keyIdentifier
=== "Down" && this._selectNextCallFrameOnStack())
408 __proto__
: WebInspector
.SidebarPane
.prototype
413 * @extends {WebInspector.UIList.Item}
414 * @param {!WebInspector.DebuggerModel.CallFrame} callFrame
415 * @param {!WebInspector.UIList.Item=} asyncCallFrame
417 WebInspector
.CallStackSidebarPane
.CallFrame = function(callFrame
, asyncCallFrame
)
419 WebInspector
.UIList
.Item
.call(this, WebInspector
.beautifyFunctionName(callFrame
.functionName
), "");
420 WebInspector
.debuggerWorkspaceBinding
.createCallFrameLiveLocation(callFrame
, this._update
.bind(this));
421 this._callFrame
= callFrame
;
422 this._asyncCallFrame
= asyncCallFrame
;
425 WebInspector
.CallStackSidebarPane
.CallFrame
.prototype = {
427 * @param {!WebInspector.UILocation} uiLocation
429 _update: function(uiLocation
)
431 var text
= uiLocation
.linkText();
432 this.setSubtitle(text
.trimMiddle(30));
433 this.subtitleElement
.title
= text
;
436 __proto__
: WebInspector
.UIList
.Item
.prototype