LibWeb: Make DOMException GC-allocated

This commit is contained in:
Andreas Kling 2022-09-04 16:56:15 +02:00
parent 0e47754ac8
commit 497ead37bc
Notes: sideshowbarker 2024-07-17 23:07:41 +09:00
58 changed files with 307 additions and 278 deletions

View file

@ -167,6 +167,8 @@ static bool impl_is_wrapper(Type const& type)
return true;
if (type.name == "URLSearchParams"sv)
return true;
if (type.name == "DOMException"sv)
return true;
return false;
}

View file

@ -14,7 +14,6 @@
#include <LibJS/Runtime/PropertyDescriptor.h>
#include <LibJS/Runtime/PropertyKey.h>
#include <LibWeb/Bindings/CrossOriginAbstractOperations.h>
#include <LibWeb/Bindings/DOMExceptionWrapper.h>
#include <LibWeb/Bindings/LocationObject.h>
#include <LibWeb/Bindings/MainThreadVM.h>
#include <LibWeb/DOM/DOMException.h>
@ -79,7 +78,7 @@ JS::ThrowCompletionOr<JS::PropertyDescriptor> cross_origin_property_fallback(JS:
return JS::PropertyDescriptor { .value = JS::js_undefined(), .writable = false, .enumerable = false, .configurable = true };
// 2. Throw a "SecurityError" DOMException.
return vm.throw_completion<DOMExceptionWrapper>(DOM::SecurityError::create(String::formatted("Can't access property '{}' on cross-origin object", property_key)));
return throw_completion(DOM::SecurityError::create(vm.current_realm()->global_object(), String::formatted("Can't access property '{}' on cross-origin object", property_key)));
}
// 7.2.3.3 IsPlatformObjectSameOrigin ( O ), https://html.spec.whatwg.org/multipage/browsers.html#isplatformobjectsameorigin-(-o-)
@ -197,7 +196,7 @@ JS::ThrowCompletionOr<JS::Value> cross_origin_get(JS::VM& vm, JS::Object const&
// 6. If getter is undefined, then throw a "SecurityError" DOMException.
if (!getter.has_value())
return vm.throw_completion<DOMExceptionWrapper>(DOM::SecurityError::create(String::formatted("Can't get property '{}' on cross-origin object", property_key)));
return throw_completion(DOM::SecurityError::create(vm.current_realm()->global_object(), String::formatted("Can't get property '{}' on cross-origin object", property_key)));
// 7. Return ? Call(getter, Receiver).
return JS::call(vm, *getter, receiver);
@ -223,7 +222,7 @@ JS::ThrowCompletionOr<bool> cross_origin_set(JS::VM& vm, JS::Object& object, JS:
}
// 4. Throw a "SecurityError" DOMException.
return vm.throw_completion<DOMExceptionWrapper>(DOM::SecurityError::create(String::formatted("Can't set property '{}' on cross-origin object", property_key)));
return throw_completion(DOM::SecurityError::create(vm.current_realm()->global_object(), String::formatted("Can't set property '{}' on cross-origin object", property_key)));
}
// 7.2.3.7 CrossOriginOwnPropertyKeys ( O ), https://html.spec.whatwg.org/multipage/browsers.html#crossoriginownpropertykeys-(-o-)

View file

@ -9,7 +9,6 @@
#include <AK/Optional.h>
#include <AK/StdLibExtras.h>
#include <LibJS/Runtime/VM.h>
#include <LibWeb/Bindings/DOMExceptionWrapper.h>
#include <LibWeb/DOM/ExceptionOr.h>
namespace Web::Bindings {
@ -74,8 +73,8 @@ ALWAYS_INLINE JS::Completion dom_exception_to_throw_completion(auto&& vm, auto&&
VERIFY_NOT_REACHED();
}
},
[&](NonnullRefPtr<DOM::DOMException> exception) {
return vm.template throw_completion<DOMExceptionWrapper>(move(exception));
[&](JS::NonnullGCPtr<DOM::DOMException> const& exception) {
return throw_completion(exception);
});
}

View file

@ -12,7 +12,6 @@
#include <LibJS/Runtime/PropertyDescriptor.h>
#include <LibJS/Runtime/PropertyKey.h>
#include <LibWeb/Bindings/CrossOriginAbstractOperations.h>
#include <LibWeb/Bindings/DOMExceptionWrapper.h>
#include <LibWeb/Bindings/LocationObject.h>
#include <LibWeb/Bindings/LocationPrototype.h>
#include <LibWeb/DOM/DOMException.h>
@ -308,8 +307,6 @@ JS::ThrowCompletionOr<Optional<JS::PropertyDescriptor>> LocationObject::internal
// 7.10.5.6 [[DefineOwnProperty]] ( P, Desc ), https://html.spec.whatwg.org/multipage/history.html#location-defineownproperty
JS::ThrowCompletionOr<bool> LocationObject::internal_define_own_property(JS::PropertyKey const& property_key, JS::PropertyDescriptor const& descriptor)
{
auto& vm = this->vm();
// 1. If IsPlatformObjectSameOrigin(this) is true, then:
if (is_platform_object_same_origin(*this)) {
// 1. If the value of the [[DefaultProperties]] internal slot of this contains P, then return false.
@ -318,7 +315,7 @@ JS::ThrowCompletionOr<bool> LocationObject::internal_define_own_property(JS::Pro
}
// 2. Throw a "SecurityError" DOMException.
return vm.throw_completion<DOMExceptionWrapper>(DOM::SecurityError::create(String::formatted("Can't define property '{}' on cross-origin object", property_key)));
return throw_completion(DOM::SecurityError::create(global_object(), String::formatted("Can't define property '{}' on cross-origin object", property_key)));
}
// 7.10.5.7 [[Get]] ( P, Receiver ), https://html.spec.whatwg.org/multipage/history.html#location-get
@ -350,14 +347,12 @@ JS::ThrowCompletionOr<bool> LocationObject::internal_set(JS::PropertyKey const&
// 7.10.5.9 [[Delete]] ( P ), https://html.spec.whatwg.org/multipage/history.html#location-delete
JS::ThrowCompletionOr<bool> LocationObject::internal_delete(JS::PropertyKey const& property_key)
{
auto& vm = this->vm();
// 1. If IsPlatformObjectSameOrigin(this) is true, then return ? OrdinaryDelete(this, P).
if (is_platform_object_same_origin(*this))
return JS::Object::internal_delete(property_key);
// 2. Throw a "SecurityError" DOMException.
return vm.throw_completion<DOMExceptionWrapper>(DOM::SecurityError::create(String::formatted("Can't delete property '{}' on cross-origin object", property_key)));
return throw_completion(DOM::SecurityError::create(global_object(), String::formatted("Can't delete property '{}' on cross-origin object", property_key)));
}
// 7.10.5.10 [[OwnPropertyKeys]] ( ), https://html.spec.whatwg.org/multipage/history.html#location-ownpropertykeys

View file

@ -11,7 +11,6 @@
#include <LibJS/Runtime/PropertyDescriptor.h>
#include <LibJS/Runtime/PropertyKey.h>
#include <LibWeb/Bindings/CrossOriginAbstractOperations.h>
#include <LibWeb/Bindings/DOMExceptionWrapper.h>
#include <LibWeb/Bindings/WindowProxy.h>
#include <LibWeb/DOM/DOMException.h>
#include <LibWeb/HTML/CrossOrigin/Reporting.h>
@ -91,7 +90,7 @@ JS::ThrowCompletionOr<Optional<JS::PropertyDescriptor>> WindowProxy::internal_ge
return Optional<JS::PropertyDescriptor> {};
// 2. Throw a "SecurityError" DOMException.
return vm.throw_completion<DOMExceptionWrapper>(DOM::SecurityError::create(String::formatted("Can't access property '{}' on cross-origin object", property_key)));
return throw_completion(DOM::SecurityError::create(window(), String::formatted("Can't access property '{}' on cross-origin object", property_key)));
}
// 6. Return PropertyDescriptor{ [[Value]]: value, [[Writable]]: false, [[Enumerable]]: true, [[Configurable]]: true }.
@ -127,8 +126,6 @@ JS::ThrowCompletionOr<Optional<JS::PropertyDescriptor>> WindowProxy::internal_ge
// 7.4.6 [[DefineOwnProperty]] ( P, Desc ), https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-defineownproperty
JS::ThrowCompletionOr<bool> WindowProxy::internal_define_own_property(JS::PropertyKey const& property_key, JS::PropertyDescriptor const& descriptor)
{
auto& vm = this->vm();
// 1. Let W be the value of the [[Window]] internal slot of this.
// 2. If IsPlatformObjectSameOrigin(W) is true, then:
@ -143,7 +140,7 @@ JS::ThrowCompletionOr<bool> WindowProxy::internal_define_own_property(JS::Proper
}
// 3. Throw a "SecurityError" DOMException.
return vm.throw_completion<DOMExceptionWrapper>(DOM::SecurityError::create(String::formatted("Can't define property '{}' on cross-origin object", property_key)));
return throw_completion(DOM::SecurityError::create(window(), String::formatted("Can't define property '{}' on cross-origin object", property_key)));
}
// 7.4.7 [[Get]] ( P, Receiver ), https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-get
@ -194,8 +191,6 @@ JS::ThrowCompletionOr<bool> WindowProxy::internal_set(JS::PropertyKey const& pro
// 7.4.9 [[Delete]] ( P ), https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-delete
JS::ThrowCompletionOr<bool> WindowProxy::internal_delete(JS::PropertyKey const& property_key)
{
auto& vm = this->vm();
// 1. Let W be the value of the [[Window]] internal slot of this.
// 2. If IsPlatformObjectSameOrigin(W) is true, then:
@ -218,7 +213,7 @@ JS::ThrowCompletionOr<bool> WindowProxy::internal_delete(JS::PropertyKey const&
}
// 3. Throw a "SecurityError" DOMException.
return vm.throw_completion<DOMExceptionWrapper>(DOM::SecurityError::create(String::formatted("Can't delete property '{}' on cross-origin object", property_key)));
return throw_completion(DOM::SecurityError::create(window(), String::formatted("Can't delete property '{}' on cross-origin object", property_key)));
}
// 7.4.10 [[OwnPropertyKeys]] ( ), https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-ownpropertykeys

View file

@ -8,6 +8,7 @@
#include <AK/Forward.h>
#include <LibJS/Forward.h>
#include <LibJS/Heap/GCPtr.h>
#include <LibJS/Runtime/Object.h>
#include <LibWeb/Forward.h>
@ -30,8 +31,7 @@ public:
virtual JS::ThrowCompletionOr<bool> internal_delete(JS::PropertyKey const&) override;
virtual JS::ThrowCompletionOr<JS::MarkedVector<JS::Value>> internal_own_property_keys() const override;
HTML::Window& window() { return *m_window; }
HTML::Window const& window() const { return *m_window; }
HTML::Window& window() const { return const_cast<HTML::Window&>(*m_window); }
// NOTE: Someone will have to replace the wrapped window object as well:
// "When the browsing context is navigated, the Window object wrapped by the browsing context's associated WindowProxy object is changed."

View file

@ -82,6 +82,7 @@ set(SOURCES
DOM/Comment.cpp
DOM/CustomEvent.cpp
DOM/DOMEventListener.cpp
DOM/DOMException.cpp
DOM/DOMImplementation.cpp
DOM/DOMTokenList.cpp
DOM/DOMTokenList.idl

View file

@ -55,7 +55,7 @@ DOM::ExceptionOr<unsigned> CSSRuleList::insert_a_css_rule(Variant<StringView, CS
// 2. If index is greater than length, then throw an IndexSizeError exception.
if (index > length)
return DOM::IndexSizeError::create("CSS rule index out of bounds.");
return DOM::IndexSizeError::create(global_object(), "CSS rule index out of bounds.");
// 3. Set new rule to the results of performing parse a CSS rule on argument rule.
// NOTE: The insert-a-css-rule spec expects `rule` to be a string, but the CSSStyleSheet.insertRule()
@ -72,7 +72,7 @@ DOM::ExceptionOr<unsigned> CSSRuleList::insert_a_css_rule(Variant<StringView, CS
// 4. If new rule is a syntax error, throw a SyntaxError exception.
if (!new_rule)
return DOM::SyntaxError::create("Unable to parse CSS rule.");
return DOM::SyntaxError::create(global_object(), "Unable to parse CSS rule.");
// FIXME: 5. If new rule cannot be inserted into list at the zero-index position index due to constraints specified by CSS, then throw a HierarchyRequestError exception. [CSS21]
@ -93,7 +93,7 @@ DOM::ExceptionOr<void> CSSRuleList::remove_a_css_rule(u32 index)
// 2. If index is greater than or equal to length, then throw an IndexSizeError exception.
if (index >= length)
return DOM::IndexSizeError::create("CSS rule index out of bounds.");
return DOM::IndexSizeError::create(global_object(), "CSS rule index out of bounds.");
// 3. Set old rule to the indexth item in list.
CSSRule& old_rule = m_rules[index];

View file

@ -50,7 +50,7 @@ DOM::ExceptionOr<unsigned> CSSStyleSheet::insert_rule(StringView rule, unsigned
// 4. If parsed rule is a syntax error, return parsed rule.
if (!parsed_rule)
return DOM::SyntaxError::create("Unable to parse CSS rule.");
return DOM::SyntaxError::create(global_object(), "Unable to parse CSS rule.");
// FIXME: 5. If parsed rule is an @import rule, and the constructed flag is set, throw a SyntaxError DOMException.

View file

@ -520,14 +520,14 @@ Optional<StyleProperty> ResolvedCSSStyleDeclaration::property(PropertyID propert
DOM::ExceptionOr<void> ResolvedCSSStyleDeclaration::set_property(PropertyID, StringView, StringView)
{
// 1. If the computed flag is set, then throw a NoModificationAllowedError exception.
return DOM::NoModificationAllowedError::create("Cannot modify properties in result of getComputedStyle()");
return DOM::NoModificationAllowedError::create(global_object(), "Cannot modify properties in result of getComputedStyle()");
}
// https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-removeproperty
DOM::ExceptionOr<String> ResolvedCSSStyleDeclaration::remove_property(PropertyID)
{
// 1. If the computed flag is set, then throw a NoModificationAllowedError exception.
return DOM::NoModificationAllowedError::create("Cannot remove properties from result of getComputedStyle()");
return DOM::NoModificationAllowedError::create(global_object(), "Cannot remove properties from result of getComputedStyle()");
}
String ResolvedCSSStyleDeclaration::serialized() const

View file

@ -43,16 +43,16 @@ DOM::ExceptionOr<JS::Value> Crypto::get_random_values(JS::Value array) const
{
// 1. If array is not an Int8Array, Uint8Array, Uint8ClampedArray, Int16Array, Uint16Array, Int32Array, Uint32Array, BigInt64Array, or BigUint64Array, then throw a TypeMismatchError and terminate the algorithm.
if (!array.is_object() || !(is<JS::Int8Array>(array.as_object()) || is<JS::Uint8Array>(array.as_object()) || is<JS::Uint8ClampedArray>(array.as_object()) || is<JS::Int16Array>(array.as_object()) || is<JS::Uint16Array>(array.as_object()) || is<JS::Int32Array>(array.as_object()) || is<JS::Uint32Array>(array.as_object()) || is<JS::BigInt64Array>(array.as_object()) || is<JS::BigUint64Array>(array.as_object())))
return DOM::TypeMismatchError::create("array must be one of Int8Array, Uint8Array, Uint8ClampedArray, Int16Array, Uint16Array, Int32Array, Uint32Array, BigInt64Array, or BigUint64Array");
return DOM::TypeMismatchError::create(global_object(), "array must be one of Int8Array, Uint8Array, Uint8ClampedArray, Int16Array, Uint16Array, Int32Array, Uint32Array, BigInt64Array, or BigUint64Array");
auto& typed_array = static_cast<JS::TypedArrayBase&>(array.as_object());
// 2. If the byteLength of array is greater than 65536, throw a QuotaExceededError and terminate the algorithm.
if (typed_array.byte_length() > 65536)
return DOM::QuotaExceededError::create("array's byteLength may not be greater than 65536");
return DOM::QuotaExceededError::create(global_object(), "array's byteLength may not be greater than 65536");
// IMPLEMENTATION DEFINED: If the viewed array buffer is detached, throw a InvalidStateError and terminate the algorithm.
if (typed_array.viewed_array_buffer()->is_detached())
return DOM::InvalidStateError::create("array is detached");
return DOM::InvalidStateError::create(global_object(), "array is detached");
// FIXME: Handle SharedArrayBuffers
// 3. Overwrite all elements of array with cryptographically strong random values of the appropriate type.

View file

@ -7,7 +7,6 @@
#include <LibCrypto/Hash/HashManager.h>
#include <LibJS/Runtime/ArrayBuffer.h>
#include <LibJS/Runtime/Promise.h>
#include <LibWeb/Bindings/DOMExceptionWrapper.h>
#include <LibWeb/Bindings/IDLAbstractOperations.h>
#include <LibWeb/Bindings/MainThreadVM.h>
#include <LibWeb/Crypto/SubtleCrypto.h>
@ -38,7 +37,7 @@ JS::Promise* SubtleCrypto::digest(String const& algorithm, JS::Handle<JS::Object
// 2. Let data be the result of getting a copy of the bytes held by the data parameter passed to the digest() method.
auto data_buffer_or_error = Bindings::IDL::get_buffer_source_copy(*data.cell());
if (data_buffer_or_error.is_error()) {
auto* error = wrap(realm, DOM::OperationError::create("Failed to copy bytes from ArrayBuffer"));
auto* error = wrap(realm, DOM::OperationError::create(global_object(), "Failed to copy bytes from ArrayBuffer"));
auto* promise = JS::Promise::create(realm);
promise->reject(error);
return promise;
@ -59,7 +58,7 @@ JS::Promise* SubtleCrypto::digest(String const& algorithm, JS::Handle<JS::Object
}
// 4. If an error occurred, return a Promise rejected with normalizedAlgorithm.
else {
auto* error = wrap(realm, DOM::NotSupportedError::create(String::formatted("Invalid hash function '{}'", algorithm)));
auto* error = wrap(realm, DOM::NotSupportedError::create(global_object(), String::formatted("Invalid hash function '{}'", algorithm)));
auto* promise = JS::Promise::create(realm);
promise->reject(error);
return promise;
@ -80,7 +79,7 @@ JS::Promise* SubtleCrypto::digest(String const& algorithm, JS::Handle<JS::Object
auto digest = hash.digest();
auto result_buffer = ByteBuffer::copy(digest.immutable_data(), hash.digest_size());
if (result_buffer.is_error()) {
auto* error = wrap(realm, DOM::OperationError::create("Failed to create result buffer"));
auto* error = wrap(realm, DOM::OperationError::create(global_object(), "Failed to create result buffer"));
promise->reject(error);
return promise;
}

View file

@ -4,8 +4,6 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/Bindings/DOMExceptionWrapper.h>
#include <LibWeb/Bindings/Wrapper.h>
#include <LibWeb/DOM/AbortSignal.h>
#include <LibWeb/DOM/Document.h>
#include <LibWeb/DOM/EventDispatcher.h>
@ -45,7 +43,7 @@ void AbortSignal::signal_abort(JS::Value reason)
if (!reason.is_undefined())
m_abort_reason = reason;
else
m_abort_reason = wrap(realm(), AbortError::create("Aborted without reason"));
m_abort_reason = wrap(realm(), AbortError::create(global_object(), "Aborted without reason"));
// 3. For each algorithm in signals abort algorithms: run algorithm.
for (auto& algorithm : m_abort_algorithms)

View file

@ -38,7 +38,7 @@ ExceptionOr<String> CharacterData::substring_data(size_t offset, size_t count) c
// 2. If offset is greater than length, then throw an "IndexSizeError" DOMException.
if (offset > length)
return DOM::IndexSizeError::create("Substring offset out of range.");
return DOM::IndexSizeError::create(global_object(), "Substring offset out of range.");
// 3. If offset plus count is greater than length, return a string whose value is the code units from the offsetth code unit
// to the end of nodes data, and then return.
@ -57,7 +57,7 @@ ExceptionOr<void> CharacterData::replace_data(size_t offset, size_t count, Strin
// 2. If offset is greater than length, then throw an "IndexSizeError" DOMException.
if (offset > length)
return DOM::IndexSizeError::create("Replacement offset out of range.");
return DOM::IndexSizeError::create(global_object(), "Replacement offset out of range.");
// 3. If offset plus count is greater than length, then set count to length minus offset.
if (offset + count > length)

View file

@ -0,0 +1,34 @@
/*
* Copyright (c) 2022, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/DOM/DOMException.h>
#include <LibWeb/HTML/Window.h>
namespace Web::DOM {
JS::NonnullGCPtr<DOMException> DOMException::create(JS::Object& global_object, FlyString const& name, FlyString const& message)
{
auto& window = verify_cast<HTML::Window>(global_object);
return *window.heap().allocate<DOMException>(window.realm(), window, name, message);
}
JS::NonnullGCPtr<DOMException> DOMException::create_with_global_object(JS::Object& global_object, FlyString const& message, FlyString const& name)
{
auto& window = verify_cast<HTML::Window>(global_object);
return *window.heap().allocate<DOMException>(window.realm(), window, name, message);
}
DOMException::DOMException(HTML::Window& window, FlyString const& name, FlyString const& message)
: PlatformObject(window.realm())
, m_name(name)
, m_message(message)
{
set_prototype(&window.cached_web_prototype("DOMException"));
}
DOMException::~DOMException() = default;
}

View file

@ -7,20 +7,19 @@
#pragma once
#include <AK/FlyString.h>
#include <AK/NonnullRefPtr.h>
#include <AK/RefCounted.h>
#include <LibWeb/Bindings/Wrappable.h>
#include <LibJS/Runtime/VM.h>
#include <LibWeb/Bindings/PlatformObject.h>
namespace Web::DOM {
#define TRY_OR_RETURN_OOM(expression) \
({ \
auto _temporary_result = (expression); \
if (_temporary_result.is_error()) { \
VERIFY(_temporary_result.error().code() == ENOMEM); \
return DOM::UnknownError::create("Out of memory."sv); \
} \
_temporary_result.release_value(); \
#define TRY_OR_RETURN_OOM(global_object, expression) \
({ \
auto _temporary_result = (expression); \
if (_temporary_result.is_error()) { \
VERIFY(_temporary_result.error().code() == ENOMEM); \
return DOM::UnknownError::create(global_object, "Out of memory."sv); \
} \
_temporary_result.release_value(); \
})
// The following have a legacy code value but *don't* produce it as
@ -99,48 +98,52 @@ static u16 get_legacy_code_for_name(FlyString const& name)
}
// https://webidl.spec.whatwg.org/#idl-DOMException
class DOMException final
: public RefCounted<DOMException>
, public Bindings::Wrappable {
public:
using WrapperType = Bindings::DOMExceptionWrapper;
class DOMException final : public Bindings::PlatformObject {
WEB_PLATFORM_OBJECT(DOMException, Bindings::PlatformObject);
static NonnullRefPtr<DOMException> create(FlyString const& name, FlyString const& message)
{
return adopt_ref(*new DOMException(name, message));
}
public:
static JS::NonnullGCPtr<DOMException> create(JS::Object& global_object, FlyString const& name, FlyString const& message);
// JS constructor has message first, name second
static NonnullRefPtr<DOMException> create_with_global_object(HTML::Window&, FlyString const& message, FlyString const& name)
{
return adopt_ref(*new DOMException(name, message));
}
// FIXME: This is a completely pointless footgun, let's use the same order for both factories.
static JS::NonnullGCPtr<DOMException> create_with_global_object(JS::Object& global_object, FlyString const& message, FlyString const& name);
static JS::NonnullGCPtr<DOMException> create(JS::Realm& realm, FlyString const& message);
virtual ~DOMException() override;
FlyString const& name() const { return m_name; }
FlyString const& message() const { return m_message; }
u16 code() const { return get_legacy_code_for_name(m_name); }
protected:
DOMException(FlyString const& name, FlyString const& message)
: m_name(name)
, m_message(message)
{
}
DOMException(HTML::Window&, FlyString const& name, FlyString const& message);
private:
FlyString m_name;
FlyString m_message;
};
#define __ENUMERATE(ErrorName) \
class ErrorName final { \
public: \
static NonnullRefPtr<DOMException> create(FlyString const& message) \
{ \
return DOMException::create(#ErrorName, message); \
} \
#define __ENUMERATE(ErrorName) \
class ErrorName final { \
public: \
static JS::NonnullGCPtr<DOMException> create(JS::Object& global_object, FlyString const& message) \
{ \
return DOMException::create(global_object, #ErrorName, message); \
} \
};
ENUMERATE_DOM_EXCEPTION_ERROR_NAMES
#undef __ENUMERATE
}
WRAPPER_HACK(DOMException, Web::DOM)
namespace Web {
inline JS::Completion throw_completion(JS::NonnullGCPtr<DOM::DOMException> exception)
{
return JS::throw_completion(JS::Value(static_cast<JS::Object*>(exception.ptr())));
}
}

View file

@ -105,7 +105,7 @@ JS::NonnullGCPtr<Document> DOMImplementation::create_html_document(String const&
// https://dom.spec.whatwg.org/#dom-domimplementation-createdocumenttype
ExceptionOr<JS::NonnullGCPtr<DocumentType>> DOMImplementation::create_document_type(String const& qualified_name, String const& public_id, String const& system_id)
{
TRY(Document::validate_qualified_name(qualified_name));
TRY(Document::validate_qualified_name(global_object(), qualified_name));
auto document_type = DocumentType::create(document());
document_type->set_name(qualified_name);
document_type->set_public_id(public_id);

View file

@ -234,9 +234,9 @@ void DOMTokenList::set_value(String value)
ExceptionOr<void> DOMTokenList::validate_token(StringView token) const
{
if (token.is_empty())
return SyntaxError::create("Non-empty DOM tokens are not allowed");
return SyntaxError::create(global_object(), "Non-empty DOM tokens are not allowed");
if (any_of(token, is_ascii_space))
return InvalidCharacterError::create("DOM tokens containing ASCII whitespace are not allowed");
return InvalidCharacterError::create(global_object(), "DOM tokens containing ASCII whitespace are not allowed");
return {};
}

View file

@ -349,11 +349,11 @@ ExceptionOr<void> Document::run_the_document_write_steps(String input)
{
// 1. If document is an XML document, then throw an "InvalidStateError" DOMException.
if (m_type == Type::XML)
return DOM::InvalidStateError::create("write() called on XML document.");
return DOM::InvalidStateError::create(global_object(), "write() called on XML document.");
// 2. If document's throw-on-dynamic-markup-insertion counter is greater than 0, then throw an "InvalidStateError" DOMException.
if (m_throw_on_dynamic_markup_insertion_counter > 0)
return DOM::InvalidStateError::create("throw-on-dynamic-markup-insertion-counter greater than zero.");
return DOM::InvalidStateError::create(global_object(), "throw-on-dynamic-markup-insertion-counter greater than zero.");
// 3. If document's active parser was aborted is true, then return.
if (m_active_parser_was_aborted)
@ -384,18 +384,18 @@ ExceptionOr<Document*> Document::open(String const&, String const&)
{
// 1. If document is an XML document, then throw an "InvalidStateError" DOMException exception.
if (m_type == Type::XML)
return DOM::InvalidStateError::create("open() called on XML document.");
return DOM::InvalidStateError::create(global_object(), "open() called on XML document.");
// 2. If document's throw-on-dynamic-markup-insertion counter is greater than 0, then throw an "InvalidStateError" DOMException.
if (m_throw_on_dynamic_markup_insertion_counter > 0)
return DOM::InvalidStateError::create("throw-on-dynamic-markup-insertion-counter greater than zero.");
return DOM::InvalidStateError::create(global_object(), "throw-on-dynamic-markup-insertion-counter greater than zero.");
// FIXME: 3. Let entryDocument be the entry global object's associated Document.
auto& entry_document = *this;
// 4. If document's origin is not same origin to entryDocument's origin, then throw a "SecurityError" DOMException.
if (origin() != entry_document.origin())
return DOM::SecurityError::create("Document.origin() not the same as entryDocument's.");
return DOM::SecurityError::create(global_object(), "Document.origin() not the same as entryDocument's.");
// 5. If document has an active parser whose script nesting level is greater than 0, then return document.
if (m_parser && m_parser->script_nesting_level() > 0)
@ -455,11 +455,11 @@ ExceptionOr<void> Document::close()
{
// 1. If document is an XML document, then throw an "InvalidStateError" DOMException exception.
if (m_type == Type::XML)
return DOM::InvalidStateError::create("close() called on XML document.");
return DOM::InvalidStateError::create(global_object(), "close() called on XML document.");
// 2. If document's throw-on-dynamic-markup-insertion counter is greater than 0, then throw an "InvalidStateError" DOMException.
if (m_throw_on_dynamic_markup_insertion_counter > 0)
return DOM::InvalidStateError::create("throw-on-dynamic-markup-insertion-counter greater than zero.");
return DOM::InvalidStateError::create(global_object(), "throw-on-dynamic-markup-insertion-counter greater than zero.");
// 3. If there is no script-created parser associated with the document, then return.
if (!m_parser)
@ -563,7 +563,7 @@ HTML::HTMLElement* Document::body()
ExceptionOr<void> Document::set_body(HTML::HTMLElement* new_body)
{
if (!is<HTML::HTMLBodyElement>(new_body) && !is<HTML::HTMLFrameSetElement>(new_body))
return DOM::HierarchyRequestError::create("Invalid document body element, must be 'body' or 'frameset'");
return DOM::HierarchyRequestError::create(global_object(), "Invalid document body element, must be 'body' or 'frameset'");
auto* existing_body = body();
if (existing_body) {
@ -573,7 +573,7 @@ ExceptionOr<void> Document::set_body(HTML::HTMLElement* new_body)
auto* document_element = this->document_element();
if (!document_element)
return DOM::HierarchyRequestError::create("Missing document element");
return DOM::HierarchyRequestError::create(global_object(), "Missing document element");
(void)TRY(document_element->append_child(*new_body));
return {};
@ -1073,7 +1073,7 @@ JS::Value Document::run_javascript(StringView source, StringView filename)
DOM::ExceptionOr<JS::NonnullGCPtr<Element>> Document::create_element(String const& tag_name)
{
if (!is_valid_name(tag_name))
return DOM::InvalidCharacterError::create("Invalid character in tag name.");
return DOM::InvalidCharacterError::create(global_object(), "Invalid character in tag name.");
// FIXME: Let namespace be the HTML namespace, if this is an HTML document or thiss content type is "application/xhtml+xml", and null otherwise.
return DOM::create_element(*this, tag_name, Namespace::HTML);
@ -1085,7 +1085,7 @@ DOM::ExceptionOr<JS::NonnullGCPtr<Element>> Document::create_element(String cons
DOM::ExceptionOr<JS::NonnullGCPtr<Element>> Document::create_element_ns(String const& namespace_, String const& qualified_name)
{
// 1. Let namespace, prefix, and localName be the result of passing namespace and qualifiedName to validate and extract.
auto extracted_qualified_name = TRY(validate_and_extract(namespace_, qualified_name));
auto extracted_qualified_name = TRY(validate_and_extract(global_object(), namespace_, qualified_name));
// FIXME: 2. Let is be null.
// FIXME: 3. If options is a dictionary and options["is"] exists, then set is to it.
@ -1166,7 +1166,7 @@ DOM::ExceptionOr<JS::NonnullGCPtr<Event>> Document::create_event(String const& i
// 3. If constructor is null, then throw a "NotSupportedError" DOMException.
if (!event) {
return DOM::NotSupportedError::create("No constructor for interface found");
return DOM::NotSupportedError::create(global_object(), "No constructor for interface found");
}
// FIXME: 4. If the interface indicated by constructor is not exposed on the relevant global object of this, then throw a "NotSupportedError" DOMException.
@ -1226,7 +1226,7 @@ ExceptionOr<JS::NonnullGCPtr<Node>> Document::import_node(JS::NonnullGCPtr<Node>
{
// 1. If node is a document or shadow root, then throw a "NotSupportedError" DOMException.
if (is<Document>(*node) || is<ShadowRoot>(*node))
return DOM::NotSupportedError::create("Cannot import a document or shadow root.");
return DOM::NotSupportedError::create(global_object(), "Cannot import a document or shadow root.");
// 2. Return a clone of node, with this and the clone children flag set if deep is true.
return node->clone_node(this, deep);
@ -1273,10 +1273,10 @@ void Document::adopt_node(Node& node)
ExceptionOr<JS::NonnullGCPtr<Node>> Document::adopt_node_binding(JS::NonnullGCPtr<Node> node)
{
if (is<Document>(*node))
return DOM::NotSupportedError::create("Cannot adopt a document into a document");
return DOM::NotSupportedError::create(global_object(), "Cannot adopt a document into a document");
if (is<ShadowRoot>(*node))
return DOM::HierarchyRequestError::create("Cannot adopt a shadow root into a document");
return DOM::HierarchyRequestError::create(global_object(), "Cannot adopt a shadow root into a document");
if (is<DocumentFragment>(*node) && verify_cast<DocumentFragment>(*node).host())
return node;
@ -1665,14 +1665,14 @@ bool Document::is_valid_name(String const& name)
}
// https://dom.spec.whatwg.org/#validate
ExceptionOr<Document::PrefixAndTagName> Document::validate_qualified_name(String const& qualified_name)
ExceptionOr<Document::PrefixAndTagName> Document::validate_qualified_name(JS::Object& global_object, String const& qualified_name)
{
if (qualified_name.is_empty())
return InvalidCharacterError::create("Empty string is not a valid qualified name.");
return InvalidCharacterError::create(global_object, "Empty string is not a valid qualified name.");
Utf8View utf8view { qualified_name };
if (!utf8view.validate())
return InvalidCharacterError::create("Invalid qualified name.");
return InvalidCharacterError::create(global_object, "Invalid qualified name.");
Optional<size_t> colon_offset;
@ -1682,19 +1682,19 @@ ExceptionOr<Document::PrefixAndTagName> Document::validate_qualified_name(String
auto code_point = *it;
if (code_point == ':') {
if (colon_offset.has_value())
return InvalidCharacterError::create("More than one colon (:) in qualified name.");
return InvalidCharacterError::create(global_object, "More than one colon (:) in qualified name.");
colon_offset = utf8view.byte_offset_of(it);
at_start_of_name = true;
continue;
}
if (at_start_of_name) {
if (!is_valid_name_start_character(code_point))
return InvalidCharacterError::create("Invalid start of qualified name.");
return InvalidCharacterError::create(global_object, "Invalid start of qualified name.");
at_start_of_name = false;
continue;
}
if (!is_valid_name_character(code_point))
return InvalidCharacterError::create("Invalid character in qualified name.");
return InvalidCharacterError::create(global_object, "Invalid character in qualified name.");
}
if (!colon_offset.has_value())
@ -1704,10 +1704,10 @@ ExceptionOr<Document::PrefixAndTagName> Document::validate_qualified_name(String
};
if (*colon_offset == 0)
return InvalidCharacterError::create("Qualified name can't start with colon (:).");
return InvalidCharacterError::create(global_object, "Qualified name can't start with colon (:).");
if (*colon_offset >= (qualified_name.length() - 1))
return InvalidCharacterError::create("Qualified name can't end with colon (:).");
return InvalidCharacterError::create(global_object, "Qualified name can't end with colon (:).");
return Document::PrefixAndTagName {
.prefix = qualified_name.substring_view(0, *colon_offset),

View file

@ -328,7 +328,7 @@ public:
FlyString prefix;
FlyString tag_name;
};
static ExceptionOr<PrefixAndTagName> validate_qualified_name(String const& qualified_name);
static ExceptionOr<PrefixAndTagName> validate_qualified_name(JS::Object& global_object, String const& qualified_name);
JS::NonnullGCPtr<NodeIterator> create_node_iterator(Node& root, unsigned what_to_show, JS::GCPtr<NodeFilter>);
JS::NonnullGCPtr<TreeWalker> create_tree_walker(Node& root, unsigned what_to_show, JS::GCPtr<NodeFilter>);

View file

@ -87,7 +87,7 @@ ExceptionOr<void> Element::set_attribute(FlyString const& name, String const& va
// 1. If qualifiedName does not match the Name production in XML, then throw an "InvalidCharacterError" DOMException.
// FIXME: Proper name validation
if (name.is_empty())
return InvalidCharacterError::create("Attribute name must not be empty");
return InvalidCharacterError::create(global_object(), "Attribute name must not be empty");
// 2. If this is in the HTML namespace and its node document is an HTML document, then set qualifiedName to qualifiedName in ASCII lowercase.
// FIXME: Handle the second condition, assume it is an HTML document for now.
@ -118,14 +118,14 @@ ExceptionOr<void> Element::set_attribute(FlyString const& name, String const& va
}
// https://dom.spec.whatwg.org/#validate-and-extract
ExceptionOr<QualifiedName> validate_and_extract(FlyString namespace_, FlyString qualified_name)
ExceptionOr<QualifiedName> validate_and_extract(JS::Object& global_object, FlyString namespace_, FlyString qualified_name)
{
// 1. If namespace is the empty string, then set it to null.
if (namespace_.is_empty())
namespace_ = {};
// 2. Validate qualifiedName.
TRY(Document::validate_qualified_name(qualified_name));
TRY(Document::validate_qualified_name(global_object, qualified_name));
// 3. Let prefix be null.
FlyString prefix = {};
@ -142,19 +142,19 @@ ExceptionOr<QualifiedName> validate_and_extract(FlyString namespace_, FlyString
// 6. If prefix is non-null and namespace is null, then throw a "NamespaceError" DOMException.
if (!prefix.is_null() && namespace_.is_null())
return NamespaceError::create("Prefix is non-null and namespace is null.");
return NamespaceError::create(global_object, "Prefix is non-null and namespace is null.");
// 7. If prefix is "xml" and namespace is not the XML namespace, then throw a "NamespaceError" DOMException.
if (prefix == "xml"sv && namespace_ != Namespace::XML)
return NamespaceError::create("Prefix is 'xml' and namespace is not the XML namespace.");
return NamespaceError::create(global_object, "Prefix is 'xml' and namespace is not the XML namespace.");
// 8. If either qualifiedName or prefix is "xmlns" and namespace is not the XMLNS namespace, then throw a "NamespaceError" DOMException.
if ((qualified_name == "xmlns"sv || prefix == "xmlns"sv) && namespace_ != Namespace::XMLNS)
return NamespaceError::create("Either qualifiedName or prefix is 'xmlns' and namespace is not the XMLNS namespace.");
return NamespaceError::create(global_object, "Either qualifiedName or prefix is 'xmlns' and namespace is not the XMLNS namespace.");
// 9. If namespace is the XMLNS namespace and neither qualifiedName nor prefix is "xmlns", then throw a "NamespaceError" DOMException.
if (namespace_ == Namespace::XMLNS && !(qualified_name == "xmlns"sv || prefix == "xmlns"sv))
return NamespaceError::create("Namespace is the XMLNS namespace and neither qualifiedName nor prefix is 'xmlns'.");
return NamespaceError::create(global_object, "Namespace is the XMLNS namespace and neither qualifiedName nor prefix is 'xmlns'.");
// 10. Return namespace, prefix, and localName.
return QualifiedName { local_name, prefix, namespace_ };
@ -164,7 +164,7 @@ ExceptionOr<QualifiedName> validate_and_extract(FlyString namespace_, FlyString
ExceptionOr<void> Element::set_attribute_ns(FlyString const& namespace_, FlyString const& qualified_name, String const& value)
{
// 1. Let namespace, prefix, and localName be the result of passing namespace and qualifiedName to validate and extract.
auto extracted_qualified_name = TRY(validate_and_extract(namespace_, qualified_name));
auto extracted_qualified_name = TRY(validate_and_extract(global_object(), namespace_, qualified_name));
// FIXME: 2. Set an attribute value for this using localName, value, and also prefix and namespace.
@ -195,7 +195,7 @@ DOM::ExceptionOr<bool> Element::toggle_attribute(FlyString const& name, Optional
// 1. If qualifiedName does not match the Name production in XML, then throw an "InvalidCharacterError" DOMException.
// FIXME: Proper name validation
if (name.is_empty())
return InvalidCharacterError::create("Attribute name must not be empty");
return InvalidCharacterError::create(global_object(), "Attribute name must not be empty");
// 2. If this is in the HTML namespace and its node document is an HTML document, then set qualifiedName to qualifiedName in ASCII lowercase.
// FIXME: Handle the second condition, assume it is an HTML document for now.
@ -439,7 +439,7 @@ DOM::ExceptionOr<bool> Element::matches(StringView selectors) const
{
auto maybe_selectors = parse_selector(CSS::Parser::ParsingContext(static_cast<ParentNode&>(const_cast<Element&>(*this))), selectors);
if (!maybe_selectors.has_value())
return DOM::SyntaxError::create("Failed to parse selector");
return DOM::SyntaxError::create(global_object(), "Failed to parse selector");
auto sel = maybe_selectors.value();
for (auto& s : sel) {
@ -454,7 +454,7 @@ DOM::ExceptionOr<DOM::Element const*> Element::closest(StringView selectors) con
{
auto maybe_selectors = parse_selector(CSS::Parser::ParsingContext(static_cast<ParentNode&>(const_cast<Element&>(*this))), selectors);
if (!maybe_selectors.has_value())
return DOM::SyntaxError::create("Failed to parse selector");
return DOM::SyntaxError::create(global_object(), "Failed to parse selector");
auto matches_selectors = [](CSS::SelectorList const& selector_list, Element const* element) {
for (auto& selector : selector_list) {

View file

@ -170,7 +170,7 @@ private:
template<>
inline bool Node::fast_is<Element>() const { return is_element(); }
ExceptionOr<QualifiedName> validate_and_extract(FlyString namespace_, FlyString qualified_name);
ExceptionOr<QualifiedName> validate_and_extract(JS::Object& global_object, FlyString namespace_, FlyString qualified_name);
}

View file

@ -228,10 +228,10 @@ ExceptionOr<bool> EventTarget::dispatch_event_binding(Event& event)
{
// 1. If events dispatch flag is set, or if its initialized flag is not set, then throw an "InvalidStateError" DOMException.
if (event.dispatched())
return DOM::InvalidStateError::create("The event is already being dispatched.");
return DOM::InvalidStateError::create(global_object(), "The event is already being dispatched.");
if (!event.initialized())
return DOM::InvalidStateError::create("Cannot dispatch an uninitialized event.");
return DOM::InvalidStateError::create(global_object(), "Cannot dispatch an uninitialized event.");
// 2. Initialize events isTrusted attribute to false.
event.set_is_trusted(false);

View file

@ -49,7 +49,7 @@ public:
{
}
ExceptionOr(NonnullRefPtr<DOMException> exception)
ExceptionOr(JS::NonnullGCPtr<DOMException> exception)
: m_exception(move(exception))
{
}
@ -59,8 +59,8 @@ public:
{
}
ExceptionOr(Variant<SimpleException, NonnullRefPtr<DOMException>> exception)
: m_exception(move(exception).template downcast<Empty, SimpleException, NonnullRefPtr<DOMException>>())
ExceptionOr(Variant<SimpleException, JS::NonnullGCPtr<DOMException>> exception)
: m_exception(move(exception).template downcast<Empty, SimpleException, JS::NonnullGCPtr<DOMException>>())
{
}
@ -78,9 +78,9 @@ public:
return m_result.release_value();
}
Variant<SimpleException, NonnullRefPtr<DOMException>> exception() const
Variant<SimpleException, JS::NonnullGCPtr<DOMException>> exception() const
{
return m_exception.template downcast<SimpleException, NonnullRefPtr<DOMException>>();
return m_exception.template downcast<SimpleException, JS::NonnullGCPtr<DOMException>>();
}
bool is_exception() const
@ -90,12 +90,12 @@ public:
// These are for compatibility with the TRY() macro in AK.
[[nodiscard]] bool is_error() const { return is_exception(); }
Variant<SimpleException, NonnullRefPtr<DOMException>> release_error() { return exception(); }
Variant<SimpleException, JS::NonnullGCPtr<DOMException>> release_error() { return exception(); }
private:
Optional<ValueType> m_result;
// https://webidl.spec.whatwg.org/#idl-exceptions
Variant<Empty, SimpleException, NonnullRefPtr<DOMException>> m_exception {};
Variant<Empty, SimpleException, JS::NonnullGCPtr<DOMException>> m_exception {};
};
template<>

View file

@ -93,7 +93,7 @@ ExceptionOr<Attribute const*> NamedNodeMap::remove_named_item(StringView qualifi
// 2. If attr is null, then throw a "NotFoundError" DOMException.
if (!attribute)
return NotFoundError::create(String::formatted("Attribute with name '{}' not found", qualified_name));
return NotFoundError::create(global_object(), String::formatted("Attribute with name '{}' not found", qualified_name));
// 3. Return attr.
return nullptr;
@ -137,7 +137,7 @@ ExceptionOr<Attribute const*> NamedNodeMap::set_attribute(Attribute& attribute)
{
// 1. If attrs element is neither null nor element, throw an "InUseAttributeError" DOMException.
if ((attribute.owner_element() != nullptr) && (attribute.owner_element() != &associated_element()))
return InUseAttributeError::create("Attribute must not already be in use"sv);
return InUseAttributeError::create(global_object(), "Attribute must not already be in use"sv);
// 2. Let oldAttr be the result of getting an attribute given attrs namespace, attrs local name, and element.
// FIXME: When getNamedItemNS is implemented, use that instead.

View file

@ -310,24 +310,24 @@ ExceptionOr<void> Node::ensure_pre_insertion_validity(JS::NonnullGCPtr<Node> nod
{
// 1. If parent is not a Document, DocumentFragment, or Element node, then throw a "HierarchyRequestError" DOMException.
if (!is<Document>(this) && !is<DocumentFragment>(this) && !is<Element>(this))
return DOM::HierarchyRequestError::create("Can only insert into a document, document fragment or element");
return DOM::HierarchyRequestError::create(global_object(), "Can only insert into a document, document fragment or element");
// 2. If node is a host-including inclusive ancestor of parent, then throw a "HierarchyRequestError" DOMException.
if (node->is_host_including_inclusive_ancestor_of(*this))
return DOM::HierarchyRequestError::create("New node is an ancestor of this node");
return DOM::HierarchyRequestError::create(global_object(), "New node is an ancestor of this node");
// 3. If child is non-null and its parent is not parent, then throw a "NotFoundError" DOMException.
if (child && child->parent() != this)
return DOM::NotFoundError::create("This node is not the parent of the given child");
return DOM::NotFoundError::create(global_object(), "This node is not the parent of the given child");
// FIXME: All the following "Invalid node type for insertion" messages could be more descriptive.
// 4. If node is not a DocumentFragment, DocumentType, Element, or CharacterData node, then throw a "HierarchyRequestError" DOMException.
if (!is<DocumentFragment>(*node) && !is<DocumentType>(*node) && !is<Element>(*node) && !is<Text>(*node) && !is<Comment>(*node) && !is<ProcessingInstruction>(*node))
return DOM::HierarchyRequestError::create("Invalid node type for insertion");
return DOM::HierarchyRequestError::create(global_object(), "Invalid node type for insertion");
// 5. If either node is a Text node and parent is a document, or node is a doctype and parent is not a document, then throw a "HierarchyRequestError" DOMException.
if ((is<Text>(*node) && is<Document>(this)) || (is<DocumentType>(*node) && !is<Document>(this)))
return DOM::HierarchyRequestError::create("Invalid node type for insertion");
return DOM::HierarchyRequestError::create(global_object(), "Invalid node type for insertion");
// 6. If parent is a document, and any of the statements below, switched on the interface node implements, are true, then throw a "HierarchyRequestError" DOMException.
if (is<Document>(this)) {
@ -338,18 +338,18 @@ ExceptionOr<void> Node::ensure_pre_insertion_validity(JS::NonnullGCPtr<Node> nod
auto node_element_child_count = verify_cast<DocumentFragment>(*node).child_element_count();
if ((node_element_child_count > 1 || node->has_child_of_type<Text>())
|| (node_element_child_count == 1 && (has_child_of_type<Element>() || is<DocumentType>(child.ptr()) || (child && child->has_following_node_of_type_in_tree_order<DocumentType>())))) {
return DOM::HierarchyRequestError::create("Invalid node type for insertion");
return DOM::HierarchyRequestError::create(global_object(), "Invalid node type for insertion");
}
} else if (is<Element>(*node)) {
// Element
// If parent has an element child, child is a doctype, or child is non-null and a doctype is following child.
if (has_child_of_type<Element>() || is<DocumentType>(child.ptr()) || (child && child->has_following_node_of_type_in_tree_order<DocumentType>()))
return DOM::HierarchyRequestError::create("Invalid node type for insertion");
return DOM::HierarchyRequestError::create(global_object(), "Invalid node type for insertion");
} else if (is<DocumentType>(*node)) {
// DocumentType
// parent has a doctype child, child is non-null and an element is preceding child, or child is null and parent has an element child.
if (has_child_of_type<DocumentType>() || (child && child->has_preceding_node_of_type_in_tree_order<Element>()) || (!child && has_child_of_type<Element>()))
return DOM::HierarchyRequestError::create("Invalid node type for insertion");
return DOM::HierarchyRequestError::create(global_object(), "Invalid node type for insertion");
}
}
@ -482,7 +482,7 @@ ExceptionOr<JS::NonnullGCPtr<Node>> Node::pre_remove(JS::NonnullGCPtr<Node> chil
{
// 1. If childs parent is not parent, then throw a "NotFoundError" DOMException.
if (child->parent() != this)
return DOM::NotFoundError::create("Child does not belong to this node");
return DOM::NotFoundError::create(global_object(), "Child does not belong to this node");
// 2. Remove child.
child->remove();
@ -607,25 +607,25 @@ ExceptionOr<JS::NonnullGCPtr<Node>> Node::replace_child(JS::NonnullGCPtr<Node> n
{
// If parent is not a Document, DocumentFragment, or Element node, then throw a "HierarchyRequestError" DOMException.
if (!is<Document>(this) && !is<DocumentFragment>(this) && !is<Element>(this))
return DOM::HierarchyRequestError::create("Can only insert into a document, document fragment or element");
return DOM::HierarchyRequestError::create(global_object(), "Can only insert into a document, document fragment or element");
// 2. If node is a host-including inclusive ancestor of parent, then throw a "HierarchyRequestError" DOMException.
if (node->is_host_including_inclusive_ancestor_of(*this))
return DOM::HierarchyRequestError::create("New node is an ancestor of this node");
return DOM::HierarchyRequestError::create(global_object(), "New node is an ancestor of this node");
// 3. If childs parent is not parent, then throw a "NotFoundError" DOMException.
if (child->parent() != this)
return DOM::NotFoundError::create("This node is not the parent of the given child");
return DOM::NotFoundError::create(global_object(), "This node is not the parent of the given child");
// FIXME: All the following "Invalid node type for insertion" messages could be more descriptive.
// 4. If node is not a DocumentFragment, DocumentType, Element, or CharacterData node, then throw a "HierarchyRequestError" DOMException.
if (!is<DocumentFragment>(*node) && !is<DocumentType>(*node) && !is<Element>(*node) && !is<Text>(*node) && !is<Comment>(*node) && !is<ProcessingInstruction>(*node))
return DOM::HierarchyRequestError::create("Invalid node type for insertion");
return DOM::HierarchyRequestError::create(global_object(), "Invalid node type for insertion");
// 5. If either node is a Text node and parent is a document, or node is a doctype and parent is not a document, then throw a "HierarchyRequestError" DOMException.
if ((is<Text>(*node) && is<Document>(this)) || (is<DocumentType>(*node) && !is<Document>(this)))
return DOM::HierarchyRequestError::create("Invalid node type for insertion");
return DOM::HierarchyRequestError::create(global_object(), "Invalid node type for insertion");
// If parent is a document, and any of the statements below, switched on the interface node implements, are true, then throw a "HierarchyRequestError" DOMException.
if (is<Document>(this)) {
@ -636,18 +636,18 @@ ExceptionOr<JS::NonnullGCPtr<Node>> Node::replace_child(JS::NonnullGCPtr<Node> n
auto node_element_child_count = verify_cast<DocumentFragment>(*node).child_element_count();
if ((node_element_child_count > 1 || node->has_child_of_type<Text>())
|| (node_element_child_count == 1 && (first_child_of_type<Element>() != child || child->has_following_node_of_type_in_tree_order<DocumentType>()))) {
return DOM::HierarchyRequestError::create("Invalid node type for insertion");
return DOM::HierarchyRequestError::create(global_object(), "Invalid node type for insertion");
}
} else if (is<Element>(*node)) {
// Element
// parent has an element child that is not child or a doctype is following child.
if (first_child_of_type<Element>() != child || child->has_following_node_of_type_in_tree_order<DocumentType>())
return DOM::HierarchyRequestError::create("Invalid node type for insertion");
return DOM::HierarchyRequestError::create(global_object(), "Invalid node type for insertion");
} else if (is<DocumentType>(*node)) {
// DocumentType
// parent has a doctype child that is not child, or an element is preceding child.
if (first_child_of_type<DocumentType>() != node || child->has_preceding_node_of_type_in_tree_order<Element>())
return DOM::HierarchyRequestError::create("Invalid node type for insertion");
return DOM::HierarchyRequestError::create(global_object(), "Invalid node type for insertion");
}
}
@ -791,7 +791,7 @@ ExceptionOr<JS::NonnullGCPtr<Node>> Node::clone_node_binding(bool deep)
{
// 1. If this is a shadow root, then throw a "NotSupportedError" DOMException.
if (is<ShadowRoot>(*this))
return NotSupportedError::create("Cannot clone shadow root");
return NotSupportedError::create(global_object(), "Cannot clone shadow root");
// 2. Return a clone of this, with the clone children flag set if deep is true.
return clone_node(nullptr, deep);

View file

@ -4,6 +4,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibJS/Runtime/Realm.h>
#include <LibWeb/DOM/NodeFilter.h>
#include <LibWeb/HTML/Window.h>

View file

@ -4,7 +4,6 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/Bindings/DOMExceptionWrapper.h>
#include <LibWeb/Bindings/IDLAbstractOperations.h>
#include <LibWeb/DOM/Document.h>
#include <LibWeb/DOM/Node.h>
@ -131,7 +130,7 @@ JS::ThrowCompletionOr<NodeFilter::Result> NodeIterator::filter(Node& node)
{
// 1. If traversers active flag is set, then throw an "InvalidStateError" DOMException.
if (m_active)
return JS::throw_completion(wrap(shape().realm(), InvalidStateError::create("NodeIterator is already active")));
return JS::throw_completion(wrap(shape().realm(), InvalidStateError::create(global_object(), "NodeIterator is already active")));
// 2. Let n be nodes nodeType attribute value 1.
auto n = node.node_type() - 1;

View file

@ -11,6 +11,7 @@
#include <LibWeb/DOM/ParentNode.h>
#include <LibWeb/DOM/StaticNodeList.h>
#include <LibWeb/Dump.h>
#include <LibWeb/HTML/Window.h>
#include <LibWeb/Namespace.h>
namespace Web::DOM {
@ -19,7 +20,7 @@ ExceptionOr<JS::GCPtr<Element>> ParentNode::query_selector(StringView selector_t
{
auto maybe_selectors = parse_selector(CSS::Parser::ParsingContext(*this), selector_text);
if (!maybe_selectors.has_value())
return DOM::SyntaxError::create("Failed to parse selector");
return DOM::SyntaxError::create(global_object(), "Failed to parse selector");
auto selectors = maybe_selectors.value();
@ -42,7 +43,7 @@ ExceptionOr<JS::NonnullGCPtr<NodeList>> ParentNode::query_selector_all(StringVie
{
auto maybe_selectors = parse_selector(CSS::Parser::ParsingContext(*this), selector_text);
if (!maybe_selectors.has_value())
return DOM::SyntaxError::create("Failed to parse selector");
return DOM::SyntaxError::create(global_object(), "Failed to parse selector");
auto selectors = maybe_selectors.value();

View file

@ -137,11 +137,11 @@ ExceptionOr<void> Range::set_start_or_end(Node& node, u32 offset, StartOrEnd sta
// 1. If node is a doctype, then throw an "InvalidNodeTypeError" DOMException.
if (is<DocumentType>(node))
return InvalidNodeTypeError::create("Node cannot be a DocumentType.");
return InvalidNodeTypeError::create(global_object(), "Node cannot be a DocumentType.");
// 2. If offset is greater than nodes length, then throw an "IndexSizeError" DOMException.
if (offset > node.length())
return IndexSizeError::create(String::formatted("Node does not contain a child at offset {}", offset));
return IndexSizeError::create(global_object(), String::formatted("Node does not contain a child at offset {}", offset));
// 3. Let bp be the boundary point (node, offset).
@ -196,7 +196,7 @@ ExceptionOr<void> Range::set_start_before(Node& node)
// 2. If parent is null, then throw an "InvalidNodeTypeError" DOMException.
if (!parent)
return InvalidNodeTypeError::create("Given node has no parent.");
return InvalidNodeTypeError::create(global_object(), "Given node has no parent.");
// 3. Set the start of this to boundary point (parent, nodes index).
return set_start_or_end(*parent, node.index(), StartOrEnd::Start);
@ -210,7 +210,7 @@ ExceptionOr<void> Range::set_start_after(Node& node)
// 2. If parent is null, then throw an "InvalidNodeTypeError" DOMException.
if (!parent)
return InvalidNodeTypeError::create("Given node has no parent.");
return InvalidNodeTypeError::create(global_object(), "Given node has no parent.");
// 3. Set the start of this to boundary point (parent, nodes index plus 1).
return set_start_or_end(*parent, node.index() + 1, StartOrEnd::Start);
@ -224,7 +224,7 @@ ExceptionOr<void> Range::set_end_before(Node& node)
// 2. If parent is null, then throw an "InvalidNodeTypeError" DOMException.
if (!parent)
return InvalidNodeTypeError::create("Given node has no parent.");
return InvalidNodeTypeError::create(global_object(), "Given node has no parent.");
// 3. Set the end of this to boundary point (parent, nodes index).
return set_start_or_end(*parent, node.index(), StartOrEnd::End);
@ -238,7 +238,7 @@ ExceptionOr<void> Range::set_end_after(Node& node)
// 2. If parent is null, then throw an "InvalidNodeTypeError" DOMException.
if (!parent)
return InvalidNodeTypeError::create("Given node has no parent.");
return InvalidNodeTypeError::create(global_object(), "Given node has no parent.");
// 3. Set the end of this to boundary point (parent, nodes index plus 1).
return set_start_or_end(*parent, node.index() + 1, StartOrEnd::End);
@ -254,11 +254,11 @@ ExceptionOr<i16> Range::compare_boundary_points(u16 how, Range const& source_ran
// - END_TO_START,
// then throw a "NotSupportedError" DOMException.
if (how != HowToCompareBoundaryPoints::START_TO_START && how != HowToCompareBoundaryPoints::START_TO_END && how != HowToCompareBoundaryPoints::END_TO_END && how != HowToCompareBoundaryPoints::END_TO_START)
return NotSupportedError::create(String::formatted("Expected 'how' to be one of START_TO_START (0), START_TO_END (1), END_TO_END (2) or END_TO_START (3), got {}", how));
return NotSupportedError::create(global_object(), String::formatted("Expected 'how' to be one of START_TO_START (0), START_TO_END (1), END_TO_END (2) or END_TO_START (3), got {}", how));
// 2. If thiss root is not the same as sourceRanges root, then throw a "WrongDocumentError" DOMException.
if (&root() != &source_range.root())
return WrongDocumentError::create("This range is not in the same tree as the source range.");
return WrongDocumentError::create(global_object(), "This range is not in the same tree as the source range.");
JS::GCPtr<Node> this_point_node;
u32 this_point_offset = 0;
@ -339,7 +339,7 @@ ExceptionOr<void> Range::select(Node& node)
// 2. If parent is null, then throw an "InvalidNodeTypeError" DOMException.
if (!parent)
return InvalidNodeTypeError::create("Given node has no parent.");
return InvalidNodeTypeError::create(global_object(), "Given node has no parent.");
// 3. Let index be nodes index.
auto index = node.index();
@ -381,7 +381,7 @@ ExceptionOr<void> Range::select_node_contents(Node const& node)
{
// 1. If node is a doctype, throw an "InvalidNodeTypeError" DOMException.
if (is<DocumentType>(node))
return InvalidNodeTypeError::create("Node cannot be a DocumentType.");
return InvalidNodeTypeError::create(global_object(), "Node cannot be a DocumentType.");
// 2. Let length be the length of node.
auto length = node.length();
@ -474,11 +474,11 @@ ExceptionOr<bool> Range::is_point_in_range(Node const& node, u32 offset) const
// 2. If node is a doctype, then throw an "InvalidNodeTypeError" DOMException.
if (is<DocumentType>(node))
return InvalidNodeTypeError::create("Node cannot be a DocumentType.");
return InvalidNodeTypeError::create(global_object(), "Node cannot be a DocumentType.");
// 3. If offset is greater than nodes length, then throw an "IndexSizeError" DOMException.
if (offset > node.length())
return IndexSizeError::create(String::formatted("Node does not contain a child at offset {}", offset));
return IndexSizeError::create(global_object(), String::formatted("Node does not contain a child at offset {}", offset));
// 4. If (node, offset) is before start or after end, return false.
auto relative_position_to_start = position_of_boundary_point_relative_to_other_boundary_point(node, offset, m_start_container, m_start_offset);
@ -495,15 +495,15 @@ ExceptionOr<i16> Range::compare_point(Node const& node, u32 offset) const
{
// 1. If nodes root is different from thiss root, then throw a "WrongDocumentError" DOMException.
if (&node.root() != &root())
return WrongDocumentError::create("Given node is not in the same document as the range.");
return WrongDocumentError::create(global_object(), "Given node is not in the same document as the range.");
// 2. If node is a doctype, then throw an "InvalidNodeTypeError" DOMException.
if (is<DocumentType>(node))
return InvalidNodeTypeError::create("Node cannot be a DocumentType.");
return InvalidNodeTypeError::create(global_object(), "Node cannot be a DocumentType.");
// 3. If offset is greater than nodes length, then throw an "IndexSizeError" DOMException.
if (offset > node.length())
return IndexSizeError::create(String::formatted("Node does not contain a child at offset {}", offset));
return IndexSizeError::create(global_object(), String::formatted("Node does not contain a child at offset {}", offset));
// 4. If (node, offset) is before start, return 1.
auto relative_position_to_start = position_of_boundary_point_relative_to_other_boundary_point(node, offset, m_start_container, m_start_offset);
@ -636,7 +636,7 @@ ExceptionOr<JS::NonnullGCPtr<DocumentFragment>> Range::extract()
// 12. If any member of contained children is a doctype, then throw a "HierarchyRequestError" DOMException.
for (auto const& child : contained_children) {
if (is<DocumentType>(*child))
return DOM::HierarchyRequestError::create("Contained child is a DocumentType");
return DOM::HierarchyRequestError::create(global_object(), "Contained child is a DocumentType");
}
JS::GCPtr<Node> new_node;
@ -783,7 +783,7 @@ ExceptionOr<void> Range::insert(JS::NonnullGCPtr<Node> node)
if ((is<ProcessingInstruction>(*m_start_container) || is<Comment>(*m_start_container))
|| (is<Text>(*m_start_container) && !m_start_container->parent_node())
|| m_start_container.ptr() == node.ptr()) {
return DOM::HierarchyRequestError::create("Range has inappropriate start node for insertion");
return DOM::HierarchyRequestError::create(global_object(), "Range has inappropriate start node for insertion");
}
// 2. Let referenceNode be null.
@ -854,11 +854,11 @@ ExceptionOr<void> Range::surround_contents(JS::NonnullGCPtr<Node> new_parent)
if (is<Text>(*end_non_text_node))
end_non_text_node = end_non_text_node->parent_node();
if (start_non_text_node != end_non_text_node)
return InvalidStateError::create("Non-Text node is partially contained in range.");
return InvalidStateError::create(global_object(), "Non-Text node is partially contained in range.");
// 2. If newParent is a Document, DocumentType, or DocumentFragment node, then throw an "InvalidNodeTypeError" DOMException.
if (is<Document>(*new_parent) || is<DocumentType>(*new_parent) || is<DocumentFragment>(*new_parent))
return InvalidNodeTypeError::create("Invalid parent node type");
return InvalidNodeTypeError::create(global_object(), "Invalid parent node type");
// 3. Let fragment be the result of extracting this.
auto fragment = TRY(extract());
@ -962,7 +962,7 @@ ExceptionOr<JS::NonnullGCPtr<DocumentFragment>> Range::clone_the_contents()
// 12. If any member of contained children is a doctype, then throw a "HierarchyRequestError" DOMException.
for (auto const& child : contained_children) {
if (is<DocumentType>(*child))
return DOM::HierarchyRequestError::create("Contained child is a DocumentType");
return DOM::HierarchyRequestError::create(global_object(), "Contained child is a DocumentType");
}
// 13. If first partially contained child is a CharacterData node, then:

View file

@ -23,17 +23,17 @@ StaticRange::StaticRange(Node& start_container, u32 start_offset, Node& end_cont
StaticRange::~StaticRange() = default;
// https://dom.spec.whatwg.org/#dom-staticrange-staticrange
ExceptionOr<StaticRange*> StaticRange::create_with_global_object(HTML::Window& window_object, StaticRangeInit& init)
ExceptionOr<StaticRange*> StaticRange::create_with_global_object(HTML::Window& window, StaticRangeInit& init)
{
// 1. If init["startContainer"] or init["endContainer"] is a DocumentType or Attr node, then throw an "InvalidNodeTypeError" DOMException.
if (is<DocumentType>(*init.start_container) || is<Attribute>(*init.start_container))
return DOM::InvalidNodeTypeError::create("startContainer cannot be a DocumentType or Attribute node.");
return DOM::InvalidNodeTypeError::create(window, "startContainer cannot be a DocumentType or Attribute node.");
if (is<DocumentType>(*init.end_container) || is<Attribute>(*init.end_container))
return DOM::InvalidNodeTypeError::create("endContainer cannot be a DocumentType or Attribute node.");
return DOM::InvalidNodeTypeError::create(window, "endContainer cannot be a DocumentType or Attribute node.");
// 2. Set thiss start to (init["startContainer"], init["startOffset"]) and end to (init["endContainer"], init["endOffset"]).
return window_object.heap().allocate<StaticRange>(window_object.realm(), *init.start_container, init.start_offset, *init.end_container, init.end_offset);
return window.heap().allocate<StaticRange>(window.realm(), *init.start_container, init.start_offset, *init.end_container, init.end_offset);
}
}

View file

@ -50,7 +50,7 @@ ExceptionOr<JS::NonnullGCPtr<Text>> Text::split_text(size_t offset)
// 2. If offset is greater than length, then throw an "IndexSizeError" DOMException.
if (offset > length)
return DOM::IndexSizeError::create("Split offset is greater than length");
return DOM::IndexSizeError::create(global_object(), "Split offset is greater than length");
// 3. Let count be length minus offset.
auto count = length - offset;

View file

@ -4,9 +4,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/Bindings/DOMExceptionWrapper.h>
#include <LibWeb/Bindings/IDLAbstractOperations.h>
#include <LibWeb/Bindings/Wrapper.h>
#include <LibWeb/DOM/DOMException.h>
#include <LibWeb/DOM/Document.h>
#include <LibWeb/DOM/Node.h>
@ -233,7 +231,7 @@ JS::ThrowCompletionOr<NodeFilter::Result> TreeWalker::filter(Node& node)
{
// 1. If traversers active flag is set, then throw an "InvalidStateError" DOMException.
if (m_active)
return JS::throw_completion(wrap(shape().realm(), InvalidStateError::create("NodeIterator is already active")));
return JS::throw_completion(wrap(shape().realm(), InvalidStateError::create(global_object(), "NodeIterator is already active")));
// 2. Let n be nodes nodeType attribute value 1.
auto n = node.node_type() - 1;

View file

@ -308,6 +308,8 @@ struct LocalNameSetEntry {
// https://w3c.github.io/DOM-Parsing/#dfn-xml-serialization-of-the-attributes
static DOM::ExceptionOr<String> serialize_element_attributes(DOM::Element const& element, HashMap<FlyString, Vector<String>>& namespace_prefix_map, u64& prefix_index, HashMap<String, String> const& local_prefixes_map, bool ignore_namespace_definition_attribute, RequireWellFormed require_well_formed)
{
auto& global_object = element.global_object();
// 1. Let result be the empty string.
StringBuilder result;
@ -329,7 +331,7 @@ static DOM::ExceptionOr<String> serialize_element_attributes(DOM::Element const&
});
if (local_name_set_iterator != local_name_set.end())
return DOM::InvalidStateError::create("Element contains two attributes with identical namespaces and local names");
return DOM::InvalidStateError::create(global_object, "Element contains two attributes with identical namespaces and local names");
}
// 2. Create a new tuple consisting of attr's namespaceURI attribute and localName attribute, and add it to the localname set.
@ -382,12 +384,12 @@ static DOM::ExceptionOr<String> serialize_element_attributes(DOM::Element const&
// 2. If the require well-formed flag is set (its value is true), and the value of attr's value attribute matches the XMLNS namespace,
// then throw an exception; the serialization of this attribute would produce invalid XML because the XMLNS namespace is reserved and cannot be applied as an element's namespace via XML parsing.
if (require_well_formed == RequireWellFormed::Yes && attribute->value() == Namespace::XMLNS)
return DOM::InvalidStateError::create("The XMLNS namespace cannot be used as an element's namespace");
return DOM::InvalidStateError::create(global_object, "The XMLNS namespace cannot be used as an element's namespace");
// 3. If the require well-formed flag is set (its value is true), and the value of attr's value attribute is the empty string,
// then throw an exception; namespace prefix declarations cannot be used to undeclare a namespace (use a default namespace declaration instead).
if (require_well_formed == RequireWellFormed::Yes && attribute->value().is_empty())
return DOM::InvalidStateError::create("Attribute's value is empty");
return DOM::InvalidStateError::create(global_object, "Attribute's value is empty");
// 4. [If] the attr's prefix matches the string "xmlns", then let candidate prefix be the string "xmlns".
if (attribute->prefix() == "xmlns"sv)
@ -430,12 +432,12 @@ static DOM::ExceptionOr<String> serialize_element_attributes(DOM::Element const&
// or does not match the XML Name production or equals "xmlns" and attribute namespace is null, then throw an exception; the serialization of this attr would not be a well-formed attribute.
if (require_well_formed == RequireWellFormed::Yes) {
if (attribute->local_name().view().contains(':'))
return DOM::InvalidStateError::create("Attribute's local name contains a colon");
return DOM::InvalidStateError::create(global_object, "Attribute's local name contains a colon");
// FIXME: Check attribute's local name against the XML Name production.
if (attribute->local_name() == "xmlns"sv && attribute_namespace.is_null())
return DOM::InvalidStateError::create("Attribute's local name is 'xmlns' and the attribute has no namespace");
return DOM::InvalidStateError::create(global_object, "Attribute's local name is 'xmlns' and the attribute has no namespace");
}
// 9. Append the following strings to result, in the order listed:
@ -459,11 +461,13 @@ static DOM::ExceptionOr<String> serialize_element_attributes(DOM::Element const&
// https://w3c.github.io/DOM-Parsing/#xml-serializing-an-element-node
static DOM::ExceptionOr<String> serialize_element(DOM::Element const& element, Optional<FlyString>& namespace_, HashMap<FlyString, Vector<String>>& namespace_prefix_map, u64& prefix_index, RequireWellFormed require_well_formed)
{
auto& global_object = element.global_object();
// 1. If the require well-formed flag is set (its value is true), and this node's localName attribute contains the character ":" (U+003A COLON) or does not match the XML Name production,
// then throw an exception; the serialization of this node would not be a well-formed element.
if (require_well_formed == RequireWellFormed::Yes) {
if (element.local_name().view().contains(':'))
return DOM::InvalidStateError::create("Element's local name contains a colon");
return DOM::InvalidStateError::create(global_object, "Element's local name contains a colon");
// FIXME: Check element's local name against the XML Char production.
}
@ -535,7 +539,7 @@ static DOM::ExceptionOr<String> serialize_element(DOM::Element const& element, O
if (prefix == "xmlns"sv) {
// 1. If the require well-formed flag is set, then throw an error. An Element with prefix "xmlns" will not legally round-trip in a conforming XML parser.
if (require_well_formed == RequireWellFormed::Yes)
return DOM::InvalidStateError::create("Elements prefix is 'xmlns'");
return DOM::InvalidStateError::create(global_object, "Elements prefix is 'xmlns'");
// 2. Let candidate prefix be the value of prefix.
candidate_prefix = prefix;
@ -702,7 +706,7 @@ static DOM::ExceptionOr<String> serialize_document(DOM::Document const& document
// If the require well-formed flag is set (its value is true), and this node has no documentElement (the documentElement attribute's value is null),
// then throw an exception; the serialization of this node would not be a well-formed document.
if (require_well_formed == RequireWellFormed::Yes && !document.document_element())
return DOM::InvalidStateError::create("Document has no document element");
return DOM::InvalidStateError::create(document.global_object(), "Document has no document element");
// Otherwise, run the following steps:
// 1. Let serialized document be an empty string.
@ -726,10 +730,10 @@ static DOM::ExceptionOr<String> serialize_comment(DOM::Comment const& comment, R
// FIXME: Check comment's data against the XML Char production.
if (comment.data().contains("--"sv))
return DOM::InvalidStateError::create("Comment data contains two adjacent hyphens");
return DOM::InvalidStateError::create(comment.global_object(), "Comment data contains two adjacent hyphens");
if (comment.data().ends_with('-'))
return DOM::InvalidStateError::create("Comment data ends with a hyphen");
return DOM::InvalidStateError::create(comment.global_object(), "Comment data ends with a hyphen");
}
// Otherwise, return the concatenation of "<!--", node's data, and "-->".
@ -784,7 +788,7 @@ static DOM::ExceptionOr<String> serialize_document_type(DOM::DocumentType const&
// both a """ (U+0022 QUOTATION MARK) and a "'" (U+0027 APOSTROPHE), then throw an exception; the serialization of this node would not be a well-formed document type declaration.
// FIXME: Check systemId against the XML Char production.
if (document_type.system_id().contains('"') && document_type.system_id().contains('\''))
return DOM::InvalidStateError::create("Document type system ID contains both a quotation mark and an apostrophe");
return DOM::InvalidStateError::create(document_type.global_object(), "Document type system ID contains both a quotation mark and an apostrophe");
}
// 3. Let markup be an empty string.
@ -846,16 +850,16 @@ static DOM::ExceptionOr<String> serialize_processing_instruction(DOM::Processing
// 1. If the require well-formed flag is set (its value is true), and node's target contains a ":" (U+003A COLON) character
// or is an ASCII case-insensitive match for the string "xml", then throw an exception; the serialization of this node's target would not be well-formed.
if (processing_instruction.target().contains(':'))
return DOM::InvalidStateError::create("Processing instruction target contains a colon");
return DOM::InvalidStateError::create(processing_instruction.global_object(), "Processing instruction target contains a colon");
if (processing_instruction.target().equals_ignoring_case("xml"sv))
return DOM::InvalidStateError::create("Processing instruction target is equal to 'xml'");
return DOM::InvalidStateError::create(processing_instruction.global_object(), "Processing instruction target is equal to 'xml'");
// 2. If the require well-formed flag is set (its value is true), and node's data contains characters that are not matched by the XML Char production or contains
// the string "?>" (U+003F QUESTION MARK, U+003E GREATER-THAN SIGN), then throw an exception; the serialization of this node's data would not be well-formed.
// FIXME: Check data against the XML Char production.
if (processing_instruction.data().contains("?>"sv))
return DOM::InvalidStateError::create("Processing instruction data contains a terminator");
return DOM::InvalidStateError::create(processing_instruction.global_object(), "Processing instruction data contains a terminator");
}
// 3. Let markup be the concatenation of the following, in the order listed:

View file

@ -41,7 +41,7 @@ DOM::ExceptionOr<String> TextDecoder::decode(JS::Handle<JS::Object> const& input
auto data_buffer_or_error = Bindings::IDL::get_buffer_source_copy(*input.cell());
if (data_buffer_or_error.is_error())
return DOM::OperationError::create("Failed to copy bytes from ArrayBuffer");
return DOM::OperationError::create(global_object(), "Failed to copy bytes from ArrayBuffer");
auto& data_buffer = data_buffer_or_error.value();
return m_decoder.to_utf8({ data_buffer.data(), data_buffer.size() });
}

View file

@ -4,6 +4,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibJS/Runtime/VM.h>
#include <LibWeb/Fetch/Headers.h>
#include <LibWeb/HTML/Window.h>
@ -38,8 +39,8 @@ DOM::ExceptionOr<void> Headers::append(String const& name_string, String const&
{
// The append(name, value) method steps are to append (name, value) to this.
auto header = Infrastructure::Header {
.name = TRY_OR_RETURN_OOM(ByteBuffer::copy(name_string.bytes())),
.value = TRY_OR_RETURN_OOM(ByteBuffer::copy(value_string.bytes())),
.name = TRY_OR_RETURN_OOM(global_object(), ByteBuffer::copy(name_string.bytes())),
.value = TRY_OR_RETURN_OOM(global_object(), ByteBuffer::copy(value_string.bytes())),
};
TRY(append(move(header)));
return {};
@ -96,7 +97,7 @@ DOM::ExceptionOr<String> Headers::get(String const& name_string)
return DOM::SimpleException { DOM::SimpleExceptionType::TypeError, "Invalid header name" };
// 2. Return the result of getting name from thiss header list.
auto byte_buffer = TRY_OR_RETURN_OOM(m_header_list.get(name));
auto byte_buffer = TRY_OR_RETURN_OOM(global_object(), m_header_list.get(name));
// FIXME: Teach WrapperGenerator about Optional<String>
return byte_buffer.has_value() ? String { byte_buffer->span() } : String {};
}
@ -123,10 +124,10 @@ DOM::ExceptionOr<void> Headers::set(String const& name_string, String const& val
auto value = value_string.bytes();
// 1. Normalize value.
auto normalized_value = TRY_OR_RETURN_OOM(Infrastructure::normalize_header_value(value));
auto normalized_value = TRY_OR_RETURN_OOM(global_object(), Infrastructure::normalize_header_value(value));
auto header = Infrastructure::Header {
.name = TRY_OR_RETURN_OOM(ByteBuffer::copy(name)),
.name = TRY_OR_RETURN_OOM(global_object(), ByteBuffer::copy(name)),
.value = move(normalized_value),
};
@ -153,7 +154,7 @@ DOM::ExceptionOr<void> Headers::set(String const& name_string, String const& val
return {};
// 7. Set (name, value) in thiss header list.
TRY_OR_RETURN_OOM(m_header_list.set(move(header)));
TRY_OR_RETURN_OOM(global_object(), m_header_list.set(move(header)));
// 8. If thiss guard is "request-no-cors", then remove privileged no-CORS request headers from this.
if (m_guard == Guard::RequestNoCORS)
@ -206,7 +207,7 @@ DOM::ExceptionOr<void> Headers::append(Infrastructure::Header header)
auto& [name, value] = header;
// 1. Normalize value.
value = TRY_OR_RETURN_OOM(Infrastructure::normalize_header_value(value));
value = TRY_OR_RETURN_OOM(global_object(), Infrastructure::normalize_header_value(value));
// 2. If name is not a header name or value is not a header value, then throw a TypeError.
if (!Infrastructure::is_header_name(name))
@ -225,21 +226,21 @@ DOM::ExceptionOr<void> Headers::append(Infrastructure::Header header)
// 5. Otherwise, if headerss guard is "request-no-cors":
if (m_guard == Guard::RequestNoCORS) {
// 1. Let temporaryValue be the result of getting name from headerss header list.
auto temporary_value = TRY_OR_RETURN_OOM(m_header_list.get(name));
auto temporary_value = TRY_OR_RETURN_OOM(global_object(), m_header_list.get(name));
// 2. If temporaryValue is null, then set temporaryValue to value.
if (!temporary_value.has_value()) {
temporary_value = TRY_OR_RETURN_OOM(ByteBuffer::copy(value));
temporary_value = TRY_OR_RETURN_OOM(global_object(), ByteBuffer::copy(value));
}
// 3. Otherwise, set temporaryValue to temporaryValue, followed by 0x2C 0x20, followed by value.
else {
TRY_OR_RETURN_OOM(temporary_value->try_append(0x2c));
TRY_OR_RETURN_OOM(temporary_value->try_append(0x20));
TRY_OR_RETURN_OOM(temporary_value->try_append(value));
TRY_OR_RETURN_OOM(global_object(), temporary_value->try_append(0x2c));
TRY_OR_RETURN_OOM(global_object(), temporary_value->try_append(0x20));
TRY_OR_RETURN_OOM(global_object(), temporary_value->try_append(value));
}
auto temporary_header = Infrastructure::Header {
.name = TRY_OR_RETURN_OOM(ByteBuffer::copy(name)),
.name = TRY_OR_RETURN_OOM(global_object(), ByteBuffer::copy(name)),
.value = temporary_value.release_value(),
};
@ -253,7 +254,7 @@ DOM::ExceptionOr<void> Headers::append(Infrastructure::Header header)
return {};
// 7. Append (name, value) to headerss header list.
TRY_OR_RETURN_OOM(m_header_list.append(move(header)));
TRY_OR_RETURN_OOM(global_object(), m_header_list.append(move(header)));
// 8. If headerss guard is "request-no-cors", then remove privileged no-CORS request headers from headers.
if (m_guard == Guard::RequestNoCORS)
@ -276,8 +277,8 @@ DOM::ExceptionOr<void> Headers::fill(HeadersInit const& object)
// 2. Append (headers first item, headers second item) to headers.
auto header = Fetch::Infrastructure::Header {
.name = TRY_OR_RETURN_OOM(ByteBuffer::copy(entry[0].bytes())),
.value = TRY_OR_RETURN_OOM(ByteBuffer::copy(entry[1].bytes())),
.name = TRY_OR_RETURN_OOM(global_object(), ByteBuffer::copy(entry[0].bytes())),
.value = TRY_OR_RETURN_OOM(global_object(), ByteBuffer::copy(entry[1].bytes())),
};
TRY(append(move(header)));
}
@ -287,8 +288,8 @@ DOM::ExceptionOr<void> Headers::fill(HeadersInit const& object)
[this](OrderedHashMap<String, String> const& object) -> DOM::ExceptionOr<void> {
for (auto const& entry : object) {
auto header = Fetch::Infrastructure::Header {
.name = TRY_OR_RETURN_OOM(ByteBuffer::copy(entry.key.bytes())),
.value = TRY_OR_RETURN_OOM(ByteBuffer::copy(entry.value.bytes())),
.name = TRY_OR_RETURN_OOM(global_object(), ByteBuffer::copy(entry.key.bytes())),
.value = TRY_OR_RETURN_OOM(global_object(), ByteBuffer::copy(entry.value.bytes())),
};
TRY(append(move(header)));
}

View file

@ -11,6 +11,7 @@
#include <AK/NonnullRefPtr.h>
#include <AK/Optional.h>
#include <AK/Variant.h>
#include <LibJS/Heap/Handle.h>
#include <LibWeb/FileAPI/Blob.h>
namespace Web::Fetch::Infrastructure {

View file

@ -8,7 +8,6 @@
#include <AK/StdLibExtras.h>
#include <LibJS/Runtime/ArrayBuffer.h>
#include <LibWeb/Bindings/BlobPrototype.h>
#include <LibWeb/Bindings/DOMExceptionWrapper.h>
#include <LibWeb/Bindings/IDLAbstractOperations.h>
#include <LibWeb/FileAPI/Blob.h>
#include <LibWeb/HTML/Window.h>
@ -145,7 +144,7 @@ DOM::ExceptionOr<JS::NonnullGCPtr<Blob>> Blob::create(HTML::Window& window, Opti
ByteBuffer byte_buffer {};
// 2. Let bytes be the result of processing blob parts given blobParts and options.
if (blob_parts.has_value()) {
byte_buffer = TRY_OR_RETURN_OOM(process_blob_parts(blob_parts.value(), options));
byte_buffer = TRY_OR_RETURN_OOM(window, process_blob_parts(blob_parts.value(), options));
}
auto type = String::empty();
@ -233,7 +232,7 @@ DOM::ExceptionOr<JS::NonnullGCPtr<Blob>> Blob::slice(Optional<i64> start, Option
// a. S refers to span consecutive bytes from this, beginning with the byte at byte-order position relativeStart.
// b. S.size = span.
// c. S.type = relativeContentType.
auto byte_buffer = TRY_OR_RETURN_OOM(m_byte_buffer.slice(relative_start, span));
auto byte_buffer = TRY_OR_RETURN_OOM(global_object(), m_byte_buffer.slice(relative_start, span));
return JS::NonnullGCPtr(*heap().allocate<Blob>(realm(), global_object(), move(byte_buffer), move(relative_content_type)));
}

View file

@ -23,7 +23,7 @@ File::~File() = default;
DOM::ExceptionOr<JS::NonnullGCPtr<File>> File::create(HTML::Window& window, Vector<BlobPart> const& file_bits, String const& file_name, Optional<FilePropertyBag> const& options)
{
// 1. Let bytes be the result of processing blob parts given fileBits and options.
auto bytes = TRY_OR_RETURN_OOM(process_blob_parts(file_bits, static_cast<Optional<BlobPropertyBag> const&>(*options)));
auto bytes = TRY_OR_RETURN_OOM(window, process_blob_parts(file_bits, static_cast<Optional<BlobPropertyBag> const&>(*options)));
// 2. Let n be the fileName argument to the constructor.
// NOTE: Underlying OS filesystems use differing conventions for file name; with constructed files, mandating UTF-16 lessens ambiquity when file names are converted to byte sequences.

View file

@ -448,7 +448,6 @@ class URLSearchParamsIterator;
}
namespace Web::Bindings {
class DOMExceptionWrapper;
class LocationObject;
class OptionConstructor;
class RangePrototype;

View file

@ -7,6 +7,7 @@
#include <AK/ExtraMathConstants.h>
#include <LibWeb/HTML/Canvas/CanvasPath.h>
#include <LibWeb/HTML/Window.h>
namespace Web::HTML {
@ -38,17 +39,17 @@ void CanvasPath::bezier_curve_to(double cp1x, double cp1y, double cp2x, double c
DOM::ExceptionOr<void> CanvasPath::arc(float x, float y, float radius, float start_angle, float end_angle, bool counter_clockwise)
{
if (radius < 0)
return DOM::IndexSizeError::create(String::formatted("The radius provided ({}) is negative.", radius));
return DOM::IndexSizeError::create(m_self.global_object(), String::formatted("The radius provided ({}) is negative.", radius));
return ellipse(x, y, radius, radius, 0, start_angle, end_angle, counter_clockwise);
}
DOM::ExceptionOr<void> CanvasPath::ellipse(float x, float y, float radius_x, float radius_y, float rotation, float start_angle, float end_angle, bool counter_clockwise)
{
if (radius_x < 0)
return DOM::IndexSizeError::create(String::formatted("The major-axis radius provided ({}) is negative.", radius_x));
return DOM::IndexSizeError::create(m_self.global_object(), String::formatted("The major-axis radius provided ({}) is negative.", radius_x));
if (radius_y < 0)
return DOM::IndexSizeError::create(String::formatted("The minor-axis radius provided ({}) is negative.", radius_y));
return DOM::IndexSizeError::create(m_self.global_object(), String::formatted("The minor-axis radius provided ({}) is negative.", radius_y));
if (constexpr float tau = M_TAU; (!counter_clockwise && (end_angle - start_angle) >= tau)
|| (counter_clockwise && (start_angle - end_angle) >= tau)) {

View file

@ -30,9 +30,13 @@ public:
Gfx::Path const& path() const { return m_path; }
protected:
CanvasPath() = default;
explicit CanvasPath(Bindings::PlatformObject& self)
: m_self(self)
{
}
private:
Bindings::PlatformObject& m_self;
Gfx::Path m_path;
};

View file

@ -53,14 +53,14 @@ DOM::ExceptionOr<void> CanvasGradient::add_color_stop(double offset, String cons
{
// 1. If the offset is less than 0 or greater than 1, then throw an "IndexSizeError" DOMException.
if (offset < 0 || offset > 1)
return DOM::IndexSizeError::create("CanvasGradient color stop offset out of bounds");
return DOM::IndexSizeError::create(global_object(), "CanvasGradient color stop offset out of bounds");
// 2. Let parsed color be the result of parsing color.
auto parsed_color = Color::from_string(color);
// 3. If parsed color is failure, throw a "SyntaxError" DOMException.
if (!parsed_color.has_value())
return DOM::SyntaxError::create("Could not parse color for CanvasGradient");
return DOM::SyntaxError::create(global_object(), "Could not parse color for CanvasGradient");
// 4. Place a new stop on the gradient, at offset offset relative to the whole gradient, and with the color parsed color.
m_color_stops.append(ColorStop { offset, parsed_color.value() });

View file

@ -29,6 +29,7 @@ JS::NonnullGCPtr<CanvasRenderingContext2D> CanvasRenderingContext2D::create(HTML
CanvasRenderingContext2D::CanvasRenderingContext2D(HTML::Window& window, HTMLCanvasElement& element)
: PlatformObject(window.realm())
, CanvasPath(static_cast<Bindings::PlatformObject&>(*this))
, m_element(element)
{
set_prototype(&window.cached_web_prototype("CanvasRenderingContext2D"));
@ -326,11 +327,11 @@ DOM::ExceptionOr<JS::GCPtr<ImageData>> CanvasRenderingContext2D::get_image_data(
{
// 1. If either the sw or sh arguments are zero, then throw an "IndexSizeError" DOMException.
if (width == 0 || height == 0)
return DOM::IndexSizeError::create("Width and height must not be zero");
return DOM::IndexSizeError::create(global_object(), "Width and height must not be zero");
// 2. If the CanvasRenderingContext2D's origin-clean flag is set to false, then throw a "SecurityError" DOMException.
if (!m_origin_clean)
return DOM::SecurityError::create("CanvasRenderingContext2D is not origin-clean");
return DOM::SecurityError::create(global_object(), "CanvasRenderingContext2D is not origin-clean");
// 3. Let imageData be a new ImageData object.
// 4. Initialize imageData given sw, sh, settings set to settings, and defaultColorSpace set to this's color space.
@ -548,7 +549,7 @@ DOM::ExceptionOr<CanvasImageSourceUsability> check_usability_of_image(CanvasImag
[](JS::Handle<HTMLCanvasElement> const& canvas_element) -> DOM::ExceptionOr<Optional<CanvasImageSourceUsability>> {
// If image has either a horizontal dimension or a vertical dimension equal to zero, then throw an "InvalidStateError" DOMException.
if (canvas_element->width() == 0 || canvas_element->height() == 0)
return DOM::InvalidStateError::create("Canvas width or height is zero");
return DOM::InvalidStateError::create(canvas_element->global_object(), "Canvas width or height is zero");
return Optional<CanvasImageSourceUsability> {};
}));
if (usability.has_value())

View file

@ -126,7 +126,7 @@ DOM::ExceptionOr<void> DOMStringMap::set_value_of_new_named_property(String cons
if (current_character == '-' && character_index + 1 < name.length()) {
auto next_character = name[character_index + 1];
if (is_ascii_lower_alpha(next_character))
return DOM::SyntaxError::create("Name cannot contain a '-' followed by a lowercase character.");
return DOM::SyntaxError::create(global_object(), "Name cannot contain a '-' followed by a lowercase character.");
}
// 2. For each ASCII upper alpha in name, insert a U+002D HYPHEN-MINUS character (-) before the character and replace the character with the same character converted to ASCII lowercase.

View file

@ -104,7 +104,7 @@ DOM::ExceptionOr<void> HTMLElement::set_content_editable(String const& content_e
set_attribute(HTML::AttributeNames::contenteditable, "false");
return {};
}
return DOM::SyntaxError::create("Invalid contentEditable value, must be 'true', 'false', or 'inherit'");
return DOM::SyntaxError::create(global_object(), "Invalid contentEditable value, must be 'true', 'false', or 'inherit'");
}
void HTMLElement::set_inner_text(StringView text)

View file

@ -40,11 +40,11 @@ DOM::ExceptionOr<void> HTMLOptionsCollection::add(HTMLOptionOrOptGroupElement el
// 1. If element is an ancestor of the select element on which the HTMLOptionsCollection is rooted, then throw a "HierarchyRequestError" DOMException.
if (resolved_element->is_ancestor_of(root()))
return DOM::HierarchyRequestError::create("The provided element is an ancestor of the root select element.");
return DOM::HierarchyRequestError::create(global_object(), "The provided element is an ancestor of the root select element.");
// 2. If before is an element, but that element isn't a descendant of the select element on which the HTMLOptionsCollection is rooted, then throw a "NotFoundError" DOMException.
if (before_element && !before_element->is_descendant_of(root()))
return DOM::NotFoundError::create("The 'before' element is not a descendant of the root select element.");
return DOM::NotFoundError::create(global_object(), "The 'before' element is not a descendant of the root select element.");
// 3. If element and before are the same element, then return.
if (before_element && (resolved_element.ptr() == before_element.ptr()))

View file

@ -103,7 +103,7 @@ DOM::ExceptionOr<void> HTMLTableElement::set_t_head(HTMLTableSectionElement* the
VERIFY(thead);
if (thead->local_name() != TagNames::thead)
return DOM::HierarchyRequestError::create("Element is not thead");
return DOM::HierarchyRequestError::create(global_object(), "Element is not thead");
// FIXME: The spec requires deleting the current thead if thead is null
// Currently the wrapper generator doesn't send us a nullable value
@ -190,7 +190,7 @@ DOM::ExceptionOr<void> HTMLTableElement::set_t_foot(HTMLTableSectionElement* tfo
VERIFY(tfoot);
if (tfoot->local_name() != TagNames::tfoot)
return DOM::HierarchyRequestError::create("Element is not tfoot");
return DOM::HierarchyRequestError::create(global_object(), "Element is not tfoot");
// FIXME: The spec requires deleting the current tfoot if tfoot is null
// Currently the wrapper generator doesn't send us a nullable value
@ -286,7 +286,7 @@ DOM::ExceptionOr<JS::NonnullGCPtr<HTMLTableRowElement>> HTMLTableElement::insert
auto rows_length = rows->length();
if (index < -1 || index > (long)rows_length) {
return DOM::IndexSizeError::create("Index is negative or greater than the number of rows");
return DOM::IndexSizeError::create(global_object(), "Index is negative or greater than the number of rows");
}
auto& tr = static_cast<HTMLTableRowElement&>(*DOM::create_element(document(), TagNames::tr, Namespace::HTML));
if (rows_length == 0 && !has_child_of_type<HTMLTableRowElement>()) {
@ -313,7 +313,7 @@ DOM::ExceptionOr<void> HTMLTableElement::delete_row(long index)
// 1. If index is less than 1 or greater than or equal to the number of elements in the rows collection, then throw an "IndexSizeError" DOMException.
if (index < -1 || index >= (long)rows_length)
return DOM::IndexSizeError::create("Index is negative or greater than or equal to the number of rows");
return DOM::IndexSizeError::create(global_object(), "Index is negative or greater than or equal to the number of rows");
// 2. If index is 1, then remove the last element in the rows collection from its parent, or do nothing if the rows collection is empty.
if (index == -1) {

View file

@ -43,7 +43,7 @@ DOM::ExceptionOr<JS::NonnullGCPtr<HTMLTableRowElement>> HTMLTableSectionElement:
// 1. If index is less than 1 or greater than the number of elements in the rows collection, throw an "IndexSizeError" DOMException.
if (index < -1 || index > rows_collection_size)
return DOM::IndexSizeError::create("Index is negative or greater than the number of rows");
return DOM::IndexSizeError::create(global_object(), "Index is negative or greater than the number of rows");
// 2. Let table row be the result of creating an element given this element's node document, tr, and the HTML namespace.
auto& table_row = static_cast<HTMLTableRowElement&>(*DOM::create_element(document(), TagNames::tr, Namespace::HTML));
@ -67,7 +67,7 @@ DOM::ExceptionOr<void> HTMLTableSectionElement::delete_row(long index)
// 1. If index is less than 1 or greater than or equal to the number of elements in the rows collection, then throw an "IndexSizeError" DOMException.
if (index < -1 || index >= rows_collection_size)
return DOM::IndexSizeError::create("Index is negative or greater than or equal to the number of rows");
return DOM::IndexSizeError::create(global_object(), "Index is negative or greater than or equal to the number of rows");
// 2. If index is 1, then remove the last element in the rows collection from this element, or do nothing if the rows collection is empty.
if (index == -1) {

View file

@ -50,7 +50,7 @@ DOM::ExceptionOr<void> History::shared_history_push_replace_state(JS::Value, Str
// 2. If document is not fully active, then throw a "SecurityError" DOMException.
if (!m_associated_document->is_fully_active())
return DOM::SecurityError::create("Cannot perform pushState or replaceState on a document that isn't fully active.");
return DOM::SecurityError::create(global_object(), "Cannot perform pushState or replaceState on a document that isn't fully active.");
// 3. Optionally, return. (For example, the user agent might disallow calls to these methods that are invoked on a timer,
// or from event listeners that are not triggered in response to a clear user action, or that are invoked in rapid succession.)

View file

@ -17,6 +17,7 @@ JS::NonnullGCPtr<Path2D> Path2D::create_with_global_object(HTML::Window& window,
// https://html.spec.whatwg.org/multipage/canvas.html#dom-path2d
Path2D::Path2D(HTML::Window& window, Optional<Variant<JS::Handle<Path2D>, String>> const& path)
: PlatformObject(window.realm())
, CanvasPath(static_cast<Bindings::PlatformObject&>(*this))
{
set_prototype(&window.cached_web_prototype("Path2D"));

View file

@ -109,6 +109,7 @@ JS::Completion ClassicScript::run(RethrowErrors rethrow_errors)
// 1. Clean up after running script with settings.
settings.clean_up_after_running_script();
dbgln("rethrow");
// 2. Rethrow evaluationStatus.[[Value]].
return JS::throw_completion(*evaluation_status.value());
}
@ -118,15 +119,17 @@ JS::Completion ClassicScript::run(RethrowErrors rethrow_errors)
// 1. Clean up after running script with settings.
settings.clean_up_after_running_script();
dbgln("network error");
// 2. Throw a "NetworkError" DOMException.
return Bindings::throw_dom_exception_if_needed(vm, [] {
return DOM::NetworkError::create("Script error.");
}).release_error();
return throw_completion(DOM::NetworkError::create(settings.global_object(), "Script error."));
}
// 3. Otherwise, rethrow errors is false. Perform the following steps:
VERIFY(rethrow_errors == RethrowErrors::No);
dbgln("no rethrow, stat: {}", evaluation_status.value().value().to_string_without_side_effects());
// 1. Report the exception given by evaluationStatus.[[Value]] for script.
report_exception(evaluation_status);

View file

@ -1067,11 +1067,8 @@ JS_DEFINE_NATIVE_FUNCTION(Window::btoa)
Vector<u8> byte_string;
byte_string.ensure_capacity(string.length());
for (u32 code_point : Utf8View(string)) {
if (code_point > 0xff) {
return Bindings::throw_dom_exception_if_needed(vm, [] {
return DOM::InvalidCharacterError::create("Data contains characters outside the range U+0000 and U+00FF");
}).release_error();
}
if (code_point > 0xff)
return throw_completion(DOM::InvalidCharacterError::create(vm.current_realm()->global_object(), "Data contains characters outside the range U+0000 and U+00FF"));
byte_string.append(code_point);
}

View file

@ -65,7 +65,7 @@ DOM::ExceptionOr<JS::NonnullGCPtr<Worker>> Worker::create(FlyString const& scrip
// 4. If this fails, throw a "SyntaxError" DOMException.
if (!url.is_valid()) {
dbgln_if(WEB_WORKER_DEBUG, "WebWorker: Invalid URL loaded '{}'.", script_url);
return DOM::SyntaxError::create("url is not valid");
return DOM::SyntaxError::create(document.global_object(), "url is not valid");
}
// 5. Let worker URL be the resulting URL record.

View file

@ -14,6 +14,7 @@
#include <LibWeb/Forward.h>
#include <LibWeb/HTML/EventHandler.h>
#include <LibWeb/HTML/EventNames.h>
#include <LibWeb/HTML/Window.h>
#include <LibWeb/HTML/WorkerGlobalScope.h>
#include <LibWeb/HTML/WorkerLocation.h>
#include <LibWeb/HTML/WorkerNavigator.h>
@ -129,7 +130,7 @@ DOM::ExceptionOr<String> WorkerGlobalScope::btoa(String const& data) const
byte_string.ensure_capacity(data.length());
for (u32 code_point : Utf8View(data)) {
if (code_point > 0xff)
return DOM::InvalidCharacterError::create("Data contains characters outside the range U+0000 and U+00FF");
return DOM::InvalidCharacterError::create(global_object(), "Data contains characters outside the range U+0000 and U+00FF");
byte_string.append(code_point);
}
@ -149,7 +150,7 @@ DOM::ExceptionOr<String> WorkerGlobalScope::atob(String const& data) const
// 2. If decodedData is failure, then throw an "InvalidCharacterError" DOMException.
if (decoded_data.is_error())
return DOM::InvalidCharacterError::create("Input string is not valid base64 data");
return DOM::InvalidCharacterError::create(global_object(), "Input string is not valid base64 data");
// 3. Return decodedData.
// decode_base64() returns a byte string. LibJS uses UTF-8 for strings. Use Latin1Decoder to convert bytes 128-255 to UTF-8.

View file

@ -51,11 +51,11 @@ DOM::ExceptionOr<JS::NonnullGCPtr<WebSocket>> WebSocket::create_with_global_obje
{
AK::URL url_record(url);
if (!url_record.is_valid())
return DOM::SyntaxError::create("Invalid URL");
return DOM::SyntaxError::create(window, "Invalid URL");
if (!url_record.protocol().is_one_of("ws", "wss"))
return DOM::SyntaxError::create("Invalid protocol");
return DOM::SyntaxError::create(window, "Invalid protocol");
if (!url_record.fragment().is_empty())
return DOM::SyntaxError::create("Presence of URL fragment is invalid");
return DOM::SyntaxError::create(window, "Presence of URL fragment is invalid");
// 5. If `protocols` is a string, set `protocols` to a sequence consisting of just that string
// 6. If any of the values in `protocols` occur more than once or otherwise fail to match the requirements, throw SyntaxError
return JS::NonnullGCPtr(*window.heap().allocate<WebSocket>(window.realm(), window.impl(), url_record));
@ -137,13 +137,13 @@ DOM::ExceptionOr<void> WebSocket::close(Optional<u16> code, Optional<String> rea
{
// 1. If code is present, but is neither an integer equal to 1000 nor an integer in the range 3000 to 4999, inclusive, throw an "InvalidAccessError" DOMException.
if (code.has_value() && *code != 1000 && (*code < 3000 || *code > 4099))
return DOM::InvalidAccessError::create("The close error code is invalid");
return DOM::InvalidAccessError::create(global_object(), "The close error code is invalid");
// 2. If reason is present, then run these substeps:
if (reason.has_value()) {
// 1. Let reasonBytes be the result of encoding reason.
// 2. If reasonBytes is longer than 123 bytes, then throw a "SyntaxError" DOMException.
if (reason->bytes().size() > 123)
return DOM::SyntaxError::create("The close reason is longer than 123 bytes");
return DOM::SyntaxError::create(global_object(), "The close reason is longer than 123 bytes");
}
// 3. Run the first matching steps from the following list:
auto state = ready_state();
@ -164,7 +164,7 @@ DOM::ExceptionOr<void> WebSocket::send(String const& data)
{
auto state = ready_state();
if (state == WebSocket::ReadyState::Connecting)
return DOM::InvalidStateError::create("Websocket is still CONNECTING");
return DOM::InvalidStateError::create(global_object(), "Websocket is still CONNECTING");
if (state == WebSocket::ReadyState::Open) {
m_websocket->send(data);
// TODO : If the data cannot be sent, e.g. because it would need to be buffered but the buffer is full, the user agent must flag the WebSocket as full and then close the WebSocket connection.

View file

@ -84,7 +84,7 @@ DOM::ExceptionOr<String> XMLHttpRequest::response_text() const
{
// 1. If thiss response type is not the empty string or "text", then throw an "InvalidStateError" DOMException.
if (m_response_type != Bindings::XMLHttpRequestResponseType::Empty && m_response_type != Bindings::XMLHttpRequestResponseType::Text)
return DOM::InvalidStateError::create("XHR responseText can only be used for responseType \"\" or \"text\"");
return DOM::InvalidStateError::create(global_object(), "XHR responseText can only be used for responseType \"\" or \"text\"");
// 2. If thiss state is not loading or done, then return the empty string.
if (m_ready_state != ReadyState::Loading && m_ready_state != ReadyState::Done)
@ -334,20 +334,20 @@ DOM::ExceptionOr<void> XMLHttpRequest::set_request_header(String const& name_str
// 1. If thiss state is not opened, then throw an "InvalidStateError" DOMException.
if (m_ready_state != ReadyState::Opened)
return DOM::InvalidStateError::create("XHR readyState is not OPENED");
return DOM::InvalidStateError::create(global_object(), "XHR readyState is not OPENED");
// 2. If thiss send() flag is set, then throw an "InvalidStateError" DOMException.
if (m_send)
return DOM::InvalidStateError::create("XHR send() flag is already set");
return DOM::InvalidStateError::create(global_object(), "XHR send() flag is already set");
// 3. Normalize value.
value = MUST(Fetch::Infrastructure::normalize_header_value(value));
// 4. If name is not a header name or value is not a header value, then throw a "SyntaxError" DOMException.
if (!Fetch::Infrastructure::is_header_name(name))
return DOM::SyntaxError::create("Header name contains invalid characters.");
return DOM::SyntaxError::create(global_object(), "Header name contains invalid characters.");
if (!Fetch::Infrastructure::is_header_value(value))
return DOM::SyntaxError::create("Header value contains invalid characters.");
return DOM::SyntaxError::create(global_object(), "Header value contains invalid characters.");
// 5. If name is a forbidden header name, then return.
if (Fetch::Infrastructure::is_forbidden_header_name(name))
@ -385,15 +385,15 @@ DOM::ExceptionOr<void> XMLHttpRequest::open(String const& method_string, String
// 2. If settingsObject has a responsible document and it is not fully active, then throw an "InvalidStateError" DOMException.
if (settings_object.responsible_document() && !settings_object.responsible_document()->is_active())
return DOM::InvalidStateError::create("Invalid state: Responsible document is not fully active.");
return DOM::InvalidStateError::create(global_object(), "Invalid state: Responsible document is not fully active.");
// 3. If method is not a method, then throw a "SyntaxError" DOMException.
if (!Fetch::Infrastructure::is_method(method))
return DOM::SyntaxError::create("An invalid or illegal string was specified.");
return DOM::SyntaxError::create(global_object(), "An invalid or illegal string was specified.");
// 4. If method is a forbidden method, then throw a "SecurityError" DOMException.
if (Fetch::Infrastructure::is_forbidden_method(method))
return DOM::SecurityError::create("Forbidden method, must not be 'CONNECT', 'TRACE', or 'TRACK'");
return DOM::SecurityError::create(global_object(), "Forbidden method, must not be 'CONNECT', 'TRACE', or 'TRACK'");
// 5. Normalize method.
method = MUST(Fetch::Infrastructure::normalize_method(method));
@ -403,7 +403,7 @@ DOM::ExceptionOr<void> XMLHttpRequest::open(String const& method_string, String
// 7. If parsedURL is failure, then throw a "SyntaxError" DOMException.
if (!parsed_url.is_valid())
return DOM::SyntaxError::create("Invalid URL");
return DOM::SyntaxError::create(global_object(), "Invalid URL");
// 8. If the async argument is omitted, set async to true, and set username and password to null.
// NOTE: This is handled in the overload lacking the async argument.
@ -456,16 +456,16 @@ DOM::ExceptionOr<void> XMLHttpRequest::open(String const& method_string, String
DOM::ExceptionOr<void> XMLHttpRequest::send(Optional<XMLHttpRequestBodyInit> body)
{
if (m_ready_state != ReadyState::Opened)
return DOM::InvalidStateError::create("XHR readyState is not OPENED");
return DOM::InvalidStateError::create(global_object(), "XHR readyState is not OPENED");
if (m_send)
return DOM::InvalidStateError::create("XHR send() flag is already set");
return DOM::InvalidStateError::create(global_object(), "XHR send() flag is already set");
// If thiss request method is `GET` or `HEAD`, then set body to null.
if (m_method.is_one_of("GET"sv, "HEAD"sv))
body = {};
auto body_with_type = body.has_value() ? TRY_OR_RETURN_OOM(extract_body(body.value())) : Optional<Fetch::Infrastructure::BodyWithType> {};
auto body_with_type = body.has_value() ? TRY_OR_RETURN_OOM(global_object(), extract_body(body.value())) : Optional<Fetch::Infrastructure::BodyWithType> {};
AK::URL request_url = m_window->associated_document().parse_url(m_url.to_string());
dbgln("XHR send from {} to {}", m_window->associated_document().url(), request_url);
@ -487,19 +487,12 @@ DOM::ExceptionOr<void> XMLHttpRequest::send(Optional<XMLHttpRequestBodyInit> bod
auto request = LoadRequest::create_for_url_on_page(request_url, m_window->page());
request.set_method(m_method);
if (body_with_type.has_value()) {
TRY_OR_RETURN_OOM(body_with_type->body.source().visit(
[&](ByteBuffer const& buffer) -> ErrorOr<void> {
TRY_OR_RETURN_OOM(global_object(), body_with_type->body.source().visit([&](ByteBuffer const& buffer) -> ErrorOr<void> {
request.set_body(buffer);
return {};
},
[&](JS::Handle<FileAPI::Blob> const& blob) -> ErrorOr<void> {
return {}; }, [&](JS::Handle<FileAPI::Blob> const& blob) -> ErrorOr<void> {
auto byte_buffer = TRY(ByteBuffer::copy(blob->bytes()));
request.set_body(byte_buffer);
return {};
},
[](auto&) -> ErrorOr<void> {
return {};
}));
return {}; }, [](auto&) -> ErrorOr<void> { return {}; }));
if (body_with_type->type.has_value())
request.set_header("Content-Type", String { body_with_type->type->span() });
}
@ -611,7 +604,7 @@ DOM::ExceptionOr<void> XMLHttpRequest::override_mime_type(String const& mime)
{
// 1. If thiss state is loading or done, then throw an "InvalidStateError" DOMException.
if (m_ready_state == ReadyState::Loading || m_ready_state == ReadyState::Done)
return DOM::InvalidStateError::create("Cannot override MIME type when state is Loading or Done.");
return DOM::InvalidStateError::create(global_object(), "Cannot override MIME type when state is Loading or Done.");
// 2. Set thiss override MIME type to the result of parsing mime.
m_override_mime_type = MimeSniff::MimeType::from_string(mime);
@ -629,7 +622,7 @@ DOM::ExceptionOr<void> XMLHttpRequest::set_timeout(u32 timeout)
// 1. If the current global object is a Window object and thiss synchronous flag is set,
// then throw an "InvalidAccessError" DOMException.
if (is<HTML::Window>(HTML::current_global_object()) && m_synchronous)
return DOM::InvalidAccessError::create("Use of XMLHttpRequest's timeout attribute is not supported in the synchronous mode in window context.");
return DOM::InvalidAccessError::create(global_object(), "Use of XMLHttpRequest's timeout attribute is not supported in the synchronous mode in window context.");
// 2. Set thiss timeout to the given value.
m_timeout = timeout;

View file

@ -31,7 +31,7 @@ libweb_js_wrapper(DOM/CustomEvent NO_INSTANCE)
libweb_js_wrapper(DOM/Document NO_INSTANCE)
libweb_js_wrapper(DOM/DocumentFragment NO_INSTANCE)
libweb_js_wrapper(DOM/DocumentType NO_INSTANCE)
libweb_js_wrapper(DOM/DOMException)
libweb_js_wrapper(DOM/DOMException NO_INSTANCE)
libweb_js_wrapper(DOM/DOMImplementation NO_INSTANCE)
libweb_js_wrapper(DOM/DOMTokenList NO_INSTANCE)
libweb_js_wrapper(DOM/Element NO_INSTANCE)