Unreviewed, rolling out r234489.
[WebKit-https.git] / Source / WebCore / inspector / InspectorCanvas.cpp
1 /*
2  * Copyright (C) 2017 Apple Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "InspectorCanvas.h"
28
29 #include "AffineTransform.h"
30 #include "CachedImage.h"
31 #include "CanvasGradient.h"
32 #include "CanvasPattern.h"
33 #include "CanvasRenderingContext.h"
34 #include "CanvasRenderingContext2D.h"
35 #include "Document.h"
36 #include "FloatPoint.h"
37 #include "Gradient.h"
38 #include "HTMLCanvasElement.h"
39 #include "HTMLImageElement.h"
40 #include "HTMLVideoElement.h"
41 #include "Image.h"
42 #include "ImageBitmap.h"
43 #include "ImageBitmapRenderingContext.h"
44 #include "ImageBuffer.h"
45 #include "ImageData.h"
46 #include "InspectorDOMAgent.h"
47 #include "InstrumentingAgents.h"
48 #include "JSCanvasDirection.h"
49 #include "JSCanvasFillRule.h"
50 #include "JSCanvasLineCap.h"
51 #include "JSCanvasLineJoin.h"
52 #include "JSCanvasTextAlign.h"
53 #include "JSCanvasTextBaseline.h"
54 #include "JSImageSmoothingQuality.h"
55 #include "JSMainThreadExecState.h"
56 #include "Path2D.h"
57 #include "Pattern.h"
58 #include "RecordingSwizzleTypes.h"
59 #include "SVGPathUtilities.h"
60 #include "StringAdaptors.h"
61 #if ENABLE(WEBGL)
62 #include "WebGLRenderingContext.h"
63 #endif
64 #if ENABLE(WEBGL2)
65 #include "WebGL2RenderingContext.h"
66 #endif
67 #if ENABLE(WEBGPU)
68 #include "WebGPURenderingContext.h"
69 #endif
70 #include <JavaScriptCore/IdentifiersFactory.h>
71 #include <JavaScriptCore/ScriptCallStack.h>
72 #include <JavaScriptCore/ScriptCallStackFactory.h>
73
74
75 namespace WebCore {
76
77 using namespace Inspector;
78
79 Ref<InspectorCanvas> InspectorCanvas::create(CanvasRenderingContext& context)
80 {
81     return adoptRef(*new InspectorCanvas(context));
82 }
83
84 InspectorCanvas::InspectorCanvas(CanvasRenderingContext& context)
85     : m_identifier("canvas:" + IdentifiersFactory::createIdentifier())
86     , m_context(context)
87 {
88 }
89
90 HTMLCanvasElement* InspectorCanvas::canvasElement()
91 {
92     auto* canvasBase = &m_context.canvasBase();
93     if (is<HTMLCanvasElement>(canvasBase))
94         return downcast<HTMLCanvasElement>(canvasBase);
95     return nullptr;
96 }
97
98 void InspectorCanvas::resetRecordingData()
99 {
100     m_initialState = nullptr;
101     m_frames = nullptr;
102     m_currentActions = nullptr;
103     m_actionNeedingSnapshot = nullptr;
104     m_serializedDuplicateData = nullptr;
105     m_indexedDuplicateData.clear();
106     m_recordingName = { };
107     m_bufferLimit = 100 * 1024 * 1024;
108     m_bufferUsed = 0;
109     m_singleFrame = true;
110
111     m_context.setCallTracingActive(false);
112 }
113
114 bool InspectorCanvas::hasRecordingData() const
115 {
116     return m_bufferUsed > 0;
117 }
118
119 bool InspectorCanvas::currentFrameHasData() const
120 {
121     return !!m_frames;
122 }
123
124 static bool shouldSnapshotWebGLAction(const String& name)
125 {
126     return name == "clear"
127         || name == "drawArrays"
128         || name == "drawElements";
129 }
130
131 void InspectorCanvas::recordAction(const String& name, Vector<RecordCanvasActionVariant>&& parameters)
132 {
133     if (!m_initialState) {
134         m_initialState = buildInitialState();
135         m_bufferUsed += m_initialState->memoryCost();
136     }
137
138     if (!m_frames)
139         m_frames = JSON::ArrayOf<Inspector::Protocol::Recording::Frame>::create();
140
141     if (!m_currentActions) {
142         m_currentActions = JSON::ArrayOf<JSON::Value>::create();
143
144         auto frame = Inspector::Protocol::Recording::Frame::create()
145             .setActions(m_currentActions)
146             .release();
147
148         m_frames->addItem(WTFMove(frame));
149
150         m_currentFrameStartTime = MonotonicTime::now();
151     }
152
153     appendActionSnapshotIfNeeded();
154
155     auto action = buildAction(name, WTFMove(parameters));
156     m_bufferUsed += action->memoryCost();
157     m_currentActions->addItem(action.ptr());
158
159 #if ENABLE(WEBGL)
160     if (is<WebGLRenderingContext>(m_context) && shouldSnapshotWebGLAction(name))
161         m_actionNeedingSnapshot = WTFMove(action);
162 #endif
163 }
164
165 RefPtr<Inspector::Protocol::Recording::InitialState>&& InspectorCanvas::releaseInitialState()
166 {
167     return WTFMove(m_initialState);
168 }
169
170 RefPtr<JSON::ArrayOf<Inspector::Protocol::Recording::Frame>>&& InspectorCanvas::releaseFrames()
171 {
172     appendActionSnapshotIfNeeded();
173
174     return WTFMove(m_frames);
175 }
176
177 RefPtr<JSON::ArrayOf<JSON::Value>>&& InspectorCanvas::releaseData()
178 {
179     m_indexedDuplicateData.clear();
180     return WTFMove(m_serializedDuplicateData);
181 }
182
183 void InspectorCanvas::finalizeFrame()
184 {
185     if (m_frames && m_frames->length() && !std::isnan(m_currentFrameStartTime)) {
186         auto currentFrame = static_cast<Inspector::Protocol::Recording::Frame*>(m_frames->get(m_frames->length() - 1).get());
187         currentFrame->setDuration((MonotonicTime::now() - m_currentFrameStartTime).milliseconds());
188
189         m_currentFrameStartTime = MonotonicTime::nan();
190     }
191
192     m_currentActions = nullptr;
193 }
194
195 void InspectorCanvas::markCurrentFrameIncomplete()
196 {
197     if (!m_currentActions || !m_frames || !m_frames->length())
198         return;
199
200     static_cast<Inspector::Protocol::Recording::Frame*>(m_frames->get(m_frames->length() - 1).get())->setIncomplete(true);
201 }
202
203 void InspectorCanvas::setBufferLimit(long memoryLimit)
204 {
205     m_bufferLimit = std::min<long>(memoryLimit, std::numeric_limits<int>::max());
206 }
207
208 bool InspectorCanvas::hasBufferSpace() const
209 {
210     return m_bufferUsed < m_bufferLimit;
211 }
212
213 Ref<Inspector::Protocol::Canvas::Canvas> InspectorCanvas::buildObjectForCanvas(InstrumentingAgents& instrumentingAgents, bool captureBacktrace)
214 {
215     Inspector::Protocol::Canvas::ContextType contextType;
216     if (is<CanvasRenderingContext2D>(m_context))
217         contextType = Inspector::Protocol::Canvas::ContextType::Canvas2D;
218     else if (is<ImageBitmapRenderingContext>(m_context))
219         contextType = Inspector::Protocol::Canvas::ContextType::BitmapRenderer;
220 #if ENABLE(WEBGL)
221     else if (is<WebGLRenderingContext>(m_context))
222         contextType = Inspector::Protocol::Canvas::ContextType::WebGL;
223 #endif
224 #if ENABLE(WEBGL2)
225     else if (is<WebGL2RenderingContext>(m_context))
226         contextType = Inspector::Protocol::Canvas::ContextType::WebGL2;
227 #endif
228 #if ENABLE(WEBGPU)
229     else if (is<WebGPURenderingContext>(m_context))
230         contextType = Inspector::Protocol::Canvas::ContextType::WebGPU;
231 #endif
232     else {
233         ASSERT_NOT_REACHED();
234         contextType = Inspector::Protocol::Canvas::ContextType::Canvas2D;
235     }
236
237     auto canvas = Inspector::Protocol::Canvas::Canvas::create()
238         .setCanvasId(m_identifier)
239         .setContextType(contextType)
240         .release();
241
242     if (auto* node = canvasElement()) {
243         String cssCanvasName = node->document().nameForCSSCanvasElement(*node);
244         if (!cssCanvasName.isEmpty())
245             canvas->setCssCanvasName(cssCanvasName);
246         else {
247             InspectorDOMAgent* domAgent = instrumentingAgents.inspectorDOMAgent();
248             int nodeId = domAgent->boundNodeId(node);
249             if (!nodeId) {
250                 if (int documentNodeId = domAgent->boundNodeId(&node->document())) {
251                     ErrorString ignored;
252                     nodeId = domAgent->pushNodeToFrontend(ignored, documentNodeId, node);
253                 }
254             }
255
256             if (nodeId)
257                 canvas->setNodeId(nodeId);
258         }
259     }
260
261     if (is<ImageBitmapRenderingContext>(m_context)) {
262         auto contextAttributes = Inspector::Protocol::Canvas::ContextAttributes::create()
263             .release();
264         contextAttributes->setAlpha(downcast<ImageBitmapRenderingContext>(m_context).hasAlpha());
265         canvas->setContextAttributes(WTFMove(contextAttributes));
266     }
267 #if ENABLE(WEBGL)
268     else if (is<WebGLRenderingContextBase>(m_context)) {
269         if (std::optional<WebGLContextAttributes> attributes = downcast<WebGLRenderingContextBase>(m_context).getContextAttributes()) {
270             auto contextAttributes = Inspector::Protocol::Canvas::ContextAttributes::create()
271                 .release();
272             contextAttributes->setAlpha(attributes->alpha);
273             contextAttributes->setDepth(attributes->depth);
274             contextAttributes->setStencil(attributes->stencil);
275             contextAttributes->setAntialias(attributes->antialias);
276             contextAttributes->setPremultipliedAlpha(attributes->premultipliedAlpha);
277             contextAttributes->setPreserveDrawingBuffer(attributes->preserveDrawingBuffer);
278             contextAttributes->setFailIfMajorPerformanceCaveat(attributes->failIfMajorPerformanceCaveat);
279             canvas->setContextAttributes(WTFMove(contextAttributes));
280         }
281     }
282 #endif
283
284     // FIXME: <https://webkit.org/b/180833> Web Inspector: support OffscreenCanvas for Canvas related operations
285
286     if (auto* node = canvasElement()) {
287         if (size_t memoryCost = node->memoryCost())
288             canvas->setMemoryCost(memoryCost);
289     }
290
291     if (captureBacktrace) {
292         auto stackTrace = Inspector::createScriptCallStack(JSMainThreadExecState::currentState(), Inspector::ScriptCallStack::maxCallStackSizeToCapture);
293         canvas->setBacktrace(stackTrace->buildInspectorArray());
294     }
295
296     return canvas;
297 }
298
299 void InspectorCanvas::appendActionSnapshotIfNeeded()
300 {
301     if (!m_actionNeedingSnapshot)
302         return;
303
304     m_actionNeedingSnapshot->addItem(indexForData(getCanvasContentAsDataURL()));
305     m_actionNeedingSnapshot = nullptr;
306 }
307
308 String InspectorCanvas::getCanvasContentAsDataURL()
309 {
310     // FIXME: <https://webkit.org/b/180833> Web Inspector: support OffscreenCanvas for Canvas related operations
311
312     auto* node = canvasElement();
313     if (!node)
314         return String();
315
316 #if ENABLE(WEBGL)
317     if (is<WebGLRenderingContextBase>(m_context))
318         downcast<WebGLRenderingContextBase>(m_context).setPreventBufferClearForInspector(true);
319 #endif
320
321     ExceptionOr<UncachedString> result = node->toDataURL("image/png"_s);
322
323 #if ENABLE(WEBGL)
324     if (is<WebGLRenderingContextBase>(m_context))
325         downcast<WebGLRenderingContextBase>(m_context).setPreventBufferClearForInspector(false);
326 #endif
327
328     if (result.hasException())
329         return String();
330
331     return result.releaseReturnValue().string;
332 }
333
334 int InspectorCanvas::indexForData(DuplicateDataVariant data)
335 {
336     size_t index = m_indexedDuplicateData.find(data);
337     if (index != notFound) {
338         ASSERT(index < std::numeric_limits<int>::max());
339         return static_cast<int>(index);
340     }
341
342     if (!m_serializedDuplicateData)
343         m_serializedDuplicateData = JSON::ArrayOf<JSON::Value>::create();
344
345     RefPtr<JSON::Value> item;
346     WTF::switchOn(data,
347         [&] (const HTMLImageElement* imageElement) {
348             String dataURL = "data:,"_s;
349
350             if (CachedImage* cachedImage = imageElement->cachedImage()) {
351                 Image* image = cachedImage->image();
352                 if (image && image != &Image::nullImage()) {
353                     std::unique_ptr<ImageBuffer> imageBuffer = ImageBuffer::create(image->size(), RenderingMode::Unaccelerated);
354                     imageBuffer->context().drawImage(*image, FloatPoint(0, 0));
355                     dataURL = imageBuffer->toDataURL("image/png");
356                 }
357             }
358
359             index = indexForData(dataURL);
360         },
361 #if ENABLE(VIDEO)
362         [&] (HTMLVideoElement* videoElement) {
363             String dataURL = "data:,"_s;
364
365             unsigned videoWidth = videoElement->videoWidth();
366             unsigned videoHeight = videoElement->videoHeight();
367             std::unique_ptr<ImageBuffer> imageBuffer = ImageBuffer::create(FloatSize(videoWidth, videoHeight), RenderingMode::Unaccelerated);
368             if (imageBuffer) {
369                 videoElement->paintCurrentFrameInContext(imageBuffer->context(), FloatRect(0, 0, videoWidth, videoHeight));
370                 dataURL = imageBuffer->toDataURL("image/png");
371             }
372
373             index = indexForData(dataURL);
374         },
375 #endif
376         [&] (HTMLCanvasElement* canvasElement) {
377             String dataURL = "data:,"_s;
378
379             ExceptionOr<UncachedString> result = canvasElement->toDataURL("image/png"_s);
380             if (!result.hasException())
381                 dataURL = result.releaseReturnValue().string;
382
383             index = indexForData(dataURL);
384         },
385         [&] (const CanvasGradient* canvasGradient) { item = buildArrayForCanvasGradient(*canvasGradient); },
386         [&] (const CanvasPattern* canvasPattern) { item = buildArrayForCanvasPattern(*canvasPattern); },
387         [&] (const ImageData* imageData) { item = buildArrayForImageData(*imageData); },
388         [&] (ImageBitmap* imageBitmap) {
389             index = indexForData(imageBitmap->buffer()->toDataURL("image/png"));
390         },
391         [&] (const ScriptCallFrame& scriptCallFrame) {
392             auto array = JSON::ArrayOf<double>::create();
393             array->addItem(indexForData(scriptCallFrame.functionName()));
394             array->addItem(indexForData(scriptCallFrame.sourceURL()));
395             array->addItem(static_cast<int>(scriptCallFrame.lineNumber()));
396             array->addItem(static_cast<int>(scriptCallFrame.columnNumber()));
397             item = WTFMove(array);
398         },
399         [&] (const String& value) { item = JSON::Value::create(value); }
400     );
401
402     if (item) {
403         m_bufferUsed += item->memoryCost();
404         m_serializedDuplicateData->addItem(WTFMove(item));
405
406         m_indexedDuplicateData.append(data);
407         index = m_indexedDuplicateData.size() - 1;
408     }
409
410     ASSERT(index < std::numeric_limits<int>::max());
411     return static_cast<int>(index);
412 }
413
414 static Ref<JSON::ArrayOf<double>> buildArrayForAffineTransform(const AffineTransform& affineTransform)
415 {
416     auto array = JSON::ArrayOf<double>::create();
417     array->addItem(affineTransform.a());
418     array->addItem(affineTransform.b());
419     array->addItem(affineTransform.c());
420     array->addItem(affineTransform.d());
421     array->addItem(affineTransform.e());
422     array->addItem(affineTransform.f());
423     return array;
424 }
425
426 template<typename T> static Ref<JSON::ArrayOf<JSON::Value>> buildArrayForVector(const Vector<T>& vector)
427 {
428     auto array = JSON::ArrayOf<JSON::Value>::create();
429     for (auto& item : vector)
430         array->addItem(item);
431     return array;
432 }
433
434 Ref<Inspector::Protocol::Recording::InitialState> InspectorCanvas::buildInitialState()
435 {
436     auto initialState = Inspector::Protocol::Recording::InitialState::create().release();
437
438     auto attributes = JSON::Object::create();
439     attributes->setInteger("width"_s, m_context.canvasBase().width());
440     attributes->setInteger("height"_s, m_context.canvasBase().height());
441
442     auto parameters = JSON::ArrayOf<JSON::Value>::create();
443
444     if (is<CanvasRenderingContext2D>(m_context)) {
445         auto& context2d = downcast<CanvasRenderingContext2D>(m_context);
446         auto& state = context2d.state();
447
448         attributes->setArray("setTransform"_s, buildArrayForAffineTransform(state.transform));
449         attributes->setDouble("globalAlpha"_s, context2d.globalAlpha());
450         attributes->setInteger("globalCompositeOperation"_s, indexForData(context2d.globalCompositeOperation()));
451         attributes->setDouble("lineWidth"_s, context2d.lineWidth());
452         attributes->setInteger("lineCap"_s, indexForData(convertEnumerationToString(context2d.lineCap())));
453         attributes->setInteger("lineJoin"_s, indexForData(convertEnumerationToString(context2d.lineJoin())));
454         attributes->setDouble("miterLimit"_s, context2d.miterLimit());
455         attributes->setDouble("shadowOffsetX"_s, context2d.shadowOffsetX());
456         attributes->setDouble("shadowOffsetY"_s, context2d.shadowOffsetY());
457         attributes->setDouble("shadowBlur"_s, context2d.shadowBlur());
458         attributes->setInteger("shadowColor"_s, indexForData(context2d.shadowColor()));
459
460         // The parameter to `setLineDash` is itself an array, so we need to wrap the parameters
461         // list in an array to allow spreading.
462         auto setLineDash = JSON::ArrayOf<JSON::Value>::create();
463         setLineDash->addItem(buildArrayForVector(state.lineDash));
464         attributes->setArray("setLineDash"_s, WTFMove(setLineDash));
465
466         attributes->setDouble("lineDashOffset"_s, context2d.lineDashOffset());
467         attributes->setInteger("font"_s, indexForData(context2d.font()));
468         attributes->setInteger("textAlign"_s, indexForData(convertEnumerationToString(context2d.textAlign())));
469         attributes->setInteger("textBaseline"_s, indexForData(convertEnumerationToString(context2d.textBaseline())));
470         attributes->setInteger("direction"_s, indexForData(convertEnumerationToString(context2d.direction())));
471
472         int strokeStyleIndex;
473         if (auto canvasGradient = state.strokeStyle.canvasGradient())
474             strokeStyleIndex = indexForData(canvasGradient.get());
475         else if (auto canvasPattern = state.strokeStyle.canvasPattern())
476             strokeStyleIndex = indexForData(canvasPattern.get());
477         else
478             strokeStyleIndex = indexForData(state.strokeStyle.color());
479         attributes->setInteger("strokeStyle"_s, strokeStyleIndex);
480
481         int fillStyleIndex;
482         if (auto canvasGradient = state.fillStyle.canvasGradient())
483             fillStyleIndex = indexForData(canvasGradient.get());
484         else if (auto canvasPattern = state.fillStyle.canvasPattern())
485             fillStyleIndex = indexForData(canvasPattern.get());
486         else
487             fillStyleIndex = indexForData(state.fillStyle.color());
488         attributes->setInteger("fillStyle"_s, fillStyleIndex);
489
490         attributes->setBoolean("imageSmoothingEnabled"_s, context2d.imageSmoothingEnabled());
491         attributes->setInteger("imageSmoothingQuality"_s, indexForData(convertEnumerationToString(context2d.imageSmoothingQuality())));
492
493         auto setPath = JSON::ArrayOf<JSON::Value>::create();
494         setPath->addItem(indexForData(buildStringFromPath(context2d.getPath()->path())));
495         attributes->setArray("setPath"_s, WTFMove(setPath));
496     }
497 #if ENABLE(WEBGL)
498     else if (is<WebGLRenderingContextBase>(m_context)) {
499         WebGLRenderingContextBase& contextWebGLBase = downcast<WebGLRenderingContextBase>(m_context);
500         if (std::optional<WebGLContextAttributes> attributes = contextWebGLBase.getContextAttributes()) {
501             RefPtr<JSON::Object> contextAttributes = JSON::Object::create();
502             contextAttributes->setBoolean("alpha"_s, attributes->alpha);
503             contextAttributes->setBoolean("depth"_s, attributes->depth);
504             contextAttributes->setBoolean("stencil"_s, attributes->stencil);
505             contextAttributes->setBoolean("antialias"_s, attributes->antialias);
506             contextAttributes->setBoolean("premultipliedAlpha"_s, attributes->premultipliedAlpha);
507             contextAttributes->setBoolean("preserveDrawingBuffer"_s, attributes->preserveDrawingBuffer);
508             contextAttributes->setBoolean("failIfMajorPerformanceCaveat"_s, attributes->failIfMajorPerformanceCaveat);
509             parameters->addItem(WTFMove(contextAttributes));
510         }
511     }
512 #endif
513
514     initialState->setAttributes(WTFMove(attributes));
515
516     if (parameters->length())
517         initialState->setParameters(WTFMove(parameters));
518
519     initialState->setContent(getCanvasContentAsDataURL());
520
521     return initialState;
522 }
523
524 Ref<JSON::ArrayOf<JSON::Value>> InspectorCanvas::buildAction(const String& name, Vector<RecordCanvasActionVariant>&& parameters)
525 {
526     auto action = JSON::ArrayOf<JSON::Value>::create();
527     action->addItem(indexForData(name));
528
529     auto parametersData = JSON::ArrayOf<JSON::Value>::create();
530     auto swizzleTypes = JSON::ArrayOf<int>::create();
531
532     auto addParameter = [&parametersData, &swizzleTypes] (auto value, RecordingSwizzleTypes swizzleType) {
533         parametersData->addItem(value);
534         swizzleTypes->addItem(static_cast<int>(swizzleType));
535     };
536
537     for (auto& item : parameters) {
538         WTF::switchOn(item,
539             [&] (CanvasDirection value) { addParameter(indexForData(convertEnumerationToString(value)), RecordingSwizzleTypes::String); },
540             [&] (CanvasFillRule value) { addParameter(indexForData(convertEnumerationToString(value)), RecordingSwizzleTypes::String); },
541             [&] (CanvasLineCap value) { addParameter(indexForData(convertEnumerationToString(value)), RecordingSwizzleTypes::String); },
542             [&] (CanvasLineJoin value) { addParameter(indexForData(convertEnumerationToString(value)), RecordingSwizzleTypes::String); },
543             [&] (CanvasTextAlign value) { addParameter(indexForData(convertEnumerationToString(value)), RecordingSwizzleTypes::String); },
544             [&] (CanvasTextBaseline value) { addParameter(indexForData(convertEnumerationToString(value)), RecordingSwizzleTypes::String); },
545             [&] (const DOMMatrix2DInit& value) {
546                 auto array = JSON::ArrayOf<double>::create();
547                 array->addItem(value.a.value_or(1));
548                 array->addItem(value.b.value_or(0));
549                 array->addItem(value.c.value_or(0));
550                 array->addItem(value.d.value_or(1));
551                 array->addItem(value.e.value_or(0));
552                 array->addItem(value.f.value_or(0));
553                 addParameter(array.ptr(), RecordingSwizzleTypes::DOMMatrix);
554             },
555             [&] (const Element*) {
556                 // Elements are not serializable, so add a string as a placeholder since the actual
557                 // element cannot be reconstructed in the frontend.
558                 addParameter(indexForData("Element"), RecordingSwizzleTypes::None);
559             },
560             [&] (HTMLImageElement* value) { addParameter(indexForData(value), RecordingSwizzleTypes::Image); },
561             [&] (ImageBitmap* value) { addParameter(indexForData(value), RecordingSwizzleTypes::ImageBitmap); },
562             [&] (ImageData* value) { addParameter(indexForData(value), RecordingSwizzleTypes::ImageData); },
563             [&] (ImageSmoothingQuality value) { addParameter(indexForData(convertEnumerationToString(value)), RecordingSwizzleTypes::String); },
564             [&] (const Path2D* value) { addParameter(indexForData(buildStringFromPath(value->path())), RecordingSwizzleTypes::Path2D); },
565 #if ENABLE(WEBGL)
566             // FIXME: <https://webkit.org/b/176009> Web Inspector: send data for WebGL objects during a recording instead of a placeholder string
567             [&] (const WebGLBuffer*) { addParameter(0, RecordingSwizzleTypes::WebGLBuffer); },
568             [&] (const WebGLFramebuffer*) { addParameter(0, RecordingSwizzleTypes::WebGLFramebuffer); },
569             [&] (const WebGLProgram*) { addParameter(0, RecordingSwizzleTypes::WebGLProgram); },
570             [&] (const WebGLRenderbuffer*) { addParameter(0, RecordingSwizzleTypes::WebGLRenderbuffer); },
571             [&] (const WebGLShader*) { addParameter(0, RecordingSwizzleTypes::WebGLShader); },
572             [&] (const WebGLTexture*) { addParameter(0, RecordingSwizzleTypes::WebGLTexture); },
573             [&] (const WebGLUniformLocation*) { addParameter(0, RecordingSwizzleTypes::WebGLUniformLocation); },
574 #endif
575             [&] (const RefPtr<ArrayBuffer>&) { addParameter(0, RecordingSwizzleTypes::TypedArray); },
576             [&] (const RefPtr<ArrayBufferView>&) { addParameter(0, RecordingSwizzleTypes::TypedArray); },
577             [&] (const RefPtr<CanvasGradient>& value) { addParameter(indexForData(value.get()), RecordingSwizzleTypes::CanvasGradient); },
578             [&] (const RefPtr<CanvasPattern>& value) { addParameter(indexForData(value.get()), RecordingSwizzleTypes::CanvasPattern); },
579             [&] (const RefPtr<Float32Array>&) { addParameter(0, RecordingSwizzleTypes::TypedArray); },
580             [&] (const RefPtr<HTMLCanvasElement>& value) { addParameter(indexForData(value.get()), RecordingSwizzleTypes::Image); },
581             [&] (const RefPtr<HTMLImageElement>& value) { addParameter(indexForData(value.get()), RecordingSwizzleTypes::Image); },
582 #if ENABLE(VIDEO)
583             [&] (const RefPtr<HTMLVideoElement>& value) { addParameter(indexForData(value.get()), RecordingSwizzleTypes::Image); },
584 #endif
585             [&] (const RefPtr<ImageBitmap>& value) { addParameter(indexForData(value.get()), RecordingSwizzleTypes::ImageBitmap); },
586             [&] (const RefPtr<ImageData>& value) { addParameter(indexForData(value.get()), RecordingSwizzleTypes::ImageData); },
587             [&] (const RefPtr<Int32Array>&) { addParameter(0, RecordingSwizzleTypes::TypedArray); },
588             [&] (const Vector<float>& value) { addParameter(buildArrayForVector(value).ptr(), RecordingSwizzleTypes::Array); },
589             [&] (const Vector<int>& value) { addParameter(buildArrayForVector(value).ptr(), RecordingSwizzleTypes::Array); },
590             [&] (const String& value) { addParameter(indexForData(value), RecordingSwizzleTypes::String); },
591             [&] (double value) { addParameter(value, RecordingSwizzleTypes::Number); },
592             [&] (float value) { addParameter(value, RecordingSwizzleTypes::Number); },
593             [&] (int64_t value) { addParameter(static_cast<double>(value), RecordingSwizzleTypes::Number); },
594             [&] (uint32_t value) { addParameter(static_cast<double>(value), RecordingSwizzleTypes::Number); },
595             [&] (int32_t value) { addParameter(value, RecordingSwizzleTypes::Number); },
596             [&] (uint8_t value) { addParameter(static_cast<int>(value), RecordingSwizzleTypes::Number); },
597             [&] (bool value) { addParameter(value, RecordingSwizzleTypes::Boolean); }
598         );
599     }
600
601     action->addItem(WTFMove(parametersData));
602     action->addItem(WTFMove(swizzleTypes));
603
604     auto trace = JSON::ArrayOf<double>::create();
605     auto stackTrace = Inspector::createScriptCallStack(JSMainThreadExecState::currentState(), Inspector::ScriptCallStack::maxCallStackSizeToCapture);
606     for (size_t i = 0; i < stackTrace->size(); ++i)
607         trace->addItem(indexForData(stackTrace->at(i)));
608     action->addItem(WTFMove(trace));
609
610     return action;
611 }
612
613 Ref<JSON::ArrayOf<JSON::Value>> InspectorCanvas::buildArrayForCanvasGradient(const CanvasGradient& canvasGradient)
614 {
615     const auto& gradient = canvasGradient.gradient();
616
617     String type = gradient.type() == Gradient::Type::Radial ? "radial-gradient"_s : "linear-gradient"_s;
618
619     auto parameters = JSON::ArrayOf<float>::create();
620     WTF::switchOn(gradient.data(),
621         [&parameters] (const Gradient::LinearData& data) {
622             parameters->addItem(data.point0.x());
623             parameters->addItem(data.point0.y());
624             parameters->addItem(data.point1.x());
625             parameters->addItem(data.point1.y());
626         },
627         [&parameters] (const Gradient::RadialData& data) {
628             parameters->addItem(data.point0.x());
629             parameters->addItem(data.point0.y());
630             parameters->addItem(data.startRadius);
631             parameters->addItem(data.point1.x());
632             parameters->addItem(data.point1.y());
633             parameters->addItem(data.endRadius);
634         }
635     );
636
637     auto stops = JSON::ArrayOf<JSON::Value>::create();
638     for (auto& colorStop : gradient.stops()) {
639         auto stop = JSON::ArrayOf<JSON::Value>::create();
640         stop->addItem(colorStop.offset);
641         stop->addItem(indexForData(colorStop.color.cssText()));
642         stops->addItem(WTFMove(stop));
643     }
644
645     auto array = JSON::ArrayOf<JSON::Value>::create();
646     array->addItem(indexForData(type));
647     array->addItem(WTFMove(parameters));
648     array->addItem(WTFMove(stops));
649     return array;
650 }
651
652 Ref<JSON::ArrayOf<JSON::Value>> InspectorCanvas::buildArrayForCanvasPattern(const CanvasPattern& canvasPattern)
653 {
654     Image& tileImage = canvasPattern.pattern().tileImage();
655     auto imageBuffer = ImageBuffer::create(tileImage.size(), RenderingMode::Unaccelerated);
656     imageBuffer->context().drawImage(tileImage, FloatPoint(0, 0));
657
658     String repeat;
659     bool repeatX = canvasPattern.pattern().repeatX();
660     bool repeatY = canvasPattern.pattern().repeatY();
661     if (repeatX && repeatY)
662         repeat = "repeat"_s;
663     else if (repeatX && !repeatY)
664         repeat = "repeat-x"_s;
665     else if (!repeatX && repeatY)
666         repeat = "repeat-y"_s;
667     else
668         repeat = "no-repeat"_s;
669
670     auto array = JSON::ArrayOf<JSON::Value>::create();
671     array->addItem(indexForData(imageBuffer->toDataURL("image/png")));
672     array->addItem(indexForData(repeat));
673     return array;
674 }
675
676 Ref<JSON::ArrayOf<JSON::Value>> InspectorCanvas::buildArrayForImageData(const ImageData& imageData)
677 {
678     auto data = JSON::ArrayOf<int>::create();
679     for (size_t i = 0; i < imageData.data()->length(); ++i)
680         data->addItem(imageData.data()->item(i));
681
682     auto array = JSON::ArrayOf<JSON::Value>::create();
683     array->addItem(WTFMove(data));
684     array->addItem(imageData.width());
685     array->addItem(imageData.height());
686     return array;
687 }
688
689 } // namespace WebCore
690