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