295static RefPtr<Inspector::Protocol::Array<double>> buildArrayForAffineTransform(const AffineTransform& affineTransform)
296{
297 RefPtr<Inspector::Protocol::Array<double>> array = Inspector::Protocol::Array<double>::create();
298 array->addItem(affineTransform.a());
299 array->addItem(affineTransform.b());
300 array->addItem(affineTransform.c());
301 array->addItem(affineTransform.d());
302 array->addItem(affineTransform.e());
303 array->addItem(affineTransform.f());
304 return array;
305}
306
307static RefPtr<InspectorObject> buildObjectForImageData(const ImageData& imageData)
308{
309 RefPtr<InspectorObject> object = InspectorObject::create();
310 object->setInteger(ASCIILiteral("width"), imageData.width());
311 object->setInteger(ASCIILiteral("height"), imageData.height());
312
313 RefPtr<Inspector::Protocol::Array<int>> data = Inspector::Protocol::Array<int>::create();
314 for (size_t i = 0; i < imageData.data()->length(); ++i)
315 data->addItem(imageData.data()->item(i));
316 object->setArray(ASCIILiteral("data"), WTFMove(data));
317
318 return object;
319}
320
321static RefPtr<InspectorObject> buildObjectForCanvasGradient(const CanvasGradient& canvasGradient)
322{
323 bool isRadial = canvasGradient.gradient().isRadial();
324 RefPtr<InspectorObject> object = InspectorObject::create();
325 object->setString("type", isRadial ? "radial-gradient" : "linear-gradient");
326
327 RefPtr<Inspector::Protocol::Array<float>> parameters = Inspector::Protocol::Array<float>::create();
328 parameters->addItem(canvasGradient.gradient().p0().x());
329 parameters->addItem(canvasGradient.gradient().p0().y());
330 if (isRadial)
331 parameters->addItem(canvasGradient.gradient().startRadius());
332 parameters->addItem(canvasGradient.gradient().p1().x());
333 parameters->addItem(canvasGradient.gradient().p1().y());
334 if (isRadial)
335 parameters->addItem(canvasGradient.gradient().endRadius());
336 object->setArray(ASCIILiteral("parameters"), WTFMove(parameters));
337
338 if (!canvasGradient.gradient().stops().isEmpty()) {
339 RefPtr<Inspector::Protocol::Array<InspectorValue>> stops = Inspector::Protocol::Array<InspectorValue>::create();
340 for (const Gradient::ColorStop& colorStop : canvasGradient.gradient().stops()) {
341 RefPtr<Inspector::Protocol::Array<InspectorValue>> stop = Inspector::Protocol::Array<InspectorValue>::create();
342 stop->addItem(colorStop.offset);
343 stop->addItem(colorStop.color.cssText());
344 stops->addItem(WTFMove(stop));
345 }
346 object->setArray(ASCIILiteral("stops"), WTFMove(stops));
347 }
348
349 return object;
350}
351
352static RefPtr<InspectorObject> buildObjectForCanvasPattern(const CanvasPattern& canvasPattern)
353{
354 RefPtr<InspectorObject> object = InspectorObject::create();
355 object->setString(ASCIILiteral("type"), ASCIILiteral("pattern"));
356 object->setString(ASCIILiteral("image"), ASCIILiteral("data:,"));
357 // object->setString(ASCIILiteral("image"), "data:image/png," + base64Encode(pattern->tileImage().data()));
358
359 String repeat;
360 bool repeatX = canvasPattern.pattern().repeatX();
361 bool repeatY = canvasPattern.pattern().repeatY();
362 if (repeatX && repeatY)
363 repeat = ASCIILiteral("repeat");
364 else if (repeatX && !repeatY)
365 repeat = ASCIILiteral("repeat-x");
366 else if (!repeatX && repeatY)
367 repeat = ASCIILiteral("repeat-y");
368 else
369 repeat = ASCIILiteral("no-repeat");
370 object->setString(ASCIILiteral("repeat"), repeat);
371
372 return object;
373}
374
375static RefPtr<Inspector::Protocol::Array<double>> buildArrayForVector(const Vector<float>& vector)
376{
377 RefPtr<Inspector::Protocol::Array<double>> array = Inspector::Protocol::Array<double>::create();
378 for (double item : vector)
379 array->addItem(item);
380 return array;
381}
382
383void InspectorCanvasAgent::didStartRecordingCanvas(HTMLCanvasElement& canvasElement, bool fromConsole)
384{
385 CanvasEntry* canvasEntry = getCanvasEntry(canvasElement);
386 if (!canvasEntry)
387 return;
388
389 auto initialState = Inspector::Protocol::Canvas::InitialState::create()
390 .release();
391
392 RefPtr<InspectorObject> attributes = InspectorObject::create();
393 RefPtr<InspectorObject> functions = InspectorObject::create();
394
395 attributes->setInteger(ASCIILiteral("width"), canvasEntry->element->width());
396 attributes->setInteger(ASCIILiteral("height"), canvasEntry->element->height());
397
398 CanvasRenderingContext* context = canvasEntry->element->renderingContext();
399 if (is<CanvasRenderingContext2D>(context)) {
400 CanvasRenderingContext2D* context2d = downcast<CanvasRenderingContext2D>(context);
401
402 functions->setArray(ASCIILiteral("transform"), buildArrayForAffineTransform(context2d->state().transform));
403 attributes->setDouble(ASCIILiteral("globalAlpha"), context2d->globalAlpha());
404 attributes->setString(ASCIILiteral("globalCompositeOperation"), context2d->globalCompositeOperation());
405 attributes->setDouble(ASCIILiteral("lineWidth"), context2d->lineWidth());
406 attributes->setString(ASCIILiteral("lineCap"), context2d->lineCap());
407 attributes->setString(ASCIILiteral("lineJoin"), context2d->lineJoin());
408 attributes->setDouble(ASCIILiteral("miterLimit"), context2d->miterLimit());
409 attributes->setDouble(ASCIILiteral("shadowOffsetX"), context2d->shadowOffsetX());
410 attributes->setDouble(ASCIILiteral("shadowOffsetY"), context2d->shadowOffsetY());
411 attributes->setDouble(ASCIILiteral("shadowBlur"), context2d->shadowBlur());
412 attributes->setString(ASCIILiteral("shadowColor"), context2d->shadowColor());
413 functions->setArray(ASCIILiteral("setLineDash"), buildArrayForVector(context2d->getLineDash()));
414 attributes->setDouble(ASCIILiteral("lineDashOffset"), context2d->lineDashOffset());
415 attributes->setString(ASCIILiteral("font"), context2d->font());
416 attributes->setString(ASCIILiteral("textAlign"), context2d->textAlign());
417 attributes->setString(ASCIILiteral("textBaseline"), context2d->textBaseline());
418 attributes->setString(ASCIILiteral("direction"), context2d->direction());
419
420 if (const CanvasGradient* canvasGradient = context2d->state().strokeStyle.canvasGradient())
421 attributes->setObject(ASCIILiteral("strokeStyle"), buildObjectForCanvasGradient(*canvasGradient));
422 else if (const CanvasPattern* canvasPattern = context2d->state().strokeStyle.canvasPattern())
423 attributes->setObject(ASCIILiteral("strokeStyle"), buildObjectForCanvasPattern(*canvasPattern));
424 else
425 attributes->setString(ASCIILiteral("strokeStyle"), context2d->state().strokeStyle.color());
426
427 if (const CanvasGradient* canvasGradient = context2d->state().fillStyle.canvasGradient())
428 attributes->setObject(ASCIILiteral("fillStyle"), buildObjectForCanvasGradient(*canvasGradient));
429 else if (const CanvasPattern* canvasPattern = context2d->state().fillStyle.canvasPattern())
430 attributes->setObject(ASCIILiteral("fillStyle"), buildObjectForCanvasPattern(*canvasPattern));
431 else
432 attributes->setString(ASCIILiteral("fillStyle"), context2d->state().fillStyle.color());
433
434 attributes->setBoolean(ASCIILiteral("imageSmoothingEnabled"), context2d->imageSmoothingEnabled());
435 attributes->setString(ASCIILiteral("imageSmoothingQuality"), CanvasRenderingContext2D::stringForImageSmoothingQuality(context2d->imageSmoothingQuality()));
436 }
437
438 if (attributes->size())
439 initialState->setAttributes(WTFMove(attributes));
440
441 if (functions->size())
442 initialState->setFunctions(WTFMove(functions));
443
444 ExceptionOr<String> result = canvasEntry->element->toDataURL(ASCIILiteral("image/png"));
445 if (!result.hasException())
446 initialState->setContent(result.releaseReturnValue());
447
448 m_recordingData.set(canvasEntry->element, CanvasRecordingData(WTFMove(initialState), fromConsole));
449
450 if (!fromConsole && !m_canvasRecordingTimer.isActive())
451 m_canvasRecordingTimer.startOneShot(0_s);
452
453 canvasElement.setRecordingState(HTMLCanvasElement::RecordingState::Active);
454}
455
456void InspectorCanvasAgent::recordCanvasAction(HTMLCanvasElement& canvasElement, const String& name, Vector<Canvas2DParameterVariant>&& parameters)
457{
458 if (canvasElement.recordingState() == HTMLCanvasElement::RecordingState::Requested)
459 didStartRecordingCanvas(canvasElement);
460
461 CanvasRecordingData* data = getRecordingData(canvasElement);
462 if (!data)
463 return;
464
465 auto action = Inspector::Protocol::Canvas::RecordingAction::create()
466 .setName(name)
467 .release();
468
469 if (!parameters.isEmpty()) {
470 RefPtr<Inspector::Protocol::Array<Inspector::InspectorValue>> parametersData = Inspector::Protocol::Array<Inspector::InspectorValue>::create();
471 for (const Canvas2DParameterVariant& item : parameters) {
472 WTF::switchOn(item,
473 [&] (const RefPtr<CanvasGradient>& value) { parametersData->addItem(buildObjectForCanvasGradient(*value)); },
474 [&] (const RefPtr<CanvasPattern>& value) { parametersData->addItem(buildObjectForCanvasPattern(*value)); },
475 [&] (const RefPtr<ImageData>& value) { parametersData->addItem(buildObjectForImageData(*value)); },
476 [&] (const Vector<float>& value) { parametersData->addItem(buildArrayForVector(value)); },
477 [&] (const String& value) { parametersData->addItem(value); },
478 [&] (double value) { parametersData->addItem(value); },
479 [&] (float value) { parametersData->addItem(value); },
480 [&] (unsigned value) { parametersData->addItem(static_cast<int>(value)); },
481 [&] (bool value) { parametersData->addItem(value); }
482 );
483 }
484 action->setParameters(WTFMove(parametersData));
485 }
486
487 data->actions->addItem(WTFMove(action));
488}
489
490void InspectorCanvasAgent::didFinishRecordingCanvas(HTMLCanvasElement& canvasElement, bool fromConsole)
491{
492 CanvasEntry* canvasEntry = getCanvasEntry(canvasElement);
493 if (!canvasEntry) {
494 clearRecordingData(canvasElement);
495 return;
496 }
497
498 CanvasRecordingData* data = getRecordingData(*canvasEntry->element);
499 if (!data) {
500 clearRecordingData(canvasElement);
501 return;
502 }
503
504 if (!fromConsole && data->fromConsole)
505 return;
506
507 m_frontendDispatcher->recordingFinished(canvasEntry->identifier, data->initialState, data->actions);
508
509 clearRecordingData(canvasElement);
510}
511