Changeset 235095 in webkit
- Timestamp:
- Aug 20, 2018 2:31:55 PM (6 years ago)
- Location:
- trunk/Source/WebInspectorUI
- Files:
-
- 2 deleted
- 13 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebInspectorUI/ChangeLog
r235093 r235095 1 2018-08-20 Devin Rousso <drousso@apple.com> 2 3 Web Inspector: Canvas tab: allow recording processing to be stopped midway 4 https://bugs.webkit.org/show_bug.cgi?id=185152 5 6 Reviewed by Joseph Pecoraro. 7 8 Previously, `WI.Recording` used a `WI.YieldableTask` to process every action in such a way 9 as to not block the UI. The downside to this approach was that it used a message view to 10 indicate the progress of this process, and prevented the user from viewing the `WI.Recording` 11 until that process was completed. 12 13 This patch changes `WI.Recording` to instead use `async/await` and fire events whenever a 14 `WI.RecordingAction` (and `WI.RecordingFrame`) finished processing, allowing it to be added 15 to the recording `WI.TreeOutline` and selected by the user. Additionally, a pause/resume 16 button is added to the `WI.CanvasSidebarPanel` so the user has greater control over what 17 how much of the `WI.Recording` they want to process. 18 19 * Localizations/en.lproj/localizedStrings.js: 20 21 * UserInterface/Base/Utilities.js: 22 (Promise.delay) 23 Utility function for promisifying `setTimeout`. 24 25 * UserInterface/Models/Recording.js: 26 (WI.Recording): 27 (WI.Recording.prototype.get processing): Added. 28 (WI.Recording.prototype.get ready): Added. 29 (WI.Recording.prototype.startProcessing): Added. 30 (WI.Recording.prototype.stopProcessing): Added. 31 (WI.Recording.prototype.async._process): Added. 32 (WI.Recording.prototype.process): Deleted. 33 (WI.Recording.prototype.async.yieldableTaskWillProcessItem): Deleted. 34 (WI.Recording.prototype.async.yieldableTaskDidFinish): Deleted. 35 36 * UserInterface/Models/RecordingAction.js: 37 (WI.RecordingAction): 38 (WI.RecordingAction.prototype.get ready): Added. 39 (WI.RecordingAction.prototype.async.swizzle): 40 (WI.RecordingAction.prototype.apply): 41 42 * UserInterface/Models/RecordingInitialStateAction.js: 43 (WI.RecordingInitialStateAction): 44 45 * UserInterface/Views/CanvasSidebarPanel.js: 46 (WI.CanvasSidebarPanel): 47 (WI.CanvasSidebarPanel.prototype.set recording): 48 (WI.CanvasSidebarPanel.prototype.set action): 49 (WI.CanvasSidebarPanel.prototype._recordingAdded): 50 (WI.CanvasSidebarPanel.prototype._recordingRemoved): 51 (WI.CanvasSidebarPanel.prototype._currentRepresentedObjectsDidChange): 52 (WI.CanvasSidebarPanel.prototype._treeOutlineSelectionDidChange): 53 (WI.CanvasSidebarPanel.prototype._recordingChanged): 54 (WI.CanvasSidebarPanel.prototype._recordingChanged.createPauseButton): Added. 55 (WI.CanvasSidebarPanel.prototype._recordingChanged.createResumeButton): Added. 56 (WI.CanvasSidebarPanel.prototype._createRecordingFrameTreeElement): Added. 57 (WI.CanvasSidebarPanel.prototype._createRecordingActionTreeElement): Added. 58 (WI.CanvasSidebarPanel.prototype._handleRecordingProcessedAction): Added. 59 (WI.CanvasSidebarPanel.prototype._handleRecordingStartProcessingFrame): Added. 60 * UserInterface/Views/CanvasSidebarPanel.css: 61 (.sidebar > .panel.navigation.canvas > .content > .recording-content > .tree-outline .item.processing .subtitle > progress): Added. 62 (.sidebar > .panel.navigation.canvas > .content > .recording-content > .tree-outline:matches(:focus, .force-focus) .item.processing.selected .subtitle > progress): Added. 63 (.sidebar > .panel.navigation.canvas > .content > .recording-content > .tree-outline .item.processing .subtitle::before): Added. 64 (.sidebar > .panel.navigation.canvas > .content > .recording-content > .recording-processing-options): Added. 65 (.sidebar > .panel.navigation.canvas > .content > .recording-content > .recording-processing-options > .indeterminate-progress-spinner): Added. 66 (.sidebar > .panel.navigation.canvas > .content > .recording-content > .indeterminate-progress-spinner): Deleted. 67 68 * UserInterface/Views/RecordingContentView.js: 69 (WI.RecordingContentView): 70 (WI.RecordingContentView.prototype.updateActionIndex): 71 (WI.RecordingContentView.prototype.initialLayout): 72 (WI.RecordingContentView.prototype._updateCanvasPath): 73 (WI.RecordingContentView.prototype._handleRecordingProcessedAction): Added. 74 (WI.RecordingContentView.prototype._updateProcessProgress): Deleted. 75 (WI.RecordingContentView.prototype._handleRecordingProcessedActionSwizzle): Deleted. 76 (WI.RecordingContentView.prototype._handleRecordingProcessedActionApply): Deleted. 77 * UserInterface/Views/RecordingContentView.css: 78 (.content-view:not(.tab).recording > header > .slider-container > .slider-value): Added. 79 80 * UserInterface/Views/FolderTreeElement.js: 81 (WI.FolderTreeElement): 82 83 * UserInterface/Views/GeneralTreeElement.js: 84 (WI.GeneralTreeElement.prototype.get statusElement): Added. 85 (WI.GeneralTreeElement.prototype._updateTitleElements): 86 87 * UserInterface/Views/RecordingContentView.js: 88 (WI.CanvasContentView.prototype._handleViewShaderButtonClicked): 89 (WI.CanvasContentView.prototype._handleViewRecordingButtonClicked): 90 Drive-by: `WI.Collection` doesn't have a `values()` accessor for the underlying `Set`. 91 92 * UserInterface/Views/RecordingNavigationSidebarPanel.css: Removed. 93 * UserInterface/Views/RecordingNavigationSidebarPanel.js: Removed. 94 These files are no longer used since they were "merged" into `WI.CanvasSidebarPanel`. 95 1 96 2018-08-20 Devin Rousso <webkit@devinrousso.com> 2 97 -
trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js
r235093 r235095 569 569 localizedStrings["Load \u2014 %s"] = "Load \u2014 %s"; 570 570 localizedStrings["Load cancelled"] = "Load cancelled"; 571 localizedStrings["Loading Recording"] = "Loading Recording";572 571 localizedStrings["Local File"] = "Local File"; 573 572 localizedStrings["Local Storage"] = "Local Storage"; … … 642 641 localizedStrings["No Properties \u2014 Click to Edit"] = "No Properties \u2014 Click to Edit"; 643 642 localizedStrings["No Query Parameters"] = "No Query Parameters"; 644 localizedStrings["No Recording Data"] = "No Recording Data";645 643 localizedStrings["No Request Headers"] = "No Request Headers"; 646 644 localizedStrings["No Response Headers"] = "No Response Headers"; … … 705 703 localizedStrings["Passive"] = "Passive"; 706 704 localizedStrings["Path"] = "Path"; 705 localizedStrings["Pause Processing"] = "Pause Processing"; 707 706 localizedStrings["Pause Reason"] = "Pause Reason"; 708 707 localizedStrings["Pause script execution (%s or %s)"] = "Pause script execution (%s or %s)"; … … 731 730 localizedStrings["Probes"] = "Probes"; 732 731 localizedStrings["Processing Instruction"] = "Processing Instruction"; 733 localizedStrings["Processing Recording"] = "Processing Recording";734 732 localizedStrings["Program %d"] = "Program %d"; 735 733 localizedStrings["Properties"] = "Properties"; … … 799 797 localizedStrings["Restart (%s)"] = "Restart (%s)"; 800 798 localizedStrings["Restart animation"] = "Restart animation"; 799 localizedStrings["Resume Processing"] = "Resume Processing"; 801 800 localizedStrings["Resume Thread"] = "Resume Thread"; 802 801 localizedStrings["Retained Size"] = "Retained Size"; -
trunk/Source/WebInspectorUI/UserInterface/Base/Utilities.js
r230021 r235095 1338 1338 }); 1339 1339 1340 Object.defineProperty(Promise, "delay", 1341 { 1342 value(delay) 1343 { 1344 return new Promise((resolve) => setTimeout(resolve, delay || 0)); 1345 } 1346 }); 1347 1340 1348 (function() { 1341 1349 // The `debounce` function lets you call any function on an object with a delay -
trunk/Source/WebInspectorUI/UserInterface/Models/Recording.js
r231218 r235095 42 42 this._source = null; 43 43 44 this._swizzleTask = null;45 this._applyTask = null;46 44 this._processContext = null; 47 this._process Promise = null;45 this._processing = false; 48 46 } 49 47 … … 163 161 set source(source) { this._source = source; } 164 162 165 process() 166 { 167 if (!this._processPromise) { 168 this._processPromise = new WI.WrappedPromise; 169 170 let items = this._actions.map((action, index) => { return {action, index} }); 171 this._swizzleTask = new WI.YieldableTask(this, items); 172 this._applyTask = new WI.YieldableTask(this, items); 173 174 this._swizzleTask.start(); 175 } 176 return this._processPromise.promise; 163 get processing() { return this._processing; } 164 165 get ready() 166 { 167 return this._actions.lastValue.ready; 168 } 169 170 startProcessing() 171 { 172 console.assert(!this._processing, "Cannot start an already started process()."); 173 console.assert(!this.ready, "Cannot start a completed process()."); 174 if (this._processing || this.ready) 175 return; 176 177 this._processing = true; 178 179 this._process(); 180 } 181 182 stopProcessing() 183 { 184 console.assert(this._processing, "Cannot stop an already stopped process()."); 185 console.assert(!this.ready, "Cannot stop a completed process()."); 186 if (!this._processing || this.ready) 187 return; 188 189 this._processing = false; 177 190 } 178 191 … … 333 346 } 334 347 335 // YieldableTask delegate 336 337 async yieldableTaskWillProcessItem(task, item) 338 { 339 if (task === this._swizzleTask) { 340 await item.action.swizzle(this); 341 342 this.dispatchEventToListeners(WI.Recording.Event.ProcessedActionSwizzle, {index: item.index}); 343 } else if (task === this._applyTask) { 344 item.action.process(this, this._processContext); 345 346 if (item.action.isVisual) 347 this._visualActionIndexes.push(item.index); 348 349 this.dispatchEventToListeners(WI.Recording.Event.ProcessedActionApply, {index: item.index}); 350 } 351 } 352 353 async yieldableTaskDidFinish(task) 354 { 355 if (task === this._swizzleTask) { 356 this._swizzleTask = null; 357 348 // Private 349 350 async _process() 351 { 352 if (!this._processContext) { 358 353 this._processContext = this.createContext(); 359 354 … … 412 407 } 413 408 } 414 415 this._applyTask.start(); 416 } else if (task === this._applyTask) { 417 this._applyTask = null; 418 this._processContext = null; 419 this._processPromise.resolve(); 420 } 409 } 410 411 // The first action is always a WI.RecordingInitialStateAction, which doesn't need to swizzle(). 412 // Since it is not associated with a WI.RecordingFrame, it has to manually process(). 413 if (!this._actions[0].ready) { 414 this._actions[0].process(this, this._processContext); 415 this.dispatchEventToListeners(WI.Recording.Event.ProcessedAction, {action: this._actions[0], index: 0}); 416 } 417 418 const workInterval = 10; 419 let startTime = Date.now(); 420 421 let cumulativeActionIndex = 0; 422 for (let frameIndex = 0; frameIndex < this._frames.length; ++frameIndex) { 423 let frame = this._frames[frameIndex]; 424 425 if (frame.actions.lastValue.ready) { 426 cumulativeActionIndex += frame.actions.length; 427 continue; 428 } 429 430 for (let actionIndex = 0; actionIndex < frame.actions.length; ++actionIndex) { 431 ++cumulativeActionIndex; 432 433 let action = frame.actions[actionIndex]; 434 if (action.ready) 435 continue; 436 437 await action.swizzle(this); 438 439 action.process(this, this._processContext); 440 441 if (action.isVisual) 442 this._visualActionIndexes.push(cumulativeActionIndex); 443 444 if (!actionIndex) 445 this.dispatchEventToListeners(WI.Recording.Event.StartProcessingFrame, {frame, index: frameIndex}); 446 447 this.dispatchEventToListeners(WI.Recording.Event.ProcessedAction, {action, index: cumulativeActionIndex}); 448 449 if (Date.now() - startTime > workInterval) { 450 await Promise.delay(); // yield 451 452 startTime = Date.now(); 453 } 454 455 if (!this._processing) 456 return; 457 } 458 459 if (!this._processing) 460 return; 461 } 462 463 this._processContext = null; 464 this._processing = false; 421 465 } 422 466 }; 423 467 424 468 WI.Recording.Event = { 425 ProcessedAction Apply: "recording-processed-action-apply",426 ProcessedActionSwizzle: "recording-processed-action-swizzle",469 ProcessedAction: "recording-processed-action", 470 StartProcessingFrame: "recording-start-processing-frame", 427 471 }; 428 472 -
trunk/Source/WebInspectorUI/UserInterface/Models/RecordingAction.js
r231981 r235095 49 49 this._state = null; 50 50 this._stateModifiers = new Set; 51 52 this._swizzled = false; 53 this._processed = false; 51 54 } 52 55 … … 109 112 get stateModifiers() { return this._stateModifiers; } 110 113 114 get ready() 115 { 116 return this._swizzled && this._processed; 117 } 118 111 119 process(recording, context) 112 120 { 121 console.assert(this._swizzled, "You must swizzle() before you can process()."); 122 console.assert(!this._processed, "You should only process() once."); 123 124 this._processed = true; 125 113 126 if (recording.type === WI.Recording.Type.CanvasWebGL) { 114 127 // We add each RecordingAction to the list of visualActionIndexes after it is processed. … … 188 201 async swizzle(recording) 189 202 { 190 if (!this._valid) 203 console.assert(!this._swizzled, "You should only swizzle() once."); 204 205 if (!this._valid) { 206 this._swizzled = true; 191 207 return; 208 } 192 209 193 210 let swizzleParameter = (item, index) => { … … 255 272 } 256 273 } 274 275 this._swizzled = true; 257 276 } 258 277 259 278 apply(context, options = {}) 260 279 { 280 console.assert(this._swizzled, "You must swizzle() before you can apply()."); 281 console.assert(this._processed, "You must process() before you can apply()."); 282 261 283 if (!this.valid) 262 284 return; -
trunk/Source/WebInspectorUI/UserInterface/Models/RecordingInitialStateAction.js
r231218 r235095 33 33 34 34 this._valid = false; 35 36 this._swizzled = true; 35 37 } 36 38 }; -
trunk/Source/WebInspectorUI/UserInterface/Views/CanvasContentView.js
r235093 r235095 436 436 437 437 if (shaderPrograms.size === 1) { 438 WI.showRepresentedObject( shaderPrograms.values().next().value);438 WI.showRepresentedObject(Array.from(shaderPrograms)[0]); 439 439 return; 440 440 } … … 459 459 460 460 if (recordings.size === 1) { 461 WI.showRepresentedObject( recordings.values().next().value);461 WI.showRepresentedObject(Array.from(recordings)[0]); 462 462 return; 463 463 } -
trunk/Source/WebInspectorUI/UserInterface/Views/CanvasSidebarPanel.css
r231218 r235095 73 73 } 74 74 75 .sidebar > .panel.navigation.canvas > .content > .recording-content > .indeterminate-progress-spinner { 76 margin: 16px auto; 75 .sidebar > .panel.navigation.canvas > .content > .recording-content > .tree-outline .item.processing .subtitle > progress { 76 width: 100%; 77 max-width: 100px; 78 margin: 2px 4px 0; 77 79 } 80 81 .sidebar > .panel.navigation.canvas > .content > .recording-content > .tree-outline:matches(:focus, .force-focus) .item.processing.selected .subtitle > progress { 82 filter: brightness(10); 83 } 84 85 .sidebar > .panel.navigation.canvas > .content > .recording-content > .tree-outline .item.processing .subtitle::before { 86 content: ""; 87 } 88 89 .sidebar > .panel.navigation.canvas > .content > .recording-content > .recording-processing-options { 90 display: flex; 91 flex-direction: column; 92 align-items: center; 93 margin: 16px 0; 94 } 95 96 .sidebar > .panel.navigation.canvas > .content > .recording-content > .recording-processing-options > .indeterminate-progress-spinner { 97 margin-bottom: 4px; 98 } -
trunk/Source/WebInspectorUI/UserInterface/Views/CanvasSidebarPanel.js
r235093 r235095 69 69 WI.canvasManager.addEventListener(WI.CanvasManager.Event.RecordingStopped, this._updateRecordNavigationItem, this); 70 70 71 this._recordingProcessPromise = null; 72 this._recordingProcessSpinner = null; 71 this._recordingProcessingOptionsContainer = null; 72 73 this._selectedRecordingActionIndex = NaN; 73 74 } 74 75 … … 104 105 return; 105 106 107 if (this._recording) { 108 this._recording.removeEventListener(WI.Recording.Event.ProcessedAction, this._handleRecordingProcessedAction, this); 109 this._recording.removeEventListener(WI.Recording.Event.StartProcessingFrame, this._handleRecordingStartProcessingFrame, this); 110 } 111 106 112 if (recording) 107 113 this.canvas = recording.source; 108 114 109 115 this._recording = recording; 116 117 if (this._recording) { 118 this._recording.addEventListener(WI.Recording.Event.ProcessedAction, this._handleRecordingProcessedAction, this); 119 this._recording.addEventListener(WI.Recording.Event.StartProcessingFrame, this._handleRecordingStartProcessingFrame, this); 120 } 121 110 122 this._recordingChanged(); 111 123 } … … 113 125 set action(action) 114 126 { 115 if (!this._recording || this._recordingProcessPromise) 127 if (!this._recording) 128 return; 129 130 if (action === this._recording.actions[this._selectedRecordingActionIndex]) 116 131 return; 117 132 … … 139 154 const selectedByUser = false; 140 155 treeElement.revealAndSelect(omitFocus, selectedByUser); 156 157 this._selectedRecordingActionIndex = this._recording.actions.indexOf(action); 141 158 } 142 159 … … 205 222 _recordingAdded(event) 206 223 { 207 this.recording = event.data.item;208 209 224 this._updateRecordNavigationItem(); 210 225 this._updateRecordingScopeBar(); 226 227 this.recording = event.data.item; 211 228 } 212 229 213 230 _recordingRemoved(event) 214 231 { 232 this._updateRecordingScopeBar(); 233 215 234 let recording = event.data.item; 216 235 if (recording === this.recording) 217 236 this.recording = this._canvas ? Array.from(this._canvas.recordingCollection).lastValue : null; 218 219 this._updateRecordingScopeBar();220 237 } 221 238 … … 261 278 let recording = objects.find((object) => object instanceof WI.Recording); 262 279 if (recording) { 280 this.recording = recording; 281 263 282 let recordingAction = objects.find((object) => object instanceof WI.RecordingAction); 264 283 if (recordingAction !== recording[WI.CanvasSidebarPanel.SelectedActionSymbol]) 265 284 this.action = recordingAction; 266 285 267 this.recording = recording;268 286 return; 269 287 } … … 298 316 const onlyExisting = true; 299 317 let recordingContentView = this.contentBrowser.contentViewForRepresentedObject(this._recording, onlyExisting); 300 if (recordingContentView) 301 recordingContentView.updateActionIndex(treeElement.index); 318 if (!recordingContentView) 319 return; 320 321 this._selectedRecordingActionIndex = treeElement.index; 322 recordingContentView.updateActionIndex(this._selectedRecordingActionIndex); 302 323 } 303 324 … … 330 351 this._recordingTreeOutline.removeChildren(); 331 352 332 if (!this._recording) 333 return; 334 335 if (!this._recordingProcessSpinner) { 336 this._recordingProcessSpinner = new WI.IndeterminateProgressSpinner; 337 this._recordingContentContainer.appendChild(this._recordingProcessSpinner.element); 353 if (!this._recording) { 354 if (this._recordingProcessingOptionsContainer) { 355 this._recordingProcessingOptionsContainer.remove(); 356 this._recordingProcessingOptionsContainer = null; 357 } 358 return; 359 } 360 361 if (!this._recording.ready) { 362 if (!this._recording.processing) 363 this._recording.startProcessing(); 364 365 if (!this._recordingProcessingOptionsContainer) { 366 this._recordingProcessingOptionsContainer = this._recordingContentContainer.appendChild(document.createElement("div")); 367 this._recordingProcessingOptionsContainer.classList.add("recording-processing-options"); 368 369 let createPauseButton = () => { 370 let spinner = new WI.IndeterminateProgressSpinner; 371 this._recordingProcessingOptionsContainer.appendChild(spinner.element); 372 373 let pauseButton = this._recordingProcessingOptionsContainer.appendChild(document.createElement("button")); 374 pauseButton.textContent = WI.UIString("Pause Processing"); 375 pauseButton.addEventListener("click", (event) => { 376 this._recording.stopProcessing(); 377 378 spinner.element.remove(); 379 pauseButton.remove(); 380 createResumeButton(); 381 }); 382 }; 383 384 let createResumeButton = () => { 385 let resumeButton = this._recordingProcessingOptionsContainer.appendChild(document.createElement("button")); 386 resumeButton.textContent = WI.UIString("Resume Processing"); 387 resumeButton.addEventListener("click", (event) => { 388 this._recording.startProcessing(); 389 390 resumeButton.remove(); 391 createPauseButton(); 392 }); 393 }; 394 395 if (this._recording.processing) 396 createPauseButton(); 397 else 398 createResumeButton(); 399 } 338 400 } 339 401 340 402 this.contentBrowser.showContentViewForRepresentedObject(this._recording); 341 403 342 let recording = this._recording; 343 344 let promise = this._recording.process().then(() => { 345 if (recording !== this._recording || promise !== this._recordingProcessPromise) 404 if (this._scopeBar) { 405 let scopeBarItem = this._scopeBar.item(this._recording.displayName); 406 console.assert(scopeBarItem, "Missing scopeBarItem for recording.", this._recording); 407 scopeBarItem.selected = true; 408 } 409 410 if (this._recording.actions[0].ready) { 411 this._recordingTreeOutline.appendChild(new WI.RecordingActionTreeElement(this._recording.actions[0], 0, this._recording.type)); 412 413 if (!this._recording[WI.CanvasSidebarPanel.SelectedActionSymbol]) 414 this.action = this._recording.actions[0]; 415 } 416 417 let cumulativeActionIndex = 0; 418 this._recording.frames.forEach((frame, frameIndex) => { 419 if (!frame.actions[0].ready) 346 420 return; 347 421 348 this._recordingProcessPromise = null; 349 350 if (this._recordingProcessSpinner) { 351 this._recordingProcessSpinner.element.remove(); 352 this._recordingProcessSpinner = null; 422 let folder = this._createRecordingFrameTreeElement(frame, frameIndex, this._recordingTreeOutline); 423 424 for (let action of frame.actions) { 425 if (!action.ready) 426 break; 427 428 ++cumulativeActionIndex; 429 430 this._createRecordingActionTreeElement(action, cumulativeActionIndex, folder); 353 431 } 354 355 this._recordingTreeOutline.element.dataset.indent = Number.countDigits(this._recording.actions.length);356 357 if (this._recording.actions[0] instanceof WI.RecordingInitialStateAction)358 this._recordingTreeOutline.appendChild(new WI.RecordingActionTreeElement(this._recording.actions[0], 0, this._recording.type));359 360 let cumulativeActionIndex = 1;361 this._recording.frames.forEach((frame, frameIndex) => {362 let folder = new WI.FolderTreeElement(WI.UIString("Frame %d").format((frameIndex + 1).toLocaleString()));363 this._recordingTreeOutline.appendChild(folder);364 365 for (let i = 0; i < frame.actions.length; ++i)366 folder.appendChild(new WI.RecordingActionTreeElement(frame.actions[i], cumulativeActionIndex + i, this._recording.type));367 368 if (!isNaN(frame.duration)) {369 const higherResolution = true;370 folder.status = Number.secondsToString(frame.duration / 1000, higherResolution);371 }372 373 if (frame.incomplete)374 folder.subtitle = WI.UIString("Incomplete");375 376 if (this._recording.frames.length === 1)377 folder.expand();378 379 cumulativeActionIndex += frame.actions.length;380 });381 382 if (this._scopeBar) {383 let scopeBarItem = this._scopeBar.item(this._recording.displayName);384 console.assert(scopeBarItem, "Missing scopeBarItem for recording.", this._recording);385 scopeBarItem.selected = true;386 }387 388 this.action = this._recording[WI.CanvasSidebarPanel.SelectedActionSymbol] || this._recording.actions[0];389 432 }); 390 391 this._recordingProcessPromise = promise;392 433 } 393 434 … … 448 489 this._recordingNavigationBar.insertNavigationItem(this._scopeBar, 0); 449 490 } 491 492 _createRecordingFrameTreeElement(frame, index, parent) 493 { 494 let folder = new WI.FolderTreeElement(WI.UIString("Frame %d").format((index + 1).toLocaleString()), frame); 495 496 if (!isNaN(frame.duration)) { 497 const higherResolution = true; 498 folder.status = Number.secondsToString(frame.duration / 1000, higherResolution); 499 } 500 501 parent.appendChild(folder); 502 503 return folder; 504 } 505 506 _createRecordingActionTreeElement(action, index, parent) 507 { 508 let treeElement = new WI.RecordingActionTreeElement(action, index, this._recording.type); 509 510 parent.appendChild(treeElement); 511 512 if (parent instanceof WI.FolderTreeElement && parent.representedObject instanceof WI.RecordingFrame) { 513 if (action !== parent.representedObject.actions.lastValue) { 514 parent.addClassName("processing"); 515 516 if (!(parent.subtitle instanceof HTMLProgressElement)) 517 parent.subtitle = document.createElement("progress"); 518 519 if (parent.statusElement) 520 parent.subtitle.style.setProperty("width", `calc(100% - ${parent.statusElement.offsetWidth + 4}px`); 521 522 parent.subtitle.value = parent.representedObject.actions.indexOf(action) / parent.representedObject.actions.length; 523 } else { 524 parent.removeClassName("processing"); 525 if (parent.representedObject.incomplete) 526 parent.subtitle = WI.UIString("Incomplete"); 527 else 528 parent.subtitle = ""; 529 } 530 } 531 532 if (action === this._recording[WI.CanvasSidebarPanel.SelectedActionSymbol]) 533 this.action = action; 534 535 return treeElement; 536 } 537 538 _handleRecordingProcessedAction(event) 539 { 540 let {action, index} = event.data; 541 542 this._recordingTreeOutline.element.dataset.indent = Number.countDigits(index); 543 544 let isInitialStateAction = !index; 545 546 console.assert(isInitialStateAction || this._recordingTreeOutline.children.lastValue instanceof WI.FolderTreeElement, "There should be a WI.FolderTreeElement for the frame for this action."); 547 this._createRecordingActionTreeElement(action, index, isInitialStateAction ? this._recordingTreeOutline : this._recordingTreeOutline.children.lastValue); 548 549 if (isInitialStateAction && !this._recording[WI.CanvasSidebarPanel.SelectedActionSymbol]) 550 this.action = action; 551 552 if (action === this._recording.actions.lastValue && this._recordingProcessingOptionsContainer) { 553 this._recordingProcessingOptionsContainer.remove(); 554 this._recordingProcessingOptionsContainer = null; 555 } 556 } 557 558 _handleRecordingStartProcessingFrame(event) 559 { 560 let {frame, index} = event.data; 561 562 this._createRecordingFrameTreeElement(frame, index, this._recordingTreeOutline); 563 } 450 564 }; 451 565 -
trunk/Source/WebInspectorUI/UserInterface/Views/FolderTreeElement.js
r220119 r235095 28 28 constructor(title, representedObject) 29 29 { 30 console.assert(!representedObject || representedObject instanceof WI.Collection);31 32 30 const classNames = [WI.FolderTreeElement.FolderIconStyleClassName]; 33 31 const subtitle = null; -
trunk/Source/WebInspectorUI/UserInterface/Views/GeneralTreeElement.js
r234563 r235095 51 51 } 52 52 53 get statusElement() 54 { 55 return this._statusElement; 56 } 57 53 58 get titlesElement() 54 59 { … … 302 307 this._subtitleElement.removeChildren(); 303 308 this._subtitleElement.appendChild(this._subtitle); 309 this._titlesElement.classList.remove(WI.GeneralTreeElement.NoSubtitleStyleClassName); 304 310 } else { 305 311 if (this._subtitleElement) -
trunk/Source/WebInspectorUI/UserInterface/Views/RecordingContentView.css
r231218 r235095 48 48 } 49 49 50 .content-view:not(.tab).recording > header > .slider-container > .slider-value { 51 font-family: Menlo, monospace; 52 font-family: 11px; 53 } 54 50 55 .content-view:not(.tab).recording > header > .slider-container > input[type=range] { 51 56 flex: 1; -
trunk/Source/WebInspectorUI/UserInterface/Views/RecordingContentView.js
r231218 r235095 62 62 this._exportButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, () => { this._exportRecording(); }); 63 63 } 64 65 this._processing = true;66 this._processMessageTextView = null;67 64 } 68 65 … … 124 121 this._index = index; 125 122 126 if (this._processing)127 return;128 129 123 this._updateSliderValue(); 130 124 … … 179 173 180 174 let sliderContainer = previewHeader.appendChild(document.createElement("div")); 181 sliderContainer.className = "slider-container hidden";175 sliderContainer.className = "slider-container"; 182 176 183 177 this._previewContainer = this.element.appendChild(document.createElement("div")); … … 193 187 this._sliderElement.max = 0; 194 188 195 this.representedObject.addEventListener(WI.Recording.Event.ProcessedActionSwizzle, this._handleRecordingProcessedActionSwizzle, this); 196 this.representedObject.addEventListener(WI.Recording.Event.ProcessedActionApply, this._handleRecordingProcessedActionApply, this); 197 198 this.representedObject.process().then(() => { 199 if (this._processMessageTextView) 200 this._processMessageTextView.remove(); 201 202 sliderContainer.classList.remove("hidden"); 203 this._sliderElement.max = this.representedObject.visualActionIndexes.length; 204 this._updateSliderValue(); 205 206 this._processing = false; 207 208 let index = this._index; 209 if (!isNaN(index)) { 210 this._index = NaN; 211 this.updateActionIndex(index); 212 } 213 }); 189 this.representedObject.addEventListener(WI.Recording.Event.ProcessedAction, this._handleRecordingProcessedAction, this); 214 190 } 215 191 … … 455 431 let activated = WI.settings.showCanvasPath.value; 456 432 457 if (this._showPathButtonNavigationItem.activated !== activated && !this._processing)433 if (this._showPathButtonNavigationItem.activated !== activated) 458 434 this._generateContentCanvas2D(this._index); 459 435 … … 487 463 } 488 464 489 _updateProcessProgress(message, index)490 {491 if (this._processMessageTextView)492 this._processMessageTextView.remove();493 494 this._processMessageTextView = WI.createMessageTextView(message);495 this.element.appendChild(this._processMessageTextView);496 497 this._processProgressElement = this._processMessageTextView.appendChild(document.createElement("progress"));498 this._processProgressElement.value = index / this.representedObject.actions.length;499 }500 501 465 _showPathButtonClicked(event) 502 466 { … … 524 488 } 525 489 526 _handleRecordingProcessedActionSwizzle(event) 527 { 528 this._updateProcessProgress(WI.UIString("Loading Recording"), event.data.index); 529 } 530 531 _handleRecordingProcessedActionApply(event) 532 { 533 this._updateProcessProgress(WI.UIString("Processing Recording"), event.data.index); 490 _handleRecordingProcessedAction(event) 491 { 492 this._sliderElement.max = this.representedObject.visualActionIndexes.length; 493 this._updateSliderValue(); 534 494 } 535 495 };
Note: See TracChangeset
for help on using the changeset viewer.