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:
parent
b9fe5e8e37
commit
77703e8ba2
7 changed files with 99 additions and 10 deletions
30
ext/canvas/error.rs
Normal file
30
ext/canvas/error.rs
Normal 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")
|
||||
}
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
BIN
tests/testdata/image/1x1-animation-rgba8.webp
vendored
Normal file
BIN
tests/testdata/image/1x1-animation-rgba8.webp
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 188 B |
BIN
tests/testdata/image/1x1-red32f.exr
vendored
Normal file
BIN
tests/testdata/image/1x1-red32f.exr
vendored
Normal file
Binary file not shown.
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
|
Loading…
Add table
Reference in a new issue