Blink Reformat | 4c46d09 | 2018-04-07 15:32:37 | [diff] [blame^] | 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 | Timeline.TimelinePaintProfilerView = class extends UI.SplitWidget { |
| 6 | /** |
| 7 | * @param {!TimelineModel.TimelineFrameModel} frameModel |
| 8 | */ |
| 9 | constructor(frameModel) { |
| 10 | super(false, false); |
| 11 | this.element.classList.add('timeline-paint-profiler-view'); |
| 12 | this.setSidebarSize(60); |
| 13 | this.setResizable(false); |
| 14 | |
| 15 | this._frameModel = frameModel; |
| 16 | this._logAndImageSplitWidget = new UI.SplitWidget(true, false); |
| 17 | this._logAndImageSplitWidget.element.classList.add('timeline-paint-profiler-log-split'); |
| 18 | this.setMainWidget(this._logAndImageSplitWidget); |
| 19 | this._imageView = new Timeline.TimelinePaintImageView(); |
| 20 | this._logAndImageSplitWidget.setMainWidget(this._imageView); |
| 21 | |
| 22 | this._paintProfilerView = new LayerViewer.PaintProfilerView(this._imageView.showImage.bind(this._imageView)); |
| 23 | this._paintProfilerView.addEventListener( |
| 24 | LayerViewer.PaintProfilerView.Events.WindowChanged, this._onWindowChanged, this); |
| 25 | this.setSidebarWidget(this._paintProfilerView); |
| 26 | |
| 27 | this._logTreeView = new LayerViewer.PaintProfilerCommandLogView(); |
| 28 | this._logAndImageSplitWidget.setSidebarWidget(this._logTreeView); |
| 29 | |
| 30 | this._needsUpdateWhenVisible = false; |
| 31 | /** @type {?SDK.PaintProfilerSnapshot} */ |
| 32 | this._pendingSnapshot = null; |
| 33 | /** @type {?SDK.TracingModel.Event} */ |
| 34 | this._event = null; |
| 35 | /** @type {?SDK.PaintProfilerModel} */ |
| 36 | this._paintProfilerModel = null; |
| 37 | /** @type {?SDK.PaintProfilerSnapshot} */ |
| 38 | this._lastLoadedSnapshot = null; |
| 39 | } |
| 40 | |
| 41 | /** |
| 42 | * @override |
| 43 | */ |
| 44 | wasShown() { |
| 45 | if (this._needsUpdateWhenVisible) { |
| 46 | this._needsUpdateWhenVisible = false; |
| 47 | this._update(); |
| 48 | } |
| 49 | } |
| 50 | |
| 51 | /** |
| 52 | * @param {!SDK.PaintProfilerSnapshot} snapshot |
| 53 | */ |
| 54 | setSnapshot(snapshot) { |
| 55 | this._releaseSnapshot(); |
| 56 | this._pendingSnapshot = snapshot; |
| 57 | this._event = null; |
| 58 | this._updateWhenVisible(); |
| 59 | } |
| 60 | |
| 61 | /** |
| 62 | * @param {!SDK.PaintProfilerModel} paintProfilerModel |
| 63 | * @param {!SDK.TracingModel.Event} event |
| 64 | * @return {boolean} |
| 65 | */ |
| 66 | setEvent(paintProfilerModel, event) { |
| 67 | this._releaseSnapshot(); |
| 68 | this._paintProfilerModel = paintProfilerModel; |
| 69 | this._pendingSnapshot = null; |
| 70 | this._event = event; |
| 71 | |
| 72 | this._updateWhenVisible(); |
| 73 | if (this._event.name === TimelineModel.TimelineModel.RecordType.Paint) |
| 74 | return !!TimelineModel.TimelineData.forEvent(event).picture; |
| 75 | if (this._event.name === TimelineModel.TimelineModel.RecordType.RasterTask) |
| 76 | return this._frameModel.hasRasterTile(this._event); |
| 77 | return false; |
| 78 | } |
| 79 | |
| 80 | _updateWhenVisible() { |
| 81 | if (this.isShowing()) |
| 82 | this._update(); |
| 83 | else |
| 84 | this._needsUpdateWhenVisible = true; |
| 85 | } |
| 86 | |
| 87 | _update() { |
| 88 | this._logTreeView.setCommandLog([]); |
| 89 | this._paintProfilerView.setSnapshotAndLog(null, [], null); |
| 90 | |
| 91 | let snapshotPromise; |
| 92 | if (this._pendingSnapshot) { |
| 93 | snapshotPromise = Promise.resolve({rect: null, snapshot: this._pendingSnapshot}); |
| 94 | } else if (this._event.name === TimelineModel.TimelineModel.RecordType.Paint) { |
| 95 | const picture = TimelineModel.TimelineData.forEvent(this._event).picture; |
| 96 | snapshotPromise = picture.objectPromise() |
| 97 | .then(data => this._paintProfilerModel.loadSnapshot(data['skp64'])) |
| 98 | .then(snapshot => snapshot && {rect: null, snapshot: snapshot}); |
| 99 | } else if (this._event.name === TimelineModel.TimelineModel.RecordType.RasterTask) { |
| 100 | snapshotPromise = this._frameModel.rasterTilePromise(this._event); |
| 101 | } else { |
| 102 | console.assert(false, 'Unexpected event type or no snapshot'); |
| 103 | return; |
| 104 | } |
| 105 | snapshotPromise.then(snapshotWithRect => { |
| 106 | this._releaseSnapshot(); |
| 107 | if (!snapshotWithRect) { |
| 108 | this._imageView.showImage(); |
| 109 | return; |
| 110 | } |
| 111 | const snapshot = snapshotWithRect.snapshot; |
| 112 | this._lastLoadedSnapshot = snapshot; |
| 113 | this._imageView.setMask(snapshotWithRect.rect); |
| 114 | snapshot.commandLog().then(log => onCommandLogDone.call(this, snapshot, snapshotWithRect.rect, log)); |
| 115 | }); |
| 116 | |
| 117 | /** |
| 118 | * @param {!SDK.PaintProfilerSnapshot} snapshot |
| 119 | * @param {?Protocol.DOM.Rect} clipRect |
| 120 | * @param {!Array.<!SDK.PaintProfilerLogItem>=} log |
| 121 | * @this {Timeline.TimelinePaintProfilerView} |
| 122 | */ |
| 123 | function onCommandLogDone(snapshot, clipRect, log) { |
| 124 | this._logTreeView.setCommandLog(log || []); |
| 125 | this._paintProfilerView.setSnapshotAndLog(snapshot, log || [], clipRect); |
| 126 | } |
| 127 | } |
| 128 | |
| 129 | _releaseSnapshot() { |
| 130 | if (!this._lastLoadedSnapshot) |
| 131 | return; |
| 132 | this._lastLoadedSnapshot.release(); |
| 133 | this._lastLoadedSnapshot = null; |
| 134 | } |
| 135 | |
| 136 | _onWindowChanged() { |
| 137 | this._logTreeView.updateWindow(this._paintProfilerView.selectionWindow()); |
| 138 | } |
| 139 | }; |
| 140 | |
| 141 | /** |
| 142 | * @unrestricted |
| 143 | */ |
| 144 | Timeline.TimelinePaintImageView = class extends UI.Widget { |
| 145 | constructor() { |
| 146 | super(true); |
| 147 | this.registerRequiredCSS('timeline/timelinePaintProfiler.css'); |
| 148 | this.contentElement.classList.add('fill', 'paint-profiler-image-view'); |
| 149 | this._imageContainer = this.contentElement.createChild('div', 'paint-profiler-image-container'); |
| 150 | this._imageElement = this._imageContainer.createChild('img'); |
| 151 | this._maskElement = this._imageContainer.createChild('div'); |
| 152 | this._imageElement.addEventListener('load', this._updateImagePosition.bind(this), false); |
| 153 | |
| 154 | this._transformController = new LayerViewer.TransformController(this.contentElement, true); |
| 155 | this._transformController.addEventListener( |
| 156 | LayerViewer.TransformController.Events.TransformChanged, this._updateImagePosition, this); |
| 157 | } |
| 158 | |
| 159 | /** |
| 160 | * @override |
| 161 | */ |
| 162 | onResize() { |
| 163 | if (this._imageElement.src) |
| 164 | this._updateImagePosition(); |
| 165 | } |
| 166 | |
| 167 | _updateImagePosition() { |
| 168 | const width = this._imageElement.naturalWidth; |
| 169 | const height = this._imageElement.naturalHeight; |
| 170 | const clientWidth = this.contentElement.clientWidth; |
| 171 | const clientHeight = this.contentElement.clientHeight; |
| 172 | |
| 173 | const paddingFraction = 0.1; |
| 174 | const paddingX = clientWidth * paddingFraction; |
| 175 | const paddingY = clientHeight * paddingFraction; |
| 176 | const scaleX = (clientWidth - paddingX) / width; |
| 177 | const scaleY = (clientHeight - paddingY) / height; |
| 178 | const scale = Math.min(scaleX, scaleY); |
| 179 | |
| 180 | if (this._maskRectangle) { |
| 181 | const style = this._maskElement.style; |
| 182 | style.width = width + 'px'; |
| 183 | style.height = height + 'px'; |
| 184 | style.borderLeftWidth = this._maskRectangle.x + 'px'; |
| 185 | style.borderTopWidth = this._maskRectangle.y + 'px'; |
| 186 | style.borderRightWidth = (width - this._maskRectangle.x - this._maskRectangle.width) + 'px'; |
| 187 | style.borderBottomWidth = (height - this._maskRectangle.y - this._maskRectangle.height) + 'px'; |
| 188 | } |
| 189 | this._transformController.setScaleConstraints(0.5, 10 / scale); |
| 190 | let matrix = new WebKitCSSMatrix() |
| 191 | .scale(this._transformController.scale(), this._transformController.scale()) |
| 192 | .translate(clientWidth / 2, clientHeight / 2) |
| 193 | .scale(scale, scale) |
| 194 | .translate(-width / 2, -height / 2); |
| 195 | const bounds = UI.Geometry.boundsForTransformedPoints(matrix, [0, 0, 0, width, height, 0]); |
| 196 | this._transformController.clampOffsets( |
| 197 | paddingX - bounds.maxX, clientWidth - paddingX - bounds.minX, paddingY - bounds.maxY, |
| 198 | clientHeight - paddingY - bounds.minY); |
| 199 | matrix = new WebKitCSSMatrix() |
| 200 | .translate(this._transformController.offsetX(), this._transformController.offsetY()) |
| 201 | .multiply(matrix); |
| 202 | this._imageContainer.style.webkitTransform = matrix.toString(); |
| 203 | } |
| 204 | |
| 205 | /** |
| 206 | * @param {string=} imageURL |
| 207 | */ |
| 208 | showImage(imageURL) { |
| 209 | this._imageContainer.classList.toggle('hidden', !imageURL); |
| 210 | if (imageURL) |
| 211 | this._imageElement.src = imageURL; |
| 212 | } |
| 213 | |
| 214 | /** |
| 215 | * @param {?Protocol.DOM.Rect} maskRectangle |
| 216 | */ |
| 217 | setMask(maskRectangle) { |
| 218 | this._maskRectangle = maskRectangle; |
| 219 | this._maskElement.classList.toggle('hidden', !maskRectangle); |
| 220 | } |
| 221 | }; |