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.bodyElement.addEventListener("keydown", this._keyDown.bind(this), true);
34 this.bodyElement.tabIndex = 0;
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);
43 WebInspector.CallStackSidebarPane.Events = {
44 CallFrameSelected: "CallFrameSelected"
47 WebInspector.CallStackSidebarPane.prototype = {
49 * @param {?WebInspector.DebuggerPausedDetails} details
51 update: function(details)
53 this.bodyElement.removeChildren();
56 var infoElement = this.bodyElement.createChild("div", "info");
57 infoElement.textContent = WebInspector.UIString("Not Paused");
61 this._target = details.target();
62 var callFrames = details.callFrames;
63 var asyncStackTrace = details.asyncStackTrace;
65 delete this._statusMessageElement;
66 delete this._hiddenPlacardsMessageElement;
67 /** @type {!Array.<!WebInspector.CallStackSidebarPane.Placard>} */
69 this._hiddenPlacards = 0;
71 this._appendSidebarPlacards(callFrames);
72 var topStackHidden = (this._hiddenPlacards === this.placards.length);
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;
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).");
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;
103 * @param {!Array.<!WebInspector.DebuggerModel.CallFrame>} callFrames
104 * @param {!WebInspector.Placard=} asyncPlacard
106 _appendSidebarPlacards: function(callFrames, asyncPlacard)
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);
117 if (WebInspector.BlackboxSupport.isBlackboxed(callFrame.script.sourceURL, callFrame.script.isContentScript())) {
118 placard.setHidden(true);
119 placard.element.classList.add("dimmed");
120 ++this._hiddenPlacards;
122 allPlacardsHidden = false;
125 if (allPlacardsHidden && asyncPlacard)
126 asyncPlacard.setHidden(true);
129 _revealHiddenPlacards: function()
131 if (!this._hiddenPlacards)
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);
140 if (this._hiddenPlacardsMessageElement) {
141 this._hiddenPlacardsMessageElement.remove();
142 delete this._hiddenPlacardsMessageElement;
147 * @param {!WebInspector.CallStackSidebarPane.Placard} placard
148 * @param {!Event} event
150 _placardContextMenu: function(placard, event)
152 var contextMenu = new WebInspector.ContextMenu(event);
154 if (!placard._callFrame.isAsync())
155 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Restart frame" : "Restart Frame"), this._restartFrame.bind(this, placard));
157 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Copy stack trace" : "Copy Stack Trace"), this._copyStackTrace.bind(this));
159 var script = placard._callFrame.script;
160 if (!script.isSnippet()) {
161 contextMenu.appendSeparator();
162 this.appendBlackboxURLContextMenuItems(contextMenu, script.sourceURL, script.isContentScript());
169 * @param {number} index
170 * @param {!Event} event
172 _asyncPlacardContextMenu: function(index, event)
174 for (; index < this.placards.length; ++index) {
175 var placard = this.placards[index];
176 if (!placard.isHidden()) {
177 this._placardContextMenu(placard, event);
184 * @param {!WebInspector.ContextMenu} contextMenu
185 * @param {string} url
186 * @param {boolean} isContentScript
188 appendBlackboxURLContextMenuItems: function(contextMenu, url, isContentScript)
190 var blackboxed = WebInspector.BlackboxSupport.isBlackboxed(url, isContentScript);
192 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Stop blackboxing" : "Stop Blackboxing"), this._handleContextMenuBlackboxURL.bind(this, url, isContentScript, false));
194 if (WebInspector.BlackboxSupport.canBlackboxURL(url))
195 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Blackbox script" : "Blackbox Script"), this._handleContextMenuBlackboxURL.bind(this, url, false, true));
197 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Blackbox all content scripts" : "Blackbox All Content Scripts"), this._handleContextMenuBlackboxURL.bind(this, url, true, true));
202 * @param {string} url
203 * @param {boolean} isContentScript
204 * @param {boolean} blackbox
206 _handleContextMenuBlackboxURL: function(url, isContentScript, blackbox)
210 WebInspector.settings.skipContentScripts.set(true);
212 WebInspector.BlackboxSupport.blackboxURL(url);
214 WebInspector.BlackboxSupport.unblackbox(url, isContentScript);
218 _blackboxingStateChanged: function()
222 var details = this._target.debuggerModel.debuggerPausedDetails();
225 this.update(details);
226 var selectedCallFrame = this._target.debuggerModel.selectedCallFrame();
227 if (selectedCallFrame)
228 this.setSelectedCallFrame(selectedCallFrame);
232 * @param {!WebInspector.CallStackSidebarPane.Placard} placard
234 _restartFrame: function(placard)
236 placard._callFrame.restart();
239 _asyncStackTracesStateChanged: function()
241 var enabled = WebInspector.settings.enableAsyncStackTraces.get();
242 if (!enabled && this.placards)
243 this._removeAsyncPlacards();
246 _removeAsyncPlacards: function()
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();
258 lastSyncPlacardIndex = i;
261 this.placards.length = lastSyncPlacardIndex + 1;
262 if (shouldSelectTopFrame)
263 this._selectNextVisiblePlacard(0);
267 * @param {!WebInspector.DebuggerModel.CallFrame} x
269 setSelectedCallFrame: function(x)
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();
282 _selectNextCallFrameOnStack: function()
284 var index = this._selectedCallFrameIndex();
287 return this._selectNextVisiblePlacard(index + 1);
293 _selectPreviousCallFrameOnStack: function()
295 var index = this._selectedCallFrameIndex();
298 return this._selectNextVisiblePlacard(index - 1, true);
302 * @param {number} index
303 * @param {boolean=} backward
306 _selectNextVisiblePlacard: function(index, backward)
308 while (0 <= index && index < this.placards.length) {
309 var placard = this.placards[index];
310 if (!placard.isHidden()) {
311 this._placardSelected(placard);
314 index += backward ? -1 : 1;
322 _selectedCallFrameIndex: function()
324 var selectedCallFrame = this._target.debuggerModel.selectedCallFrame();
325 if (!selectedCallFrame)
327 for (var i = 0; i < this.placards.length; ++i) {
328 var placard = this.placards[i];
329 if (placard._callFrame === selectedCallFrame)
336 * @param {!WebInspector.CallStackSidebarPane.Placard} placard
338 _placardSelected: function(placard)
340 placard.element.scrollIntoViewIfNeeded();
341 this.dispatchEventToListeners(WebInspector.CallStackSidebarPane.Events.CallFrameSelected, placard._callFrame);
344 _copyStackTrace: function()
347 var lastPlacard = null;
348 for (var i = 0; i < this.placards.length; ++i) {
349 var placard = this.placards[i];
350 if (placard.isHidden())
352 if (lastPlacard && placard._asyncPlacard !== lastPlacard._asyncPlacard)
353 text += placard._asyncPlacard.title + "\n";
354 text += placard.title + " (" + placard.subtitle + ")\n";
355 lastPlacard = placard;
357 InspectorFrontendHost.copyText(text);
361 * @param {function(!Array.<!WebInspector.KeyboardShortcut.Descriptor>, function(!Event=):boolean)} registerShortcutDelegate
363 registerShortcuts: function(registerShortcutDelegate)
365 registerShortcutDelegate(WebInspector.ShortcutsScreen.SourcesPanelShortcuts.NextCallFrame, this._selectNextCallFrameOnStack.bind(this));
366 registerShortcutDelegate(WebInspector.ShortcutsScreen.SourcesPanelShortcuts.PrevCallFrame, this._selectPreviousCallFrameOnStack.bind(this));
370 * @param {!Element|string} status
372 setStatus: function(status)
374 if (!this._statusMessageElement)
375 this._statusMessageElement = this.bodyElement.createChild("div", "info");
376 if (typeof status === "string") {
377 this._statusMessageElement.textContent = status;
379 this._statusMessageElement.removeChildren();
380 this._statusMessageElement.appendChild(status);
384 _keyDown: function(event)
386 if (event.altKey || event.shiftKey || event.metaKey || event.ctrlKey)
388 if (event.keyIdentifier === "Up" && this._selectPreviousCallFrameOnStack() || event.keyIdentifier === "Down" && this._selectNextCallFrameOnStack())
392 __proto__: WebInspector.SidebarPane.prototype
397 * @extends {WebInspector.Placard}
398 * @param {!WebInspector.DebuggerModel.CallFrame} callFrame
399 * @param {!WebInspector.Placard=} asyncPlacard
401 WebInspector.CallStackSidebarPane.Placard = function(callFrame, asyncPlacard)
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;
409 WebInspector.CallStackSidebarPane.Placard.prototype = {
411 * @param {!WebInspector.UILocation} uiLocation
413 _update: function(uiLocation)
415 var text = uiLocation.linkText();
416 this.subtitle = text.trimMiddle(30);
417 this.subtitleElement.title = text;
420 __proto__: WebInspector.Placard.prototype