2022-01-07 22:09:52 -05:00
|
|
|
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
|
2020-08-07 10:55:02 -04:00
|
|
|
|
2021-07-05 09:34:37 -04:00
|
|
|
mod blob;
|
2022-01-24 12:03:06 -05:00
|
|
|
mod compression;
|
2021-06-21 13:53:52 -04:00
|
|
|
mod message_port;
|
2022-02-15 06:17:30 -05:00
|
|
|
mod timers;
|
2021-06-21 13:53:52 -04:00
|
|
|
|
2021-06-05 17:10:07 -04:00
|
|
|
use deno_core::error::range_error;
|
|
|
|
use deno_core::error::type_error;
|
2021-06-05 13:30:20 -04:00
|
|
|
use deno_core::error::AnyError;
|
2021-04-28 12:41:50 -04:00
|
|
|
use deno_core::include_js_files;
|
2022-03-14 13:44:15 -04:00
|
|
|
use deno_core::op;
|
2022-09-17 07:18:15 -04:00
|
|
|
use deno_core::serde_v8;
|
2021-06-10 09:26:10 -04:00
|
|
|
use deno_core::url::Url;
|
2022-09-17 07:18:15 -04:00
|
|
|
use deno_core::v8;
|
2022-03-05 14:12:30 -05:00
|
|
|
use deno_core::ByteString;
|
2022-04-18 18:00:14 -04:00
|
|
|
use deno_core::CancelHandle;
|
2021-04-28 12:41:50 -04:00
|
|
|
use deno_core::Extension;
|
2021-06-05 17:10:07 -04:00
|
|
|
use deno_core::OpState;
|
|
|
|
use deno_core::Resource;
|
|
|
|
use deno_core::ResourceId;
|
2022-03-15 19:22:00 -04:00
|
|
|
use deno_core::U16String;
|
2021-06-05 17:10:07 -04:00
|
|
|
use deno_core::ZeroCopyBuf;
|
2022-09-17 07:18:15 -04:00
|
|
|
|
2021-06-05 17:10:07 -04:00
|
|
|
use encoding_rs::CoderResult;
|
|
|
|
use encoding_rs::Decoder;
|
|
|
|
use encoding_rs::DecoderResult;
|
|
|
|
use encoding_rs::Encoding;
|
|
|
|
use std::borrow::Cow;
|
|
|
|
use std::cell::RefCell;
|
2021-06-05 13:30:20 -04:00
|
|
|
use std::fmt;
|
2022-03-16 20:25:44 -04:00
|
|
|
use std::path::PathBuf;
|
2021-06-05 17:10:07 -04:00
|
|
|
use std::usize;
|
2020-08-07 10:55:02 -04:00
|
|
|
|
2021-07-05 09:34:37 -04:00
|
|
|
use crate::blob::op_blob_create_object_url;
|
|
|
|
use crate::blob::op_blob_create_part;
|
fix: a `Request` whose URL is a revoked blob URL should still fetch (#11947)
In the spec, a URL record has an associated "blob URL entry", which for
`blob:` URLs is populated during parsing to contain a reference to the
`Blob` object that backs that object URL. It is this blob URL entry that
the `fetch` API uses to resolve an object URL.
Therefore, since the `Request` constructor parses URL inputs, it will
have an associated blob URL entry which will be used when fetching, even
if the object URL has been revoked since the construction of the
`Request` object. (The `Request` constructor takes the URL as a string
and parses it, so the object URL must be live at the time it is called.)
This PR adds a new `blobFromObjectUrl` JS function (backed by a new
`op_blob_from_object_url` op) that, if the URL is a valid object URL,
returns a new `Blob` object whose parts are references to the same Rust
`BlobPart`s used by the original `Blob` object. It uses this function to
add a new `blobUrlEntry` field to inner requests, which will be `null`
or such a `Blob`, and then uses `Blob.prototype.stream()` as the
response's body. As a result of this, the `blob:` URL resolution from
`op_fetch` is now useless, and has been removed.
2021-09-08 05:29:21 -04:00
|
|
|
use crate::blob::op_blob_from_object_url;
|
2021-07-05 09:34:37 -04:00
|
|
|
use crate::blob::op_blob_read_part;
|
|
|
|
use crate::blob::op_blob_remove_part;
|
|
|
|
use crate::blob::op_blob_revoke_object_url;
|
|
|
|
use crate::blob::op_blob_slice_part;
|
|
|
|
pub use crate::blob::Blob;
|
|
|
|
pub use crate::blob::BlobPart;
|
|
|
|
pub use crate::blob::BlobStore;
|
|
|
|
pub use crate::blob::InMemoryBlobPart;
|
|
|
|
|
|
|
|
pub use crate::message_port::create_entangled_message_port;
|
2021-06-21 13:53:52 -04:00
|
|
|
use crate::message_port::op_message_port_create_entangled;
|
|
|
|
use crate::message_port::op_message_port_post_message;
|
|
|
|
use crate::message_port::op_message_port_recv_message;
|
2021-07-05 09:34:37 -04:00
|
|
|
pub use crate::message_port::JsMessageData;
|
|
|
|
pub use crate::message_port::MessagePort;
|
2021-06-21 13:53:52 -04:00
|
|
|
|
2022-02-15 06:17:30 -05:00
|
|
|
use crate::timers::op_now;
|
|
|
|
use crate::timers::op_sleep;
|
|
|
|
use crate::timers::op_timer_handle;
|
|
|
|
use crate::timers::StartTime;
|
|
|
|
pub use crate::timers::TimersPermission;
|
|
|
|
|
2020-09-30 10:51:01 -04:00
|
|
|
/// Load and execute the javascript code.
|
2022-02-15 06:17:30 -05:00
|
|
|
pub fn init<P: TimersPermission + 'static>(
|
|
|
|
blob_store: BlobStore,
|
|
|
|
maybe_location: Option<Url>,
|
|
|
|
) -> Extension {
|
2021-04-28 18:16:45 -04:00
|
|
|
Extension::builder()
|
|
|
|
.js(include_js_files!(
|
2021-08-11 06:27:05 -04:00
|
|
|
prefix "deno:ext/web",
|
2021-04-28 18:16:45 -04:00
|
|
|
"00_infra.js",
|
|
|
|
"01_dom_exception.js",
|
|
|
|
"01_mimesniff.js",
|
|
|
|
"02_event.js",
|
2021-06-07 08:19:33 -04:00
|
|
|
"02_structured_clone.js",
|
2022-02-15 06:17:30 -05:00
|
|
|
"02_timers.js",
|
2021-04-28 18:16:45 -04:00
|
|
|
"03_abort_signal.js",
|
|
|
|
"04_global_interfaces.js",
|
2021-06-05 17:10:07 -04:00
|
|
|
"05_base64.js",
|
2021-06-14 07:51:02 -04:00
|
|
|
"06_streams.js",
|
2021-04-28 18:16:45 -04:00
|
|
|
"08_text_encoding.js",
|
2021-06-10 09:26:10 -04:00
|
|
|
"09_file.js",
|
|
|
|
"10_filereader.js",
|
|
|
|
"11_blob_url.js",
|
2021-04-28 18:16:45 -04:00
|
|
|
"12_location.js",
|
2021-06-21 13:53:52 -04:00
|
|
|
"13_message_port.js",
|
2022-01-24 12:03:06 -05:00
|
|
|
"14_compression.js",
|
2022-02-15 06:17:30 -05:00
|
|
|
"15_performance.js",
|
2021-04-28 18:16:45 -04:00
|
|
|
))
|
2021-06-05 17:10:07 -04:00
|
|
|
.ops(vec![
|
2022-03-14 13:44:15 -04:00
|
|
|
op_base64_decode::decl(),
|
|
|
|
op_base64_encode::decl(),
|
|
|
|
op_base64_atob::decl(),
|
|
|
|
op_base64_btoa::decl(),
|
|
|
|
op_encoding_normalize_label::decl(),
|
2022-05-17 09:52:48 -04:00
|
|
|
op_encoding_decode_single::decl(),
|
2022-03-14 13:44:15 -04:00
|
|
|
op_encoding_new_decoder::decl(),
|
|
|
|
op_encoding_decode::decl(),
|
|
|
|
op_encoding_encode_into::decl(),
|
|
|
|
op_blob_create_part::decl(),
|
|
|
|
op_blob_slice_part::decl(),
|
|
|
|
op_blob_read_part::decl(),
|
|
|
|
op_blob_remove_part::decl(),
|
|
|
|
op_blob_create_object_url::decl(),
|
|
|
|
op_blob_revoke_object_url::decl(),
|
|
|
|
op_blob_from_object_url::decl(),
|
|
|
|
op_message_port_create_entangled::decl(),
|
|
|
|
op_message_port_post_message::decl(),
|
|
|
|
op_message_port_recv_message::decl(),
|
|
|
|
compression::op_compression_new::decl(),
|
|
|
|
compression::op_compression_write::decl(),
|
|
|
|
compression::op_compression_finish::decl(),
|
|
|
|
op_now::decl::<P>(),
|
|
|
|
op_timer_handle::decl(),
|
2022-04-18 18:00:14 -04:00
|
|
|
op_cancel_handle::decl(),
|
2022-03-14 13:44:15 -04:00
|
|
|
op_sleep::decl(),
|
2021-06-05 17:10:07 -04:00
|
|
|
])
|
2021-06-10 09:26:10 -04:00
|
|
|
.state(move |state| {
|
2021-07-05 09:34:37 -04:00
|
|
|
state.put(blob_store.clone());
|
2021-06-10 09:26:10 -04:00
|
|
|
if let Some(location) = maybe_location.clone() {
|
|
|
|
state.put(Location(location));
|
|
|
|
}
|
2022-02-15 06:17:30 -05:00
|
|
|
state.put(StartTime::now());
|
2021-06-10 09:26:10 -04:00
|
|
|
Ok(())
|
|
|
|
})
|
2021-04-28 18:16:45 -04:00
|
|
|
.build()
|
2020-08-07 10:55:02 -04:00
|
|
|
}
|
|
|
|
|
2022-03-14 13:44:15 -04:00
|
|
|
#[op]
|
2022-03-15 19:33:46 -04:00
|
|
|
fn op_base64_decode(input: String) -> Result<ZeroCopyBuf, AnyError> {
|
2022-06-30 04:48:06 -04:00
|
|
|
let mut s = input.into_bytes();
|
|
|
|
let decoded_len = forgiving_base64_decode(&mut s)?;
|
|
|
|
s.truncate(decoded_len);
|
|
|
|
Ok(s.into())
|
2022-03-05 14:12:30 -05:00
|
|
|
}
|
|
|
|
|
2022-03-14 13:44:15 -04:00
|
|
|
#[op]
|
2022-06-30 04:48:06 -04:00
|
|
|
fn op_base64_atob(mut s: ByteString) -> Result<ByteString, AnyError> {
|
|
|
|
let decoded_len = forgiving_base64_decode(&mut s)?;
|
|
|
|
s.truncate(decoded_len);
|
|
|
|
Ok(s)
|
2022-06-29 11:42:39 -04:00
|
|
|
}
|
2022-03-05 14:12:30 -05:00
|
|
|
|
2022-06-29 11:42:39 -04:00
|
|
|
/// See <https://infra.spec.whatwg.org/#forgiving-base64>
|
2022-06-30 04:48:06 -04:00
|
|
|
#[inline]
|
|
|
|
fn forgiving_base64_decode(input: &mut [u8]) -> Result<usize, AnyError> {
|
2022-06-29 11:42:39 -04:00
|
|
|
let error: _ =
|
|
|
|
|| DomExceptionInvalidCharacterError::new("Failed to decode base64");
|
2022-06-30 04:48:06 -04:00
|
|
|
let decoded = base64_simd::Base64::forgiving_decode_inplace(input)
|
2022-06-29 11:42:39 -04:00
|
|
|
.map_err(|_| error())?;
|
2022-06-30 04:48:06 -04:00
|
|
|
Ok(decoded.len())
|
2021-06-05 17:10:07 -04:00
|
|
|
}
|
|
|
|
|
2022-03-14 13:44:15 -04:00
|
|
|
#[op]
|
2022-09-07 06:51:47 -04:00
|
|
|
fn op_base64_encode(s: &[u8]) -> String {
|
|
|
|
forgiving_base64_encode(s)
|
2022-03-05 14:12:30 -05:00
|
|
|
}
|
|
|
|
|
2022-03-14 13:44:15 -04:00
|
|
|
#[op]
|
2022-05-13 04:36:31 -04:00
|
|
|
fn op_base64_btoa(s: ByteString) -> String {
|
2022-06-29 11:42:39 -04:00
|
|
|
forgiving_base64_encode(s.as_ref())
|
2022-03-05 14:12:30 -05:00
|
|
|
}
|
|
|
|
|
2022-06-29 11:42:39 -04:00
|
|
|
/// See <https://infra.spec.whatwg.org/#forgiving-base64>
|
2022-06-30 04:48:06 -04:00
|
|
|
#[inline]
|
2022-06-29 11:42:39 -04:00
|
|
|
fn forgiving_base64_encode(s: &[u8]) -> String {
|
2022-06-30 04:48:06 -04:00
|
|
|
const BASE64_STANDARD: base64_simd::Base64 = base64_simd::Base64::STANDARD;
|
|
|
|
BASE64_STANDARD.encode_to_boxed_str(s).into_string()
|
2021-06-05 17:10:07 -04:00
|
|
|
}
|
|
|
|
|
2022-03-14 13:44:15 -04:00
|
|
|
#[op]
|
2022-03-15 19:33:46 -04:00
|
|
|
fn op_encoding_normalize_label(label: String) -> Result<String, AnyError> {
|
2021-06-05 17:10:07 -04:00
|
|
|
let encoding = Encoding::for_label_no_replacement(label.as_bytes())
|
|
|
|
.ok_or_else(|| {
|
|
|
|
range_error(format!(
|
|
|
|
"The encoding label provided ('{}') is invalid.",
|
|
|
|
label
|
|
|
|
))
|
|
|
|
})?;
|
|
|
|
Ok(encoding.name().to_lowercase())
|
|
|
|
}
|
|
|
|
|
2022-05-17 09:52:48 -04:00
|
|
|
#[op]
|
|
|
|
fn op_encoding_decode_single(
|
2022-09-07 06:51:47 -04:00
|
|
|
data: &[u8],
|
2022-09-01 06:51:13 -04:00
|
|
|
label: String,
|
|
|
|
fatal: bool,
|
|
|
|
ignore_bom: bool,
|
2022-05-17 09:52:48 -04:00
|
|
|
) -> Result<U16String, AnyError> {
|
|
|
|
let encoding = Encoding::for_label(label.as_bytes()).ok_or_else(|| {
|
|
|
|
range_error(format!(
|
|
|
|
"The encoding label provided ('{}') is invalid.",
|
|
|
|
label
|
|
|
|
))
|
|
|
|
})?;
|
|
|
|
|
|
|
|
let mut decoder = if ignore_bom {
|
|
|
|
encoding.new_decoder_without_bom_handling()
|
|
|
|
} else {
|
|
|
|
encoding.new_decoder_with_bom_removal()
|
|
|
|
};
|
|
|
|
|
|
|
|
let max_buffer_length = decoder
|
|
|
|
.max_utf16_buffer_length(data.len())
|
|
|
|
.ok_or_else(|| range_error("Value too large to decode."))?;
|
|
|
|
|
|
|
|
let mut output = vec![0; max_buffer_length];
|
|
|
|
|
|
|
|
if fatal {
|
|
|
|
let (result, _, written) =
|
2022-09-07 06:51:47 -04:00
|
|
|
decoder.decode_to_utf16_without_replacement(data, &mut output, true);
|
2022-05-17 09:52:48 -04:00
|
|
|
match result {
|
|
|
|
DecoderResult::InputEmpty => {
|
|
|
|
output.truncate(written);
|
|
|
|
Ok(output.into())
|
|
|
|
}
|
|
|
|
DecoderResult::OutputFull => {
|
|
|
|
Err(range_error("Provided buffer too small."))
|
|
|
|
}
|
|
|
|
DecoderResult::Malformed(_, _) => {
|
|
|
|
Err(type_error("The encoded data is not valid."))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
let (result, _, written, _) =
|
2022-09-07 06:51:47 -04:00
|
|
|
decoder.decode_to_utf16(data, &mut output, true);
|
2022-05-17 09:52:48 -04:00
|
|
|
match result {
|
|
|
|
CoderResult::InputEmpty => {
|
|
|
|
output.truncate(written);
|
|
|
|
Ok(output.into())
|
|
|
|
}
|
|
|
|
CoderResult::OutputFull => Err(range_error("Provided buffer too small.")),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-14 13:44:15 -04:00
|
|
|
#[op]
|
2021-06-05 17:10:07 -04:00
|
|
|
fn op_encoding_new_decoder(
|
|
|
|
state: &mut OpState,
|
2022-09-01 06:51:13 -04:00
|
|
|
label: String,
|
|
|
|
fatal: bool,
|
|
|
|
ignore_bom: bool,
|
2021-06-05 17:10:07 -04:00
|
|
|
) -> Result<ResourceId, AnyError> {
|
|
|
|
let encoding = Encoding::for_label(label.as_bytes()).ok_or_else(|| {
|
|
|
|
range_error(format!(
|
|
|
|
"The encoding label provided ('{}') is invalid.",
|
|
|
|
label
|
|
|
|
))
|
|
|
|
})?;
|
|
|
|
|
|
|
|
let decoder = if ignore_bom {
|
|
|
|
encoding.new_decoder_without_bom_handling()
|
|
|
|
} else {
|
|
|
|
encoding.new_decoder_with_bom_removal()
|
|
|
|
};
|
|
|
|
|
|
|
|
let rid = state.resource_table.add(TextDecoderResource {
|
|
|
|
decoder: RefCell::new(decoder),
|
|
|
|
fatal,
|
|
|
|
});
|
|
|
|
|
|
|
|
Ok(rid)
|
|
|
|
}
|
|
|
|
|
2022-03-14 13:44:15 -04:00
|
|
|
#[op]
|
2021-06-05 17:10:07 -04:00
|
|
|
fn op_encoding_decode(
|
|
|
|
state: &mut OpState,
|
2022-09-07 06:51:47 -04:00
|
|
|
data: &[u8],
|
2022-09-01 06:51:13 -04:00
|
|
|
rid: ResourceId,
|
|
|
|
stream: bool,
|
2022-03-15 19:22:00 -04:00
|
|
|
) -> Result<U16String, AnyError> {
|
2021-08-15 07:29:19 -04:00
|
|
|
let resource = state.resource_table.get::<TextDecoderResource>(rid)?;
|
2021-06-05 17:10:07 -04:00
|
|
|
|
|
|
|
let mut decoder = resource.decoder.borrow_mut();
|
|
|
|
let fatal = resource.fatal;
|
|
|
|
|
2022-03-15 19:22:00 -04:00
|
|
|
let max_buffer_length = decoder
|
|
|
|
.max_utf16_buffer_length(data.len())
|
|
|
|
.ok_or_else(|| range_error("Value too large to decode."))?;
|
2021-06-05 17:10:07 -04:00
|
|
|
|
2022-04-02 08:37:11 -04:00
|
|
|
let mut output = vec![0; max_buffer_length];
|
2021-06-05 17:10:07 -04:00
|
|
|
|
|
|
|
if fatal {
|
2022-03-15 19:22:00 -04:00
|
|
|
let (result, _, written) =
|
2022-09-07 06:51:47 -04:00
|
|
|
decoder.decode_to_utf16_without_replacement(data, &mut output, !stream);
|
2021-06-05 17:10:07 -04:00
|
|
|
match result {
|
2022-03-15 19:22:00 -04:00
|
|
|
DecoderResult::InputEmpty => {
|
|
|
|
output.truncate(written);
|
2022-04-02 08:37:11 -04:00
|
|
|
Ok(output.into())
|
2022-03-15 19:22:00 -04:00
|
|
|
}
|
2021-06-05 17:10:07 -04:00
|
|
|
DecoderResult::OutputFull => {
|
|
|
|
Err(range_error("Provided buffer too small."))
|
|
|
|
}
|
|
|
|
DecoderResult::Malformed(_, _) => {
|
|
|
|
Err(type_error("The encoded data is not valid."))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2022-03-15 19:22:00 -04:00
|
|
|
let (result, _, written, _) =
|
2022-09-07 06:51:47 -04:00
|
|
|
decoder.decode_to_utf16(data, &mut output, !stream);
|
2021-06-05 17:10:07 -04:00
|
|
|
match result {
|
2022-03-15 19:22:00 -04:00
|
|
|
CoderResult::InputEmpty => {
|
|
|
|
output.truncate(written);
|
2022-04-02 08:37:11 -04:00
|
|
|
Ok(output.into())
|
2022-03-15 19:22:00 -04:00
|
|
|
}
|
2021-06-05 17:10:07 -04:00
|
|
|
CoderResult::OutputFull => Err(range_error("Provided buffer too small.")),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct TextDecoderResource {
|
|
|
|
decoder: RefCell<Decoder>,
|
|
|
|
fatal: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Resource for TextDecoderResource {
|
|
|
|
fn name(&self) -> Cow<str> {
|
|
|
|
"textDecoder".into()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-17 07:18:15 -04:00
|
|
|
#[op(v8)]
|
2021-06-05 17:10:07 -04:00
|
|
|
fn op_encoding_encode_into(
|
2022-09-17 07:18:15 -04:00
|
|
|
scope: &mut v8::HandleScope,
|
|
|
|
input: serde_v8::Value,
|
2022-09-07 06:51:47 -04:00
|
|
|
buffer: &mut [u8],
|
2022-09-17 07:18:15 -04:00
|
|
|
out_buf: &mut [u32],
|
|
|
|
) -> Result<(), AnyError> {
|
|
|
|
let s = v8::Local::<v8::String>::try_from(input.v8_value)?;
|
|
|
|
|
|
|
|
let mut nchars = 0;
|
|
|
|
out_buf[1] = s.write_utf8(
|
|
|
|
scope,
|
|
|
|
buffer,
|
|
|
|
Some(&mut nchars),
|
|
|
|
v8::WriteOptions::NO_NULL_TERMINATION
|
|
|
|
| v8::WriteOptions::REPLACE_INVALID_UTF8,
|
|
|
|
) as u32;
|
|
|
|
out_buf[0] = nchars as u32;
|
|
|
|
Ok(())
|
2021-06-05 17:10:07 -04:00
|
|
|
}
|
|
|
|
|
2022-04-18 18:00:14 -04:00
|
|
|
/// Creates a [`CancelHandle`] resource that can be used to cancel invocations of certain ops.
|
|
|
|
#[op]
|
2022-05-13 04:36:31 -04:00
|
|
|
pub fn op_cancel_handle(state: &mut OpState) -> ResourceId {
|
|
|
|
state.resource_table.add(CancelHandle::new())
|
2022-04-18 18:00:14 -04:00
|
|
|
}
|
|
|
|
|
2022-03-16 20:25:44 -04:00
|
|
|
pub fn get_declaration() -> PathBuf {
|
|
|
|
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("lib.deno_web.d.ts")
|
|
|
|
}
|
|
|
|
|
2021-06-05 13:30:20 -04:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct DomExceptionQuotaExceededError {
|
|
|
|
pub msg: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl DomExceptionQuotaExceededError {
|
|
|
|
pub fn new(msg: &str) -> Self {
|
|
|
|
DomExceptionQuotaExceededError {
|
|
|
|
msg: msg.to_string(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-05 17:10:07 -04:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct DomExceptionInvalidCharacterError {
|
|
|
|
pub msg: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl DomExceptionInvalidCharacterError {
|
|
|
|
pub fn new(msg: &str) -> Self {
|
|
|
|
DomExceptionInvalidCharacterError {
|
|
|
|
msg: msg.to_string(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-05 13:30:20 -04:00
|
|
|
impl fmt::Display for DomExceptionQuotaExceededError {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
f.pad(&self.msg)
|
|
|
|
}
|
|
|
|
}
|
2021-06-05 17:10:07 -04:00
|
|
|
impl fmt::Display for DomExceptionInvalidCharacterError {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
f.pad(&self.msg)
|
|
|
|
}
|
|
|
|
}
|
2021-06-05 13:30:20 -04:00
|
|
|
|
|
|
|
impl std::error::Error for DomExceptionQuotaExceededError {}
|
|
|
|
|
2021-06-05 17:10:07 -04:00
|
|
|
impl std::error::Error for DomExceptionInvalidCharacterError {}
|
|
|
|
|
|
|
|
pub fn get_error_class_name(e: &AnyError) -> Option<&'static str> {
|
2021-06-05 13:30:20 -04:00
|
|
|
e.downcast_ref::<DomExceptionQuotaExceededError>()
|
|
|
|
.map(|_| "DOMExceptionQuotaExceededError")
|
2021-06-05 17:10:07 -04:00
|
|
|
.or_else(|| {
|
|
|
|
e.downcast_ref::<DomExceptionInvalidCharacterError>()
|
|
|
|
.map(|_| "DOMExceptionInvalidCharacterError")
|
|
|
|
})
|
2021-06-05 13:30:20 -04:00
|
|
|
}
|
2021-06-10 09:26:10 -04:00
|
|
|
pub struct Location(pub Url);
|