Source/JavaScriptCore/ChangeLog

112017-11-02 Devin Rousso <webkit@devinrousso.com>
22
 3 Web Inspector: provide method for recording CanvasRenderingContext2D from JavaScript
 4 https://bugs.webkit.org/show_bug.cgi?id=175166
 5 <rdar://problem/34040740>
 6
 7 Reviewed by NOBODY (OOPS!).
 8
 9 * inspector/protocol/Recording.json:
 10 Add optional `name` that will be used by the frontend for uniquely identifying the Recording.
 11
 12 * inspector/JSGlobalObjectConsoleClient.h:
 13 * inspector/JSGlobalObjectConsoleClient.cpp:
 14 (Inspector::JSGlobalObjectConsoleClient::record):
 15 (Inspector::JSGlobalObjectConsoleClient::recordEnd):
 16
 17 * runtime/ConsoleClient.h:
 18 * runtime/ConsoleObject.cpp:
 19 (JSC::ConsoleObject::finishCreation):
 20 (JSC::consoleProtoFuncRecord):
 21 (JSC::consoleProtoFuncRecordEnd):
 22
 232017-11-02 Devin Rousso <webkit@devinrousso.com>
 24
325 Web Inspector: Canvas Tab: show supported GL extensions for selected canvas
426 https://bugs.webkit.org/show_bug.cgi?id=179070
527 <rdar://problem/35278276>

Source/WebCore/ChangeLog

 12017-11-02 Devin Rousso <webkit@devinrousso.com>
 2
 3 Web Inspector: provide method for recording CanvasRenderingContext2D from JavaScript
 4 https://bugs.webkit.org/show_bug.cgi?id=175166
 5 <rdar://problem/34040740>
 6
 7 Reviewed by NOBODY (OOPS!).
 8
 9 No new tests, updated existing tests.
 10
 11 * inspector/InspectorCanvas.h:
 12 * inspector/InspectorCanvas.cpp:
 13 (WebCore::InspectorCanvas::resetRecordingData):
 14
 15 * inspector/InspectorCanvasAgent.h:
 16 * inspector/InspectorCanvasAgent.cpp:
 17 (WebCore::InspectorCanvasAgent::didFinishRecordingCanvasFrame):
 18 (WebCore::InspectorCanvasAgent::consoleStartRecordingCanvas):
 19
 20 * inspector/InspectorInstrumentation.h:
 21 * inspector/InspectorInstrumentation.cpp:
 22 (WebCore::InspectorInstrumentation::consoleStartRecordingCanvas):
 23 (WebCore::InspectorInstrumentation::consoleStartRecordingCanvasImpl):
 24
 25 * page/PageConsoleClient.h:
 26 * page/PageConsoleClient.cpp:
 27 (WebCore::PageConsoleClient::record):
 28 (WebCore::PageConsoleClient::recordEnd):
 29
 30 * workers/WorkerConsoleClient.h:
 31 * workers/WorkerConsoleClient.cpp:
 32 (WebCore::WorkerConsoleClient::record):
 33 (WebCore::WorkerConsoleClient::recordEnd):
 34
1352017-11-01 Ryosuke Niwa <rniwa@webkit.org>
236
337 Assert that updateStyle and updateLayout are only called when it's safe to dispatch events

Source/WebInspectorUI/ChangeLog

112017-11-02 Devin Rousso <webkit@devinrousso.com>
22
 3 Web Inspector: provide method for recording CanvasRenderingContext2D from JavaScript
 4 https://bugs.webkit.org/show_bug.cgi?id=175166
 5 <rdar://problem/34040740>
 6
 7 Reviewed by NOBODY (OOPS!).
 8
 9 * UserInterface/Controllers/CanvasManager.js:
 10 (WI.CanvasManager.prototype.recordingFinished):
 11 If a `name` is sent with the payload, use it as the suggested name.
 12
 13 * UserInterface/Models/NativeFunctionParameters.js:
 14 Add `console.record` and `console.recordEnd`.
 15
 16 * UserInterface/Views/CanvasTabContentView.js:
 17 (WI.CanvasTabContentView.prototype.showRepresentedObject):
 18 Drive-by: remove logic that toggled the collapsed state of the navigation sidebar, as this
 19 was not very controllable by the user and often was aggravating.
 20
 21 (WI.CanvasTabContentView.prototype._recordingStopped):
 22 Only show the recording if it was not started from the console. This can determined by
 23 CanvasManager when it recieves a recording if the recording's source is not the same as the
 24 current canvas being recorded.
 25
 262017-11-02 Devin Rousso <webkit@devinrousso.com>
 27
328 Web Inspector: Canvas Tab: show supported GL extensions for selected canvas
429 https://bugs.webkit.org/show_bug.cgi?id=179070
530 <rdar://problem/35278276>

Source/JavaScriptCore/inspector/JSGlobalObjectConsoleClient.cpp

@@void JSGlobalObjectConsoleClient::timeStamp(ExecState*, Ref<ScriptArguments>&&)
165165 warnUnimplemented(ASCIILiteral("console.timeStamp"));
166166}
167167
 168void JSGlobalObjectConsoleClient::record(ExecState*, Ref<ScriptArguments>&&) { }
 169void JSGlobalObjectConsoleClient::recordEnd(ExecState*, Ref<ScriptArguments>&&) { }
 170
168171void JSGlobalObjectConsoleClient::warnUnimplemented(const String& method)
169172{
170173 String message = method + " is currently ignored in JavaScript context inspection.";

Source/JavaScriptCore/inspector/JSGlobalObjectConsoleClient.h

@@protected:
5353 void time(JSC::ExecState*, const String& title) override;
5454 void timeEnd(JSC::ExecState*, const String& title) override;
5555 void timeStamp(JSC::ExecState*, Ref<ScriptArguments>&&) override;
 56 void record(JSC::ExecState*, Ref<ScriptArguments>&&) override;
 57 void recordEnd(JSC::ExecState*, Ref<ScriptArguments>&&) override;
5658
5759private:
5860 void warnUnimplemented(const String& method);

Source/JavaScriptCore/inspector/protocol/Recording.json

3535 { "name": "type", "$ref": "Type" },
3636 { "name": "initialState", "$ref": "InitialState", "description": "JSON data of inital state of object before recording." },
3737 { "name": "frames", "type": "array", "items": { "$ref": "Frame" }, "description": "JSON data of all object API calls." },
38  { "name": "data", "type": "array", "items": { "type": "any" }, "description": "Array of objects that can be referenced by index. Used to avoid duplicating objects." }
 38 { "name": "data", "type": "array", "items": { "type": "any" }, "description": "Array of objects that can be referenced by index. Used to avoid duplicating objects." },
 39 { "name": "name", "type": "string", "optional": true }
3940 ]
4041 }
4142 ]

Source/JavaScriptCore/runtime/ConsoleClient.h

@@public:
6262 virtual void time(ExecState*, const String& title) = 0;
6363 virtual void timeEnd(ExecState*, const String& title) = 0;
6464 virtual void timeStamp(ExecState*, Ref<Inspector::ScriptArguments>&&) = 0;
 65 virtual void record(ExecState*, Ref<Inspector::ScriptArguments>&&) = 0;
 66 virtual void recordEnd(ExecState*, Ref<Inspector::ScriptArguments>&&) = 0;
6567
6668private:
6769 enum ArgumentRequirement { ArgumentRequired, ArgumentNotRequired };

Source/JavaScriptCore/runtime/ConsoleObject.cpp

@@static EncodedJSValue JSC_HOST_CALL consoleProtoFuncTimeStamp(ExecState*);
5757static EncodedJSValue JSC_HOST_CALL consoleProtoFuncGroup(ExecState*);
5858static EncodedJSValue JSC_HOST_CALL consoleProtoFuncGroupCollapsed(ExecState*);
5959static EncodedJSValue JSC_HOST_CALL consoleProtoFuncGroupEnd(ExecState*);
 60static EncodedJSValue JSC_HOST_CALL consoleProtoFuncRecord(ExecState*);
 61static EncodedJSValue JSC_HOST_CALL consoleProtoFuncRecordEnd(ExecState*);
6062
6163const ClassInfo ConsoleObject::s_info = { "Console", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(ConsoleObject) };
6264

@@void ConsoleObject::finishCreation(VM& vm, JSGlobalObject* globalObject)
9597 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("group", consoleProtoFuncGroup, static_cast<unsigned>(PropertyAttribute::None), 0);
9698 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("groupCollapsed", consoleProtoFuncGroupCollapsed, static_cast<unsigned>(PropertyAttribute::None), 0);
9799 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("groupEnd", consoleProtoFuncGroupEnd, static_cast<unsigned>(PropertyAttribute::None), 0);
 100 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("record", consoleProtoFuncRecord, static_cast<unsigned>(PropertyAttribute::None), 0);
 101 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("recordEnd", consoleProtoFuncRecordEnd, static_cast<unsigned>(PropertyAttribute::None), 0);
98102}
99103
100104static String valueToStringWithUndefinedOrNullCheck(ExecState* exec, JSValue value)

@@static EncodedJSValue JSC_HOST_CALL consoleProtoFuncGroupEnd(ExecState* exec)
367371 return JSValue::encode(jsUndefined());
368372}
369373
 374static EncodedJSValue JSC_HOST_CALL consoleProtoFuncRecord(ExecState* exec)
 375{
 376 ConsoleClient* client = exec->lexicalGlobalObject()->consoleClient();
 377 if (!client)
 378 return JSValue::encode(jsUndefined());
 379
 380 client->record(exec, Inspector::createScriptArguments(exec, 0));
 381 return JSValue::encode(jsUndefined());
 382}
 383
 384static EncodedJSValue JSC_HOST_CALL consoleProtoFuncRecordEnd(ExecState* exec)
 385{
 386 ConsoleClient* client = exec->lexicalGlobalObject()->consoleClient();
 387 if (!client)
 388 return JSValue::encode(jsUndefined());
 389
 390 client->recordEnd(exec, Inspector::createScriptArguments(exec, 0));
 391 return JSValue::encode(jsUndefined());
 392}
 393
370394} // namespace JSC

Source/WebCore/inspector/InspectorCanvas.cpp

@@void InspectorCanvas::resetRecordingData()
100100 m_actionNeedingSnapshot = nullptr;
101101 m_serializedDuplicateData = nullptr;
102102 m_indexedDuplicateData.clear();
 103 m_recordingName = { };
103104 m_bufferLimit = 100 * 1024 * 1024;
104105 m_bufferUsed = 0;
105106 m_singleFrame = true;

Source/WebCore/inspector/InspectorCanvas.h

@@public:
6868 void markNewFrame();
6969 void markCurrentFrameIncomplete();
7070
 71 const String& recordingName() const { return m_recordingName; }
 72 void setRecordingName(const String& name) { m_recordingName = name; }
 73
7174 void setBufferLimit(long);
7275 bool hasBufferSpace() const;
7376

@@private:
115118 RefPtr<Inspector::Protocol::Array<Inspector::InspectorValue>> m_actionNeedingSnapshot;
116119 RefPtr<Inspector::Protocol::Array<Inspector::InspectorValue>> m_serializedDuplicateData;
117120 Vector<DuplicateDataVariant> m_indexedDuplicateData;
 121 String m_recordingName;
118122 size_t m_bufferLimit { 100 * 1024 * 1024 };
119123 size_t m_bufferUsed { 0 };
120124 bool m_singleFrame { true };

Source/WebCore/inspector/InspectorInstrumentation.cpp

@@void InspectorInstrumentation::stopProfilingImpl(InstrumentingAgents& instrument
898898 timelineAgent->stopFromConsole(exec, title);
899899}
900900
 901void InspectorInstrumentation::consoleStartRecordingCanvasImpl(InstrumentingAgents& instrumentingAgents, HTMLCanvasElement& canvasElement, JSC::ExecState* exec, JSC::JSObject* options)
 902{
 903 if (InspectorCanvasAgent* canvasAgent = instrumentingAgents.inspectorCanvasAgent())
 904 canvasAgent->consoleStartRecordingCanvas(canvasElement, exec, options);
 905}
 906
901907void InspectorInstrumentation::didOpenDatabaseImpl(InstrumentingAgents& instrumentingAgents, RefPtr<Database>&& database, const String& domain, const String& name, const String& version)
902908{
903909 if (!instrumentingAgents.inspectorEnvironment().developerExtrasEnabled())

Source/WebCore/inspector/InspectorInstrumentation.h

4747#include "StorageArea.h"
4848#include "WorkerGlobalScope.h"
4949#include "WorkerInspectorController.h"
 50#include <runtime/JSCInlines.h>
5051#include <wtf/MemoryPressureHandler.h>
5152#include <wtf/RefPtr.h>
5253

@@public:
218219 static void consoleTimeStamp(Frame&, Ref<Inspector::ScriptArguments>&&);
219220 static void startProfiling(Page&, JSC::ExecState*, const String& title);
220221 static void stopProfiling(Page&, JSC::ExecState*, const String& title);
 222 static void consoleStartRecordingCanvas(HTMLCanvasElement&, JSC::ExecState*, JSC::JSObject* options);
221223
222224 static void didRequestAnimationFrame(Document&, int callbackId);
223225 static void didCancelAnimationFrame(Document&, int callbackId);

@@private:
384386 static void stopConsoleTimingImpl(InstrumentingAgents&, Frame&, const String& title, Ref<Inspector::ScriptCallStack>&&);
385387 static void stopConsoleTimingImpl(InstrumentingAgents&, const String& title, Ref<Inspector::ScriptCallStack>&&);
386388 static void consoleTimeStampImpl(InstrumentingAgents&, Frame&, Ref<Inspector::ScriptArguments>&&);
 389 static void startProfilingImpl(InstrumentingAgents&, JSC::ExecState*, const String& title);
 390 static void stopProfilingImpl(InstrumentingAgents&, JSC::ExecState*, const String& title);
 391 static void consoleStartRecordingCanvasImpl(InstrumentingAgents&, HTMLCanvasElement&, JSC::ExecState*, JSC::JSObject* options);
387392
388393 static void didRequestAnimationFrameImpl(InstrumentingAgents&, int callbackId, Document&);
389394 static void didCancelAnimationFrameImpl(InstrumentingAgents&, int callbackId, Document&);
390395 static InspectorInstrumentationCookie willFireAnimationFrameImpl(InstrumentingAgents&, int callbackId, Document&);
391396 static void didFireAnimationFrameImpl(const InspectorInstrumentationCookie&);
392397
393  static void startProfilingImpl(InstrumentingAgents&, JSC::ExecState*, const String& title);
394  static void stopProfilingImpl(InstrumentingAgents&, JSC::ExecState*, const String& title);
395 
396398 static void didOpenDatabaseImpl(InstrumentingAgents&, RefPtr<Database>&&, const String& domain, const String& name, const String& version);
397399
398400 static void didDispatchDOMStorageEventImpl(InstrumentingAgents&, const String& key, const String& oldValue, const String& newValue, StorageType, SecurityOrigin*);

@@inline void InspectorInstrumentation::stopProfiling(Page& page, JSC::ExecState*
13101312 stopProfilingImpl(instrumentingAgentsForPage(page), exec, title);
13111313}
13121314
 1315inline void InspectorInstrumentation::consoleStartRecordingCanvas(HTMLCanvasElement& canvasElement, JSC::ExecState* exec, JSC::JSObject* options)
 1316{
 1317 FAST_RETURN_IF_NO_FRONTENDS(void());
 1318 if (InstrumentingAgents* instrumentingAgents = instrumentingAgentsForDocument(&canvasElement.document()))
 1319 consoleStartRecordingCanvasImpl(*instrumentingAgents, canvasElement, exec, options);
 1320}
 1321
13131322inline void InspectorInstrumentation::didRequestAnimationFrame(Document& document, int callbackId)
13141323{
13151324 if (InstrumentingAgents* instrumentingAgents = instrumentingAgentsForDocument(document))

Source/WebCore/inspector/agents/InspectorCanvasAgent.cpp

@@void InspectorCanvasAgent::didFinishRecordingCanvasFrame(HTMLCanvasElement& canv
516516 .setData(inspectorCanvas->releaseData())
517517 .release();
518518
 519 const String& name = inspectorCanvas->recordingName();
 520 if (!name.isEmpty())
 521 recording->setName(name);
 522
519523 m_frontendDispatcher->recordingFinished(inspectorCanvas->identifier(), WTFMove(recording));
520524
521525 inspectorCanvas->resetRecordingData();
522526}
523527
 528void InspectorCanvasAgent::consoleStartRecordingCanvas(HTMLCanvasElement& canvasElement, JSC::ExecState* exec, JSC::JSObject* options)
 529{
 530 auto* inspectorCanvas = findInspectorCanvas(canvasElement);
 531 if (!inspectorCanvas)
 532 return;
 533
 534 if (inspectorCanvas->canvas().renderingContext()->callTracingActive())
 535 return;
 536
 537 inspectorCanvas->resetRecordingData();
 538
 539 if (exec && options) {
 540 if (JSC::JSValue optionName = options->get(exec, JSC::Identifier::fromString(exec, "name")))
 541 inspectorCanvas->setRecordingName(optionName.toWTFString(exec));
 542 if (JSC::JSValue optionSingleFrame = options->get(exec, JSC::Identifier::fromString(exec, "singleFrame")))
 543 inspectorCanvas->setSingleFrame(optionSingleFrame.toBoolean(exec));
 544 if (JSC::JSValue optionMemoryLimit = options->get(exec, JSC::Identifier::fromString(exec, "memoryLimit")))
 545 inspectorCanvas->setBufferLimit(optionMemoryLimit.toNumber(exec));
 546 }
 547
 548 inspectorCanvas->canvas().renderingContext()->setCallTracingActive(true);
 549}
 550
524551#if ENABLE(WEBGL)
525552void InspectorCanvasAgent::didEnableExtension(WebGLRenderingContextBase& context, const String& extension)
526553{

Source/WebCore/inspector/agents/InspectorCanvasAgent.h

@@public:
8787 void didChangeCanvasMemory(HTMLCanvasElement&);
8888 void recordCanvasAction(CanvasRenderingContext&, const String&, Vector<RecordCanvasActionVariant>&& = { });
8989 void didFinishRecordingCanvasFrame(HTMLCanvasElement&, bool forceDispatch = false);
 90 void consoleStartRecordingCanvas(HTMLCanvasElement&, JSC::ExecState*, JSC::JSObject* options);
9091#if ENABLE(WEBGL)
9192 void didEnableExtension(WebGLRenderingContextBase&, const String&);
9293 void didCreateProgram(WebGLRenderingContextBase&, WebGLProgram&);

Source/WebCore/page/PageConsoleClient.cpp

2929#include "config.h"
3030#include "PageConsoleClient.h"
3131
 32#include "CanvasRenderingContext2D.h"
3233#include "Chrome.h"
3334#include "ChromeClient.h"
3435#include "Document.h"
3536#include "Frame.h"
 37#include "HTMLCanvasElement.h"
3638#include "InspectorController.h"
3739#include "InspectorInstrumentation.h"
 40#include "JSCanvasRenderingContext2D.h"
 41#include "JSHTMLCanvasElement.h"
3842#include "JSMainThreadExecState.h"
3943#include "MainFrame.h"
4044#include "Page.h"
4145#include "ScriptableDocumentParser.h"
4246#include "Settings.h"
 47#include <bindings/ScriptValue.h>
4348#include <inspector/ConsoleMessage.h>
4449#include <inspector/ScriptArguments.h>
4550#include <inspector/ScriptCallStack.h>
4651#include <inspector/ScriptCallStackFactory.h>
 52#include <runtime/JSCInlines.h>
 53#include <wtf/text/WTFString.h>
 54
 55#if ENABLE(WEBGL)
 56#include "JSWebGLRenderingContext.h"
 57#include "WebGLRenderingContext.h"
 58#endif
4759
4860
4961namespace WebCore {

@@void PageConsoleClient::timeStamp(JSC::ExecState*, Ref<ScriptArguments>&& argume
206218 InspectorInstrumentation::consoleTimeStamp(m_page.mainFrame(), WTFMove(arguments));
207219}
208220
 221void PageConsoleClient::record(JSC::ExecState* exec, Ref<ScriptArguments>&& arguments)
 222{
 223 if (arguments->argumentCount() < 1)
 224 return;
 225
 226 JSC::JSObject* target = arguments->argumentAt(0).jsValue().getObject();
 227 if (!target)
 228 return;
 229
 230 JSC::JSObject* options = nullptr;
 231 if (arguments->argumentCount() >= 2)
 232 options = arguments->argumentAt(1).jsValue().getObject();
 233
 234 if (HTMLCanvasElement* canvasElement = JSHTMLCanvasElement::toWrapped(*target->vm(), target))
 235 InspectorInstrumentation::consoleStartRecordingCanvas(*canvasElement, exec, options);
 236 else if (CanvasRenderingContext2D* context2d = JSCanvasRenderingContext2D::toWrapped(*target->vm(), target))
 237 InspectorInstrumentation::consoleStartRecordingCanvas(context2d->canvas(), exec, options);
 238#if ENABLE(WEBGL)
 239 else if (WebGLRenderingContext* contextWebGL = JSWebGLRenderingContext::toWrapped(*target->vm(), target))
 240 InspectorInstrumentation::consoleStartRecordingCanvas(contextWebGL->canvas(), exec, options);
 241#endif
 242}
 243
 244void PageConsoleClient::recordEnd(JSC::ExecState*, Ref<ScriptArguments>&& arguments)
 245{
 246 if (arguments->argumentCount() < 1)
 247 return;
 248
 249 JSC::JSObject* target = arguments->argumentAt(0).jsValue().getObject();
 250 if (!target)
 251 return;
 252
 253 if (HTMLCanvasElement* canvasElement = JSHTMLCanvasElement::toWrapped(*target->vm(), target))
 254 InspectorInstrumentation::didFinishRecordingCanvasFrame(*canvasElement, true);
 255 else if (CanvasRenderingContext2D* context2d = JSCanvasRenderingContext2D::toWrapped(*target->vm(), target))
 256 InspectorInstrumentation::didFinishRecordingCanvasFrame(context2d->canvas(), true);
 257#if ENABLE(WEBGL)
 258 else if (WebGLRenderingContext* contextWebGL = JSWebGLRenderingContext::toWrapped(*target->vm(), target))
 259 InspectorInstrumentation::didFinishRecordingCanvasFrame(contextWebGL->canvas(), true);
 260#endif
 261}
 262
209263} // namespace WebCore

Source/WebCore/page/PageConsoleClient.h

@@protected:
7474 void time(JSC::ExecState*, const String& title) override;
7575 void timeEnd(JSC::ExecState*, const String& title) override;
7676 void timeStamp(JSC::ExecState*, Ref<Inspector::ScriptArguments>&&) override;
 77 void record(JSC::ExecState*, Ref<Inspector::ScriptArguments>&&) override;
 78 void recordEnd(JSC::ExecState*, Ref<Inspector::ScriptArguments>&&) override;
7779
7880private:
7981 Page& m_page;

Source/WebCore/workers/WorkerConsoleClient.cpp

@@void WorkerConsoleClient::profileEnd(JSC::ExecState*, const String&) { }
7474void WorkerConsoleClient::takeHeapSnapshot(JSC::ExecState*, const String&) { }
7575void WorkerConsoleClient::timeStamp(JSC::ExecState*, Ref<ScriptArguments>&&) { }
7676
 77void WorkerConsoleClient::record(JSC::ExecState*, Ref<ScriptArguments>&&) { }
 78void WorkerConsoleClient::recordEnd(JSC::ExecState*, Ref<ScriptArguments>&&) { }
 79
7780} // namespace WebCore

Source/WebCore/workers/WorkerConsoleClient.h

@@protected:
5050 void time(JSC::ExecState*, const String& title) override;
5151 void timeEnd(JSC::ExecState*, const String& title) override;
5252 void timeStamp(JSC::ExecState*, Ref<Inspector::ScriptArguments>&&) override;
 53 void record(JSC::ExecState*, Ref<Inspector::ScriptArguments>&&) override;
 54 void recordEnd(JSC::ExecState*, Ref<Inspector::ScriptArguments>&&) override;
5355
5456private:
5557 WorkerGlobalScope& m_workerGlobalScope;

Source/WebInspectorUI/UserInterface/Controllers/CanvasManager.js

@@WI.CanvasManager = class CanvasManager extends WI.Object
142142 {
143143 // Called from WI.CanvasObserver.
144144
145  this._recordingCanvas = null;
146 
147145 let canvas = this._canvasIdentifierMap.get(canvasIdentifier);
148146 console.assert(canvas);
 147
 148 let fromConsole = canvas !== this._recordingCanvas;
 149 if (!fromConsole)
 150 this._recordingCanvas = null;
 151
149152 if (!canvas)
150153 return;
151154
152155 let recording = recordingPayload ? WI.Recording.fromPayload(recordingPayload) : null;
153156 if (recording) {
154157 recording.source = canvas;
155  recording.createDisplayName();
 158 recording.createDisplayName(recordingPayload.name);
156159
157160 canvas.recordingCollection.add(recording);
158161 }
159162
160  this.dispatchEventToListeners(WI.CanvasManager.Event.RecordingStopped, {canvas, recording});
 163 this.dispatchEventToListeners(WI.CanvasManager.Event.RecordingStopped, {canvas, recording, fromConsole});
161164 }
162165
163166 extensionEnabled(canvasIdentifier, extension)

Source/WebInspectorUI/UserInterface/Models/NativeFunctionParameters.js

@@WI.NativeConstructorFunctionParameters = {
173173 log: "message, [...values]",
174174 profile: "name",
175175 profileEnd: "name",
 176 record: "object, [options]",
 177 recordEnd: "object",
176178 table: "data, [columns]",
177179 takeHeapSnapshot: "[label]",
178180 time: "name = \"default\"",

Source/WebInspectorUI/UserInterface/Views/CanvasTabContentView.js

@@WI.CanvasTabContentView = class CanvasTabContentView extends WI.ContentBrowserTa
103103
104104 if (representedObject instanceof WI.CanvasCollection || representedObject instanceof WI.ShaderProgram) {
105105 this._overviewNavigationItem.hidden = true;
106  WI.navigationSidebar.collapsed = true;
107106 return;
108107 }
109108
110109 if (representedObject instanceof WI.Recording) {
111110 this._overviewNavigationItem.hidden = false;
112  representedObject.actions.then((actions) => {
113  this.navigationSidebarPanel.recording = representedObject;
114  WI.navigationSidebar.collapsed = false;
115  });
 111 this.navigationSidebarPanel.recording = representedObject;
116112 return;
117113 }
118114

@@WI.CanvasTabContentView = class CanvasTabContentView extends WI.ContentBrowserTa
245241 return;
246242 }
247243
248  this._recordingAdded(recording);
 244 this._recordingAdded(recording, {
 245 suppressShowRecording: event.data.fromConsole,
 246 });
249247 }
250248
251249 _navigationSidebarImport(event)

Source/WebInspectorUI/UserInterface/Views/RecordingNavigationSidebarPanel.js

@@WI.RecordingNavigationSidebarPanel = class RecordingNavigationSidebarPanel exten
5858 }
5959
6060 this._recording.actions.then((actions) => {
 61 if (recording !== this._recording)
 62 return;
 63
6164 this.contentTreeOutline.element.dataset.indent = Number.countDigits(actions.length);
6265
6366 if (actions[0] instanceof WI.RecordingInitialStateAction)

LayoutTests/ChangeLog

112017-11-02 Devin Rousso <webkit@devinrousso.com>
22
 3 Web Inspector: provide method for recording CanvasRenderingContext2D from JavaScript
 4 https://bugs.webkit.org/show_bug.cgi?id=175166
 5 <rdar://problem/34040740>
 6
 7 Reviewed by NOBODY (OOPS!).
 8
 9 * inspector/canvas/recording-2d-expected.txt:
 10 * inspector/canvas/recording-2d.html:
 11 (performConsoleRecording):
 12 * inspector/canvas/recording-webgl-expected.txt:
 13 * inspector/canvas/recording-webgl.html:
 14 (performConsoleRecording):
 15 * inspector/canvas/resources/recording-utilities.js:
 16 (TestPage.registerInitializer):
 17 * js/console-expected.txt:
 18 * js/console.html:
 19
 202017-11-02 Devin Rousso <webkit@devinrousso.com>
 21
322 Web Inspector: Canvas Tab: show supported GL extensions for selected canvas
423 https://bugs.webkit.org/show_bug.cgi?id=179070
524 <rdar://problem/35278276>

LayoutTests/inspector/canvas/recording-2d-expected.txt

@@data:
15221522 29: "_evaluateAndWrap"
15231523 30: [29,10,122,108]
15241524
 1525-- Running test case: Canvas.recording2D.Console
 1526PASS: The recording should have the name "TEST".
 1527PASS: The recording should have one frame.
 1528PASS: The first frame should have one action.
 1529
15251530-- Running test case: Canvas.recording2D.ActionParameterNaN
15261531PASS: The recording should have 1 frame.
15271532PASS: The first frame should have 1 action.

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

@@function performActions() {
395395 executeFrameFunction();
396396}
397397
 398function performConsoleActions() {
 399 console.record(ctx, {name: "TEST"});
 400
 401 ctx.fill();
 402
 403 console.recordEnd(ctx);
 404
 405 ctx.stroke();
 406}
 407
398408function performNaNActions() {
399409 ctx.globalAlpha = NaN;
400410}

@@function test() {
436446 });
437447
438448 suite.addTestCase({
 449 name: "Canvas.recording2D.Console",
 450 description: "Check that a recording can be triggered by console.record().",
 451 test(resolve, reject) {
 452 consoleRecord(resolve, reject);
 453 },
 454 });
 455
 456 suite.addTestCase({
439457 name: "Canvas.recording2D.ActionParameterNaN",
440458 description: "Check that NaN is converted into the proper value for serialization.",
441459 test(resolve, reject) {

@@function test() {
476494 description: "Check that a canvas is still able to be recorded after stopping a recording with no actions.",
477495 test(resolve, reject) {
478496 let canvas = getCanvas(WI.Canvas.ContextType.Canvas2D);
479  if (!canvas)
480  throw "Missing 2D canvas.";
 497 if (!canvas) {
 498 reject("Missing 2D canvas.");
 499 return;
 500 }
481501
482502 let eventCount = 0;
483503 function handleRecordingStopped(event) {
484504 InspectorTest.assert(event.data.canvas === canvas, "We should have stopped recording the selected canvas.");
485  ++eventCount;
 505 InspectorTest.assert(!event.data.recording, "The recording payload should be null.");
486506
 507 ++eventCount;
487508 if (eventCount == 1)
488509 InspectorTest.pass("A recording should have been started and stopped once.");
489 
490  if (eventCount >= 2) {
 510 else if (eventCount >= 2) {
491511 InspectorTest.pass("A recording should have been started and stopped twice.");
492512
493513 WI.canvasManager.removeEventListener(handleRecordingStopped);

LayoutTests/inspector/canvas/recording-webgl-expected.txt

@@data:
15001500 16: "_evaluateAndWrap"
15011501 17: [16,3,122,108]
15021502
 1503-- Running test case: Canvas.recordingWebGL.Console
 1504PASS: The recording should have the name "TEST".
 1505PASS: The recording should have one frame.
 1506PASS: The first frame should have one action.
 1507

LayoutTests/inspector/canvas/recording-webgl.html

@@function performActions() {
496496 executeFrameFunction();
497497}
498498
 499function performConsoleActions() {
 500 console.record(context, {name: "TEST"});
 501
 502 context.createTexture();
 503
 504 console.recordEnd(context);
 505
 506 context.createBuffer();
 507}
 508
499509function test() {
500510 let suite = InspectorTest.createAsyncSuite("Canvas.recordingWebGL");
501511

@@function test() {
532542 },
533543 });
534544
 545 suite.addTestCase({
 546 name: "Canvas.recordingWebGL.Console",
 547 description: "Check that a recording can be triggered by console.record().",
 548 test(resolve, reject) {
 549 consoleRecord(resolve, reject);
 550 },
 551 });
 552
535553 suite.runTestCasesAndFinish();
536554}
537555</script>

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

@@TestPage.registerInitializer(() => {
124124
125125 return canvas;
126126 };
 127
 128 window.consoleRecord = function(resolve, reject) {
 129 WI.canvasManager.awaitEvent(WI.CanvasManager.Event.RecordingStopped).then((event) => {
 130 let recording = event.data.recording;
 131 InspectorTest.expectEqual(recording.displayName, "TEST", "The recording should have the name \"TEST\".");
 132 InspectorTest.expectEqual(recording.frames.length, 1, "The recording should have one frame.");
 133 InspectorTest.expectEqual(recording.frames[0].actions.length, 1, "The first frame should have one action.");
 134 }).then(resolve, reject);
 135
 136 InspectorTest.evaluateInPage(`performConsoleActions()`);
 137 };
127138});

LayoutTests/js/console-expected.txt

@@PASS descriptor.configurable is true
163163PASS descriptor.writable is true
164164PASS descriptor.enumerable is true
165165
 166console.record
 167PASS typeof console.record is "function"
 168PASS console.record.length is 0
 169PASS descriptor.configurable is true
 170PASS descriptor.writable is true
 171PASS descriptor.enumerable is true
 172
 173console.recordEnd
 174PASS typeof console.recordEnd is "function"
 175PASS console.recordEnd.length is 0
 176PASS descriptor.configurable is true
 177PASS descriptor.writable is true
 178PASS descriptor.enumerable is true
 179
166180PASS Object.getOwnPropertyNames(console).length is enumerablePropertyCount
 181
 182fuzzing of target for console.record
 183PASS console.record() did not throw exception.
 184PASS console.record(undefined) did not throw exception.
 185PASS console.record(null) did not throw exception.
 186PASS console.record(1) did not throw exception.
 187PASS console.record("test") did not throw exception.
 188PASS console.record([]) did not throw exception.
 189PASS console.record({}) did not throw exception.
 190PASS console.record(window) did not throw exception.
 191PASS console.record(console) did not throw exception.
 192
 193fuzzing of options for console.record
 194PASS console.record({}, undefined) did not throw exception.
 195PASS console.record({}, null) did not throw exception.
 196PASS console.record({}, 1) did not throw exception.
 197PASS console.record({}, "test") did not throw exception.
 198PASS console.record({}, []) did not throw exception.
 199PASS console.record({}, {}) did not throw exception.
 200PASS console.record({}, window) did not throw exception.
 201PASS console.record({}, console) did not throw exception.
 202
 203fuzzing of target for console.recordEnd
 204PASS console.recordEnd() did not throw exception.
 205PASS console.recordEnd(undefined) did not throw exception.
 206PASS console.recordEnd(null) did not throw exception.
 207PASS console.recordEnd(1) did not throw exception.
 208PASS console.recordEnd("test") did not throw exception.
 209PASS console.recordEnd([]) did not throw exception.
 210PASS console.recordEnd({}) did not throw exception.
 211PASS console.recordEnd(window) did not throw exception.
 212PASS console.recordEnd(console) did not throw exception.
 213
 214ensure multiple calls don't throw
 215PASS console.recordEnd(window.canvas) did not throw exception.
 216PASS console.recordEnd(window.canvas) did not throw exception.
 217PASS console.recordEnd(window.canvas) did not throw exception.
 218PASS console.record(window.canvas) did not throw exception.
 219PASS console.record(window.canvas) did not throw exception.
 220PASS console.record(window.canvas) did not throw exception.
167221PASS successfullyParsed is true
168222
169223TEST COMPLETE

LayoutTests/js/console.html

@@for (property in console) {
4646
4747debug("");
4848shouldBe("Object.getOwnPropertyNames(console).length", "enumerablePropertyCount");
 49
 50const consoleRecordArguments = [
 51 `undefined`,
 52 `null`,
 53 `1`,
 54 `"test"`,
 55 `[]`,
 56 `{}`,
 57 `window`,
 58 `console`,
 59];
 60
 61debug("");
 62debug("fuzzing of target for console.record");
 63shouldNotThrow(`console.record()`);
 64for (let argument of consoleRecordArguments)
 65 shouldNotThrow(`console.record(${argument})`);
 66
 67debug("");
 68debug("fuzzing of options for console.record");
 69for (let argument of consoleRecordArguments)
 70 shouldNotThrow(`console.record({}, ${argument})`);
 71
 72debug("");
 73debug("fuzzing of target for console.recordEnd");
 74shouldNotThrow(`console.recordEnd()`);
 75for (let argument of consoleRecordArguments)
 76 shouldNotThrow(`console.recordEnd(${argument})`);
 77
 78debug("");
 79debug("ensure multiple calls don't throw");
 80shouldNotThrow(`console.recordEnd(window.canvas)`);
 81shouldNotThrow(`console.recordEnd(window.canvas)`);
 82shouldNotThrow(`console.recordEnd(window.canvas)`);
 83shouldNotThrow(`console.record(window.canvas)`);
 84shouldNotThrow(`console.record(window.canvas)`);
 85shouldNotThrow(`console.record(window.canvas)`);
 86
4987</script>
5088<script src="../resources/js-test-post.js"></script>
 89<canvas id="canvas"></canvas>
5190</body>
5291</html>