File
Blob: src/workerd/jsg/dom-exception.h
| 1 | // Copyright (c) 2017-2022 Cloudflare, Inc. |
| 2 | // Licensed under the Apache 2.0 license found in the LICENSE file or at: |
| 3 | // https://opensource.org/licenses/Apache-2.0 |
| 4 | |
| 5 | #pragma once |
| 6 | |
| 7 | #include "jsg.h" |
| 8 | |
| 9 | #define JSG_DOM_EXCEPTION_FOR_EACH_ERROR_NAME(f) \ |
| 10 | f(INDEX_SIZE_ERR, 1, "IndexSizeError") f(DOMSTRING_SIZE_ERR, 2, "DOMStringSizeError") \ |
| 11 | f(HIERARCHY_REQUEST_ERR, 3, "HierarchyRequestError") f(WRONG_DOCUMENT_ERR, 4, \ |
| 12 | "WrongDocumentError") f(INVALID_CHARACTER_ERR, 5, "InvalidCharacterError") \ |
| 13 | f(NO_DATA_ALLOWED_ERR, 6, "NoDataAllowedError") \ |
| 14 | f(NO_MODIFICATION_ALLOWED_ERR, 7, "NoModificationAllowedError") f( \ |
| 15 | NOT_FOUND_ERR, 8, "NotFoundError") f(NOT_SUPPORTED_ERR, 9, "NotSupportedError") \ |
| 16 | f(INUSE_ATTRIBUTE_ERR, 10, "InUseAttributeError") f( \ |
| 17 | INVALID_STATE_ERR, 11, "InvalidStateError") f(SYNTAX_ERR, 12, "SyntaxError") \ |
| 18 | f(INVALID_MODIFICATION_ERR, 13, "InvalidModificationError") f(NAMESPACE_ERR, \ |
| 19 | 14, "NamespaceError") f(INVALID_ACCESS_ERR, 15, "InvalidAccessError") \ |
| 20 | f(VALIDATION_ERR, 16, "ValidationError") f(TYPE_MISMATCH_ERR, 17, \ |
| 21 | "TypeMismatchError") f(SECURITY_ERR, 18, "SecurityError") \ |
| 22 | f(NETWORK_ERR, 19, "NetworkError") f(ABORT_ERR, 20, "AbortError") \ |
| 23 | f(URL_MISMATCH_ERR, 21, "URLMismatchError") \ |
| 24 | f(QUOTA_EXCEEDED_ERR, 22, "QuotaExceededError") \ |
| 25 | f(TIMEOUT_ERR, 23, "TimeoutError") \ |
| 26 | f(INVALID_NODE_TYPE_ERR, 24, "InvalidNodeTypeError") \ |
| 27 | f(DATA_CLONE_ERR, 25, "DataCloneError") |
| 28 | |
| 29 | namespace workerd::jsg { |
| 30 | class Serializer; |
| 31 | class Deserializer; |
| 32 | |
| 33 | // JSG allows DOMExceptions to be tunneled through kj::Exceptions (see exceptionToJs() for |
| 34 | // details). While this feature is activated conditionally at run-time, and thus does not depend |
| 35 | // on any specific concrete C++ type, JSG needs to be able to unit test the tunneled exception |
| 36 | // functionality, thus the existence of this implementation. |
| 37 | // |
| 38 | // Note that DOMException is currently the only user-defined exception to get this special |
| 39 | // treatment because it is the only non-builtin JS exception that standard web APIs are allowed to |
| 40 | // throw, per Web IDL. |
| 41 | // |
| 42 | // Users of JSG are free (and encouraged) to use this implementation, but they can also opt into |
| 43 | // the same tunneled exception feature by defining their own globally-accessible type named |
| 44 | // "DOMException". |
| 45 | class DOMException: public Object { |
| 46 | public: |
| 47 | DOMException(kj::String message, kj::String name): message(kj::mv(message)), name(kj::mv(name)) {} |
| 48 | |
| 49 | // JS API |
| 50 | |
| 51 | static Ref<DOMException> constructor(const v8::FunctionCallbackInfo<v8::Value>& args, |
| 52 | Optional<kj::String> message, |
| 53 | Optional<kj::String> name); |
| 54 | |
| 55 | kj::StringPtr getName() const; |
| 56 | kj::StringPtr getMessage() const; |
| 57 | int getCode() const; |
| 58 | |
| 59 | #define JSG_DOM_EXCEPTION_CONSTANT_CXX(name, code, friendlyName) static constexpr int name = code; |
| 60 | #define JSG_DOM_EXCEPTION_CONSTANT_JS(name, code, friendlyName) JSG_STATIC_CONSTANT(name); |
| 61 | |
| 62 | // Define constexpr codes for every INDEX_SIZE_ERR, DOMSTRING_SIZE_ERR, etc. |
| 63 | JSG_DOM_EXCEPTION_FOR_EACH_ERROR_NAME(JSG_DOM_EXCEPTION_CONSTANT_CXX) |
| 64 | |
| 65 | JSG_RESOURCE_TYPE(DOMException) { |
| 66 | JSG_INHERIT_INTRINSIC(v8::kErrorPrototype); |
| 67 | |
| 68 | // TODO(conform): Per the spec, these should be prototype properties |
| 69 | // and not instance properties. Fixing this does require use of the |
| 70 | // flags.getJsgPropertyOnPrototypeTemplate() compatibility flag. |
| 71 | // The standard definition of DOMException can be found here: |
| 72 | // https://webidl.spec.whatwg.org/#idl-DOMException |
| 73 | JSG_LAZY_READONLY_INSTANCE_PROPERTY(message, getMessage); |
| 74 | JSG_LAZY_READONLY_INSTANCE_PROPERTY(name, getName); |
| 75 | JSG_LAZY_READONLY_INSTANCE_PROPERTY(code, getCode); |
| 76 | |
| 77 | // Declare static JS constants for every INDEX_SIZE_ERR, DOMSTRING_SIZE_ERR, etc. |
| 78 | JSG_DOM_EXCEPTION_FOR_EACH_ERROR_NAME(JSG_DOM_EXCEPTION_CONSTANT_JS) |
| 79 | |
| 80 | JSG_TS_OVERRIDE({ |
| 81 | get stack(): any; |
| 82 | set stack(value: any); |
| 83 | }); |
| 84 | } |
| 85 | JSG_REFLECTION(stack); |
| 86 | |
| 87 | #undef JSG_DOM_EXCEPTION_CONSTANT_CXX |
| 88 | #undef JSG_DOM_EXCEPTION_CONSTANT_JS |
| 89 | |
| 90 | void visitForMemoryInfo(MemoryTracker& tracker) const { |
| 91 | tracker.trackField("message", message); |
| 92 | tracker.trackField("name", name); |
| 93 | } |
| 94 | |
| 95 | // TODO(cleanup): The value is taken from worker-interface.capnp, which we can't |
| 96 | // depend on directly here because we cannot introduce the dependency into JSG. |
| 97 | // Therefore we have to set it manually. A better solution long term is to actually |
| 98 | // move DOMException into workerd/api, but we'll do that separately. |
| 99 | static constexpr uint SERIALIZATION_TAG = 7; |
| 100 | static constexpr uint SERIALIZATION_TAG_V2 = 8; |
| 101 | JSG_SERIALIZABLE(SERIALIZATION_TAG_V2, SERIALIZATION_TAG); |
| 102 | |
| 103 | void serialize(jsg::Lock& js, jsg::Serializer& serializer); |
| 104 | static jsg::Ref<DOMException> deserialize( |
| 105 | jsg::Lock& js, uint tag, jsg::Deserializer& deserializer); |
| 106 | |
| 107 | private: |
| 108 | kj::String message; |
| 109 | kj::String name; |
| 110 | PropertyReflection<kj::String> stack; |
| 111 | }; |
| 112 | |
| 113 | } // namespace workerd::jsg |