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