| 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 |