OLD | NEW |
| (Empty) |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 /** | |
6 * @constructor | |
7 * @extends {WebInspector.VBox} | |
8 * @implements {WebInspector.TargetManager.Observer} | |
9 */ | |
10 WebInspector.PromisePane = function() | |
11 { | |
12 WebInspector.VBox.call(this); | |
13 this.registerRequiredCSS("promises/promisePane.css"); | |
14 this.element.classList.add("promises"); | |
15 | |
16 var toolbar = new WebInspector.Toolbar("", this.element); | |
17 this._recordButton = new WebInspector.ToolbarToggle("", "record-toolbar-item
"); | |
18 this._recordButton.addEventListener("click", this._recordButtonClicked.bind(
this)); | |
19 toolbar.appendToolbarItem(this._recordButton); | |
20 | |
21 var clearButton = new WebInspector.ToolbarButton(WebInspector.UIString("Clea
r"), "clear-toolbar-item"); | |
22 clearButton.addEventListener("click", this._clearButtonClicked.bind(this)); | |
23 toolbar.appendToolbarItem(clearButton); | |
24 toolbar.appendSeparator(); | |
25 | |
26 this._promiseStatusFiltersSetting = WebInspector.settings.createSetting("pro
miseStatusFilters", {}); | |
27 this._hideCollectedPromisesSetting = WebInspector.settings.createSetting("hi
deCollectedPromises", false); | |
28 | |
29 this._createFilterBar(); | |
30 toolbar.appendToolbarItem(this._filterBar.filterButton()); | |
31 | |
32 var garbageCollectButton = new WebInspector.ToolbarButton(WebInspector.UIStr
ing("Collect garbage"), "garbage-collect-toolbar-item"); | |
33 garbageCollectButton.addEventListener("click", this._garbageCollectButtonCli
cked, this); | |
34 toolbar.appendToolbarItem(garbageCollectButton); | |
35 | |
36 toolbar.appendSeparator(); | |
37 var asyncCheckbox = new WebInspector.ToolbarCheckbox(WebInspector.UIString("
Async"), WebInspector.UIString("Capture async stack traces"), WebInspector.modul
eSetting("enableAsyncStackTraces")); | |
38 toolbar.appendToolbarItem(asyncCheckbox); | |
39 | |
40 this._filterBar.show(this.element); | |
41 | |
42 this._hiddenByFilterCount = 0; | |
43 this._filterStatusMessageElement = this.element.createChild("div", "promises
-filter-status hidden"); | |
44 this._filterStatusTextElement = this._filterStatusMessageElement.createChild
("span"); | |
45 this._filterStatusMessageElement.createTextChild(" "); | |
46 var resetFiltersLink = this._filterStatusMessageElement.createChild("span",
"link"); | |
47 resetFiltersLink.textContent = WebInspector.UIString("Show all promises."); | |
48 resetFiltersLink.addEventListener("click", this._resetFilters.bind(this), tr
ue); | |
49 | |
50 // FIXME: Make "status" column width fixed to ~16px. | |
51 var columns = [ | |
52 { id: "status", weight: 1 }, | |
53 { id: "function", title: WebInspector.UIString("Function"), disclosure:
true, weight: 10 }, | |
54 { id: "created", title: WebInspector.UIString("Created"), weight: 10 }, | |
55 { id: "settled", title: WebInspector.UIString("Settled"), weight: 10 }, | |
56 { id: "tts", title: WebInspector.UIString("Time to settle"), weight: 10
} | |
57 ]; | |
58 this._dataGrid = new WebInspector.ViewportDataGrid(columns, undefined, undef
ined, undefined, this._onContextMenu.bind(this)); | |
59 this._dataGrid.setStickToBottom(true); | |
60 this._dataGrid.asWidget().show(this.element); | |
61 | |
62 this._linkifier = new WebInspector.Linkifier(); | |
63 | |
64 /** @type {!Map.<!WebInspector.DebuggerModel, !Map.<number, !WebInspector.Pr
omiseDetails>>} */ | |
65 this._promiseDetailsByDebuggerModel = new Map(); | |
66 /** @type {!Map.<number, !WebInspector.DataGridNode>} */ | |
67 this._promiseIdToNode = new Map(); | |
68 | |
69 this._popoverHelper = new WebInspector.PopoverHelper(this.element, this._get
PopoverAnchor.bind(this), this._showPopover.bind(this)); | |
70 this._popoverHelper.setTimeout(250, 250); | |
71 | |
72 this.element.addEventListener("click", this._hidePopover.bind(this), true); | |
73 | |
74 WebInspector.targetManager.addModelListener(WebInspector.DebuggerModel, WebI
nspector.DebuggerModel.Events.PromiseUpdated, this._onPromiseUpdated, this); | |
75 WebInspector.targetManager.addModelListener(WebInspector.ResourceTreeModel,
WebInspector.ResourceTreeModel.EventTypes.MainFrameNavigated, this._mainFrameNav
igated, this); | |
76 WebInspector.context.addFlavorChangeListener(WebInspector.Target, this._targ
etChanged, this); | |
77 | |
78 WebInspector.targetManager.observeTargets(this); | |
79 } | |
80 | |
81 WebInspector.PromisePane._maxPromiseCount = 10000; | |
82 | |
83 | |
84 /** | |
85 * @constructor | |
86 * @param {!DebuggerAgent.PromiseDetails} details | |
87 */ | |
88 WebInspector.PromiseDetails = function(details) | |
89 { | |
90 this.id = details.id; | |
91 this.isGarbageCollected = false; | |
92 this.update(details); | |
93 } | |
94 | |
95 WebInspector.PromiseDetails.prototype = { | |
96 /** | |
97 * @param {!DebuggerAgent.PromiseDetails} details | |
98 */ | |
99 update: function(details) | |
100 { | |
101 if (this.id !== details.id) | |
102 throw new Error("Invalid id, expected " + this.id + " was " + detail
s.id); | |
103 if (details.status) | |
104 this.status = details.status; | |
105 if (details.parentId) | |
106 this.parentId = details.parentId; | |
107 if (details.creationTime) | |
108 this.creationTime = details.creationTime; | |
109 if (details.settlementTime) | |
110 this.settlementTime = details.settlementTime; | |
111 if (details.creationStack) { | |
112 this.creationStack = details.creationStack; | |
113 if (this.creationStack.callFrames.length) | |
114 this.callFrame = this.creationStack.callFrames[0]; | |
115 } | |
116 if (details.asyncCreationStack) | |
117 this.asyncCreationStack = details.asyncCreationStack; | |
118 if (details.settlementStack) | |
119 this.settlementStack = details.settlementStack; | |
120 if (details.asyncSettlementStack) | |
121 this.asyncSettlementStack = details.asyncSettlementStack; | |
122 } | |
123 } | |
124 | |
125 | |
126 WebInspector.PromisePane.prototype = { | |
127 _createFilterBar: function() | |
128 { | |
129 this._filterBar = new WebInspector.FilterBar("promisePane"); | |
130 | |
131 this._textFilterUI = new WebInspector.TextFilterUI(true); | |
132 this._textFilterUI.addEventListener(WebInspector.FilterUI.Events.FilterC
hanged, this._onFilterChanged, this); | |
133 this._filterBar.addFilter(this._textFilterUI); | |
134 | |
135 var statuses = [ | |
136 { name: "pending", label: WebInspector.UIString("Pending") }, | |
137 { name: "resolved", label: WebInspector.UIString("Fulfilled") }, | |
138 { name: "rejected", label: WebInspector.UIString("Rejected") } | |
139 ]; | |
140 this._statusFilterUI = new WebInspector.NamedBitSetFilterUI(statuses, th
is._promiseStatusFiltersSetting); | |
141 this._statusFilterUI.addEventListener(WebInspector.FilterUI.Events.Filte
rChanged, this._onFilterChanged, this); | |
142 this._filterBar.addFilter(this._statusFilterUI); | |
143 | |
144 var hideCollectedCheckbox = new WebInspector.CheckboxFilterUI("hide-coll
ected-promises", WebInspector.UIString("Hide collected promises"), true, this._h
ideCollectedPromisesSetting); | |
145 hideCollectedCheckbox.addEventListener(WebInspector.FilterUI.Events.Filt
erChanged, this._onFilterChanged, this); | |
146 this._filterBar.addFilter(hideCollectedCheckbox); | |
147 }, | |
148 | |
149 /** | |
150 * @param {!WebInspector.PromiseDetails} details | |
151 * @param {!WebInspector.DataGridNode} node | |
152 * @return {boolean} | |
153 */ | |
154 _shouldBeVisible: function(details, node) | |
155 { | |
156 if (!this._statusFilterUI.accept(details.status)) | |
157 return false; | |
158 | |
159 if (this._hideCollectedPromisesSetting.get() && details.isGarbageCollect
ed) | |
160 return false; | |
161 | |
162 var regex = this._textFilterUI.regex(); | |
163 if (!regex) | |
164 return true; | |
165 | |
166 var text = node.dataTextForSearch(); | |
167 regex.lastIndex = 0; | |
168 return regex.test(text); | |
169 }, | |
170 | |
171 _onFilterChanged: function() | |
172 { | |
173 if (this._filterChangedTimeout) | |
174 clearTimeout(this._filterChangedTimeout); | |
175 this._filterChangedTimeout = setTimeout(onTimerFired.bind(this), 100); | |
176 | |
177 /** | |
178 * @this {WebInspector.PromisePane} | |
179 */ | |
180 function onTimerFired() | |
181 { | |
182 delete this._filterChangedTimeout; | |
183 this._refresh(); | |
184 } | |
185 }, | |
186 | |
187 /** | |
188 * @override | |
189 * @return {!Array.<!Element>} | |
190 */ | |
191 elementsToRestoreScrollPositionsFor: function() | |
192 { | |
193 return [this._dataGrid.scrollContainer]; | |
194 }, | |
195 | |
196 /** | |
197 * @override | |
198 * @param {!WebInspector.Target} target | |
199 */ | |
200 targetAdded: function(target) | |
201 { | |
202 var debuggerModel = WebInspector.DebuggerModel.fromTarget(target); | |
203 if (debuggerModel && this._enabled) | |
204 this._enablePromiseTracker(debuggerModel); | |
205 }, | |
206 | |
207 /** | |
208 * @override | |
209 * @param {!WebInspector.Target} target | |
210 */ | |
211 targetRemoved: function(target) | |
212 { | |
213 var debuggerModel = WebInspector.DebuggerModel.fromTarget(target); | |
214 if (!debuggerModel) | |
215 return; | |
216 this._promiseDetailsByDebuggerModel.delete(debuggerModel); | |
217 if (this._debuggerModel === debuggerModel) { | |
218 this._clear(); | |
219 delete this._debuggerModel; | |
220 } | |
221 }, | |
222 | |
223 /** | |
224 * @param {!WebInspector.Event} event | |
225 */ | |
226 _targetChanged: function(event) | |
227 { | |
228 if (!this._enabled) | |
229 return; | |
230 var target = /** @type {!WebInspector.Target} */ (event.data); | |
231 var debuggerModel = WebInspector.DebuggerModel.fromTarget(target); | |
232 if (!debuggerModel || this._debuggerModel === debuggerModel) | |
233 return; | |
234 this._debuggerModel = debuggerModel; | |
235 this._refresh(); | |
236 }, | |
237 | |
238 /** | |
239 * @param {!WebInspector.Event} event | |
240 */ | |
241 _mainFrameNavigated: function(event) | |
242 { | |
243 var frame = /** @type {!WebInspector.ResourceTreeFrame} */ (event.data); | |
244 var target = frame.target(); | |
245 var debuggerModel = WebInspector.DebuggerModel.fromTarget(target); | |
246 if (!debuggerModel) | |
247 return; | |
248 this._promiseDetailsByDebuggerModel.delete(debuggerModel); | |
249 if (this._debuggerModel === debuggerModel) | |
250 this._clear(); | |
251 }, | |
252 | |
253 /** @override */ | |
254 wasShown: function() | |
255 { | |
256 // Auto enable upon the very first show. | |
257 if (typeof this._enabled === "undefined") { | |
258 var target = WebInspector.context.flavor(WebInspector.Target); | |
259 this._debuggerModel = WebInspector.DebuggerModel.fromTarget(target); | |
260 this._updateRecordingState(true); | |
261 } | |
262 if (this._refreshIsNeeded) | |
263 this._refresh(); | |
264 }, | |
265 | |
266 /** | |
267 * @param {!WebInspector.DebuggerModel} debuggerModel | |
268 */ | |
269 _enablePromiseTracker: function(debuggerModel) | |
270 { | |
271 debuggerModel.enablePromiseTracker(true); | |
272 }, | |
273 | |
274 /** | |
275 * @param {!WebInspector.DebuggerModel} debuggerModel | |
276 */ | |
277 _disablePromiseTracker: function(debuggerModel) | |
278 { | |
279 debuggerModel.disablePromiseTracker(); | |
280 }, | |
281 | |
282 /** @override */ | |
283 willHide: function() | |
284 { | |
285 this._hidePopover(); | |
286 }, | |
287 | |
288 _hidePopover: function() | |
289 { | |
290 this._popoverHelper.hidePopover(); | |
291 }, | |
292 | |
293 _recordButtonClicked: function() | |
294 { | |
295 this._updateRecordingState(!this._recordButton.toggled()); | |
296 }, | |
297 | |
298 /** | |
299 * @param {boolean} enabled | |
300 */ | |
301 _updateRecordingState: function(enabled) | |
302 { | |
303 this._enabled = enabled; | |
304 this._recordButton.setToggled(this._enabled); | |
305 this._recordButton.setTitle(this._enabled ? WebInspector.UIString("Stop
Recording Promises Log") : WebInspector.UIString("Record Promises Log")); | |
306 WebInspector.DebuggerModel.instances().forEach(this._enabled ? this._ena
blePromiseTracker : this._disablePromiseTracker, this); | |
307 }, | |
308 | |
309 _clearButtonClicked: function() | |
310 { | |
311 this._clear(); | |
312 if (this._debuggerModel) | |
313 this._promiseDetailsByDebuggerModel.delete(this._debuggerModel); | |
314 }, | |
315 | |
316 _resetFilters: function() | |
317 { | |
318 this._hideCollectedPromisesSetting.set(false); | |
319 this._promiseStatusFiltersSetting.set({}); | |
320 this._textFilterUI.setValue(""); | |
321 }, | |
322 | |
323 _updateFilterStatus: function() | |
324 { | |
325 this._filterStatusTextElement.textContent = WebInspector.UIString(this._
hiddenByFilterCount === 1 ? "%d promise is hidden by filters." : "%d promises ar
e hidden by filters.", this._hiddenByFilterCount); | |
326 this._filterStatusMessageElement.classList.toggle("hidden", !this._hidde
nByFilterCount); | |
327 }, | |
328 | |
329 _garbageCollectButtonClicked: function() | |
330 { | |
331 var targets = WebInspector.targetManager.targets(); | |
332 for (var i = 0; i < targets.length; ++i) | |
333 targets[i].heapProfilerAgent().collectGarbage(); | |
334 }, | |
335 | |
336 /** | |
337 * @param {!WebInspector.DebuggerModel} debuggerModel | |
338 * @return {boolean} | |
339 */ | |
340 _truncateLogIfNeeded: function(debuggerModel) | |
341 { | |
342 var promiseIdToDetails = this._promiseDetailsByDebuggerModel.get(debugge
rModel); | |
343 if (!promiseIdToDetails || promiseIdToDetails.size <= WebInspector.Promi
sePane._maxPromiseCount) | |
344 return false; | |
345 | |
346 var elementsToTruncate = WebInspector.PromisePane._maxPromiseCount / 10; | |
347 var sortedDetails = promiseIdToDetails.valuesArray().sort(compare); | |
348 for (var i = 0; i < elementsToTruncate; ++i) | |
349 promiseIdToDetails.delete(sortedDetails[i].id); | |
350 return true; | |
351 | |
352 /** | |
353 * @param {!WebInspector.PromiseDetails} x | |
354 * @param {!WebInspector.PromiseDetails} y | |
355 * @return {number} | |
356 */ | |
357 function compare(x, y) | |
358 { | |
359 var t1 = x.creationTime || 0; | |
360 var t2 = y.creationTime || 0; | |
361 return t1 - t2 || x.id - y.id; | |
362 } | |
363 }, | |
364 | |
365 /** | |
366 * @param {!WebInspector.Event} event | |
367 */ | |
368 _onPromiseUpdated: function(event) | |
369 { | |
370 var debuggerModel = /** @type {!WebInspector.DebuggerModel} */ (event.ta
rget); | |
371 var eventType = /** @type {string} */ (event.data.eventType); | |
372 var protocolDetails = /** @type {!DebuggerAgent.PromiseDetails} */ (even
t.data.promise); | |
373 | |
374 var promiseIdToDetails = this._promiseDetailsByDebuggerModel.get(debugge
rModel); | |
375 if (!promiseIdToDetails) { | |
376 promiseIdToDetails = new Map(); | |
377 this._promiseDetailsByDebuggerModel.set(debuggerModel, promiseIdToDe
tails); | |
378 } | |
379 | |
380 var details = promiseIdToDetails.get(protocolDetails.id); | |
381 if (!details && eventType === "gc") | |
382 return; | |
383 | |
384 var truncated = this._truncateLogIfNeeded(debuggerModel); | |
385 if (details) | |
386 details.update(protocolDetails) | |
387 else | |
388 details = new WebInspector.PromiseDetails(protocolDetails); | |
389 promiseIdToDetails.set(details.id, details); | |
390 | |
391 if (eventType === "gc") | |
392 details.isGarbageCollected = true; | |
393 | |
394 if (debuggerModel === this._debuggerModel) { | |
395 if (!this.isShowing()) { | |
396 this._refreshIsNeeded = true; | |
397 return; | |
398 } | |
399 if (truncated || this._refreshIsNeeded) { | |
400 this._refresh(); | |
401 return; | |
402 } | |
403 | |
404 var node = /** @type {!WebInspector.DataGridNode} */ (this._promiseI
dToNode.get(details.id)); | |
405 var wasVisible = !node || !node._isPromiseHidden; | |
406 | |
407 // Check for the fast path on GC events. | |
408 if (eventType === "gc" && node && node.parent && !this._hideCollecte
dPromisesSetting.get()) | |
409 node.update(details); | |
410 else | |
411 this._attachDataGridNode(details); | |
412 | |
413 var isVisible = this._shouldBeVisible(details, /** @type {!WebInspec
tor.DataGridNode} */(this._promiseIdToNode.get(details.id))); | |
414 if (wasVisible !== isVisible) { | |
415 this._hiddenByFilterCount += wasVisible ? 1 : -1; | |
416 this._updateFilterStatus(); | |
417 } | |
418 } | |
419 }, | |
420 | |
421 /** | |
422 * @param {!WebInspector.PromiseDetails} details | |
423 */ | |
424 _attachDataGridNode: function(details) | |
425 { | |
426 var node = this._createDataGridNode(details); | |
427 var parentNode = this._findVisibleParentNodeDetails(details); | |
428 if (parentNode !== node.parent) | |
429 parentNode.appendChild(node); | |
430 if (this._shouldBeVisible(details, node)) | |
431 parentNode.expanded = true; | |
432 else | |
433 node.remove(); | |
434 }, | |
435 | |
436 /** | |
437 * @param {!WebInspector.PromiseDetails} details | |
438 * @return {!WebInspector.DataGridNode} | |
439 */ | |
440 _findVisibleParentNodeDetails: function(details) | |
441 { | |
442 var promiseIdToDetails = /** @type {!Map.<number, !WebInspector.PromiseD
etails>} */ (this._promiseDetailsByDebuggerModel.get(this._debuggerModel)); | |
443 var currentDetails = details; | |
444 while (currentDetails) { | |
445 var parentId = currentDetails.parentId; | |
446 if (typeof parentId !== "number") | |
447 break; | |
448 currentDetails = promiseIdToDetails.get(parentId); | |
449 if (!currentDetails) | |
450 break; | |
451 var node = this._promiseIdToNode.get(currentDetails.id); | |
452 if (node && this._shouldBeVisible(currentDetails, node)) | |
453 return node; | |
454 } | |
455 return this._dataGrid.rootNode(); | |
456 }, | |
457 | |
458 /** | |
459 * @param {!WebInspector.PromiseDetails} details | |
460 * @return {!WebInspector.DataGridNode} | |
461 */ | |
462 _createDataGridNode: function(details) | |
463 { | |
464 var node = this._promiseIdToNode.get(details.id); | |
465 if (!node) { | |
466 node = new WebInspector.PromiseDataGridNode(details, this._debuggerM
odel, this._linkifier, this._dataGrid); | |
467 this._promiseIdToNode.set(details.id, node); | |
468 } else { | |
469 node.update(details); | |
470 } | |
471 return node; | |
472 }, | |
473 | |
474 _refresh: function() | |
475 { | |
476 delete this._refreshIsNeeded; | |
477 this._clear(); | |
478 if (!this._debuggerModel) | |
479 return; | |
480 if (!this._promiseDetailsByDebuggerModel.has(this._debuggerModel)) | |
481 return; | |
482 | |
483 var rootNode = this._dataGrid.rootNode(); | |
484 var promiseIdToDetails = /** @type {!Map.<number, !WebInspector.PromiseD
etails>} */ (this._promiseDetailsByDebuggerModel.get(this._debuggerModel)); | |
485 | |
486 var nodesToInsert = { __proto__: null }; | |
487 // The for..of loop iterates in insertion order. | |
488 for (var pair of promiseIdToDetails) { | |
489 var id = /** @type {number} */ (pair[0]); | |
490 var details = /** @type {!WebInspector.PromiseDetails} */ (pair[1]); | |
491 var node = this._createDataGridNode(details); | |
492 node._isPromiseHidden = !this._shouldBeVisible(details, node); | |
493 if (node._isPromiseHidden) { | |
494 ++this._hiddenByFilterCount; | |
495 continue; | |
496 } | |
497 nodesToInsert[id] = { details: details, node: node }; | |
498 } | |
499 | |
500 for (var id in nodesToInsert) { | |
501 var node = nodesToInsert[id].node; | |
502 var details = nodesToInsert[id].details; | |
503 this._findVisibleParentNodeDetails(details).appendChild(node); | |
504 } | |
505 | |
506 for (var id in nodesToInsert) { | |
507 var node = nodesToInsert[id].node; | |
508 var details = nodesToInsert[id].details; | |
509 node.expanded = true; | |
510 } | |
511 | |
512 this._updateFilterStatus(); | |
513 }, | |
514 | |
515 _clear: function() | |
516 { | |
517 this._hiddenByFilterCount = 0; | |
518 this._updateFilterStatus(); | |
519 this._promiseIdToNode.clear(); | |
520 this._hidePopover(); | |
521 this._dataGrid.rootNode().removeChildren(); | |
522 this._linkifier.reset(); | |
523 }, | |
524 | |
525 /** | |
526 * @param {!WebInspector.ContextMenu} contextMenu | |
527 * @param {!WebInspector.DataGridNode} node | |
528 */ | |
529 _onContextMenu: function(contextMenu, node) | |
530 { | |
531 var debuggerModel = this._debuggerModel; | |
532 if (!debuggerModel) | |
533 return; | |
534 | |
535 var promiseId = node.promiseId(); | |
536 if (this._promiseDetailsByDebuggerModel.has(debuggerModel)) { | |
537 var details = this._promiseDetailsByDebuggerModel.get(debuggerModel)
.get(promiseId); | |
538 if (details.isGarbageCollected) | |
539 return; | |
540 } | |
541 | |
542 contextMenu.appendItem(WebInspector.UIString.capitalize("Show in ^consol
e"), showPromiseInConsole); | |
543 contextMenu.show(); | |
544 | |
545 function showPromiseInConsole() | |
546 { | |
547 debuggerModel.getPromiseById(promiseId, "console", didGetPromiseById
); | |
548 } | |
549 | |
550 /** | |
551 * @param {?RuntimeAgent.RemoteObject} promise | |
552 */ | |
553 function didGetPromiseById(promise) | |
554 { | |
555 if (!promise) | |
556 return; | |
557 var object = debuggerModel.target().runtimeModel.createRemoteObject(
promise); | |
558 object.callFunction(dumpIntoConsole); | |
559 object.release(); | |
560 /** | |
561 * @suppressReceiverCheck | |
562 * @this {Object} | |
563 */ | |
564 function dumpIntoConsole() | |
565 { | |
566 console.log(this); | |
567 } | |
568 WebInspector.console.show(); | |
569 } | |
570 }, | |
571 | |
572 /** | |
573 * @param {!Element} element | |
574 * @param {!Event} event | |
575 * @return {!Element|!AnchorBox|undefined} | |
576 */ | |
577 _getPopoverAnchor: function(element, event) | |
578 { | |
579 if (!this._debuggerModel || !this._promiseDetailsByDebuggerModel.has(thi
s._debuggerModel)) | |
580 return undefined; | |
581 var node = this._dataGrid.dataGridNodeFromNode(element); | |
582 if (!node) | |
583 return undefined; | |
584 var details = this._promiseDetailsByDebuggerModel.get(this._debuggerMode
l).get(node.promiseId()); | |
585 if (!details) | |
586 return undefined; | |
587 var anchor = element.enclosingNodeOrSelfWithClass("created-column"); | |
588 if (anchor) | |
589 return details.creationStack ? anchor : undefined; | |
590 anchor = element.enclosingNodeOrSelfWithClass("settled-column"); | |
591 return (anchor && details.settlementStack) ? anchor : undefined; | |
592 }, | |
593 | |
594 /** | |
595 * @param {!Element} anchor | |
596 * @param {!WebInspector.Popover} popover | |
597 */ | |
598 _showPopover: function(anchor, popover) | |
599 { | |
600 var node = this._dataGrid.dataGridNodeFromNode(anchor); | |
601 var details = this._promiseDetailsByDebuggerModel.get(this._debuggerMode
l).get(node.promiseId()); | |
602 | |
603 var stackTrace; | |
604 if (anchor.classList.contains("created-column")) | |
605 stackTrace = details.creationStack; | |
606 else | |
607 stackTrace = details.settlementStack; | |
608 | |
609 var content = WebInspector.DOMPresentationUtils.buildStackTracePreviewCo
ntents(this._debuggerModel.target(), this._linkifier, stackTrace); | |
610 popover.setCanShrink(true); | |
611 popover.showForAnchor(content, anchor); | |
612 }, | |
613 | |
614 __proto__: WebInspector.VBox.prototype | |
615 } | |
616 | |
617 /** | |
618 * @constructor | |
619 * @extends {WebInspector.ViewportDataGridNode} | |
620 * @param {!WebInspector.PromiseDetails} details | |
621 * @param {!WebInspector.DebuggerModel} debuggerModel | |
622 * @param {!WebInspector.Linkifier} linkifier | |
623 * @param {!WebInspector.ViewportDataGrid} dataGrid | |
624 */ | |
625 WebInspector.PromiseDataGridNode = function(details, debuggerModel, linkifier, d
ataGrid) | |
626 { | |
627 WebInspector.ViewportDataGridNode.call(this, {}); | |
628 this._details = details; | |
629 this._debuggerModel = debuggerModel; | |
630 this._linkifier = linkifier; | |
631 /** @type {!Array.<!Element>} */ | |
632 this._linkifiedAnchors = []; | |
633 this.dataGrid = dataGrid; | |
634 } | |
635 | |
636 WebInspector.PromiseDataGridNode.prototype = { | |
637 _disposeAnchors: function() | |
638 { | |
639 for (var i = 0; i < this._linkifiedAnchors.length; ++i) | |
640 this._linkifier.disposeAnchor(this._debuggerModel.target(), this._li
nkifiedAnchors[i]); | |
641 this._linkifiedAnchors = []; | |
642 }, | |
643 | |
644 /** | |
645 * @param {!WebInspector.PromiseDetails} details | |
646 */ | |
647 update: function(details) | |
648 { | |
649 this._disposeAnchors(); | |
650 this._details = details; | |
651 this.refresh(); | |
652 }, | |
653 | |
654 /** | |
655 * @override | |
656 */ | |
657 wasDetached: function() | |
658 { | |
659 this._disposeAnchors(); | |
660 }, | |
661 | |
662 /** | |
663 * @override | |
664 * @return {number} | |
665 */ | |
666 nodeSelfHeight: function() | |
667 { | |
668 return 24; | |
669 }, | |
670 | |
671 /** | |
672 * @return {number} | |
673 */ | |
674 promiseId: function() | |
675 { | |
676 return this._details.id; | |
677 }, | |
678 | |
679 /** | |
680 * @override | |
681 */ | |
682 createCells: function() | |
683 { | |
684 this._element.classList.toggle("promise-gc", !!this._details.isGarbageCo
llected); | |
685 WebInspector.ViewportDataGridNode.prototype.createCells.call(this); | |
686 }, | |
687 | |
688 /** | |
689 * @param {!Element} cell | |
690 * @param {?RuntimeAgent.CallFrame=} callFrame | |
691 */ | |
692 _appendCallFrameAnchor: function(cell, callFrame) | |
693 { | |
694 if (!callFrame) | |
695 return; | |
696 var anchor = this._linkifier.linkifyConsoleCallFrame(this._debuggerModel
.target(), callFrame); | |
697 this._linkifiedAnchors.push(anchor); | |
698 cell.appendChild(anchor); | |
699 }, | |
700 | |
701 /** | |
702 * @override | |
703 * @param {string} columnIdentifier | |
704 * @return {!Element} | |
705 */ | |
706 createCell: function(columnIdentifier) | |
707 { | |
708 var cell = this.createTD(columnIdentifier); | |
709 var details = this._details; | |
710 | |
711 switch (columnIdentifier) { | |
712 case "status": | |
713 var title = ""; | |
714 switch (details.status) { | |
715 case "pending": | |
716 title = WebInspector.UIString("Pending"); | |
717 break; | |
718 case "resolved": | |
719 title = WebInspector.UIString("Fulfilled"); | |
720 break; | |
721 case "rejected": | |
722 title = WebInspector.UIString("Rejected"); | |
723 break; | |
724 } | |
725 if (details.isGarbageCollected) | |
726 title += " " + WebInspector.UIString("(garbage collected)"); | |
727 cell.createChild("div", "status " + details.status).title = title; | |
728 break; | |
729 | |
730 case "function": | |
731 cell.createTextChild(WebInspector.beautifyFunctionName(details.callF
rame ? details.callFrame.functionName : "")); | |
732 break; | |
733 | |
734 case "created": | |
735 this._appendCallFrameAnchor(cell, details.callFrame); | |
736 break; | |
737 | |
738 case "settled": | |
739 this._appendCallFrameAnchor(cell, details.settlementStack && details
.settlementStack.callFrames.length ? details.settlementStack.callFrames[0] : nul
l); | |
740 break; | |
741 | |
742 case "tts": | |
743 cell.createTextChild(this._ttsCellText()); | |
744 break; | |
745 } | |
746 | |
747 return cell; | |
748 }, | |
749 | |
750 /** | |
751 * @return {string} | |
752 */ | |
753 _ttsCellText: function() | |
754 { | |
755 var details = this._details; | |
756 if (details.creationTime && details.settlementTime && details.settlement
Time >= details.creationTime) | |
757 return Number.millisToString(details.settlementTime - details.creati
onTime); | |
758 return ""; | |
759 }, | |
760 | |
761 /** | |
762 * @param {?RuntimeAgent.CallFrame=} callFrame | |
763 * @return {string} | |
764 */ | |
765 _callFrameAnchorTextForSearch: function(callFrame) | |
766 { | |
767 if (!callFrame) | |
768 return ""; | |
769 var script = callFrame.scriptId && this._debuggerModel ? this._debuggerM
odel.scriptForId(callFrame.scriptId) : null; | |
770 var sourceURL = script ? script.sourceURL : callFrame.url; | |
771 var lineNumber = callFrame.lineNumber || 0; | |
772 return WebInspector.displayNameForURL(sourceURL) + ":" + lineNumber; | |
773 }, | |
774 | |
775 /** | |
776 * @return {string} | |
777 */ | |
778 dataTextForSearch: function() | |
779 { | |
780 var details = this._details; | |
781 var texts = [ | |
782 WebInspector.beautifyFunctionName(details.callFrame ? details.callFr
ame.functionName : ""), | |
783 this._callFrameAnchorTextForSearch(details.callFrame), | |
784 this._callFrameAnchorTextForSearch(details.settlementStack && detail
s.settlementStack.callFrames.length ? details.settlementStack.callFrames[0] : nu
ll), | |
785 this._ttsCellText().replace(/\u2009/g, " ") // \u2009 is a thin spac
e. | |
786 ]; | |
787 return texts.join(" "); | |
788 }, | |
789 | |
790 __proto__: WebInspector.ViewportDataGridNode.prototype | |
791 } | |
OLD | NEW |