Skip to content

Commit

Permalink
Canvas drawImage() API does not throw an exception when the image is …
Browse files Browse the repository at this point in the history
…in broken state

https://bugs.webkit.org/show_bug.cgi?id=274575
rdar://128588063

Reviewed by Kimmo Kinnunen.

If the image source is HTMLOrSVGImageElement and the image in a broken state,
canvas drawImage() should throw an exception.

Canvas image sources specifications:
https://html.spec.whatwg.org/multipage/canvas.html#image-sources-for-2d-rendering-contexts

* LayoutTests/http/tests/lazyload/lazy-image-load-in-iframes-scripting-disabled-expected.txt:
* LayoutTests/http/tests/lazyload/placeholder.js:
(is_image_fully_loaded):
* LayoutTests/imported/w3c/web-platform-tests/html/canvas/element/drawing-images-to-the-canvas/2d.drawImage.nonexistent-expected.txt:
* Source/WebCore/html/canvas/CanvasRenderingContext2DBase.cpp:
(WebCore::CanvasRenderingContext2DBase::drawImage):
* Source/WebCore/html/canvas/CanvasRenderingContext2DBase.h:

Canonical link: https://commits.webkit.org/279319@main
  • Loading branch information
shallawa authored and Said Abou-Hallawa committed May 25, 2024
1 parent e59f7bd commit 6cd33ff
Show file tree
Hide file tree
Showing 5 changed files with 32 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ CONSOLE MESSAGE: Blocked script execution in 'http://127.0.0.1:8000/lazyload/res
CONSOLE MESSAGE: Blocked script execution in 'http://127.0.0.1:8000/lazyload/resources/lazy-load-in-iframe.html' because the document's frame is sandboxed and the 'allow-scripts' permission is not set.
CONSOLE MESSAGE: Blocked script execution in 'http://127.0.0.1:8000/lazyload/resources/lazy-load-in-iframe.html' because the document's frame is sandboxed and the 'allow-scripts' permission is not set.
CONSOLE MESSAGE: Blocked script execution in 'http://127.0.0.1:8000/lazyload/resources/lazy-load-in-iframe.html' because the document's frame is sandboxed and the 'allow-scripts' permission is not set.
CONSOLE MESSAGE: InvalidStateError: The HTMLImageElement provided is in the 'broken' state.

PASS Verify that iframe's with scripting off disallow lazy image loading.

8 changes: 7 additions & 1 deletion LayoutTests/http/tests/lazyload/placeholder.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@ function is_image_fully_loaded(image) {
let canvas = document.createElement('canvas');
canvas.width = canvas.height = 1;
let canvasContext = canvas.getContext("2d");
canvasContext.drawImage(image, 0, 0);

try {
canvasContext.drawImage(image, 0, 0);
} catch (error) {
console.error(error);
}

let data = canvasContext.getImageData(0, 0, canvas.width, canvas.height).data;

// Fully loaded image should not be a placeholder which is drawn as a
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
2d.drawImage.nonexistent
Actual output:

FAIL Canvas test: 2d.drawImage.nonexistent assert_throws_dom: function "function() { ctx.drawImage(img, 0, 0); }" did not throw
PASS Canvas test: 2d.drawImage.nonexistent

32 changes: 22 additions & 10 deletions Source/WebCore/html/canvas/CanvasRenderingContext2DBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1593,7 +1593,15 @@ ExceptionOr<void> CanvasRenderingContext2DBase::drawImage(HTMLImageElement& imag
{
if (!imageElement.complete())
return { };
FloatRect imageRect = FloatRect(FloatPoint(), size(imageElement, ImageSizeType::BeforeDevicePixelRatio));

auto* cachedImage = imageElement.cachedImage();
if (!cachedImage)
return { };

if (cachedImage->status() == CachedImage::Status::DecodeError)
return Exception { ExceptionCode::InvalidStateError, "The HTMLImageElement provided is in the 'broken' state."_s };

auto imageRect = FloatRect(FloatPoint(), size(imageElement, ImageSizeType::BeforeDevicePixelRatio));

auto orientation = ImageOrientation::Orientation::FromImage;
if (imageElement.allowsOrientationOverride()) {
Expand All @@ -1603,7 +1611,7 @@ ExceptionOr<void> CanvasRenderingContext2DBase::drawImage(HTMLImageElement& imag
orientation = computedStyle->imageOrientation().orientation();
}

auto result = drawImage(imageElement.document(), imageElement.cachedImage(), imageElement.renderer(), imageRect, srcRect, dstRect, op, blendMode, orientation);
auto result = drawImage(imageElement.document(), *cachedImage, imageElement.renderer(), imageRect, srcRect, dstRect, op, blendMode, orientation);

if (!result.hasException())
checkOrigin(&imageElement);
Expand All @@ -1617,9 +1625,16 @@ ExceptionOr<void> CanvasRenderingContext2DBase::drawImage(SVGImageElement& image

ExceptionOr<void> CanvasRenderingContext2DBase::drawImage(SVGImageElement& imageElement, const FloatRect& srcRect, const FloatRect& dstRect, const CompositeOperator& op, const BlendMode& blendMode)
{
FloatRect imageRect = FloatRect(FloatPoint(), size(imageElement, ImageSizeType::BeforeDevicePixelRatio));
auto* cachedImage = imageElement.cachedImage();
if (!cachedImage)
return { };

if (cachedImage->status() == CachedImage::Status::DecodeError)
return Exception { ExceptionCode::InvalidStateError, "The SVGImageElement provided is in the 'broken' state."_s };

auto imageRect = FloatRect(FloatPoint(), size(imageElement, ImageSizeType::BeforeDevicePixelRatio));

auto result = drawImage(imageElement.document(), imageElement.cachedImage(), imageElement.renderer(), imageRect, srcRect, dstRect, op, blendMode);
auto result = drawImage(imageElement.document(), *cachedImage, imageElement.renderer(), imageRect, srcRect, dstRect, op, blendMode);

if (!result.hasException())
checkOrigin(&imageElement);
Expand All @@ -1633,7 +1648,7 @@ ExceptionOr<void> CanvasRenderingContext2DBase::drawImage(CSSStyleImageValue& im
return { };
FloatRect imageRect = FloatRect(FloatPoint(), size(image));

auto result = drawImage(*image.document(), cachedImage, nullptr, imageRect, srcRect, dstRect, state().globalComposite, state().globalBlend);
auto result = drawImage(*image.document(), *cachedImage, nullptr, imageRect, srcRect, dstRect, state().globalComposite, state().globalBlend);

if (!result.hasException())
checkOrigin(image);
Expand Down Expand Up @@ -1666,7 +1681,7 @@ ExceptionOr<void> CanvasRenderingContext2DBase::drawImage(WebCodecsVideoFrame& f
}
#endif

ExceptionOr<void> CanvasRenderingContext2DBase::drawImage(Document& document, CachedImage* cachedImage, const RenderObject* renderer, const FloatRect& imageRect, const FloatRect& srcRect, const FloatRect& dstRect, const CompositeOperator& op, const BlendMode& blendMode, ImageOrientation orientation)
ExceptionOr<void> CanvasRenderingContext2DBase::drawImage(Document& document, CachedImage& cachedImage, const RenderObject* renderer, const FloatRect& imageRect, const FloatRect& srcRect, const FloatRect& dstRect, const CompositeOperator& op, const BlendMode& blendMode, ImageOrientation orientation)
{
if (!std::isfinite(dstRect.x()) || !std::isfinite(dstRect.y()) || !std::isfinite(dstRect.width()) || !std::isfinite(dstRect.height())
|| !std::isfinite(srcRect.x()) || !std::isfinite(srcRect.y()) || !std::isfinite(srcRect.width()) || !std::isfinite(srcRect.height()))
Expand Down Expand Up @@ -1697,10 +1712,7 @@ ExceptionOr<void> CanvasRenderingContext2DBase::drawImage(Document& document, Ca
if (!state().hasInvertibleTransform)
return { };

if (!cachedImage)
return { };

RefPtr<Image> image = cachedImage->imageForRenderer(renderer);
RefPtr<Image> image = cachedImage.imageForRenderer(renderer);
if (!image)
return { };

Expand Down
2 changes: 1 addition & 1 deletion Source/WebCore/html/canvas/CanvasRenderingContext2DBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,7 @@ class CanvasRenderingContext2DBase : public CanvasRenderingContext, public Canva
ExceptionOr<void> drawImage(SVGImageElement&, const FloatRect& srcRect, const FloatRect& dstRect);
ExceptionOr<void> drawImage(SVGImageElement&, const FloatRect& srcRect, const FloatRect& dstRect, const CompositeOperator&, const BlendMode&);
ExceptionOr<void> drawImage(CanvasBase&, const FloatRect& srcRect, const FloatRect& dstRect);
ExceptionOr<void> drawImage(Document&, CachedImage*, const RenderObject*, const FloatRect& imageRect, const FloatRect& srcRect, const FloatRect& dstRect, const CompositeOperator&, const BlendMode&, ImageOrientation = ImageOrientation::Orientation::FromImage);
ExceptionOr<void> drawImage(Document&, CachedImage&, const RenderObject*, const FloatRect& imageRect, const FloatRect& srcRect, const FloatRect& dstRect, const CompositeOperator&, const BlendMode&, ImageOrientation = ImageOrientation::Orientation::FromImage);
#if ENABLE(VIDEO)
ExceptionOr<void> drawImage(HTMLVideoElement&, const FloatRect& srcRect, const FloatRect& dstRect);
#endif
Expand Down

0 comments on commit 6cd33ff

Please sign in to comment.