blob: 738be4e73142a2542c6fe7672b67e8b6067318a9 [file] [log] [blame]
/*
* Copyright (C) 2017 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "InspectorCanvas.h"
#include "AffineTransform.h"
#include "CachedImage.h"
#include "CanvasGradient.h"
#include "CanvasPattern.h"
#include "CanvasRenderingContext2D.h"
#include "Document.h"
#include "FloatPoint.h"
#include "Frame.h"
#include "Gradient.h"
#include "HTMLCanvasElement.h"
#include "HTMLImageElement.h"
#include "HTMLVideoElement.h"
#include "Image.h"
#include "ImageBuffer.h"
#include "ImageData.h"
#include "InspectorDOMAgent.h"
#include "InspectorPageAgent.h"
#include "InstrumentingAgents.h"
#include "JSCanvasDirection.h"
#include "JSCanvasFillRule.h"
#include "JSCanvasLineCap.h"
#include "JSCanvasLineJoin.h"
#include "JSCanvasTextAlign.h"
#include "JSCanvasTextBaseline.h"
#include "JSImageSmoothingQuality.h"
#include "JSMainThreadExecState.h"
#include "Path2D.h"
#include "Pattern.h"
#include "SVGPathUtilities.h"
#include "StringAdaptors.h"
#if ENABLE(WEBGL)
#include "WebGLRenderingContext.h"
#endif
#if ENABLE(WEBGL2)
#include "WebGL2RenderingContext.h"
#endif
#if ENABLE(WEBGPU)
#include "WebGPURenderingContext.h"
#endif
#include <inspector/IdentifiersFactory.h>
#include <interpreter/CallFrame.h>
#include <interpreter/StackVisitor.h>
using namespace Inspector;
namespace WebCore {
Ref<InspectorCanvas> InspectorCanvas::create(HTMLCanvasElement& canvas, const String& cssCanvasName)
{
return adoptRef(*new InspectorCanvas(canvas, cssCanvasName));
}
InspectorCanvas::InspectorCanvas(HTMLCanvasElement& canvas, const String& cssCanvasName)
: m_identifier("canvas:" + IdentifiersFactory::createIdentifier())
, m_canvas(canvas)
, m_cssCanvasName(cssCanvasName)
{
}
InspectorCanvas::~InspectorCanvas()
{
resetRecordingData();
}
void InspectorCanvas::resetRecordingData()
{
m_initialState = nullptr;
m_frames = nullptr;
m_currentActions = nullptr;
m_actionNeedingSnapshot = nullptr;
m_serializedDuplicateData = nullptr;
m_indexedDuplicateData.clear();
m_bufferLimit = 100 * 1024 * 1024;
m_bufferUsed = 0;
m_singleFrame = true;
m_canvas.renderingContext()->setCallTracingActive(false);
}
bool InspectorCanvas::hasRecordingData() const
{
return m_initialState && m_frames;
}
static bool shouldSnapshotWebGLAction(const String& name)
{
return name == "clear"
|| name == "drawArrays"
|| name == "drawElements";
}
void InspectorCanvas::recordAction(const String& name, Vector<RecordCanvasActionVariant>&& parameters)
{
if (!hasRecordingData()) {
m_initialState = buildInitialState();
m_bufferUsed += m_initialState->memoryCost();
m_frames = Inspector::Protocol::Array<Inspector::Protocol::Recording::Frame>::create();
}
if (!m_currentActions) {
m_currentActions = Inspector::Protocol::Array<InspectorValue>::create();
auto frame = Inspector::Protocol::Recording::Frame::create()
.setActions(m_currentActions)
.release();
m_frames->addItem(WTFMove(frame));
}
appendActionSnapshotIfNeeded();
auto action = buildAction(name, WTFMove(parameters));
m_bufferUsed += action->memoryCost();
m_currentActions->addItem(action);
#if ENABLE(WEBGL)
if (is<WebGLRenderingContext>(m_canvas.renderingContext()) && shouldSnapshotWebGLAction(name))
m_actionNeedingSnapshot = action;
#endif
}
RefPtr<Inspector::Protocol::Recording::InitialState>&& InspectorCanvas::releaseInitialState()
{
return WTFMove(m_initialState);
}
RefPtr<Inspector::Protocol::Array<Inspector::Protocol::Recording::Frame>>&& InspectorCanvas::releaseFrames()
{
appendActionSnapshotIfNeeded();
return WTFMove(m_frames);
}
RefPtr<Inspector::Protocol::Array<InspectorValue>>&& InspectorCanvas::releaseData()
{
m_indexedDuplicateData.clear();
return WTFMove(m_serializedDuplicateData);
}
void InspectorCanvas::markNewFrame()
{
m_currentActions = nullptr;
}
void InspectorCanvas::markCurrentFrameIncomplete()
{
if (!m_currentActions)
return;
static_cast<Inspector::Protocol::Recording::Frame*>(m_frames->get(m_frames->length() - 1).get())->setIncomplete(true);
}
void InspectorCanvas::setBufferLimit(long memoryLimit)
{
m_bufferLimit = std::min<long>(memoryLimit, std::numeric_limits<int>::max());
}
bool InspectorCanvas::hasBufferSpace() const
{
return m_bufferUsed < m_bufferLimit;
}
Ref<Inspector::Protocol::Canvas::Canvas> InspectorCanvas::buildObjectForCanvas(InstrumentingAgents& instrumentingAgents)
{
Document& document = m_canvas.document();
Frame* frame = document.frame();
CanvasRenderingContext* context = m_canvas.renderingContext();
Inspector::Protocol::Canvas::ContextType contextType;
if (is<CanvasRenderingContext2D>(context))
contextType = Inspector::Protocol::Canvas::ContextType::Canvas2D;
#if ENABLE(WEBGL)
else if (is<WebGLRenderingContext>(context))
contextType = Inspector::Protocol::Canvas::ContextType::WebGL;
#endif
#if ENABLE(WEBGL2)
else if (is<WebGL2RenderingContext>(context))
contextType = Inspector::Protocol::Canvas::ContextType::WebGL2;
#endif
#if ENABLE(WEBGPU)
else if (is<WebGPURenderingContext>(context))
contextType = Inspector::Protocol::Canvas::ContextType::WebGPU;
#endif
else {
ASSERT_NOT_REACHED();
contextType = Inspector::Protocol::Canvas::ContextType::Canvas2D;
}
auto canvas = Inspector::Protocol::Canvas::Canvas::create()
.setCanvasId(m_identifier)
.setFrameId(instrumentingAgents.inspectorPageAgent()->frameId(frame))
.setContextType(contextType)
.release();
if (!m_cssCanvasName.isEmpty())
canvas->setCssCanvasName(m_cssCanvasName);
else {
InspectorDOMAgent* domAgent = instrumentingAgents.inspectorDOMAgent();
int nodeId = domAgent->boundNodeId(&m_canvas);
if (!nodeId) {
if (int documentNodeId = domAgent->boundNodeId(&m_canvas.document())) {
ErrorString ignored;
nodeId = domAgent->pushNodeToFrontend(ignored, documentNodeId, &m_canvas);
}
}
if (nodeId)
canvas->setNodeId(nodeId);
}
#if ENABLE(WEBGL)
if (is<WebGLRenderingContextBase>(context)) {
if (std::optional<WebGLContextAttributes> attributes = downcast<WebGLRenderingContextBase>(context)->getContextAttributes()) {
canvas->setContextAttributes(Inspector::Protocol::Canvas::ContextAttributes::create()
.setAlpha(attributes->alpha)
.setDepth(attributes->depth)
.setStencil(attributes->stencil)
.setAntialias(attributes->antialias)
.setPremultipliedAlpha(attributes->premultipliedAlpha)
.setPreserveDrawingBuffer(attributes->preserveDrawingBuffer)
.setFailIfMajorPerformanceCaveat(attributes->failIfMajorPerformanceCaveat)
.release());
}
}
#endif
if (size_t memoryCost = m_canvas.memoryCost())
canvas->setMemoryCost(memoryCost);
return canvas;
}
void InspectorCanvas::appendActionSnapshotIfNeeded()
{
if (!m_actionNeedingSnapshot)
return;
m_actionNeedingSnapshot->addItem(indexForData(getCanvasContentAsDataURL()));
m_actionNeedingSnapshot = nullptr;
}
String InspectorCanvas::getCanvasContentAsDataURL()
{
#if ENABLE(WEBGL)
CanvasRenderingContext* canvasRenderingContext = m_canvas.renderingContext();
if (is<WebGLRenderingContextBase>(canvasRenderingContext))
downcast<WebGLRenderingContextBase>(canvasRenderingContext)->setPreventBufferClearForInspector(true);
#endif
ExceptionOr<UncachedString> result = m_canvas.toDataURL(ASCIILiteral("image/png"));
#if ENABLE(WEBGL)
if (is<WebGLRenderingContextBase>(canvasRenderingContext))
downcast<WebGLRenderingContextBase>(canvasRenderingContext)->setPreventBufferClearForInspector(false);
#endif
if (result.hasException())
return String();
return result.releaseReturnValue().string;
}
int InspectorCanvas::indexForData(DuplicateDataVariant data)
{
size_t index = m_indexedDuplicateData.find(data);
if (index != notFound) {
ASSERT(index < std::numeric_limits<int>::max());
return static_cast<int>(index);
}
if (!m_serializedDuplicateData)
m_serializedDuplicateData = Inspector::Protocol::Array<InspectorValue>::create();
RefPtr<InspectorValue> item;
WTF::switchOn(data,
[&] (const HTMLImageElement* imageElement) {
String dataURL = ASCIILiteral("data:,");
if (CachedImage* cachedImage = imageElement->cachedImage()) {
Image* image = cachedImage->image();
if (image && image != &Image::nullImage()) {
std::unique_ptr<ImageBuffer> imageBuffer = ImageBuffer::create(image->size(), RenderingMode::Unaccelerated);
imageBuffer->context().drawImage(*image, FloatPoint(0, 0));
dataURL = imageBuffer->toDataURL("image/png");
}
}
item = InspectorValue::create(dataURL);
},
#if ENABLE(VIDEO)
[&] (HTMLVideoElement* videoElement) {
String dataURL = ASCIILiteral("data:,");
unsigned videoWidth = videoElement->videoWidth();
unsigned videoHeight = videoElement->videoHeight();
std::unique_ptr<ImageBuffer> imageBuffer = ImageBuffer::create(FloatSize(videoWidth, videoHeight), RenderingMode::Unaccelerated);
if (imageBuffer) {
videoElement->paintCurrentFrameInContext(imageBuffer->context(), FloatRect(0, 0, videoWidth, videoHeight));
dataURL = imageBuffer->toDataURL("image/png");
}
item = InspectorValue::create(dataURL);
},
#endif
[&] (HTMLCanvasElement* canvasElement) {
String dataURL = ASCIILiteral("data:,");
ExceptionOr<UncachedString> result = canvasElement->toDataURL(ASCIILiteral("image/png"));
if (!result.hasException())
dataURL = result.releaseReturnValue().string;
item = InspectorValue::create(dataURL);
},
[&] (const CanvasGradient* canvasGradient) { item = buildArrayForCanvasGradient(*canvasGradient); },
[&] (const CanvasPattern* canvasPattern) { item = buildArrayForCanvasPattern(*canvasPattern); },
[&] (const ImageData* imageData) { item = buildArrayForImageData(*imageData); },
[&] (const ScriptCallFrame& scriptCallFrame) {
auto array = Inspector::Protocol::Array<double>::create();
array->addItem(indexForData(scriptCallFrame.functionName()));
array->addItem(indexForData(scriptCallFrame.sourceURL()));
array->addItem(static_cast<int>(scriptCallFrame.lineNumber()));
array->addItem(static_cast<int>(scriptCallFrame.columnNumber()));
item = WTFMove(array);
},
[&] (const String& value) { item = InspectorValue::create(value); }
);
m_bufferUsed += item->memoryCost();
m_serializedDuplicateData->addItem(WTFMove(item));
m_indexedDuplicateData.append(data);
index = m_indexedDuplicateData.size() - 1;
ASSERT(index < std::numeric_limits<int>::max());
return static_cast<int>(index);
}
static RefPtr<Inspector::Protocol::Array<double>> buildArrayForAffineTransform(const AffineTransform& affineTransform)
{
RefPtr<Inspector::Protocol::Array<double>> array = Inspector::Protocol::Array<double>::create();
array->addItem(affineTransform.a());
array->addItem(affineTransform.b());
array->addItem(affineTransform.c());
array->addItem(affineTransform.d());
array->addItem(affineTransform.e());
array->addItem(affineTransform.f());
return array;
}
static RefPtr<Inspector::Protocol::Array<double>> buildArrayForVector(const Vector<float>& vector)
{
RefPtr<Inspector::Protocol::Array<double>> array = Inspector::Protocol::Array<double>::create();
for (double item : vector)
array->addItem(item);
return array;
}
RefPtr<Inspector::Protocol::Recording::InitialState> InspectorCanvas::buildInitialState()
{
RefPtr<Inspector::Protocol::Recording::InitialState> initialState = Inspector::Protocol::Recording::InitialState::create()
.release();
auto attributes = InspectorObject::create();
attributes->setInteger(ASCIILiteral("width"), canvas().width());
attributes->setInteger(ASCIILiteral("height"), canvas().height());
auto parameters = Inspector::Protocol::Array<InspectorValue>::create();
CanvasRenderingContext* canvasRenderingContext = canvas().renderingContext();
if (is<CanvasRenderingContext2D>(canvasRenderingContext)) {
const CanvasRenderingContext2D* context2d = downcast<CanvasRenderingContext2D>(canvasRenderingContext);
const CanvasRenderingContext2D::State& state = context2d->state();
attributes->setArray(ASCIILiteral("setTransform"), buildArrayForAffineTransform(state.transform));
attributes->setDouble(ASCIILiteral("globalAlpha"), context2d->globalAlpha());
attributes->setInteger(ASCIILiteral("globalCompositeOperation"), indexForData(context2d->globalCompositeOperation()));
attributes->setDouble(ASCIILiteral("lineWidth"), context2d->lineWidth());
attributes->setInteger(ASCIILiteral("lineCap"), indexForData(convertEnumerationToString(context2d->lineCap())));
attributes->setInteger(ASCIILiteral("lineJoin"), indexForData(convertEnumerationToString(context2d->lineJoin())));
attributes->setDouble(ASCIILiteral("miterLimit"), context2d->miterLimit());
attributes->setDouble(ASCIILiteral("shadowOffsetX"), context2d->shadowOffsetX());
attributes->setDouble(ASCIILiteral("shadowOffsetY"), context2d->shadowOffsetY());
attributes->setDouble(ASCIILiteral("shadowBlur"), context2d->shadowBlur());
attributes->setInteger(ASCIILiteral("shadowColor"), indexForData(context2d->shadowColor()));
// The parameter to `setLineDash` is itself an array, so we need to wrap the parameters
// list in an array to allow spreading.
auto setLineDash = Inspector::Protocol::Array<InspectorValue>::create();
setLineDash->addItem(buildArrayForVector(state.lineDash));
attributes->setArray(ASCIILiteral("setLineDash"), WTFMove(setLineDash));
attributes->setDouble(ASCIILiteral("lineDashOffset"), context2d->lineDashOffset());
attributes->setInteger(ASCIILiteral("font"), indexForData(context2d->font()));
attributes->setInteger(ASCIILiteral("textAlign"), indexForData(convertEnumerationToString(context2d->textAlign())));
attributes->setInteger(ASCIILiteral("textBaseline"), indexForData(convertEnumerationToString(context2d->textBaseline())));
attributes->setInteger(ASCIILiteral("direction"), indexForData(convertEnumerationToString(context2d->direction())));
int strokeStyleIndex;
if (CanvasGradient* canvasGradient = state.strokeStyle.canvasGradient())
strokeStyleIndex = indexForData(canvasGradient);
else if (CanvasPattern* canvasPattern = state.strokeStyle.canvasPattern())
strokeStyleIndex = indexForData(canvasPattern);
else
strokeStyleIndex = indexForData(state.strokeStyle.color());
attributes->setInteger(ASCIILiteral("strokeStyle"), strokeStyleIndex);
int fillStyleIndex;
if (CanvasGradient* canvasGradient = state.fillStyle.canvasGradient())
fillStyleIndex = indexForData(canvasGradient);
else if (CanvasPattern* canvasPattern = state.fillStyle.canvasPattern())
fillStyleIndex = indexForData(canvasPattern);
else
fillStyleIndex = indexForData(state.fillStyle.color());
attributes->setInteger(ASCIILiteral("fillStyle"), fillStyleIndex);
attributes->setBoolean(ASCIILiteral("imageSmoothingEnabled"), context2d->imageSmoothingEnabled());
attributes->setInteger(ASCIILiteral("imageSmoothingQuality"), indexForData(convertEnumerationToString(context2d->imageSmoothingQuality())));
auto setPath = Inspector::Protocol::Array<InspectorValue>::create();
setPath->addItem(indexForData(buildStringFromPath(context2d->getPath()->path())));
attributes->setArray(ASCIILiteral("setPath"), WTFMove(setPath));
}
#if ENABLE(WEBGL)
else if (is<WebGLRenderingContextBase>(canvasRenderingContext)) {
WebGLRenderingContextBase* contextWebGLBase = downcast<WebGLRenderingContextBase>(canvasRenderingContext);
if (std::optional<WebGLContextAttributes> attributes = contextWebGLBase->getContextAttributes()) {
RefPtr<InspectorObject> contextAttributes = InspectorObject::create();
contextAttributes->setBoolean(ASCIILiteral("alpha"), attributes->alpha);
contextAttributes->setBoolean(ASCIILiteral("depth"), attributes->depth);
contextAttributes->setBoolean(ASCIILiteral("stencil"), attributes->stencil);
contextAttributes->setBoolean(ASCIILiteral("antialias"), attributes->antialias);
contextAttributes->setBoolean(ASCIILiteral("premultipliedAlpha"), attributes->premultipliedAlpha);
contextAttributes->setBoolean(ASCIILiteral("preserveDrawingBuffer"), attributes->preserveDrawingBuffer);
contextAttributes->setBoolean(ASCIILiteral("failIfMajorPerformanceCaveat"), attributes->failIfMajorPerformanceCaveat);
parameters->addItem(WTFMove(contextAttributes));
}
}
#endif
initialState->setAttributes(WTFMove(attributes));
if (parameters->length())
initialState->setParameters(WTFMove(parameters));
initialState->setContent(getCanvasContentAsDataURL());
return initialState;
}
RefPtr<Inspector::Protocol::Array<Inspector::InspectorValue>> InspectorCanvas::buildAction(const String& name, Vector<RecordCanvasActionVariant>&& parameters)
{
RefPtr<Inspector::Protocol::Array<InspectorValue>> action = Inspector::Protocol::Array<InspectorValue>::create();
action->addItem(indexForData(name));
RefPtr<Inspector::Protocol::Array<InspectorValue>> parametersData = Inspector::Protocol::Array<Inspector::InspectorValue>::create();
for (RecordCanvasActionVariant& item : parameters) {
WTF::switchOn(item,
[&] (CanvasFillRule value) {
parametersData->addItem(indexForData(convertEnumerationToString(value)));
},
[&] (ImageSmoothingQuality value) {
parametersData->addItem(indexForData(convertEnumerationToString(value)));
},
[&] (CanvasDirection value) {
parametersData->addItem(indexForData(convertEnumerationToString(value)));
},
[&] (CanvasLineCap value) {
parametersData->addItem(indexForData(convertEnumerationToString(value)));
},
[&] (CanvasLineJoin value) {
parametersData->addItem(indexForData(convertEnumerationToString(value)));
},
[&] (CanvasTextAlign value) {
parametersData->addItem(indexForData(convertEnumerationToString(value)));
},
[&] (CanvasTextBaseline value) {
parametersData->addItem(indexForData(convertEnumerationToString(value)));
},
[&] (const DOMMatrix2DInit& value) {
RefPtr<Inspector::Protocol::Array<double>> array = Inspector::Protocol::Array<double>::create();
array->addItem(value.a.value_or(1));
array->addItem(value.b.value_or(0));
array->addItem(value.c.value_or(0));
array->addItem(value.d.value_or(1));
array->addItem(value.e.value_or(0));
array->addItem(value.f.value_or(0));
parametersData->addItem(WTFMove(array));
},
[&] (const Path2D* value) {
parametersData->addItem(indexForData(buildStringFromPath(value->path())));
},
[&] (const Element*) {
// Elements are not serializable, so add a string as a placeholder since the actual
// element cannot be reconstructed in the frontend.
parametersData->addItem(indexForData(String("element")));
},
[&] (HTMLImageElement* value) { parametersData->addItem(indexForData(value)); },
[&] (ImageData* value) { parametersData->addItem(indexForData(value)); },
#if ENABLE(WEBGL)
// FIXME: <https://webkit.org/b/176009> Web Inspector: send data for WebGL objects during a recording instead of a placeholder string
[&] (const WebGLBuffer*) { parametersData->addItem(indexForData(String("WebGLBuffer"))); },
[&] (const WebGLFramebuffer*) { parametersData->addItem(indexForData(String("WebGLFramebuffer"))); },
[&] (const WebGLProgram*) { parametersData->addItem(indexForData(String("WebGLProgram"))); },
[&] (const WebGLRenderbuffer*) { parametersData->addItem(indexForData(String("WebGLRenderbuffer"))); },
[&] (const WebGLShader*) { parametersData->addItem(indexForData(String("WebGLShader"))); },
[&] (const WebGLTexture*) { parametersData->addItem(indexForData(String("WebGLTexture"))); },
[&] (const WebGLUniformLocation*) { parametersData->addItem(indexForData(String("WebGLUniformLocation"))); },
#endif
[&] (const RefPtr<ArrayBuffer>&) { parametersData->addItem(indexForData("BufferDataSource")); },
[&] (const RefPtr<ArrayBufferView>&) { parametersData->addItem(indexForData("BufferDataSource")); },
[&] (const RefPtr<CanvasGradient>& value) { parametersData->addItem(indexForData(value.get())); },
[&] (const RefPtr<CanvasPattern>& value) { parametersData->addItem(indexForData(value.get())); },
[&] (const RefPtr<Float32Array>&) { parametersData->addItem(indexForData("Float32List")); },
[&] (RefPtr<HTMLCanvasElement>& value) { parametersData->addItem(indexForData(value.get())); },
[&] (const RefPtr<HTMLImageElement>& value) { parametersData->addItem(indexForData(value.get())); },
#if ENABLE(VIDEO)
[&] (RefPtr<HTMLVideoElement>& value) { parametersData->addItem(indexForData(value.get())); },
#endif
[&] (const RefPtr<ImageData>& value) { parametersData->addItem(indexForData(value.get())); },
[&] (const RefPtr<Int32Array>&) { parametersData->addItem(indexForData("Int32List")); },
[&] (const Vector<float>& value) { parametersData->addItem(buildArrayForVector(value)); },
[&] (const Vector<int>&) { parametersData->addItem(indexForData("Int32List")); },
[&] (const String& value) { parametersData->addItem(indexForData(value)); },
[&] (double value) { parametersData->addItem(value); },
[&] (float value) { parametersData->addItem(value); },
[&] (int64_t value) { parametersData->addItem(static_cast<double>(value)); },
[&] (uint32_t value) { parametersData->addItem(static_cast<double>(value)); },
[&] (int32_t value) { parametersData->addItem(value); },
[&] (uint8_t value) { parametersData->addItem(static_cast<int>(value)); },
[&] (bool value) { parametersData->addItem(value); }
);
}
action->addItem(WTFMove(parametersData));
RefPtr<Inspector::Protocol::Array<double>> trace = Inspector::Protocol::Array<double>::create();
if (JSC::CallFrame* callFrame = JSMainThreadExecState::currentState()->vm().topCallFrame) {
callFrame->iterate([&] (JSC::StackVisitor& visitor) {
// Only skip Native frames if they are the first frame (e.g. CanvasRenderingContext2D.prototype.save).
if (!trace->length() && visitor->isNativeFrame())
return JSC::StackVisitor::Continue;
unsigned line = 0;
unsigned column = 0;
visitor->computeLineAndColumn(line, column);
ScriptCallFrame scriptCallFrame(visitor->functionName(), visitor->sourceURL(), static_cast<JSC::SourceID>(visitor->sourceID()), line, column);
trace->addItem(indexForData(scriptCallFrame));
return JSC::StackVisitor::Continue;
});
}
action->addItem(WTFMove(trace));
return action;
}
RefPtr<Inspector::Protocol::Array<InspectorValue>> InspectorCanvas::buildArrayForCanvasGradient(const CanvasGradient& canvasGradient)
{
const Gradient& gradient = canvasGradient.gradient();
bool isRadial = gradient.isRadial();
String type = isRadial ? ASCIILiteral("radial-gradient") : ASCIILiteral("linear-gradient");
RefPtr<Inspector::Protocol::Array<float>> parameters = Inspector::Protocol::Array<float>::create();
parameters->addItem(gradient.p0().x());
parameters->addItem(gradient.p0().y());
if (isRadial)
parameters->addItem(gradient.startRadius());
parameters->addItem(gradient.p1().x());
parameters->addItem(gradient.p1().y());
if (isRadial)
parameters->addItem(gradient.endRadius());
RefPtr<Inspector::Protocol::Array<InspectorValue>> stops = Inspector::Protocol::Array<InspectorValue>::create();
for (const Gradient::ColorStop& colorStop : gradient.stops()) {
RefPtr<Inspector::Protocol::Array<InspectorValue>> stop = Inspector::Protocol::Array<InspectorValue>::create();
stop->addItem(colorStop.offset);
stop->addItem(indexForData(colorStop.color.cssText()));
stops->addItem(WTFMove(stop));
}
RefPtr<Inspector::Protocol::Array<Inspector::InspectorValue>> array = Inspector::Protocol::Array<Inspector::InspectorValue>::create();
array->addItem(indexForData(type));
array->addItem(WTFMove(parameters));
array->addItem(WTFMove(stops));
return array;
}
RefPtr<Inspector::Protocol::Array<InspectorValue>> InspectorCanvas::buildArrayForCanvasPattern(const CanvasPattern& canvasPattern)
{
Image& tileImage = canvasPattern.pattern().tileImage();
std::unique_ptr<ImageBuffer> imageBuffer = ImageBuffer::create(tileImage.size(), RenderingMode::Unaccelerated);
imageBuffer->context().drawImage(tileImage, FloatPoint(0, 0));
String repeat;
bool repeatX = canvasPattern.pattern().repeatX();
bool repeatY = canvasPattern.pattern().repeatY();
if (repeatX && repeatY)
repeat = ASCIILiteral("repeat");
else if (repeatX && !repeatY)
repeat = ASCIILiteral("repeat-x");
else if (!repeatX && repeatY)
repeat = ASCIILiteral("repeat-y");
else
repeat = ASCIILiteral("no-repeat");
RefPtr<Inspector::Protocol::Array<Inspector::InspectorValue>> array = Inspector::Protocol::Array<Inspector::InspectorValue>::create();
array->addItem(indexForData("pattern"));
array->addItem(indexForData(imageBuffer->toDataURL("image/png")));
array->addItem(indexForData(repeat));
return array;
}
RefPtr<Inspector::Protocol::Array<InspectorValue>> InspectorCanvas::buildArrayForImageData(const ImageData& imageData)
{
RefPtr<Inspector::Protocol::Array<int>> data = Inspector::Protocol::Array<int>::create();
for (size_t i = 0; i < imageData.data()->length(); ++i)
data->addItem(imageData.data()->item(i));
RefPtr<Inspector::Protocol::Array<Inspector::InspectorValue>> array = Inspector::Protocol::Array<Inspector::InspectorValue>::create();
array->addItem(WTFMove(data));
array->addItem(imageData.width());
array->addItem(imageData.height());
return array;
}
} // namespace WebCore