Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / third_party / WebKit / Source / devtools / front_end / sources / CallStackSidebarPane.js
blob9866520943997d4c9f1e5192271d6bb115af4a70
1 /*
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
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.
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.
26 /**
27 * @constructor
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);
42 /** @enum {string} */
43 WebInspector.CallStackSidebarPane.Events = {
44 CallFrameSelected: "CallFrameSelected",
45 RevealHiddenCallFrames: "RevealHiddenCallFrames"
48 WebInspector.CallStackSidebarPane.prototype = {
49 /**
50 * @param {?WebInspector.DebuggerPausedDetails} details
52 update: function(details)
54 this.callFrameList.detach();
55 this.callFrameList.clear();
56 this.element.removeChildren();
58 if (!details) {
59 var infoElement = this.element.createChild("div", "callstack-info");
60 infoElement.textContent = WebInspector.UIString("Not Paused");
61 return;
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>} */
72 this.callFrames = [];
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;
87 if (topStackHidden)
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).");
93 else
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;
125 } else {
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)
139 return;
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());
175 contextMenu.show();
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);
188 break;
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)
203 return;
205 if (blackboxed) {
206 contextMenu.appendItem(WebInspector.UIString.capitalize("Stop ^blackboxing"), this._handleContextMenuBlackboxURL.bind(this, url, isContentScript, false));
207 } else {
208 if (canBlackBox)
209 contextMenu.appendItem(WebInspector.UIString.capitalize("Blackbox ^script"), this._handleContextMenuBlackboxURL.bind(this, url, false, true));
210 if (isContentScript)
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)
222 if (blackbox) {
223 if (isContentScript)
224 WebInspector.moduleSetting("skipContentScripts").set(true);
225 else
226 WebInspector.BlackboxSupport.blackboxURL(url);
227 } else {
228 WebInspector.BlackboxSupport.unblackbox(url, isContentScript);
232 _blackboxingStateChanged: function()
234 if (!this._debuggerModel)
235 return;
236 var details = this._debuggerModel.debuggerPausedDetails();
237 if (!details)
238 return;
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();
271 } else {
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();
294 * @return {boolean}
296 _selectNextCallFrameOnStack: function()
298 var index = this._selectedCallFrameIndex();
299 if (index === -1)
300 return false;
301 return this._selectNextVisibleCallFrame(index + 1);
305 * @return {boolean}
307 _selectPreviousCallFrameOnStack: function()
309 var index = this._selectedCallFrameIndex();
310 if (index === -1)
311 return false;
312 return this._selectNextVisibleCallFrame(index - 1, true);
316 * @param {number} index
317 * @param {boolean=} backward
318 * @return {boolean}
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);
326 return true;
328 index += backward ? -1 : 1;
330 return false;
334 * @return {number}
336 _selectedCallFrameIndex: function()
338 if (!this._debuggerModel)
339 return -1;
340 var selectedCallFrame = this._debuggerModel.selectedCallFrame();
341 if (!selectedCallFrame)
342 return -1;
343 for (var i = 0; i < this.callFrames.length; ++i) {
344 var callFrame = this.callFrames[i];
345 if (callFrame._callFrame === selectedCallFrame)
346 return i;
348 return -1;
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()
362 var text = "";
363 var lastCallFrame = null;
364 for (var i = 0; i < this.callFrames.length; ++i) {
365 var callFrame = this.callFrames[i];
366 if (callFrame.isHidden())
367 continue;
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;
394 } else {
395 this._statusMessageElement.removeChildren();
396 this._statusMessageElement.appendChild(status);
400 _keyDown: function(event)
402 if (event.altKey || event.shiftKey || event.metaKey || event.ctrlKey)
403 return;
404 if (event.keyIdentifier === "Up" && this._selectPreviousCallFrameOnStack() || event.keyIdentifier === "Down" && this._selectNextCallFrameOnStack())
405 event.consume(true);
408 __proto__: WebInspector.SidebarPane.prototype
412 * @constructor
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