Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 29 additions & 101 deletions ext/web/01_dom_exception.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,19 @@

import { primordials } from "ext:core/mod.js";
const {
Error,
ErrorPrototype,
ObjectDefineProperty,
ObjectCreate,
ObjectEntries,
ObjectHasOwn,
ObjectPrototypeIsPrototypeOf,
ObjectSetPrototypeOf,
ReflectConstruct,
Symbol,
SymbolFor,
} = primordials;

import * as webidl from "ext:deno_webidl/00_webidl.js";
import { createFilteredInspectProxy } from "./01_console.js";
import { DOMException } from "ext:core/ops";

const _name = Symbol("name");
const _message = Symbol("message");
Expand Down Expand Up @@ -57,109 +55,39 @@ const TIMEOUT_ERR = 23;
const INVALID_NODE_TYPE_ERR = 24;
const DATA_CLONE_ERR = 25;

// Defined in WebIDL 2.8.1.
// https://webidl.spec.whatwg.org/#dfn-error-names-table
/** @type {Record<string, number>} */
// the prototype should be null, to prevent user code from looking
// up Object.prototype properties, such as "toString"
const nameToCodeMapping = ObjectCreate(null, {
IndexSizeError: { value: INDEX_SIZE_ERR },
HierarchyRequestError: { value: HIERARCHY_REQUEST_ERR },
WrongDocumentError: { value: WRONG_DOCUMENT_ERR },
InvalidCharacterError: { value: INVALID_CHARACTER_ERR },
NoModificationAllowedError: { value: NO_MODIFICATION_ALLOWED_ERR },
NotFoundError: { value: NOT_FOUND_ERR },
NotSupportedError: { value: NOT_SUPPORTED_ERR },
InUseAttributeError: { value: INUSE_ATTRIBUTE_ERR },
InvalidStateError: { value: INVALID_STATE_ERR },
SyntaxError: { value: SYNTAX_ERR },
InvalidModificationError: { value: INVALID_MODIFICATION_ERR },
NamespaceError: { value: NAMESPACE_ERR },
InvalidAccessError: { value: INVALID_ACCESS_ERR },
TypeMismatchError: { value: TYPE_MISMATCH_ERR },
SecurityError: { value: SECURITY_ERR },
NetworkError: { value: NETWORK_ERR },
AbortError: { value: ABORT_ERR },
URLMismatchError: { value: URL_MISMATCH_ERR },
QuotaExceededError: { value: QUOTA_EXCEEDED_ERR },
TimeoutError: { value: TIMEOUT_ERR },
InvalidNodeTypeError: { value: INVALID_NODE_TYPE_ERR },
DataCloneError: { value: DATA_CLONE_ERR },
});

// Defined in WebIDL 4.3.
// https://webidl.spec.whatwg.org/#idl-DOMException
class DOMException {
[_message];
[_name];
[_code];

// https://webidl.spec.whatwg.org/#dom-domexception-domexception
constructor(message = "", name = "Error") {
message = webidl.converters.DOMString(
message,
"Failed to construct 'DOMException'",
"Argument 1",
);
name = webidl.converters.DOMString(
name,
"Failed to construct 'DOMException'",
"Argument 2",
);
const code = nameToCodeMapping[name] ?? 0;

// execute Error constructor to have stack property and [[ErrorData]] internal slot
const error = ReflectConstruct(Error, [], new.target);
error[_message] = message;
error[_name] = name;
error[_code] = code;
error[webidl.brand] = webidl.brand;

return error;
}

get message() {
webidl.assertBranded(this, DOMExceptionPrototype);
return this[_message];
}

get name() {
webidl.assertBranded(this, DOMExceptionPrototype);
return this[_name];
}

get code() {
webidl.assertBranded(this, DOMExceptionPrototype);
return this[_code];
}

[SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
if (ObjectHasOwn(this, "stack")) {
const stack = this.stack;
if (typeof stack === "string") {
return stack;
}
}
return inspect(
createFilteredInspectProxy({
object: this,
evaluate: ObjectPrototypeIsPrototypeOf(DOMExceptionPrototype, this),
keys: [
"message",
"name",
"code",
],
}),
inspectOptions,
);
}
}

ObjectSetPrototypeOf(DOMException.prototype, ErrorPrototype);

webidl.configureInterface(DOMException);
const DOMExceptionPrototype = DOMException.prototype;

ObjectDefineProperty(
DOMExceptionPrototype,
SymbolFor("Deno.privateCustomInspect"),
{
__proto__: null,
value(inspect, inspectOptions) {
if (ObjectHasOwn(this, "stack")) {
const stack = this.stack;
if (typeof stack === "string") {
return stack;
}
}
return inspect(
createFilteredInspectProxy({
object: this,
evaluate: ObjectPrototypeIsPrototypeOf(DOMExceptionPrototype, this),
keys: [
"message",
"name",
"code",
],
}),
inspectOptions,
);
},
},
);

const entries = ObjectEntries({
INDEX_SIZE_ERR,
DOMSTRING_SIZE_ERR,
Expand Down
117 changes: 117 additions & 0 deletions ext/web/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use std::sync::Arc;
pub use blob::BlobError;
pub use compression::CompressionError;
use deno_core::ByteString;
use deno_core::GarbageCollected;
use deno_core::ToJsBuffer;
use deno_core::U16String;
use deno_core::op2;
Expand Down Expand Up @@ -112,6 +113,9 @@ deno_core::extension!(deno_web,
urlpattern::op_urlpattern_process_match_input,
console::op_preview_entries,
],
objects = [
DOMException
],
esm = [
"00_infra.js",
"01_dom_exception.js",
Expand Down Expand Up @@ -452,3 +456,116 @@ fn op_encoding_encode_into_fast(
}

pub struct Location(pub Url);

pub struct DOMException {
message: String,
name: String,
code: u32,
}

// SAFETY: we're sure this can be GCed
unsafe impl GarbageCollected for DOMException {
fn trace(&self, _visitor: &mut deno_core::v8::cppgc::Visitor) {}

fn get_name(&self) -> &'static std::ffi::CStr {
c"DOMException"
}
}

// Defined in WebIDL 4.3.
// https://webidl.spec.whatwg.org/#idl-DOMException
const INDEX_SIZE_ERR: u32 = 1;
Copy link

Copilot AI Nov 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The constant DOMSTRING_SIZE_ERR = 2 is missing from the Rust implementation but is present in the JavaScript constants (line 33 of 01_dom_exception.js). This inconsistency could lead to issues if this error code is needed.

Suggested change
const INDEX_SIZE_ERR: u32 = 1;
const INDEX_SIZE_ERR: u32 = 1;
const DOMSTRING_SIZE_ERR: u32 = 2;

Copilot uses AI. Check for mistakes.
const HIERARCHY_REQUEST_ERR: u32 = 3;
const WRONG_DOCUMENT_ERR: u32 = 4;
const INVALID_CHARACTER_ERR: u32 = 5;
Copy link

Copilot AI Nov 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The constant NO_DATA_ALLOWED_ERR = 6 is missing from the Rust implementation but is present in the JavaScript constants (line 37 of 01_dom_exception.js). This inconsistency could lead to issues if this error code is needed.

Suggested change
const INVALID_CHARACTER_ERR: u32 = 5;
const INVALID_CHARACTER_ERR: u32 = 5;
const NO_DATA_ALLOWED_ERR: u32 = 6;

Copilot uses AI. Check for mistakes.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

brocacho its unused in rust

const NO_MODIFICATION_ALLOWED_ERR: u32 = 7;
const NOT_FOUND_ERR: u32 = 8;
const NOT_SUPPORTED_ERR: u32 = 9;
const INUSE_ATTRIBUTE_ERR: u32 = 10;
const INVALID_STATE_ERR: u32 = 11;
const SYNTAX_ERR: u32 = 12;
const INVALID_MODIFICATION_ERR: u32 = 13;
const NAMESPACE_ERR: u32 = 14;
const INVALID_ACCESS_ERR: u32 = 15;
const TYPE_MISMATCH_ERR: u32 = 17;
Comment on lines +489 to +490
Copy link

Copilot AI Nov 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The constant VALIDATION_ERR = 16 is missing from the Rust implementation but is present in the JavaScript constants (line 47 of 01_dom_exception.js). This inconsistency could lead to issues if this error code is needed.

Copilot uses AI. Check for mistakes.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

const SECURITY_ERR: u32 = 18;
const NETWORK_ERR: u32 = 19;
const ABORT_ERR: u32 = 20;
const URL_MISMATCH_ERR: u32 = 21;
const QUOTA_EXCEEDED_ERR: u32 = 22;
const TIMEOUT_ERR: u32 = 23;
const INVALID_NODE_TYPE_ERR: u32 = 24;
const DATA_CLONE_ERR: u32 = 25;

fn name_to_code_mapping(name: &str) -> u32 {
match name {
"IndexSizeError" => INDEX_SIZE_ERR,
"HierarchyRequestError" => HIERARCHY_REQUEST_ERR,
"WrongDocumentError" => WRONG_DOCUMENT_ERR,
"InvalidCharacterError" => INVALID_CHARACTER_ERR,
"NoModificationAllowedError" => NO_MODIFICATION_ALLOWED_ERR,
"NotFoundError" => NOT_FOUND_ERR,
"NotSupportedError" => NOT_SUPPORTED_ERR,
"InUseAttributeError" => INUSE_ATTRIBUTE_ERR,
"InvalidStateError" => INVALID_STATE_ERR,
"SyntaxError" => SYNTAX_ERR,
"InvalidModificationError" => INVALID_MODIFICATION_ERR,
"NamespaceError" => NAMESPACE_ERR,
"InvalidAccessError" => INVALID_ACCESS_ERR,
"TypeMismatchError" => TYPE_MISMATCH_ERR,
"SecurityError" => SECURITY_ERR,
"NetworkError" => NETWORK_ERR,
"AbortError" => ABORT_ERR,
"URLMismatchError" => URL_MISMATCH_ERR,
"QuotaExceededError" => QUOTA_EXCEEDED_ERR,
"TimeoutError" => TIMEOUT_ERR,
"InvalidNodeTypeError" => INVALID_NODE_TYPE_ERR,
"DataCloneError" => DATA_CLONE_ERR,
_ => 0,
}
}

#[op2]
impl DOMException {
// https://webidl.spec.whatwg.org/#dom-domexception-domexception
#[constructor]
#[reentrant]
fn new<'s>(
scope: &'s mut v8::PinScope<'_, '_>,
#[string] message: String,
#[string] name: Option<String>,
) -> v8::Local<'s, v8::Object> {
let name = name.unwrap_or_else(|| "Error".to_string());
let code = name_to_code_mapping(&name);

// TODO: gus will modify v8
let dom_exp = deno_core::cppgc::make_cppgc_object(
scope,
DOMException {
message,
name,
code,
},
);
v8::Exception::capture_stack_trace(scope.get_current_context(), dom_exp);

dom_exp
}

#[getter]
#[string]
fn message(&self) -> String {
self.message.clone()
}

#[getter]
#[string]
fn name(&self) -> String {
self.name.clone()
}

#[getter]
fn code(&self) -> u32 {
self.code
}
}
Loading