Source/JavaScriptCore/ChangeLog

 12018-10-28 Devin Rousso <drousso@apple.com>
 2
 3 Web Inspector: Canvas: create a setting for auto-recording newly created contexts
 4 https://bugs.webkit.org/show_bug.cgi?id=190856
 5
 6 Reviewed by NOBODY (OOPS!).
 7
 8 * inspector/protocol/Canvas.json:
 9 Add `setCreationRecordingFrameCount` command for setting the number of frames to record
 10 immediately after a context is created.
 11
 12 * inspector/protocol/Recording.json:
 13 Add `creation` value for `Initiator` enum.
 14
1152018-10-27 Yusuke Suzuki <yusukesuzuki@slowstart.org>
216
317 Unreviewed, partial rolling in r237254

Source/WebCore/ChangeLog

 12018-10-28 Devin Rousso <drousso@apple.com>
 2
 3 Web Inspector: Canvas: create a setting for auto-recording newly created contexts
 4 https://bugs.webkit.org/show_bug.cgi?id=190856
 5
 6 Reviewed by NOBODY (OOPS!).
 7
 8 Test: inspector/canvas/setCreationRecordingFrameCount.html
 9
 10 * inspector/agents/InspectorCanvasAgent.h:
 11 (WebCore::InspectorCanvasAgent::RecordingOptions): Added.
 12 * inspector/agents/InspectorCanvasAgent.cpp:
 13 (WebCore::InspectorCanvasAgent::setCreationRecordingFrameCount): Added.
 14 (WebCore::InspectorCanvasAgent::startRecording):
 15 (WebCore::InspectorCanvasAgent::didCreateCanvasRenderingContext):
 16 (WebCore::InspectorCanvasAgent::didFinishRecordingCanvasFrame):
 17 (WebCore::InspectorCanvasAgent::consoleStartRecordingCanvas):
 18 (WebCore::InspectorCanvasAgent::startRecording): Added.
 19 Unify the different functions that are able to start a recording to use a single path.
 20
 21 * inspector/InspectorCanvas.h:
 22 * inspector/InspectorCanvas.cpp:
 23 (WebCore::InspectorCanvas::resetRecordingData):
 24 (WebCore::InspectorCanvas::recordAction):
 25 (WebCore::InspectorCanvas::setFrameLimit): Added.
 26 (WebCore::InspectorCanvas::overFrameLimit const): Added.
 27
1282018-10-27 Charlie Turner <cturner@igalia.com>
229
330 [EME] Add a logging macro

Source/WebInspectorUI/ChangeLog

 12018-10-28 Devin Rousso <drousso@apple.com>
 2
 3 Web Inspector: Canvas: create a setting for auto-recording newly created contexts
 4 https://bugs.webkit.org/show_bug.cgi?id=190856
 5
 6 Reviewed by NOBODY (OOPS!).
 7
 8 * UserInterface/Controllers/CanvasManager.js:
 9 (WI.CanvasManager.supportsCreationRecordingFrameCount): Added.
 10 (WI.CanvasManager.prototype.setCreationRecordingFrameCount): Added.
 11
 12 * UserInterface/Models/Canvas.js:
 13 (WI.Canvas.prototype.startRecording):
 14 (WI.Canvas.prototype.recordingStarted):
 15 (WI.Canvas.prototype.recordingFinished):
 16
 17 * UserInterface/Models/Recording.js:
 18
 19 * UserInterface/Views/CanvasOverviewContentView.js:
 20 (WI.CanvasOverviewContentView):
 21 (WI.CanvasOverviewContentView.updateCreationRecordingFrameCountInput): Added.
 22 (WI.CanvasOverviewContentView.prototype.get navigationItems):
 23 * UserInterface/Views/CanvasOverviewContentView.css:
 24 (.navigation-bar > .item.set-creation-recording-frame-count): Added.
 25 (.navigation-bar > .item.set-creation-recording-frame-count > input): Added.
 26 (.popover-content > .tree-outline .item.recording > .icon): Deleted.
 27 (.popover-content > .tree-outline .item.recording:hover): Deleted.
 28 (.popover-content > .tree-outline .item.recording:hover > .icon): Deleted.
 29 Drive-by: removed unused CSS rules.
 30
 31 * UserInterface/Views/CanvasContentView.js:
 32 (WI.CanvasContentView.prototype.initialLayout):
 33 (WI.CanvasContentView.prototype._updateProgressView):
 34 (WI.CanvasContentView.prototype._updateViewRelatedItems):
 35
 36 * UserInterface/Views/CanvasTabContentView.js:
 37 (WI.CanvasTabContentView.prototype._recordingImportedOrStopped):
 38
 39 * UserInterface/Base/Setting.js:
 40
 41 * Localizations/en.lproj/localizedStrings.js:
 42
1432018-10-27 Matt Baker <mattbaker@apple.com>
244
345 Web Inspector: Table should support deleting rows

Source/JavaScriptCore/inspector/protocol/Canvas.json

104104 { "name": "object", "$ref": "Runtime.RemoteObject", "description": "JavaScript object wrapper for given canvas context." }
105105 ]
106106 },
 107 {
 108 "name": "setCreationRecordingFrameCount",
 109 "description": "Tells the backend to record `count` frames whenever a new context is created.",
 110 "parameters": [
 111 { "name": "count", "type": "integer", "description": "Number of frames to record (0 means don't record anything)." }
 112 ]
 113 },
107114 {
108115 "name": "startRecording",
109116 "description": "Record the next frame, or up to the given number of bytes of data, for the given canvas.",
110117 "parameters": [
111118 { "name": "canvasId", "$ref": "CanvasId" },
112  { "name": "singleFrame", "type": "boolean", "optional": true, "description": "Whether to record a single frame or until the memory limit is reached." },
113  { "name": "memoryLimit", "type": "integer", "optional": true, "description": "Memory limit of recorded data." }
 119 { "name": "frameCount", "type": "integer", "optional": true, "description": "Number of frames to record (unlimited when not specified)." },
 120 { "name": "memoryLimit", "type": "integer", "optional": true, "description": "Memory limit of recorded data (100MB wgeb bit specified)." }
114121 ]
115122 },
116123 {

Source/JavaScriptCore/inspector/protocol/Recording.json

1111 {
1212 "id": "Initiator",
1313 "type": "string",
14  "enum": ["frontend", "console"]
 14 "enum": ["frontend", "console", "creation"]
1515 },
1616 {
1717 "id": "InitialState",

Source/WebCore/inspector/InspectorCanvas.cpp

@@void InspectorCanvas::resetRecordingData()
105105 m_recordingName = { };
106106 m_bufferLimit = 100 * 1024 * 1024;
107107 m_bufferUsed = 0;
108  m_singleFrame = true;
 108 m_frameLimit = static_cast<size_t>(-1); // Unlimited
 109 m_frameCount = 0;
109110
110111 m_context.setCallTracingActive(false);
111112}

@@void InspectorCanvas::recordAction(const String& name, Vector<RecordCanvasAction
150151 .release();
151152
152153 m_frames->addItem(WTFMove(frame));
 154 ++m_frameCount;
153155
154156 m_currentFrameStartTime = MonotonicTime::now();
155157 }

@@bool InspectorCanvas::hasBufferSpace() const
216218 return m_bufferUsed < m_bufferLimit;
217219}
218220
 221void InspectorCanvas::setFrameLimit(long frameLimit)
 222{
 223 if (frameLimit > 0)
 224 m_frameLimit = std::min<long>(frameLimit, std::numeric_limits<int>::max());
 225 else
 226 m_frameLimit = static_cast<size_t>(-1); // Unlimited
 227}
 228
 229bool InspectorCanvas::overFrameLimit() const
 230{
 231 return m_frameCount >= m_frameLimit;
 232}
 233
219234Ref<Inspector::Protocol::Canvas::Canvas> InspectorCanvas::buildObjectForCanvas(bool captureBacktrace)
220235{
221236 Inspector::Protocol::Canvas::ContextType contextType;

Source/WebCore/inspector/InspectorCanvas.h

@@public:
7171 bool hasBufferSpace() const;
7272 long bufferUsed() const { return m_bufferUsed; }
7373
74  bool singleFrame() const { return m_singleFrame; }
75  void setSingleFrame(bool singleFrame) { m_singleFrame = singleFrame; }
 74 void setFrameLimit(long);
 75 bool overFrameLimit() const;
7676
7777 Ref<Inspector::Protocol::Canvas::Canvas> buildObjectForCanvas(bool captureBacktrace);
7878

@@private:
117117 MonotonicTime m_currentFrameStartTime { MonotonicTime::nan() };
118118 size_t m_bufferLimit { 100 * 1024 * 1024 };
119119 size_t m_bufferUsed { 0 };
120  bool m_singleFrame { false };
 120 size_t m_frameLimit { static_cast<size_t>(-1) }; // Unlimited
 121 size_t m_frameCount { 0 };
121122};
122123
123124} // namespace WebCore

Source/WebCore/inspector/agents/InspectorCanvasAgent.cpp

@@void InspectorCanvasAgent::resolveCanvasContext(ErrorString& errorString, const
261261 result = injectedScript.wrapObject(value, objectGroupName);
262262}
263263
264 void InspectorCanvasAgent::startRecording(ErrorString& errorString, const String& canvasId, const bool* singleFrame, const int* memoryLimit)
 264void InspectorCanvasAgent::setCreationRecordingFrameCount(ErrorString&, int count)
 265{
 266 m_creationRecordingFrameCount = count;
 267}
 268
 269void InspectorCanvasAgent::startRecording(ErrorString& errorString, const String& canvasId, const int* frameCount, const int* memoryLimit)
265270{
266271 auto* inspectorCanvas = assertInspectorCanvas(errorString, canvasId);
267272 if (!inspectorCanvas)

@@void InspectorCanvasAgent::startRecording(ErrorString& errorString, const String
272277 return;
273278 }
274279
275  inspectorCanvas->resetRecordingData();
276  if (singleFrame)
277  inspectorCanvas->setSingleFrame(*singleFrame);
 280 RecordingOptions recordingOptions;
 281 if (frameCount)
 282 recordingOptions.frameCount = *frameCount;
278283 if (memoryLimit)
279  inspectorCanvas->setBufferLimit(*memoryLimit);
280 
281  inspectorCanvas->context().setCallTracingActive(true);
282 
283  m_frontendDispatcher->recordingStarted(inspectorCanvas->identifier(), Inspector::Protocol::Recording::Initiator::Frontend);
 284 recordingOptions.memoryLimit = *memoryLimit;
 285 startRecording(*inspectorCanvas, Inspector::Protocol::Recording::Initiator::Frontend, WTFMove(recordingOptions));
284286}
285287
286288void InspectorCanvasAgent::stopRecording(ErrorString& errorString, const String& canvasId)

@@void InspectorCanvasAgent::didCreateCanvasRenderingContext(CanvasRenderingContex
429431
430432 context.canvasBase().addObserver(*this);
431433
432  auto inspectorCanvas = InspectorCanvas::create(context);
 434 RefPtr<InspectorCanvas> inspectorCanvas = InspectorCanvas::create(context);
 435 m_identifierToInspectorCanvas.set(inspectorCanvas->identifier(), inspectorCanvas);
433436
434  if (m_enabled) {
435  const bool captureBacktrace = true;
436  m_frontendDispatcher->canvasAdded(inspectorCanvas->buildObjectForCanvas(captureBacktrace));
437  }
 437 if (!m_enabled)
 438 return;
 439
 440 const bool captureBacktrace = true;
 441 m_frontendDispatcher->canvasAdded(inspectorCanvas->buildObjectForCanvas(captureBacktrace));
438442
439  m_identifierToInspectorCanvas.set(inspectorCanvas->identifier(), WTFMove(inspectorCanvas));
 443 if (m_creationRecordingFrameCount > 0) {
 444 RecordingOptions recordingOptions;
 445 recordingOptions.frameCount = m_creationRecordingFrameCount;
 446 startRecording(*inspectorCanvas, Inspector::Protocol::Recording::Initiator::Creation, WTFMove(recordingOptions));
 447 }
440448}
441449
442450void InspectorCanvasAgent::didChangeCanvasMemory(CanvasRenderingContext& context)

@@void InspectorCanvasAgent::didFinishRecordingCanvasFrame(CanvasRenderingContext&
523531 if (inspectorCanvas->currentFrameHasData())
524532 m_frontendDispatcher->recordingProgress(inspectorCanvas->identifier(), inspectorCanvas->releaseFrames(), inspectorCanvas->bufferUsed());
525533
526  if (!forceDispatch && !inspectorCanvas->singleFrame())
 534 if (!forceDispatch && !inspectorCanvas->overFrameLimit())
527535 return;
528536
529537 // FIXME: <https://webkit.org/b/176008> Web Inspector: Record actions performed on WebGL2RenderingContext

@@void InspectorCanvasAgent::consoleStartRecordingCanvas(CanvasRenderingContext& c
565573 if (!inspectorCanvas)
566574 return;
567575
568  if (inspectorCanvas->context().callTracingActive())
569  return;
570 
571  inspectorCanvas->resetRecordingData();
572 
 576 RecordingOptions recordingOptions;
573577 if (options) {
574  if (JSC::JSValue optionName = options->get(&exec, JSC::Identifier::fromString(&exec, "name")))
575  inspectorCanvas->setRecordingName(optionName.toWTFString(&exec));
576578 if (JSC::JSValue optionSingleFrame = options->get(&exec, JSC::Identifier::fromString(&exec, "singleFrame")))
577  inspectorCanvas->setSingleFrame(optionSingleFrame.toBoolean(&exec));
 579 recordingOptions.frameCount = optionSingleFrame.toBoolean(&exec) ? 1 : 0;
 580 if (JSC::JSValue optionFrameCount = options->get(&exec, JSC::Identifier::fromString(&exec, "frameCount")))
 581 recordingOptions.frameCount = optionFrameCount.toNumber(&exec);
578582 if (JSC::JSValue optionMemoryLimit = options->get(&exec, JSC::Identifier::fromString(&exec, "memoryLimit")))
579  inspectorCanvas->setBufferLimit(optionMemoryLimit.toNumber(&exec));
 583 recordingOptions.memoryLimit = optionMemoryLimit.toNumber(&exec);
 584 if (JSC::JSValue optionName = options->get(&exec, JSC::Identifier::fromString(&exec, "name")))
 585 recordingOptions.name = optionName.toWTFString(&exec);
580586 }
581 
582  inspectorCanvas->context().setCallTracingActive(true);
583 
584  m_frontendDispatcher->recordingStarted(inspectorCanvas->identifier(), Inspector::Protocol::Recording::Initiator::Console);
 587 startRecording(*inspectorCanvas, Inspector::Protocol::Recording::Initiator::Console, WTFMove(recordingOptions));
585588}
586589
587590#if ENABLE(WEBGL)

@@bool InspectorCanvasAgent::isShaderProgramHighlighted(WebGLProgram& program)
640643}
641644#endif
642645
 646void InspectorCanvasAgent::startRecording(InspectorCanvas& inspectorCanvas, Inspector::Protocol::Recording::Initiator initiator, RecordingOptions&& recordingOptions)
 647{
 648 auto& canvasRenderingContext = inspectorCanvas.context();
 649
 650 if (!is<CanvasRenderingContext2D>(canvasRenderingContext)
 651#if ENABLE(WEBGL)
 652 && !is<WebGLRenderingContext>(canvasRenderingContext)
 653#endif
 654 && !is<ImageBitmapRenderingContext>(canvasRenderingContext))
 655 return;
 656
 657 if (canvasRenderingContext.callTracingActive())
 658 return;
 659
 660 inspectorCanvas.resetRecordingData();
 661 if (recordingOptions.frameCount)
 662 inspectorCanvas.setFrameLimit(*recordingOptions.frameCount);
 663 if (recordingOptions.memoryLimit)
 664 inspectorCanvas.setBufferLimit(*recordingOptions.memoryLimit);
 665 if (recordingOptions.name)
 666 inspectorCanvas.setRecordingName(*recordingOptions.name);
 667 canvasRenderingContext.setCallTracingActive(true);
 668
 669 m_frontendDispatcher->recordingStarted(inspectorCanvas.identifier(), initiator);
 670}
 671
643672void InspectorCanvasAgent::canvasDestroyedTimerFired()
644673{
645674 if (!m_removedCanvasIdentifiers.size())

Source/WebCore/inspector/agents/InspectorCanvasAgent.h

@@public:
7373 void requestContent(ErrorString&, const String& canvasId, String* content) override;
7474 void requestCSSCanvasClientNodes(ErrorString&, const String& canvasId, RefPtr<JSON::ArrayOf<int>>&) override;
7575 void resolveCanvasContext(ErrorString&, const String& canvasId, const String* objectGroup, RefPtr<Inspector::Protocol::Runtime::RemoteObject>&) override;
76  void startRecording(ErrorString&, const String& canvasId, const bool* singleFrame, const int* memoryLimit) override;
 76 void setCreationRecordingFrameCount(ErrorString&, int count) override;
 77 void startRecording(ErrorString&, const String& canvasId, const int* frameCount, const int* memoryLimit) override;
7778 void stopRecording(ErrorString&, const String& canvasId) override;
7879 void requestShaderSource(ErrorString&, const String& programId, const String& shaderType, String*) override;
7980 void updateShader(ErrorString&, const String& programId, const String& shaderType, const String& source) override;

@@public:
103104 void canvasDestroyed(CanvasBase&) override;
104105
105106private:
 107 struct RecordingOptions {
 108 std::optional<long> frameCount;
 109 std::optional<long> memoryLimit;
 110 std::optional<String> name;
 111 };
 112 void startRecording(InspectorCanvas&, Inspector::Protocol::Recording::Initiator, RecordingOptions&& = { });
 113
106114 void canvasDestroyedTimerFired();
107115 void canvasRecordingTimerFired();
108116 void clearCanvasData();

@@private:
124132 Vector<String> m_removedCanvasIdentifiers;
125133 Timer m_canvasDestroyedTimer;
126134 Timer m_canvasRecordingTimer;
 135 int m_creationRecordingFrameCount { 0 };
127136
128137 bool m_enabled { false };
129138};

Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js

@@localizedStrings["Range Issue"] = "Range Issue";
652652localizedStrings["Readonly"] = "Readonly";
653653localizedStrings["Reasons for compositing"] = "Reasons for compositing";
654654localizedStrings["Reasons for compositing:"] = "Reasons for compositing:";
 655localizedStrings["Record %s frames after creation"] = "Record %s frames after creation";
655656localizedStrings["Recording"] = "Recording";
656657localizedStrings["Recording %d"] = "Recording %d";
657658localizedStrings["Recording Timeline Data"] = "Recording Timeline Data";

Source/WebInspectorUI/UserInterface/Base/Setting.js

@@WI.settings = {
114114 zoomFactor: new WI.Setting("zoom-factor", 1),
115115 showScopeChainOnPause: new WI.Setting("show-scope-chain-sidebar", true),
116116 showImageGrid: new WI.Setting("show-image-grid", false),
 117 creationRecordingFrameCount: new WI.Setting("creation-recording-frame-count", 1),
117118 showCanvasPath: new WI.Setting("show-canvas-path", false),
118119 selectedNetworkDetailContentViewIdentifier: new WI.Setting("network-detail-content-view-identifier", "preview"),
119120 showRulers: new WI.Setting("show-rulers", false),

Source/WebInspectorUI/UserInterface/Controllers/CanvasManager.js

@@WI.CanvasManager = class CanvasManager extends WI.Object
3939 CanvasAgent.enable();
4040 }
4141
 42 // Static
 43
 44 static supportsCreationRecordingFrameCount()
 45 {
 46 return window.CanvasAgent && CanvasAgent.setCreationRecordingFrameCount;
 47 }
 48
4249 // Public
4350
4451 get importedRecordings() { return this._importedRecordings; }

@@WI.CanvasManager = class CanvasManager extends WI.Object
8491 });
8592 }
8693
 94 setCreationRecordingFrameCount(frameCount)
 95 {
 96 console.assert(!isNaN(frameCount) && frameCount >= 0);
 97
 98 CanvasAgent.setCreationRecordingFrameCount(frameCount, (error) => {
 99 if (error) {
 100 console.error(error);
 101 return;
 102 }
 103
 104 WI.settings.creationRecordingFrameCount.value = frameCount;
 105 });
 106 }
 107
87108 canvasAdded(canvasPayload)
88109 {
89110 // Called from WI.CanvasObserver.

Source/WebInspectorUI/UserInterface/Models/Canvas.js

@@WI.Canvas = class Canvas extends WI.Object
269269
270270 startRecording(singleFrame)
271271 {
272  CanvasAgent.startRecording(this._identifier, singleFrame, (error) => {
 272 let handleStartRecording = (error) => {
273273 if (error) {
274274 console.error(error);
275275 return;

@@WI.Canvas = class Canvas extends WI.Object
285285 this._recordingBufferUsed = 0;
286286
287287 this.dispatchEventToListeners(WI.Canvas.Event.RecordingStarted);
288  });
 288 };
 289
 290 // COMPATIBILITY (iOS 12.0): `frameCount` did not exist yet
 291 if (CanvasAgent.startRecording.supports("singleFrame")) {
 292 CanvasAgent.startRecording(this._identifier, singleFrame, handleStartRecording);
 293 return;
 294 }
 295
 296 if (singleFrame) {
 297 const frameCount = 1;
 298 CanvasAgent.startRecording(this._identifier, frameCount, handleStartRecording);
 299 } else
 300 CanvasAgent.startRecording(this._identifier, handleStartRecording);
289301 }
290302
291303 stopRecording()

@@WI.Canvas = class Canvas extends WI.Object
332344
333345 if (initiator === WI.Recording.Initiator.Console)
334346 this._recordingState = WI.Canvas.RecordingState.ActiveConsole;
 347 else if (initiator === WI.Recording.Initiator.Creation)
 348 this._recordingState = WI.Canvas.RecordingState.ActiveCreation;
335349 else {
336350 console.assert(initiator === WI.Recording.Initiator.Frontend);
337351 this._recordingState = WI.Canvas.RecordingState.ActiveFrontend;

@@WI.Canvas = class Canvas extends WI.Object
358372 {
359373 // Called from WI.CanvasManager.
360374
361  let fromConsole = this._recordingState === WI.Canvas.RecordingState.ActiveConsole;
 375 let initiatedByUser = this._recordingState === WI.Canvas.RecordingState.ActiveFrontend;
362376
363377 // COMPATIBILITY (iOS 12.1): Canvas.event.recordingStarted did not exist yet
364  if (!fromConsole && !CanvasAgent.hasEvent("recordingStarted"))
365  fromConsole = !this.recordingActive;
 378 if (!initiatedByUser && !CanvasAgent.hasEvent("recordingStarted"))
 379 initiatedByUser = !!this.recordingActive;
366380
367381 let recording = recordingPayload ? WI.Recording.fromPayload(recordingPayload, this._recordingFrames) : null;
368382 if (recording) {

@@WI.Canvas = class Canvas extends WI.Object
376390 this._recordingFrames = [];
377391 this._recordingBufferUsed = 0;
378392
379  this.dispatchEventToListeners(WI.Canvas.Event.RecordingStopped, {recording, fromConsole});
 393 this.dispatchEventToListeners(WI.Canvas.Event.RecordingStopped, {recording, initiatedByUser});
380394 }
381395
382396 nextShaderProgramDisplayNumber()

@@WI.Canvas.RecordingState = {
405419 Inactive: "canvas-recording-state-inactive",
406420 ActiveFrontend: "canvas-recording-state-active-frontend",
407421 ActiveConsole: "canvas-recording-state-active-console",
 422 ActiveCreation: "canvas-recording-state-active-creation",
408423};
409424
410425WI.Canvas.Event = {

Source/WebInspectorUI/UserInterface/Models/Recording.js

@@WI.Recording.Type = {
542542WI.Recording.Initiator = {
543543 Frontend: "frontend",
544544 Console: "console",
 545 Creation: "creation",
545546};
546547
547548// Keep this in sync with WebCore::RecordingSwizzleTypes.

Source/WebInspectorUI/UserInterface/Views/CanvasContentView.js

@@WI.CanvasContentView = class CanvasContentView extends WI.ContentView
149149
150150 if (this._errorElement)
151151 this._showError();
 152
 153 this._updateProgressView();
152154 }
153155
154156 layout()

@@WI.CanvasContentView = class CanvasContentView extends WI.ContentView
371373
372374 _updateProgressView()
373375 {
 376 if (!this._previewContainerElement)
 377 return;
 378
374379 if (!this.representedObject.recordingActive) {
375380 if (this._progressView && this._progressView.parentView) {
376381 this.removeSubview(this._progressView);

@@WI.CanvasContentView = class CanvasContentView extends WI.ContentView
398403
399404 _updateViewRelatedItems()
400405 {
 406 if (!this._viewRelatedItemsContainer)
 407 return;
 408
401409 this._viewRelatedItemsContainer.removeChildren();
402410
403411 if (this.representedObject.shaderProgramCollection.size)

Source/WebInspectorUI/UserInterface/Views/CanvasOverviewContentView.css

173173 -webkit-padding-start: 4px;
174174}
175175
176 .popover-content > .tree-outline .item.recording > .icon {
177  content: url(../Images/Recording.svg);
178 }
179 
180 .popover-content > .tree-outline .item.recording:hover {
181  color: var(--selected-foreground-color);
182  background-color: var(--selected-background-color);
183  border-radius: 3px;
 176.navigation-bar > .item.set-creation-recording-frame-count {
 177 padding: 0 8px;
184178}
185179
186 .popover-content > .tree-outline .item.recording:hover > .icon {
187  filter: invert();
 180.navigation-bar > .item.set-creation-recording-frame-count > input {
 181 width: 28px;
 182 min-width: 28px;
 183 margin: 0 4px;
 184 -webkit-padding-start: 3px;
 185 text-align: end;
188186}
189187
190188@media (prefers-dark-interface) {

Source/WebInspectorUI/UserInterface/Views/CanvasOverviewContentView.js

@@WI.CanvasOverviewContentView = class CanvasOverviewContentView extends WI.Collec
4545
4646 this.element.classList.add("canvas-overview");
4747
 48 if (WI.CanvasManager.supportsCreationRecordingFrameCount()) {
 49 this._creationRecordingFrameCountNavigationItem = new WI.NavigationItem("set-creation-recording-frame-count");
 50 this._creationRecordingFrameCountNavigationItem.visibilityPriority = WI.NavigationItem.VisibilityPriority.Low;
 51
 52 let creationRecordingFrameCountInput = document.createElement("input");
 53 creationRecordingFrameCountInput.type = "number";
 54 creationRecordingFrameCountInput.min = 0;
 55 creationRecordingFrameCountInput.value = WI.settings.creationRecordingFrameCount.value;
 56
 57 function updateCreationRecordingFrameCountInput() {
 58 let frameCount = parseInt(creationRecordingFrameCountInput.value);
 59 if (isNaN(frameCount) || frameCount < 0) {
 60 frameCount = 0;
 61 creationRecordingFrameCountInput.value = frameCount;
 62 }
 63
 64 creationRecordingFrameCountInput.style.setProperty("width", ((creationRecordingFrameCountInput.value.length * 7) + 21) + "px");
 65
 66 WI.canvasManager.setCreationRecordingFrameCount(frameCount);
 67 }
 68 creationRecordingFrameCountInput.addEventListener("input", updateCreationRecordingFrameCountInput);
 69 updateCreationRecordingFrameCountInput();
 70
 71 String.format(WI.UIString("Record %s frames after creation"), [creationRecordingFrameCountInput], String.standardFormatters, this._creationRecordingFrameCountNavigationItem.element, (a, b) => {
 72 a.append(b);
 73 return a;
 74 });
 75 }
 76
4877 this._importButtonNavigationItem = new WI.ButtonNavigationItem("import-recording", WI.UIString("Import"), "Images/Import.svg", 15, 15);
4978 this._importButtonNavigationItem.toolTip = WI.UIString("Import recording from file");
5079 this._importButtonNavigationItem.buttonStyle = WI.ButtonNavigationItem.Style.ImageAndText;

@@WI.CanvasOverviewContentView = class CanvasOverviewContentView extends WI.Collec
6493
6594 get navigationItems()
6695 {
67  return [this._importButtonNavigationItem, new WI.DividerNavigationItem, this._refreshButtonNavigationItem, this._showGridButtonNavigationItem];
 96 let navigationItems = [this._importButtonNavigationItem, new WI.DividerNavigationItem, this._refreshButtonNavigationItem, this._showGridButtonNavigationItem];
 97 if (WI.CanvasManager.supportsCreationRecordingFrameCount())
 98 navigationItems.unshift(this._creationRecordingFrameCountNavigationItem, new WI.DividerNavigationItem);
 99 return navigationItems;
68100 }
69101
70102 hidden()

Source/WebInspectorUI/UserInterface/Views/CanvasTabContentView.js

@@WI.CanvasTabContentView = class CanvasTabContentView extends WI.ContentBrowserTa
234234
235235 _recordingImportedOrStopped(event)
236236 {
237  let recording = event.data.recording;
 237 let {recording, initiatedByUser} = event.data;
238238 if (!recording)
239239 return;
240240
241241 this._addRecording(recording, {
242  suppressShowRecording: event.data.fromConsole || this.contentBrowser.currentRepresentedObjects.some((representedObject) => representedObject instanceof WI.Recording),
 242 suppressShowRecording: !initiatedByUser || this.contentBrowser.currentRepresentedObjects.some((representedObject) => representedObject instanceof WI.Recording),
243243 });
244244 }
245245

LayoutTests/ChangeLog

 12018-10-28 Devin Rousso <drousso@apple.com>
 2
 3 Web Inspector: Canvas: create a setting for auto-recording newly created contexts
 4 https://bugs.webkit.org/show_bug.cgi?id=190856
 5
 6 Reviewed by NOBODY (OOPS!).
 7
 8 * inspector/canvas/resources/recording-utilities.js:
 9 (TestPage.registerInitializer.window.startRecording):
 10 (TestPage.registerInitializer.handleRecordingProgress):
 11 * inspector/canvas/recording-2d.html:
 12 * inspector/canvas/recording-bitmaprenderer.html:
 13 * inspector/canvas/recording-webgl-snapshots.html:
 14 * inspector/canvas/recording-webgl.html:
 15
 16 * inspector/canvas/setCreationRecordingFrameCount-expected.txt: Added.
 17 * inspector/canvas/setCreationRecordingFrameCount.html: Added.
 18
1192018-10-27 Matt Baker <mattbaker@apple.com>
220
321 Web Inspector: Table should support deleting rows

LayoutTests/inspector/canvas/recording-2d.html

@@function test() {
436436 name: "Canvas.recording2D.singleFrame",
437437 description: "Check that the recording is stopped after a single frame.",
438438 test(resolve, reject) {
439  startRecording(WI.Canvas.ContextType.Canvas2D, resolve, reject, {singleFrame: true});
 439 startRecording(WI.Canvas.ContextType.Canvas2D, resolve, reject, {frameCount: 1});
440440 },
441441 });
442442

@@function test() {
444444 name: "Canvas.recording2D.multipleFrames",
445445 description: "Check that recording data is serialized correctly for multiple frames.",
446446 test(resolve, reject) {
447  startRecording(WI.Canvas.ContextType.Canvas2D, resolve, reject, {singleFrame: false});
 447 startRecording(WI.Canvas.ContextType.Canvas2D, resolve, reject);
448448 },
449449 });
450450

@@function test() {
493493 InspectorTest.evaluateInPage(`performNaNActions()`);
494494 });
495495
496  const singleFrame = true;
497  CanvasAgent.startRecording(canvas.identifier, singleFrame)
 496 const frameCount = 1;
 497 CanvasAgent.startRecording(canvas.identifier, frameCount)
498498 .catch(reject);
499499 },
500500 });

@@function test() {
538538
539539 InspectorTest.evaluateInPage(`performSavePreActions()`)
540540 .then(() => {
541  const singleFrame = true;
542  CanvasAgent.startRecording(canvas.identifier, singleFrame).catch(reject);
 541 const frameCount = 1;
 542 CanvasAgent.startRecording(canvas.identifier, frameCount).catch(reject);
543543 }, reject);
544544 },
545545 });

LayoutTests/inspector/canvas/recording-bitmaprenderer.html

@@function test() {
8787 name: "Canvas.recordingBitmapRenderer.singleFrame",
8888 description: "Check that the recording is stopped after a single frame.",
8989 test(resolve, reject) {
90  startRecording(WI.Canvas.ContextType.BitmapRenderer, resolve, reject, {singleFrame: true});
 90 startRecording(WI.Canvas.ContextType.BitmapRenderer, resolve, reject, {frameCount: 1});
9191 },
9292 });
9393

@@function test() {
9595 name: "Canvas.recordingBitmapRenderer.multipleFrames",
9696 description: "Check that recording data is serialized correctly for multiple frames.",
9797 test(resolve, reject) {
98  startRecording(WI.Canvas.ContextType.BitmapRenderer, resolve, reject, {singleFrame: false});
 98 startRecording(WI.Canvas.ContextType.BitmapRenderer, resolve, reject);
9999 },
100100 });
101101

LayoutTests/inspector/canvas/recording-webgl-snapshots.html

@@function test() {
100100 name: "Canvas.recordingWebGL.snapshots",
101101 description: "Check that the snapshot taken after each visual action is different.",
102102 test(resolve, reject) {
103  startRecording(WI.Canvas.ContextType.WebGL, resolve, reject, {singleFrame: true});
 103 startRecording(WI.Canvas.ContextType.WebGL, resolve, reject, {frameCount: 1});
104104 },
105105 });
106106

LayoutTests/inspector/canvas/recording-webgl.html

@@function test() {
513513 name: "Canvas.recordingWebGL.singleFrame",
514514 description: "Check that the recording is stopped after a single frame.",
515515 test(resolve, reject) {
516  startRecording(WI.Canvas.ContextType.WebGL, resolve, reject, {singleFrame: true});
 516 startRecording(WI.Canvas.ContextType.WebGL, resolve, reject, {frameCount: 1});
517517 },
518518 });
519519

@@function test() {
521521 name: "Canvas.recordingWebGL.multipleFrames",
522522 description: "Check that recording data is serialized correctly for multiple frames.",
523523 test(resolve, reject) {
524  startRecording(WI.Canvas.ContextType.WebGL, resolve, reject, {singleFrame: false});
 524 startRecording(WI.Canvas.ContextType.WebGL, resolve, reject);
525525 },
526526 });
527527

LayoutTests/inspector/canvas/resources/recording-utilities.js

@@TestPage.registerInitializer(() => {
8383 return canvases[0];
8484 };
8585
86  window.startRecording = function(type, resolve, reject, {singleFrame, memoryLimit} = {}) {
 86 window.startRecording = function(type, resolve, reject, {frameCount, memoryLimit} = {}) {
8787 let canvas = getCanvas(type);
8888 if (!canvas) {
8989 reject(`Missing canvas with type "${type}".`);

@@TestPage.registerInitializer(() => {
110110 });
111111
112112 let bufferUsed = 0;
113  let frameCount = 0;
 113 let recordingFrameCount = 0;
114114 function handleRecordingProgress(event) {
115  InspectorTest.assert(canvas.recordingFrameCount > frameCount, "Additional frames were captured for this progress event.");
116  frameCount = canvas.recordingFrameCount;
 115 InspectorTest.assert(canvas.recordingFrameCount > recordingFrameCount, "Additional frames were captured for this progress event.");
 116 recordingFrameCount = canvas.recordingFrameCount;
117117
118118 InspectorTest.assert(canvas.recordingBufferUsed > bufferUsed, "Total memory usage increases with each progress event.");
119119 bufferUsed = canvas.recordingBufferUsed;

@@TestPage.registerInitializer(() => {
129129 InspectorTest.assert(recording.source === canvas, "Recording should be of the given canvas.");
130130 InspectorTest.assert(recording.source.contextType === type, `Recording should be of a canvas with type "${type}".`);
131131 InspectorTest.assert(recording.source.recordingCollection.has(recording), "Recording should be in the canvas' list of recordings.");
132  InspectorTest.assert(recording.frames.length === frameCount, `Recording should have ${frameCount} frames.`)
 132 InspectorTest.assert(recording.frames.length === recordingFrameCount, `Recording should have ${recordingFrameCount} frames.`)
 133
 134 if (frameCount)
 135 InspectorTest.assert(recording.frames.length === frameCount, `Recording frame count should match the provided value ${frameCount}.`)
133136
134137 Promise.all(recording.actions.map((action) => action.swizzle(recording))).then(() => {
135138 swizzled = true;

@@TestPage.registerInitializer(() => {
148151 InspectorTest.evaluateInPage(`performActions()`).catch(reject);
149152 });
150153
151  CanvasAgent.startRecording(canvas.identifier, singleFrame, memoryLimit).catch(reject);
 154 CanvasAgent.startRecording(canvas.identifier, frameCount, memoryLimit).catch(reject);
152155 };
153156
154157 window.consoleRecord = function(type, resolve, reject) {

LayoutTests/inspector/canvas/setCreationRecordingFrameCount-expected.txt

 1Test that contexts created after calling Canvas.setCreationRecordingFrameCount are properly recorded.
 2
 3
 4== Running test suite: Canvas.setCreationRecordingFrameCount
 5-- Running test case: Canvas.setCreationRecordingFrameCount.2D.None
 6PASS: Canvas should have no finished recordings.
 7
 8-- Running test case: Canvas.setCreationRecordingFrameCount.2D.Single
 9PASS: Recording stopped.
 10PASS: Recording should have one frame.
 11PASS: Recording should have one action.
 12PASS: Canvas should have one finished recording.
 13
 14-- Running test case: Canvas.setCreationRecordingFrameCount.2D.Multiple
 15PASS: Canvas should have no finished recordings.
 16PASS: Canvas should be actively recording.
 17PASS: Recording should have 2 frames.
 18
 19-- Running test case: Canvas.setCreationRecordingFrameCount.WebGL.None
 20PASS: Canvas should have no finished recordings.
 21
 22-- Running test case: Canvas.setCreationRecordingFrameCount.WebGL.Single
 23PASS: Recording stopped.
 24PASS: Recording should have one frame.
 25PASS: Recording should have one action.
 26PASS: Canvas should have one finished recording.
 27
 28-- Running test case: Canvas.setCreationRecordingFrameCount.WebGL.Multiple
 29PASS: Canvas should have no finished recordings.
 30PASS: Canvas should be actively recording.
 31PASS: Recording should have 2 frames.
 32

LayoutTests/inspector/canvas/setCreationRecordingFrameCount.html

 1<!DOCTYPE html>
 2<html>
 3<head>
 4<script src="../../http/tests/inspector/resources/inspector-test.js"></script>
 5<script>
 6if (window.internals)
 7 window.internals.settings.setWebGLErrorsToConsoleEnabled(false);
 8
 9let timeoutID = NaN;
 10
 11function cancelActions() {
 12 clearTimeout(timeoutID);
 13 timeoutID = NaN;
 14}
 15
 16function performActions(frames) {
 17 let index = 0;
 18 function executeFrameFunction() {
 19 frames[index++]();
 20
 21 if (index < frames.length)
 22 timeoutID = setTimeout(executeFrameFunction, 0);
 23 else {
 24 setTimeout(() => {
 25 TestPage.dispatchEventToFrontend("LastFrame");
 26 }, 0);
 27 }
 28 };
 29 executeFrameFunction();
 30}
 31
 32function performActions2D() {
 33 let context = document.createElement("canvas").getContext("2d");
 34 performActions([
 35 () => {
 36 context.fill();
 37 },
 38 () => {
 39 context.stroke();
 40 },
 41 ]);
 42}
 43
 44function performActionsWebGL() {
 45 let context = document.createElement("canvas").getContext("webgl");
 46 performActions([
 47 () => {
 48 context.drawArrays(1, 2, 3);
 49 },
 50 () => {
 51 context.drawElements(1, 2, 3, 4);
 52 },
 53 ]);
 54}
 55
 56function test() {
 57 let suite = InspectorTest.createAsyncSuite("Canvas.setCreationRecordingFrameCount");
 58
 59 function addTest({name, description, frameCount, expression, handleRecordingStarted, handleRecordingStopped, handleLastFrame}) {
 60 suite.addTestCase({
 61 name,
 62 description,
 63 test(resolve, reject) {
 64 CanvasAgent.setCreationRecordingFrameCount(frameCount).catch(reject);
 65
 66 let canvas = null;
 67
 68 function handleRecordingStartedWrapper(event)
 69 {
 70 InspectorTest.assert(event.target === canvas, "Should be recording the canvas.");
 71
 72 handleRecordingStarted(canvas);
 73 }
 74
 75 function handleRecordingStoppedWrapper(event)
 76 {
 77 InspectorTest.assert(event.target === canvas, "Should be recording the canvas.");
 78
 79 handleRecordingStopped(canvas, event.data.recording);
 80 }
 81
 82 WI.canvasManager.awaitEvent(WI.CanvasManager.Event.CanvasAdded)
 83 .then((event) => {
 84 canvas = event.data.canvas;
 85
 86 canvas.addEventListener(WI.Canvas.Event.RecordingStarted, handleRecordingStartedWrapper);
 87 canvas.addEventListener(WI.Canvas.Event.RecordingStopped, handleRecordingStoppedWrapper);
 88 });
 89
 90 InspectorTest.awaitEvent("LastFrame")
 91 .then((event) => {
 92 canvas.removeEventListener(WI.Canvas.Event.RecordingStarted, handleRecordingStartedWrapper);
 93 canvas.removeEventListener(WI.Canvas.Event.RecordingStopped, handleRecordingStoppedWrapper);
 94
 95 handleLastFrame(canvas);
 96 })
 97 .then(resolve, reject);
 98
 99 InspectorTest.evaluateInPage(expression).catch(reject);
 100 },
 101 });
 102 }
 103
 104 addTest({
 105 name: "Canvas.setCreationRecordingFrameCount.2D.None",
 106 description: "Check that newly created 2D contexts are not recorded when setCreationRecordingFrameCount is 0.",
 107 frameCount: 0,
 108 expression: `performActions2D()`,
 109 handleRecordingStarted(canvas) {
 110 InspectorTest.fail("Canvas should not be recording.");
 111 },
 112 handleRecordingStopped(canvas, recording) {
 113 InspectorTest.fail("Canvas should not be recording.");
 114 },
 115 handleLastFrame(canvas) {
 116 InspectorTest.expectEqual(canvas.recordingCollection.size, 0, "Canvas should have no finished recordings.");
 117 },
 118 });
 119
 120 addTest({
 121 name: "Canvas.setCreationRecordingFrameCount.2D.Single",
 122 description: "Check that newly created 2D contexts only record one frame when setCreationRecordingFrameCount is 1.",
 123 frameCount: 1,
 124 expression: `performActions2D()`,
 125 handleRecordingStarted(canvas) {
 126 InspectorTest.pass("Recording started.");
 127 },
 128 handleRecordingStopped(canvas, recording) {
 129 InspectorTest.pass("Recording stopped.");
 130 InspectorTest.expectEqual(recording.frames.length, 1, "Recording should have one frame.");
 131 InspectorTest.expectEqual(recording.frames[0].actions.length, 1, "Recording should have one action.");
 132 },
 133 handleLastFrame(canvas) {
 134 InspectorTest.expectEqual(canvas.recordingCollection.size, 1, "Canvas should have one finished recording.");
 135 },
 136 });
 137
 138 addTest({
 139 name: "Canvas.setCreationRecordingFrameCount.2D.Multiple",
 140 description: "Check that newly created 2D contexts don't finish recording when setCreationRecordingFrameCount is 10.",
 141 frameCount: 10,
 142 expression: `performActions2D()`,
 143 handleRecordingStarted(canvas) {
 144 InspectorTest.pass("Recording started.");
 145 },
 146 handleRecordingStopped(canvas, recording) {
 147 InspectorTest.fail("Canvas should not be done recording.");
 148 },
 149 handleLastFrame(canvas) {
 150 InspectorTest.expectEqual(canvas.recordingCollection.size, 0, "Canvas should have no finished recordings.");
 151 InspectorTest.expectThat(canvas.recordingActive, "Canvas should be actively recording.");
 152 InspectorTest.expectEqual(canvas.recordingFrameCount, 2, "Recording should have 2 frames.");
 153
 154 canvas.stopRecording();
 155 },
 156 });
 157
 158 addTest({
 159 name: "Canvas.setCreationRecordingFrameCount.WebGL.None",
 160 description: "Check that newly created WebGL contexts are not recorded when setCreationRecordingFrameCount is 0.",
 161 frameCount: 0,
 162 expression: `performActionsWebGL()`,
 163 handleRecordingStarted(canvas) {
 164 InspectorTest.fail("Canvas should not be recording.");
 165 },
 166 handleRecordingStopped(canvas, recording) {
 167 InspectorTest.fail("Canvas should not be recording.");
 168 },
 169 handleLastFrame(canvas) {
 170 InspectorTest.expectEqual(canvas.recordingCollection.size, 0, "Canvas should have no finished recordings.");
 171 },
 172 });
 173
 174 addTest({
 175 name: "Canvas.setCreationRecordingFrameCount.WebGL.Single",
 176 description: "Check that newly created WebGL contexts only record one frame when setCreationRecordingFrameCount is 1.",
 177 frameCount: 1,
 178 expression: `performActionsWebGL()`,
 179 handleRecordingStarted(canvas) {
 180 InspectorTest.pass("Recording started.");
 181 },
 182 handleRecordingStopped(canvas, recording) {
 183 InspectorTest.pass("Recording stopped.");
 184 InspectorTest.expectEqual(recording.frames.length, 1, "Recording should have one frame.");
 185 InspectorTest.expectEqual(recording.frames[0].actions.length, 1, "Recording should have one action.");
 186 },
 187 handleLastFrame(canvas) {
 188 InspectorTest.expectEqual(canvas.recordingCollection.size, 1, "Canvas should have one finished recording.");
 189 },
 190 });
 191
 192 addTest({
 193 name: "Canvas.setCreationRecordingFrameCount.WebGL.Multiple",
 194 description: "Check that newly created WebGL contexts don't finish recording when setCreationRecordingFrameCount is 10.",
 195 frameCount: 10,
 196 expression: `performActionsWebGL()`,
 197 handleRecordingStarted(canvas) {
 198 InspectorTest.pass("Recording started.");
 199 },
 200 handleRecordingStopped(canvas, recording) {
 201 InspectorTest.fail("Canvas should not be done recording.");
 202 },
 203 handleLastFrame(canvas) {
 204 InspectorTest.expectEqual(canvas.recordingCollection.size, 0, "Canvas should have no finished recordings.");
 205 InspectorTest.expectThat(canvas.recordingActive, "Canvas should be actively recording.");
 206 InspectorTest.expectEqual(canvas.recordingFrameCount, 2, "Recording should have 2 frames.");
 207
 208 canvas.stopRecording();
 209 },
 210 });
 211
 212 suite.runTestCasesAndFinish();
 213}
 214</script>
 215</head>
 216<body onload="runTest()">
 217 <p>Test that contexts created after calling Canvas.setCreationRecordingFrameCount are properly recorded.</p>
 218</body>
 219</html>