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