| 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.TimelineModel} | |
| 8 * @implements {WebInspector.TargetManager.Observer} | |
| 9 */ | |
| 10 WebInspector.TimelineModelImpl = function() | |
| 11 { | |
| 12 WebInspector.TimelineModel.call(this); | |
| 13 /** @type {?WebInspector.Target} */ | |
| 14 this._currentTarget = null; | |
| 15 this._filters = []; | |
| 16 this._bindings = new WebInspector.TimelineModelImpl.InterRecordBindings(); | |
| 17 | |
| 18 this.reset(); | |
| 19 | |
| 20 WebInspector.targetManager.addModelListener(WebInspector.TimelineManager, We
bInspector.TimelineManager.EventTypes.TimelineEventRecorded, this._onRecordAdded
, this); | |
| 21 WebInspector.targetManager.addModelListener(WebInspector.TimelineManager, We
bInspector.TimelineManager.EventTypes.TimelineStarted, this._onStarted, this); | |
| 22 WebInspector.targetManager.addModelListener(WebInspector.TimelineManager, We
bInspector.TimelineManager.EventTypes.TimelineStopped, this._onStopped, this); | |
| 23 WebInspector.targetManager.addModelListener(WebInspector.TimelineManager, We
bInspector.TimelineManager.EventTypes.TimelineProgress, this._onProgress, this); | |
| 24 WebInspector.targetManager.observeTargets(this); | |
| 25 } | |
| 26 | |
| 27 WebInspector.TimelineModelImpl.TransferChunkLengthBytes = 5000000; | |
| 28 | |
| 29 WebInspector.TimelineModelImpl.prototype = { | |
| 30 /** | |
| 31 * @param {!WebInspector.Target} target | |
| 32 */ | |
| 33 targetAdded: function(target) { }, | |
| 34 | |
| 35 /** | |
| 36 * @param {!WebInspector.Target} target | |
| 37 */ | |
| 38 targetRemoved: function(target) | |
| 39 { | |
| 40 if (this._currentTarget === target) | |
| 41 this._currentTarget = null; | |
| 42 }, | |
| 43 | |
| 44 /** | |
| 45 * @param {boolean} captureCauses | |
| 46 * @param {boolean} captureMemory | |
| 47 * @param {boolean} capturePictures | |
| 48 */ | |
| 49 startRecording: function(captureCauses, captureMemory, capturePictures) | |
| 50 { | |
| 51 console.assert(!capturePictures, "Legacy timeline does not support captu
ring pictures"); | |
| 52 this.reset(); | |
| 53 this._currentTarget = WebInspector.context.flavor(WebInspector.Target); | |
| 54 console.assert(this._currentTarget); | |
| 55 | |
| 56 this._clientInitiatedRecording = true; | |
| 57 var maxStackFrames = captureCauses ? 30 : 0; | |
| 58 var includeGPUEvents = Runtime.experiments.isEnabled("gpuTimeline"); | |
| 59 var liveEvents = [ WebInspector.TimelineModel.RecordType.BeginFrame, | |
| 60 WebInspector.TimelineModel.RecordType.DrawFrame, | |
| 61 WebInspector.TimelineModel.RecordType.RequestMainThre
adFrame, | |
| 62 WebInspector.TimelineModel.RecordType.ActivateLayerTr
ee ]; | |
| 63 this._currentTarget.timelineManager.start(maxStackFrames, liveEvents.joi
n(","), captureMemory, includeGPUEvents, this._fireRecordingStarted.bind(this)); | |
| 64 }, | |
| 65 | |
| 66 stopRecording: function() | |
| 67 { | |
| 68 if (!this._currentTarget) | |
| 69 return; | |
| 70 | |
| 71 if (!this._clientInitiatedRecording) { | |
| 72 this._currentTarget.timelineManager.start(undefined, undefined, unde
fined, undefined, stopTimeline.bind(this)); | |
| 73 return; | |
| 74 } | |
| 75 | |
| 76 /** | |
| 77 * Console started this one and we are just sniffing it. Initiate record
ing so that we | |
| 78 * could stop it. | |
| 79 * @this {WebInspector.TimelineModelImpl} | |
| 80 */ | |
| 81 function stopTimeline() | |
| 82 { | |
| 83 this._currentTarget.timelineManager.stop(this._fireRecordingStopped.
bind(this)); | |
| 84 } | |
| 85 | |
| 86 this._clientInitiatedRecording = false; | |
| 87 this._currentTarget.timelineManager.stop(this._fireRecordingStopped.bind
(this)); | |
| 88 }, | |
| 89 | |
| 90 /** | |
| 91 * @return {!Array.<!WebInspector.TimelineModel.Record>} | |
| 92 */ | |
| 93 records: function() | |
| 94 { | |
| 95 return this._records; | |
| 96 }, | |
| 97 | |
| 98 /** | |
| 99 * @param {!WebInspector.Event} event | |
| 100 */ | |
| 101 _onRecordAdded: function(event) | |
| 102 { | |
| 103 var timelineManager = /** @type {!WebInspector.TimelineManager} */ (even
t.target); | |
| 104 if (this._collectionEnabled && timelineManager.target() === this._curren
tTarget) | |
| 105 this._addRecord(/** @type {!TimelineAgent.TimelineEvent} */(event.da
ta)); | |
| 106 }, | |
| 107 | |
| 108 /** | |
| 109 * @param {!WebInspector.Event} event | |
| 110 */ | |
| 111 _onStarted: function(event) | |
| 112 { | |
| 113 if (!event.data || this._collectionEnabled) | |
| 114 return; | |
| 115 // Started from console. | |
| 116 var timelineManager = /** @type {!WebInspector.TimelineManager} */ (even
t.target); | |
| 117 if (this._currentTarget !== timelineManager.target()) { | |
| 118 this.reset(); | |
| 119 this._currentTarget = timelineManager.target(); | |
| 120 } | |
| 121 this._fireRecordingStarted(); | |
| 122 }, | |
| 123 | |
| 124 /** | |
| 125 * @param {!WebInspector.Event} event | |
| 126 */ | |
| 127 _onStopped: function(event) | |
| 128 { | |
| 129 var timelineManager = /** @type {!WebInspector.TimelineManager} */ (even
t.target); | |
| 130 if (timelineManager.target() !== this._currentTarget) | |
| 131 return; | |
| 132 // We were buffering events, discard those that got through, the real on
es are coming! | |
| 133 this.reset(); | |
| 134 this._currentTarget = timelineManager.target(); | |
| 135 | |
| 136 var events = /** @type {!Array.<!TimelineAgent.TimelineEvent>} */ (event
.data.events); | |
| 137 for (var i = 0; i < events.length; ++i) | |
| 138 this._addRecord(events[i]); | |
| 139 | |
| 140 if (event.data.consoleTimeline) { | |
| 141 // Stopped from console. | |
| 142 this._fireRecordingStopped(null); | |
| 143 } | |
| 144 | |
| 145 this._collectionEnabled = false; | |
| 146 }, | |
| 147 | |
| 148 /** | |
| 149 * @param {!WebInspector.Event} event | |
| 150 */ | |
| 151 _onProgress: function(event) | |
| 152 { | |
| 153 var timelineManager = /** @type {!WebInspector.TimelineManager} */ (even
t.target); | |
| 154 if (timelineManager.target() === this._currentTarget) | |
| 155 this.dispatchEventToListeners(WebInspector.TimelineModel.Events.Reco
rdingProgress, event.data); | |
| 156 }, | |
| 157 | |
| 158 _fireRecordingStarted: function() | |
| 159 { | |
| 160 this._collectionEnabled = true; | |
| 161 this.dispatchEventToListeners(WebInspector.TimelineModel.Events.Recordin
gStarted); | |
| 162 }, | |
| 163 | |
| 164 /** | |
| 165 * @param {?Protocol.Error} error | |
| 166 */ | |
| 167 _fireRecordingStopped: function(error) | |
| 168 { | |
| 169 this.dispatchEventToListeners(WebInspector.TimelineModel.Events.Recordin
gStopped); | |
| 170 }, | |
| 171 | |
| 172 /** | |
| 173 * @param {!TimelineAgent.TimelineEvent} payload | |
| 174 */ | |
| 175 _addRecord: function(payload) | |
| 176 { | |
| 177 this._internStrings(payload); | |
| 178 this._payloads.push(payload); | |
| 179 | |
| 180 var record = this._innerAddRecord(payload, null); | |
| 181 this._updateBoundaries(record); | |
| 182 this._records.push(record); | |
| 183 if (record.type() === WebInspector.TimelineModel.RecordType.Program) | |
| 184 this._mainThreadTasks.push(record); | |
| 185 if (record.type() === WebInspector.TimelineModel.RecordType.GPUTask) | |
| 186 this._gpuThreadTasks.push(record); | |
| 187 | |
| 188 this.dispatchEventToListeners(WebInspector.TimelineModel.Events.RecordAd
ded, record); | |
| 189 }, | |
| 190 | |
| 191 /** | |
| 192 * @param {!TimelineAgent.TimelineEvent} payload | |
| 193 * @param {?WebInspector.TimelineModel.Record} parentRecord | |
| 194 * @return {!WebInspector.TimelineModel.Record} | |
| 195 */ | |
| 196 _innerAddRecord: function(payload, parentRecord) | |
| 197 { | |
| 198 var record = new WebInspector.TimelineModel.RecordImpl(this, payload, pa
rentRecord); | |
| 199 if (WebInspector.TimelineUIUtilsImpl.isEventDivider(record)) | |
| 200 this._eventDividerRecords.push(record); | |
| 201 | |
| 202 for (var i = 0; payload.children && i < payload.children.length; ++i) | |
| 203 this._innerAddRecord.call(this, payload.children[i], record); | |
| 204 | |
| 205 if (parentRecord) | |
| 206 parentRecord._selfTime -= record.endTime() - record.startTime(); | |
| 207 return record; | |
| 208 }, | |
| 209 | |
| 210 /** | |
| 211 * @param {!WebInspector.ChunkedFileReader} fileReader | |
| 212 * @param {!WebInspector.Progress} progress | |
| 213 * @return {!WebInspector.OutputStream} | |
| 214 */ | |
| 215 createLoader: function(fileReader, progress) | |
| 216 { | |
| 217 return new WebInspector.TimelineModelLoader(this, fileReader, progress); | |
| 218 }, | |
| 219 | |
| 220 /** | |
| 221 * @param {!WebInspector.OutputStream} stream | |
| 222 */ | |
| 223 writeToStream: function(stream) | |
| 224 { | |
| 225 var saver = new WebInspector.TimelineSaver(stream); | |
| 226 saver.save(this._payloads, window.navigator.appVersion); | |
| 227 }, | |
| 228 | |
| 229 reset: function() | |
| 230 { | |
| 231 if (!this._collectionEnabled) | |
| 232 this._currentTarget = null; | |
| 233 this._payloads = []; | |
| 234 this._stringPool = {}; | |
| 235 this._bindings._reset(); | |
| 236 this._minimumRecordTime = 0; | |
| 237 this._maximumRecordTime = 0; | |
| 238 WebInspector.TimelineModel.prototype.reset.call(this); | |
| 239 }, | |
| 240 | |
| 241 /** | |
| 242 * @return {number} | |
| 243 */ | |
| 244 minimumRecordTime: function() | |
| 245 { | |
| 246 return this._minimumRecordTime; | |
| 247 }, | |
| 248 | |
| 249 /** | |
| 250 * @return {number} | |
| 251 */ | |
| 252 maximumRecordTime: function() | |
| 253 { | |
| 254 return this._maximumRecordTime; | |
| 255 }, | |
| 256 | |
| 257 /** | |
| 258 * @param {!WebInspector.TimelineModel.Record} record | |
| 259 */ | |
| 260 _updateBoundaries: function(record) | |
| 261 { | |
| 262 var startTime = record.startTime(); | |
| 263 var endTime = record.endTime(); | |
| 264 | |
| 265 if (!this._minimumRecordTime || startTime < this._minimumRecordTime) | |
| 266 this._minimumRecordTime = startTime; | |
| 267 if (endTime > this._maximumRecordTime) | |
| 268 this._maximumRecordTime = endTime; | |
| 269 }, | |
| 270 | |
| 271 /** | |
| 272 * @param {!TimelineAgent.TimelineEvent} record | |
| 273 */ | |
| 274 _internStrings: function(record) | |
| 275 { | |
| 276 for (var name in record) { | |
| 277 var value = record[name]; | |
| 278 if (typeof value !== "string") | |
| 279 continue; | |
| 280 | |
| 281 var interned = this._stringPool[value]; | |
| 282 if (typeof interned === "string") | |
| 283 record[name] = interned; | |
| 284 else | |
| 285 this._stringPool[value] = value; | |
| 286 } | |
| 287 | |
| 288 var children = record.children; | |
| 289 for (var i = 0; children && i < children.length; ++i) | |
| 290 this._internStrings(children[i]); | |
| 291 }, | |
| 292 | |
| 293 __proto__: WebInspector.TimelineModel.prototype | |
| 294 } | |
| 295 | |
| 296 | |
| 297 /** | |
| 298 * @constructor | |
| 299 */ | |
| 300 WebInspector.TimelineModelImpl.InterRecordBindings = function() { | |
| 301 this._reset(); | |
| 302 } | |
| 303 | |
| 304 WebInspector.TimelineModelImpl.InterRecordBindings.prototype = { | |
| 305 _reset: function() | |
| 306 { | |
| 307 this._sendRequestRecords = {}; | |
| 308 this._timerRecords = {}; | |
| 309 this._requestAnimationFrameRecords = {}; | |
| 310 this._layoutInvalidate = {}; | |
| 311 this._lastScheduleStyleRecalculation = {}; | |
| 312 this._webSocketCreateRecords = {}; | |
| 313 } | |
| 314 } | |
| 315 | |
| 316 /** | |
| 317 * @constructor | |
| 318 * @implements {WebInspector.TimelineModel.Record} | |
| 319 * @param {!WebInspector.TimelineModel} model | |
| 320 * @param {!TimelineAgent.TimelineEvent} timelineEvent | |
| 321 * @param {?WebInspector.TimelineModel.Record} parentRecord | |
| 322 */ | |
| 323 WebInspector.TimelineModel.RecordImpl = function(model, timelineEvent, parentRec
ord) | |
| 324 { | |
| 325 this._model = model; | |
| 326 var bindings = this._model._bindings; | |
| 327 this._record = timelineEvent; | |
| 328 this._thread = this._record.thread || WebInspector.TimelineModel.MainThreadN
ame; | |
| 329 this._children = []; | |
| 330 if (parentRecord) { | |
| 331 this.parent = parentRecord; | |
| 332 parentRecord.children().push(this); | |
| 333 } | |
| 334 | |
| 335 this._selfTime = this.endTime() - this.startTime(); | |
| 336 | |
| 337 var recordTypes = WebInspector.TimelineModel.RecordType; | |
| 338 switch (timelineEvent.type) { | |
| 339 case recordTypes.ResourceSendRequest: | |
| 340 // Make resource receive record last since request was sent; make finish
record last since response received. | |
| 341 bindings._sendRequestRecords[timelineEvent.data["requestId"]] = this; | |
| 342 break; | |
| 343 | |
| 344 case recordTypes.ResourceReceiveResponse: | |
| 345 case recordTypes.ResourceReceivedData: | |
| 346 case recordTypes.ResourceFinish: | |
| 347 this._initiator = bindings._sendRequestRecords[timelineEvent.data["reque
stId"]]; | |
| 348 break; | |
| 349 | |
| 350 case recordTypes.TimerInstall: | |
| 351 bindings._timerRecords[timelineEvent.data["timerId"]] = this; | |
| 352 break; | |
| 353 | |
| 354 case recordTypes.TimerFire: | |
| 355 this._initiator = bindings._timerRecords[timelineEvent.data["timerId"]]; | |
| 356 break; | |
| 357 | |
| 358 case recordTypes.RequestAnimationFrame: | |
| 359 bindings._requestAnimationFrameRecords[timelineEvent.data["id"]] = this; | |
| 360 break; | |
| 361 | |
| 362 case recordTypes.FireAnimationFrame: | |
| 363 this._initiator = bindings._requestAnimationFrameRecords[timelineEvent.d
ata["id"]]; | |
| 364 break; | |
| 365 | |
| 366 case recordTypes.ScheduleStyleRecalculation: | |
| 367 bindings._lastScheduleStyleRecalculation[this.frameId()] = this; | |
| 368 break; | |
| 369 | |
| 370 case recordTypes.RecalculateStyles: | |
| 371 this._initiator = bindings._lastScheduleStyleRecalculation[this.frameId(
)]; | |
| 372 break; | |
| 373 | |
| 374 case recordTypes.InvalidateLayout: | |
| 375 // Consider style recalculation as a reason for layout invalidation, | |
| 376 // but only if we had no earlier layout invalidation records. | |
| 377 var layoutInitator = this; | |
| 378 if (!bindings._layoutInvalidate[this.frameId()] && parentRecord.type() =
== recordTypes.RecalculateStyles) | |
| 379 layoutInitator = parentRecord._initiator; | |
| 380 bindings._layoutInvalidate[this.frameId()] = layoutInitator; | |
| 381 break; | |
| 382 | |
| 383 case recordTypes.Layout: | |
| 384 this._initiator = bindings._layoutInvalidate[this.frameId()]; | |
| 385 bindings._layoutInvalidate[this.frameId()] = null; | |
| 386 if (this.stackTrace()) | |
| 387 this.addWarning(WebInspector.UIString("Forced synchronous layout is
a possible performance bottleneck.")); | |
| 388 break; | |
| 389 | |
| 390 case recordTypes.WebSocketCreate: | |
| 391 bindings._webSocketCreateRecords[timelineEvent.data["identifier"]] = thi
s; | |
| 392 break; | |
| 393 | |
| 394 case recordTypes.WebSocketSendHandshakeRequest: | |
| 395 case recordTypes.WebSocketReceiveHandshakeResponse: | |
| 396 case recordTypes.WebSocketDestroy: | |
| 397 this._initiator = bindings._webSocketCreateRecords[timelineEvent.data["i
dentifier"]]; | |
| 398 break; | |
| 399 } | |
| 400 } | |
| 401 | |
| 402 WebInspector.TimelineModel.RecordImpl.prototype = { | |
| 403 /** | |
| 404 * @return {?Array.<!ConsoleAgent.CallFrame>} | |
| 405 */ | |
| 406 callSiteStackTrace: function() | |
| 407 { | |
| 408 return this._initiator ? this._initiator.stackTrace() : null; | |
| 409 }, | |
| 410 | |
| 411 /** | |
| 412 * @return {?WebInspector.TimelineModel.Record} | |
| 413 */ | |
| 414 initiator: function() | |
| 415 { | |
| 416 return this._initiator; | |
| 417 }, | |
| 418 | |
| 419 /** | |
| 420 * @return {?WebInspector.Target} | |
| 421 */ | |
| 422 target: function() | |
| 423 { | |
| 424 return this._model._currentTarget; | |
| 425 }, | |
| 426 | |
| 427 /** | |
| 428 * @return {number} | |
| 429 */ | |
| 430 selfTime: function() | |
| 431 { | |
| 432 return this._selfTime; | |
| 433 }, | |
| 434 | |
| 435 /** | |
| 436 * @return {!Array.<!WebInspector.TimelineModel.Record>} | |
| 437 */ | |
| 438 children: function() | |
| 439 { | |
| 440 return this._children; | |
| 441 }, | |
| 442 | |
| 443 /** | |
| 444 * @return {number} | |
| 445 */ | |
| 446 startTime: function() | |
| 447 { | |
| 448 return this._record.startTime; | |
| 449 }, | |
| 450 | |
| 451 /** | |
| 452 * @return {string} | |
| 453 */ | |
| 454 thread: function() | |
| 455 { | |
| 456 return this._thread; | |
| 457 }, | |
| 458 | |
| 459 /** | |
| 460 * @return {number} | |
| 461 */ | |
| 462 endTime: function() | |
| 463 { | |
| 464 return this._endTime || this._record.endTime || this._record.startTime; | |
| 465 }, | |
| 466 | |
| 467 /** | |
| 468 * @param {number} endTime | |
| 469 */ | |
| 470 setEndTime: function(endTime) | |
| 471 { | |
| 472 this._endTime = endTime; | |
| 473 }, | |
| 474 | |
| 475 /** | |
| 476 * @return {!Object} | |
| 477 */ | |
| 478 data: function() | |
| 479 { | |
| 480 return this._record.data; | |
| 481 }, | |
| 482 | |
| 483 /** | |
| 484 * @return {string} | |
| 485 */ | |
| 486 type: function() | |
| 487 { | |
| 488 return this._record.type; | |
| 489 }, | |
| 490 | |
| 491 /** | |
| 492 * @return {string} | |
| 493 */ | |
| 494 frameId: function() | |
| 495 { | |
| 496 return this._record.frameId || ""; | |
| 497 }, | |
| 498 | |
| 499 /** | |
| 500 * @return {?Array.<!ConsoleAgent.CallFrame>} | |
| 501 */ | |
| 502 stackTrace: function() | |
| 503 { | |
| 504 if (this._record.stackTrace && this._record.stackTrace.length) | |
| 505 return this._record.stackTrace; | |
| 506 return null; | |
| 507 }, | |
| 508 | |
| 509 /** | |
| 510 * @param {string} key | |
| 511 * @return {?Object} | |
| 512 */ | |
| 513 getUserObject: function(key) | |
| 514 { | |
| 515 if (!this._userObjects) | |
| 516 return null; | |
| 517 return this._userObjects.get(key); | |
| 518 }, | |
| 519 | |
| 520 /** | |
| 521 * @param {string} key | |
| 522 * @param {?Object|undefined} value | |
| 523 */ | |
| 524 setUserObject: function(key, value) | |
| 525 { | |
| 526 if (!this._userObjects) | |
| 527 this._userObjects = new Map(); | |
| 528 this._userObjects.set(key, value); | |
| 529 }, | |
| 530 | |
| 531 /** | |
| 532 * @param {string} message | |
| 533 */ | |
| 534 addWarning: function(message) | |
| 535 { | |
| 536 if (!this._warnings) | |
| 537 this._warnings = []; | |
| 538 this._warnings.push(message); | |
| 539 }, | |
| 540 | |
| 541 /** | |
| 542 * @return {?Array.<string>} | |
| 543 */ | |
| 544 warnings: function() | |
| 545 { | |
| 546 return this._warnings; | |
| 547 } | |
| 548 } | |
| 549 | |
| 550 /** | |
| 551 * @constructor | |
| 552 * @implements {WebInspector.OutputStream} | |
| 553 * @param {!WebInspector.TimelineModel} model | |
| 554 * @param {!{cancel: function()}} reader | |
| 555 * @param {!WebInspector.Progress} progress | |
| 556 */ | |
| 557 WebInspector.TimelineModelLoader = function(model, reader, progress) | |
| 558 { | |
| 559 this._model = model; | |
| 560 this._reader = reader; | |
| 561 this._progress = progress; | |
| 562 this._buffer = ""; | |
| 563 this._firstChunk = true; | |
| 564 } | |
| 565 | |
| 566 WebInspector.TimelineModelLoader.prototype = { | |
| 567 /** | |
| 568 * @param {string} chunk | |
| 569 */ | |
| 570 write: function(chunk) | |
| 571 { | |
| 572 var data = this._buffer + chunk; | |
| 573 var lastIndex = 0; | |
| 574 var index; | |
| 575 do { | |
| 576 index = lastIndex; | |
| 577 lastIndex = WebInspector.TextUtils.findBalancedCurlyBrackets(data, i
ndex); | |
| 578 } while (lastIndex !== -1) | |
| 579 | |
| 580 var json = data.slice(0, index) + "]"; | |
| 581 this._buffer = data.slice(index); | |
| 582 | |
| 583 if (!index) | |
| 584 return; | |
| 585 | |
| 586 if (this._firstChunk) { | |
| 587 this._firstChunk = false; | |
| 588 this._model.reset(); | |
| 589 } else { | |
| 590 // Prepending "0" to turn string into valid JSON. | |
| 591 json = "[0" + json; | |
| 592 } | |
| 593 | |
| 594 var items; | |
| 595 try { | |
| 596 items = /** @type {!Array.<!TimelineAgent.TimelineEvent>} */ (JSON.p
arse(json)); | |
| 597 } catch (e) { | |
| 598 WebInspector.console.error("Malformed timeline data."); | |
| 599 this._model.reset(); | |
| 600 this._reader.cancel(); | |
| 601 this._progress.done(); | |
| 602 return; | |
| 603 } | |
| 604 | |
| 605 // Skip 0-th element - it is either version or 0. | |
| 606 for (var i = 1, size = items.length; i < size; ++i) | |
| 607 this._model._addRecord(items[i]); | |
| 608 }, | |
| 609 | |
| 610 close: function() | |
| 611 { | |
| 612 } | |
| 613 } | |
| 614 | |
| 615 /** | |
| 616 * @constructor | |
| 617 * @param {!WebInspector.OutputStream} stream | |
| 618 */ | |
| 619 WebInspector.TimelineSaver = function(stream) | |
| 620 { | |
| 621 this._stream = stream; | |
| 622 } | |
| 623 | |
| 624 WebInspector.TimelineSaver.prototype = { | |
| 625 /** | |
| 626 * @param {!Array.<*>} payloads | |
| 627 * @param {string} version | |
| 628 */ | |
| 629 save: function(payloads, version) | |
| 630 { | |
| 631 this._payloads = payloads; | |
| 632 this._recordIndex = 0; | |
| 633 this._prologue = "[" + JSON.stringify(version); | |
| 634 | |
| 635 this._writeNextChunk(this._stream); | |
| 636 }, | |
| 637 | |
| 638 _writeNextChunk: function(stream) | |
| 639 { | |
| 640 const separator = ",\n"; | |
| 641 var data = []; | |
| 642 var length = 0; | |
| 643 | |
| 644 if (this._prologue) { | |
| 645 data.push(this._prologue); | |
| 646 length += this._prologue.length; | |
| 647 delete this._prologue; | |
| 648 } else { | |
| 649 if (this._recordIndex === this._payloads.length) { | |
| 650 stream.close(); | |
| 651 return; | |
| 652 } | |
| 653 data.push(""); | |
| 654 } | |
| 655 while (this._recordIndex < this._payloads.length) { | |
| 656 var item = JSON.stringify(this._payloads[this._recordIndex]); | |
| 657 var itemLength = item.length + separator.length; | |
| 658 if (length + itemLength > WebInspector.TimelineModelImpl.TransferChu
nkLengthBytes) | |
| 659 break; | |
| 660 length += itemLength; | |
| 661 data.push(item); | |
| 662 ++this._recordIndex; | |
| 663 } | |
| 664 if (this._recordIndex === this._payloads.length) | |
| 665 data.push(data.pop() + "]"); | |
| 666 stream.write(data.join(separator), this._writeNextChunk.bind(this)); | |
| 667 } | |
| 668 } | |
| OLD | NEW |