0
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-03-09 13:49:37 -04:00

handling unsuportted situation

- adding `DOMExceptionInvalidStateError` in global due to handling not supported image format
- animation image is not supported currently
This commit is contained in:
Hajime-san 2024-08-21 12:29:48 +09:00
parent b9fe5e8e37
commit 77703e8ba2
7 changed files with 99 additions and 10 deletions

30
ext/canvas/error.rs Normal file
View file

@ -0,0 +1,30 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use deno_core::error::AnyError;
use std::fmt;
#[derive(Debug)]
pub struct DOMExceptionInvalidStateError {
pub msg: String,
}
impl DOMExceptionInvalidStateError {
pub fn new(msg: &str) -> Self {
DOMExceptionInvalidStateError {
msg: msg.to_string(),
}
}
}
impl fmt::Display for DOMExceptionInvalidStateError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.pad(&self.msg)
}
}
impl std::error::Error for DOMExceptionInvalidStateError {}
pub fn get_error_class_name(e: &AnyError) -> Option<&'static str> {
e.downcast_ref::<DOMExceptionInvalidStateError>()
.map(|_| "DOMExceptionInvalidStateError")
}

View file

@ -1,16 +1,22 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use deno_core::error::type_error;
use deno_core::error::AnyError;
use deno_core::op2;
use deno_core::ToJsBuffer;
use image::imageops::FilterType;
use image::AnimationDecoder;
use image::GenericImageView;
use image::Pixel;
use serde::Deserialize;
use serde::Serialize;
use std::io::BufReader;
use std::io::Cursor;
use std::path::PathBuf;
pub mod error;
use error::DOMExceptionInvalidStateError;
#[derive(Debug, Deserialize)]
#[serde(rename_all = "snake_case")]
enum ImageResizeQuality {
@ -20,6 +26,13 @@ enum ImageResizeQuality {
High,
}
#[derive(Debug, Deserialize)]
// Follow the cases defined in the spec
enum ImageBitmapSource {
Blob,
ImageData,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct ImageProcessArgs {
@ -37,13 +50,6 @@ struct ImageProcessArgs {
image_bitmap_source: ImageBitmapSource,
}
#[derive(Debug, Deserialize)]
// Follow the cases defined in the spec
enum ImageBitmapSource {
Blob,
ImageData,
}
#[op2]
#[serde]
fn op_image_process(
@ -147,10 +153,25 @@ fn op_image_decode(
#[buffer] buf: &[u8],
#[serde] options: ImageDecodeOptions,
) -> Result<DecodedImage, AnyError> {
let reader = std::io::BufReader::new(Cursor::new(buf));
let reader = BufReader::new(Cursor::new(buf));
//
// TODO: support animated images
// It's a little hard to implement animated images along spec because of the complexity.
//
// > If this is an animated image, imageBitmap's bitmap data must only be taken from
// > the default image of the animation (the one that the format defines is to be used when animation is
// > not supported or is disabled), or, if there is no such image, the first frame of the animation.
// https://html.spec.whatwg.org/multipage/imagebitmap-and-animations.html
//
// see also browser implementations: (The implementation of Gecko and WebKit is hard to read.)
// https://source.chromium.org/chromium/chromium/src/+/bdbc054a6cabbef991904b5df9066259505cc686:third_party/blink/renderer/platform/image-decoders/image_decoder.h;l=175-189
//
let image = match &*options.mime_type {
"image/png" => {
let decoder = image::codecs::png::PngDecoder::new(reader)?;
if decoder.is_apng()? {
return Err(type_error("Animation image is not supported."));
}
image::DynamicImage::from_decoder(decoder)?
}
"image/jpeg" => {
@ -158,6 +179,11 @@ fn op_image_decode(
image::DynamicImage::from_decoder(decoder)?
}
"image/gif" => {
let decoder = image::codecs::gif::GifDecoder::new(reader)?;
if decoder.into_frames().count() > 1 {
return Err(type_error("Animation image is not supported."));
}
let reader = BufReader::new(Cursor::new(buf));
let decoder = image::codecs::gif::GifDecoder::new(reader)?;
image::DynamicImage::from_decoder(decoder)?
}
@ -171,9 +197,21 @@ fn op_image_decode(
}
"image/webp" => {
let decoder = image::codecs::webp::WebPDecoder::new(reader)?;
if decoder.has_animation() {
return Err(type_error("Animation image is not supported."));
}
image::DynamicImage::from_decoder(decoder)?
}
_ => unreachable!(),
// return an error if the mime type is not supported in the variable list of ImageTypePatternTable below
// ext/web/01_mimesniff.js
_ => {
return Err(
DOMExceptionInvalidStateError::new(
"The source image is not a supported format.",
)
.into(),
)
}
};
let (width, height) = image.dimensions();

View file

@ -159,6 +159,7 @@ pub fn get_error_class_name(e: &AnyError) -> Option<&'static str> {
.or_else(|| deno_web::get_error_class_name(e))
.or_else(|| deno_webstorage::get_not_supported_error_class_name(e))
.or_else(|| deno_websocket::get_network_error_class_name(e))
.or_else(|| deno_canvas::error::get_error_class_name(e))
.or_else(|| {
e.downcast_ref::<dlopen2::Error>()
.map(get_dlopen_error_class)

View file

@ -453,6 +453,12 @@ core.registerErrorBuilder(
return new DOMException(msg, "DataError");
},
);
core.registerErrorBuilder(
"DOMExceptionInvalidStateError",
function DOMExceptionInvalidStateError(msg) {
return new DOMException(msg, "InvalidStateError");
},
);
function runtimeStart(
denoVersion,

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 B

BIN
tests/testdata/image/1x1-red32f.exr vendored Normal file

Binary file not shown.

View file

@ -1,6 +1,6 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
import { assertEquals } from "./test_util.ts";
import { assertEquals, assertRejects } from "./test_util.ts";
function generateNumberedData(n: number): Uint8ClampedArray {
return new Uint8ClampedArray(
@ -128,6 +128,14 @@ Deno.test(async function imageBitmapFromBlob() {
// deno-fmt-ignore
assertEquals(Deno[Deno.internal].getBitmapData(imageBitmap), new Uint8Array([255, 0, 0, 255]));
}
{
// the chunk of animation webp is below (3 frames, 1x1, 8-bit, RGBA)
// [ 255, 0, 0, 127,
// 0, 255, 0, 127,
// 0, 0, 255, 127 ]
const imageData = new Blob([await Deno.readFile(`${prefix}/1x1-animation-rgba8.webp`)], { type: "image/webp" });
await assertRejects(() => createImageBitmap(imageData), TypeError);
}
{
const imageData = new Blob([await Deno.readFile(`${prefix}/1x1-red8.ico`)], { type: "image/x-icon" });
const imageBitmap = await createImageBitmap(imageData);
@ -135,4 +143,10 @@ Deno.test(async function imageBitmapFromBlob() {
// deno-fmt-ignore
assertEquals(Deno[Deno.internal].getBitmapData(imageBitmap), new Uint8Array([255, 0, 0, 255]));
}
{
// image/x-exr is a known mimetype for OpenEXR
// https://www.digipres.org/formats/sources/fdd/formats/#fdd000583
const imageData = new Blob([await Deno.readFile(`${prefix}/1x1-red32f.exr`)], { type: "image/x-exr" });
await assertRejects(() => createImageBitmap(imageData), DOMException);
}
});