| /* |
| * 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 |
| |