4ba64da25dd68501d0a65a622e1b24b6757b4648
[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 #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 = MonotonicTime::now();
152     }
153
154     appendActionSnapshotIfNeeded();
155
156     auto action = buildAction(name, WTFMove(parameters));
157     m_bufferUsed += action->memoryCost();
158     m_currentActions->addItem(action.ptr());
159
160 #if ENABLE(WEBGL)
161     if (is<WebGLRenderingContext>(m_context) && shouldSnapshotWebGLAction(name))
162         m_actionNeedingSnapshot = WTFMove(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((MonotonicTime::now() - m_currentFrameStartTime).milliseconds());
189
190         m_currentFrameStartTime = MonotonicTime::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 Ref<JSON::ArrayOf<double>> buildArrayForAffineTransform(const AffineTransform& affineTransform)
416 {
417     auto 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> static Ref<JSON::ArrayOf<JSON::Value>> buildArrayForVector(const Vector<T>& vector)
428 {
429     auto array = JSON::ArrayOf<JSON::Value>::create();
430     for (auto& item : vector)
431         array->addItem(item);
432     return array;
433 }
434
435 Ref<Inspector::Protocol::Recording::InitialState> InspectorCanvas::buildInitialState()
436 {
437     auto initialState = Inspector::Protocol::Recording::InitialState::create().release();
438
439     auto attributes = JSON::Object::create();
440     attributes->setInteger(ASCIILiteral("width"), m_context.canvasBase().width());
441     attributes->setInteger(ASCIILiteral("height"), m_context.canvasBase().height());
442
443     auto parameters = JSON::ArrayOf<JSON::Value>::create();
444
445     if (is<CanvasRenderingContext2D>(m_context)) {
446         auto& context2d = downcast<CanvasRenderingContext2D>(m_context);
447         auto& state = context2d.state();
448
449         attributes->setArray(ASCIILiteral("setTransform"), buildArrayForAffineTransform(state.transform));
450         attributes->setDouble(ASCIILiteral("globalAlpha"), context2d.globalAlpha());
451         attributes->setInteger(ASCIILiteral("globalCompositeOperation"), indexForData(context2d.globalCompositeOperation()));
452         attributes->setDouble(ASCIILiteral("lineWidth"), context2d.lineWidth());
453         attributes->setInteger(ASCIILiteral("lineCap"), indexForData(convertEnumerationToString(context2d.lineCap())));
454         attributes->setInteger(ASCIILiteral("lineJoin"), indexForData(convertEnumerationToString(context2d.lineJoin())));
455         attributes->setDouble(ASCIILiteral("miterLimit"), context2d.miterLimit());
456         attributes->setDouble(ASCIILiteral("shadowOffsetX"), context2d.shadowOffsetX());
457         attributes->setDouble(ASCIILiteral("shadowOffsetY"), context2d.shadowOffsetY());
458         attributes->setDouble(ASCIILiteral("shadowBlur"), context2d.shadowBlur());
459         attributes->setInteger(ASCIILiteral("shadowColor"), indexForData(context2d.shadowColor()));
460
461         // The parameter to `setLineDash` is itself an array, so we need to wrap the parameters
462         // list in an array to allow spreading.
463         auto setLineDash = JSON::ArrayOf<JSON::Value>::create();
464         setLineDash->addItem(buildArrayForVector(state.lineDash));
465         attributes->setArray(ASCIILiteral("setLineDash"), WTFMove(setLineDash));
466
467         attributes->setDouble(ASCIILiteral("lineDashOffset"), context2d.lineDashOffset());
468         attributes->setInteger(ASCIILiteral("font"), indexForData(context2d.font()));
469         attributes->setInteger(ASCIILiteral("textAlign"), indexForData(convertEnumerationToString(context2d.textAlign())));
470         attributes->setInteger(ASCIILiteral("textBaseline"), indexForData(convertEnumerationToString(context2d.textBaseline())));
471         attributes->setInteger(ASCIILiteral("direction"), indexForData(convertEnumerationToString(context2d.direction())));
472
473         int strokeStyleIndex;
474         if (auto canvasGradient = state.strokeStyle.canvasGradient())
475             strokeStyleIndex = indexForData(canvasGradient.get());
476         else if (auto canvasPattern = state.strokeStyle.canvasPattern())
477             strokeStyleIndex = indexForData(canvasPattern.get());
478         else
479             strokeStyleIndex = indexForData(state.strokeStyle.color());
480         attributes->setInteger(ASCIILiteral("strokeStyle"), strokeStyleIndex);
481
482         int fillStyleIndex;
483         if (auto canvasGradient = state.fillStyle.canvasGradient())
484             fillStyleIndex = indexForData(canvasGradient.get());
485         else if (auto canvasPattern = state.fillStyle.canvasPattern())
486             fillStyleIndex = indexForData(canvasPattern.get());
487         else
488             fillStyleIndex = indexForData(state.fillStyle.color());
489         attributes->setInteger(ASCIILiteral("fillStyle"), fillStyleIndex);
490
491         attributes->setBoolean(ASCIILiteral("imageSmoothingEnabled"), context2d.imageSmoothingEnabled());
492         attributes->setInteger(ASCIILiteral("imageSmoothingQuality"), indexForData(convertEnumerationToString(context2d.imageSmoothingQuality())));
493
494         auto setPath = JSON::ArrayOf<JSON::Value>::create();
495         setPath->addItem(indexForData(buildStringFromPath(context2d.getPath()->path())));
496         attributes->setArray(ASCIILiteral("setPath"), WTFMove(setPath));
497     }
498 #if ENABLE(WEBGL)
499     else if (is<WebGLRenderingContextBase>(m_context)) {
500         WebGLRenderingContextBase& contextWebGLBase = downcast<WebGLRenderingContextBase>(m_context);
501         if (std::optional<WebGLContextAttributes> attributes = contextWebGLBase.getContextAttributes()) {
502             RefPtr<JSON::Object> contextAttributes = JSON::Object::create();
503             contextAttributes->setBoolean(ASCIILiteral("alpha"), attributes->alpha);
504             contextAttributes->setBoolean(ASCIILiteral("depth"), attributes->depth);
505             contextAttributes->setBoolean(ASCIILiteral("stencil"), attributes->stencil);
506             contextAttributes->setBoolean(ASCIILiteral("antialias"), attributes->antialias);
507             contextAttributes->setBoolean(ASCIILiteral("premultipliedAlpha"), attributes->premultipliedAlpha);
508             contextAttributes->setBoolean(ASCIILiteral("preserveDrawingBuffer"), attributes->preserveDrawingBuffer);
509             contextAttributes->setBoolean(ASCIILiteral("failIfMajorPerformanceCaveat"), attributes->failIfMajorPerformanceCaveat);
510             parameters->addItem(WTFMove(contextAttributes));
511         }
512     }
513 #endif
514
515     initialState->setAttributes(WTFMove(attributes));
516
517     if (parameters->length())
518         initialState->setParameters(WTFMove(parameters));
519
520     initialState->setContent(getCanvasContentAsDataURL());
521
522     return initialState;
523 }
524
525 Ref<JSON::ArrayOf<JSON::Value>> InspectorCanvas::buildAction(const String& name, Vector<RecordCanvasActionVariant>&& parameters)
526 {
527     auto action = JSON::ArrayOf<JSON::Value>::create();
528     action->addItem(indexForData(name));
529
530     auto parametersData = JSON::ArrayOf<JSON::Value>::create();
531     auto swizzleTypes = JSON::ArrayOf<int>::create();
532
533     auto addParameter = [&parametersData, &swizzleTypes] (auto value, RecordingSwizzleTypes swizzleType) {
534         parametersData->addItem(value);
535         swizzleTypes->addItem(static_cast<int>(swizzleType));
536     };
537
538     for (auto& item : parameters) {
539         WTF::switchOn(item,
540             [&] (CanvasDirection value) { addParameter(indexForData(convertEnumerationToString(value)), RecordingSwizzleTypes::String); },
541             [&] (CanvasFillRule value) { addParameter(indexForData(convertEnumerationToString(value)), RecordingSwizzleTypes::String); },
542             [&] (CanvasLineCap value) { addParameter(indexForData(convertEnumerationToString(value)), RecordingSwizzleTypes::String); },
543             [&] (CanvasLineJoin value) { addParameter(indexForData(convertEnumerationToString(value)), RecordingSwizzleTypes::String); },
544             [&] (CanvasTextAlign value) { addParameter(indexForData(convertEnumerationToString(value)), RecordingSwizzleTypes::String); },
545             [&] (CanvasTextBaseline value) { addParameter(indexForData(convertEnumerationToString(value)), RecordingSwizzleTypes::String); },
546             [&] (const DOMMatrix2DInit& value) {
547                 auto array = JSON::ArrayOf<double>::create();
548                 array->addItem(value.a.value_or(1));
549                 array->addItem(value.b.value_or(0));
550                 array->addItem(value.c.value_or(0));
551                 array->addItem(value.d.value_or(1));
552                 array->addItem(value.e.value_or(0));
553                 array->addItem(value.f.value_or(0));
554                 addParameter(array.ptr(), RecordingSwizzleTypes::DOMMatrix);
555             },
556             [&] (const Element*) {
557                 // Elements are not serializable, so add a string as a placeholder since the actual
558                 // element cannot be reconstructed in the frontend.
559                 addParameter(indexForData("Element"), RecordingSwizzleTypes::None);
560             },
561             [&] (HTMLImageElement* value) { addParameter(indexForData(value), RecordingSwizzleTypes::Image); },
562             [&] (ImageBitmap* value) { addParameter(indexForData(value), RecordingSwizzleTypes::ImageBitmap); },
563             [&] (ImageData* value) { addParameter(indexForData(value), RecordingSwizzleTypes::ImageData); },
564             [&] (ImageSmoothingQuality value) { addParameter(indexForData(convertEnumerationToString(value)), RecordingSwizzleTypes::String); },
565             [&] (const Path2D* value) { addParameter(indexForData(buildStringFromPath(value->path())), RecordingSwizzleTypes::Path2D); },
566 #if ENABLE(WEBGL)
567             // FIXME: <https://webkit.org/b/176009> Web Inspector: send data for WebGL objects during a recording instead of a placeholder string
568             [&] (const WebGLBuffer*) { addParameter(0, RecordingSwizzleTypes::WebGLBuffer); },
569             [&] (const WebGLFramebuffer*) { addParameter(0, RecordingSwizzleTypes::WebGLFramebuffer); },
570             [&] (const WebGLProgram*) { addParameter(0, RecordingSwizzleTypes::WebGLProgram); },
571             [&] (const WebGLRenderbuffer*) { addParameter(0, RecordingSwizzleTypes::WebGLRenderbuffer); },
572             [&] (const WebGLShader*) { addParameter(0, RecordingSwizzleTypes::WebGLShader); },
573             [&] (const WebGLTexture*) { addParameter(0, RecordingSwizzleTypes::WebGLTexture); },
574             [&] (const WebGLUniformLocation*) { addParameter(0, RecordingSwizzleTypes::WebGLUniformLocation); },
575 #endif
576             [&] (const RefPtr<ArrayBuffer>&) { addParameter(0, RecordingSwizzleTypes::TypedArray); },
577             [&] (const RefPtr<ArrayBufferView>&) { addParameter(0, RecordingSwizzleTypes::TypedArray); },
578             [&] (const RefPtr<CanvasGradient>& value) { addParameter(indexForData(value.get()), RecordingSwizzleTypes::CanvasGradient); },
579             [&] (const RefPtr<CanvasPattern>& value) { addParameter(indexForData(value.get()), RecordingSwizzleTypes::CanvasPattern); },
580             [&] (const RefPtr<Float32Array>&) { addParameter(0, RecordingSwizzleTypes::TypedArray); },
581             [&] (const RefPtr<HTMLCanvasElement>& value) { addParameter(indexForData(value.get()), RecordingSwizzleTypes::Image); },
582             [&] (const RefPtr<HTMLImageElement>& value) { addParameter(indexForData(value.get()), RecordingSwizzleTypes::Image); },
583 #if ENABLE(VIDEO)
584             [&] (const RefPtr<HTMLVideoElement>& value) { addParameter(indexForData(value.get()), RecordingSwizzleTypes::Image); },
585 #endif
586             [&] (const RefPtr<ImageBitmap>& value) { addParameter(indexForData(value.get()), RecordingSwizzleTypes::ImageBitmap); },
587             [&] (const RefPtr<ImageData>& value) { addParameter(indexForData(value.get()), RecordingSwizzleTypes::ImageData); },
588             [&] (const RefPtr<Int32Array>&) { addParameter(0, RecordingSwizzleTypes::TypedArray); },
589             [&] (const Vector<float>& value) { addParameter(buildArrayForVector(value).ptr(), RecordingSwizzleTypes::Array); },
590             [&] (const Vector<int>& value) { addParameter(buildArrayForVector(value).ptr(), RecordingSwizzleTypes::Array); },
591             [&] (const String& value) { addParameter(indexForData(value), RecordingSwizzleTypes::String); },
592             [&] (double value) { addParameter(value, RecordingSwizzleTypes::Number); },
593             [&] (float value) { addParameter(value, RecordingSwizzleTypes::Number); },
594             [&] (int64_t value) { addParameter(static_cast<double>(value), RecordingSwizzleTypes::Number); },
595             [&] (uint32_t value) { addParameter(static_cast<double>(value), RecordingSwizzleTypes::Number); },
596             [&] (int32_t value) { addParameter(value, RecordingSwizzleTypes::Number); },
597             [&] (uint8_t value) { addParameter(static_cast<int>(value), RecordingSwizzleTypes::Number); },
598             [&] (bool value) { addParameter(value, RecordingSwizzleTypes::Boolean); }
599         );
600     }
601
602     action->addItem(WTFMove(parametersData));
603     action->addItem(WTFMove(swizzleTypes));
604
605     auto trace = JSON::ArrayOf<double>::create();
606     auto stackTrace = Inspector::createScriptCallStack(JSMainThreadExecState::currentState(), Inspector::ScriptCallStack::maxCallStackSizeToCapture);
607     for (size_t i = 0; i < stackTrace->size(); ++i)
608         trace->addItem(indexForData(stackTrace->at(i)));
609     action->addItem(WTFMove(trace));
610
611     return action;
612 }
613
614 Ref<JSON::ArrayOf<JSON::Value>> InspectorCanvas::buildArrayForCanvasGradient(const CanvasGradient& canvasGradient)
615 {
616     const auto& gradient = canvasGradient.gradient();
617
618     String type = gradient.type() == Gradient::Type::Radial ? ASCIILiteral("radial-gradient") : ASCIILiteral("linear-gradient");
619
620     auto parameters = JSON::ArrayOf<float>::create();
621     WTF::switchOn(gradient.data(),
622         [&parameters] (const Gradient::LinearData& data) {
623             parameters->addItem(data.point0.x());
624             parameters->addItem(data.point0.y());
625             parameters->addItem(data.point1.x());
626             parameters->addItem(data.point1.y());
627         },
628         [&parameters] (const Gradient::RadialData& data) {
629             parameters->addItem(data.point0.x());
630             parameters->addItem(data.point0.y());
631             parameters->addItem(data.startRadius);
632             parameters->addItem(data.point1.x());
633             parameters->addItem(data.point1.y());
634             parameters->addItem(data.endRadius);
635         }
636     );
637
638     auto stops = JSON::ArrayOf<JSON::Value>::create();
639     for (auto& colorStop : gradient.stops()) {
640         auto stop = JSON::ArrayOf<JSON::Value>::create();
641         stop->addItem(colorStop.offset);
642         stop->addItem(indexForData(colorStop.color.cssText()));
643         stops->addItem(WTFMove(stop));
644     }
645
646     auto array = JSON::ArrayOf<JSON::Value>::create();
647     array->addItem(indexForData(type));
648     array->addItem(WTFMove(parameters));
649     array->addItem(WTFMove(stops));
650     return array;
651 }
652
653 Ref<JSON::ArrayOf<JSON::Value>> InspectorCanvas::buildArrayForCanvasPattern(const CanvasPattern& canvasPattern)
654 {
655     Image& tileImage = canvasPattern.pattern().tileImage();
656     auto imageBuffer = ImageBuffer::create(tileImage.size(), RenderingMode::Unaccelerated);
657     imageBuffer->context().drawImage(tileImage, FloatPoint(0, 0));
658
659     String repeat;
660     bool repeatX = canvasPattern.pattern().repeatX();
661     bool repeatY = canvasPattern.pattern().repeatY();
662     if (repeatX && repeatY)
663         repeat = ASCIILiteral("repeat");
664     else if (repeatX && !repeatY)
665         repeat = ASCIILiteral("repeat-x");
666     else if (!repeatX && repeatY)
667         repeat = ASCIILiteral("repeat-y");
668     else
669         repeat = ASCIILiteral("no-repeat");
670
671     auto array = JSON::ArrayOf<JSON::Value>::create();
672     array->addItem(indexForData(imageBuffer->toDataURL("image/png")));
673     array->addItem(indexForData(repeat));
674     return array;
675 }
676
677 Ref<JSON::ArrayOf<JSON::Value>> InspectorCanvas::buildArrayForImageData(const ImageData& imageData)
678 {
679     auto data = JSON::ArrayOf<int>::create();
680     for (size_t i = 0; i < imageData.data()->length(); ++i)
681         data->addItem(imageData.data()->item(i));
682
683     auto array = JSON::ArrayOf<JSON::Value>::create();
684     array->addItem(WTFMove(data));
685     array->addItem(imageData.width());
686     array->addItem(imageData.height());
687     return array;
688 }
689
690 } // namespace WebCore
691