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