From 3449e36ded6a30db4267465e9645a08de81aa3dd Mon Sep 17 00:00:00 2001 From: "chenghuai.dtc" Date: Tue, 11 Jan 2022 21:35:12 +0800 Subject: [PATCH] refactor: refactor node, element and document. --- bridge/bindings/qjs/bom/window.cc | 12 +- bridge/bindings/qjs/bom/window.h | 9 +- bridge/bindings/qjs/dom/document.cc | 263 +++++++------- bridge/bindings/qjs/dom/document.h | 121 +++---- bridge/bindings/qjs/dom/document_fragment.cc | 31 +- bridge/bindings/qjs/dom/document_fragment.h | 28 +- bridge/bindings/qjs/dom/element.cc | 228 +++++++----- bridge/bindings/qjs/dom/element.h | 121 +++---- bridge/bindings/qjs/dom/event_target.cc | 27 +- bridge/bindings/qjs/dom/event_target.h | 17 +- bridge/bindings/qjs/dom/node.cc | 344 ++++++++++--------- bridge/bindings/qjs/dom/node.h | 117 +++---- bridge/bindings/qjs/dom/text_node.cc | 61 ++-- bridge/bindings/qjs/dom/text_node.h | 45 +-- bridge/bindings/qjs/executing_context.h | 17 +- bridge/bindings/qjs/js_context_macros.h | 8 - 16 files changed, 750 insertions(+), 699 deletions(-) diff --git a/bridge/bindings/qjs/bom/window.cc b/bridge/bindings/qjs/bom/window.cc index d83d6af16f..9e79e35950 100644 --- a/bridge/bindings/qjs/bom/window.cc +++ b/bridge/bindings/qjs/bom/window.cc @@ -162,10 +162,18 @@ IMPL_FUNCTION(Window, cancelAnimationFrame)(JSContext* ctx, JSValue this_val, in } Window* Window::create(JSContext* ctx) { - return makeGarbageCollected()->initialize(ctx, &classId, nullptr); + auto* context = static_cast(JS_GetContextOpaque(ctx)); + JSValue prototype = context->contextData()->prototypeForType(&windowTypeInfo); + + auto* window = makeGarbageCollected()->initialize(ctx, &classId, nullptr); + + // Let window inherit Window prototype methods. + JS_SetPrototype(ctx, window->toQuickJS(), prototype); + + return window; } -DocumentInstance* Window::document() { +Document* Window::document() { return context()->document(); } diff --git a/bridge/bindings/qjs/bom/window.h b/bridge/bindings/qjs/bom/window.h index 7c65081c00..2d5fe54e7e 100644 --- a/bridge/bindings/qjs/bom/window.h +++ b/bridge/bindings/qjs/bom/window.h @@ -45,7 +45,7 @@ class Window : public EventTarget { void trace(JSRuntime *rt, JSValue val, JS_MarkFunc *mark_func) const override; private: - DocumentInstance* document(); + Document* document(); Location* m_location{nullptr}; JSValue onerror{JS_NULL}; @@ -53,14 +53,7 @@ class Window : public EventTarget { }; auto windowCreator = [](JSContext* ctx, JSValueConst func_obj, JSValueConst this_val, int argc, JSValueConst* argv, int flags) -> JSValue { - - auto* type = static_cast(JS_GetOpaque(func_obj, JSValueGetClassId(func_obj))); auto* window = Window::create(ctx); - auto* context = static_cast(JS_GetContextOpaque(ctx)); - JSValue prototype = context->contextData()->prototypeForType(type); - - // Let eventTarget instance inherit EventTarget prototype methods. - JS_SetPrototype(ctx, window->toQuickJS(), prototype); return window->toQuickJS(); }; diff --git a/bridge/bindings/qjs/dom/document.cc b/bridge/bindings/qjs/dom/document.cc index abebe9d9e9..ad55cc3e68 100644 --- a/bridge/bindings/qjs/dom/document.cc +++ b/bridge/bindings/qjs/dom/document.cc @@ -34,21 +34,21 @@ namespace kraken::binding::qjs { -void traverseNode(NodeInstance* node, TraverseHandler handler) { +void traverseNode(Node* node, TraverseHandler handler) { bool shouldExit = handler(node); if (shouldExit) return; - JSContext* ctx = node->context()->ctx(); + JSContext* ctx = node->ctx(); int childNodesLen = arrayGetLength(ctx, node->childNodes); if (childNodesLen != 0) { for (int i = 0; i < childNodesLen; i++) { JSValue n = JS_GetPropertyUint32(ctx, node->childNodes, i); - auto* nextNode = static_cast(JS_GetOpaque(n, Node::classId(n))); + auto* nextNode = static_cast(JS_GetOpaque(n, JSValueGetClassId(n))); traverseNode(nextNode, handler); - JS_FreeValue(node->context()->ctx(), n); + JS_FreeValue(node->ctx(), n); } } } @@ -56,88 +56,88 @@ void traverseNode(NodeInstance* node, TraverseHandler handler) { std::once_flag kDocumentInitOnceFlag; void bindDocument(std::unique_ptr& context) { - auto* documentConstructor = Document::instance(context.get()); - context->defineGlobalProperty("Document", documentConstructor->jsObject); - JSValue documentInstance = JS_CallConstructor(context->ctx(), documentConstructor->jsObject, 0, nullptr); - context->defineGlobalProperty("document", documentInstance); + +// auto* documentConstructor = Document::instance(context.get()); +// context->defineGlobalProperty("Document", documentConstructor->jsObject); +// JSValue documentInstance = JS_CallConstructor(context->ctx(), documentConstructor->jsObject, 0, nullptr); +// context->defineGlobalProperty("document", documentInstance); } -JSClassID Document::kDocumentClassID{0}; +JSClassID Document::classId{0}; + +Document* Document::create(JSContext* ctx) { + auto* context = static_cast(JS_GetContextOpaque(ctx)); + JSValue prototype = context->contextData()->prototypeForType(&eventTargetTypeInfo); + auto* document = makeGarbageCollected()->initialize(ctx, &Document::classId, nullptr); + + JS_SetPrototype(ctx, document->toQuickJS(), prototype); -Document::Document(ExecutionContext* context) : Node(context, "Document") { - std::call_once(kDocumentInitOnceFlag, []() { JS_NewClassID(&kDocumentClassID); }); - JS_SetPrototype(m_ctx, m_prototypeObject, Node::instance(m_context)->prototype()); + return document; +} + +Document::Document() : Node() { if (!document_registered) { - defineElement("img", ImageElement::instance(m_context)); - defineElement("a", AnchorElement::instance(m_context)); - defineElement("canvas", CanvasElement::instance(m_context)); - defineElement("input", InputElement::instance(m_context)); - defineElement("object", ObjectElement::instance(m_context)); - defineElement("script", ScriptElement::instance(m_context)); - defineElement("template", TemplateElement::instance(m_context)); +// defineElement("img", ImageElement::instance(m_context)); +// defineElement("a", AnchorElement::instance(m_context)); +// defineElement("canvas", CanvasElement::instance(m_context)); +// defineElement("input", InputElement::instance(m_context)); +// defineElement("object", ObjectElement::instance(m_context)); +// defineElement("script", ScriptElement::instance(m_context)); +// defineElement("template", TemplateElement::instance(m_context)); document_registered = true; } if (!event_registered) { event_registered = true; - Event::defineEvent( - EVENT_INPUT, [](ExecutionContext* context, void* nativeEvent) -> EventInstance* { return new InputEventInstance(InputEvent::instance(context), reinterpret_cast(nativeEvent)); }); - Event::defineEvent(EVENT_MEDIA_ERROR, [](ExecutionContext* context, void* nativeEvent) -> EventInstance* { - return new MediaErrorEventInstance(MediaErrorEvent::instance(context), reinterpret_cast(nativeEvent)); - }); - Event::defineEvent(EVENT_MESSAGE, [](ExecutionContext* context, void* nativeEvent) -> EventInstance* { - return new MessageEventInstance(MessageEvent::instance(context), reinterpret_cast(nativeEvent)); - }); - Event::defineEvent( - EVENT_CLOSE, [](ExecutionContext* context, void* nativeEvent) -> EventInstance* { return new CloseEventInstance(CloseEvent::instance(context), reinterpret_cast(nativeEvent)); }); - Event::defineEvent(EVENT_INTERSECTION_CHANGE, [](ExecutionContext* context, void* nativeEvent) -> EventInstance* { - return new IntersectionChangeEventInstance(IntersectionChangeEvent::instance(context), reinterpret_cast(nativeEvent)); - }); - Event::defineEvent(EVENT_TOUCH_START, [](ExecutionContext* context, void* nativeEvent) -> EventInstance* { - return new TouchEventInstance(TouchEvent::instance(context), reinterpret_cast(nativeEvent)); - }); - Event::defineEvent(EVENT_TOUCH_END, [](ExecutionContext* context, void* nativeEvent) -> EventInstance* { - return new TouchEventInstance(TouchEvent::instance(context), reinterpret_cast(nativeEvent)); - }); - Event::defineEvent(EVENT_TOUCH_MOVE, [](ExecutionContext* context, void* nativeEvent) -> EventInstance* { - return new TouchEventInstance(TouchEvent::instance(context), reinterpret_cast(nativeEvent)); - }); - Event::defineEvent(EVENT_TOUCH_CANCEL, [](ExecutionContext* context, void* nativeEvent) -> EventInstance* { - return new TouchEventInstance(TouchEvent::instance(context), reinterpret_cast(nativeEvent)); - }); - Event::defineEvent(EVENT_SWIPE, [](ExecutionContext* context, void* nativeEvent) -> EventInstance* { - return new GestureEventInstance(GestureEvent::instance(context), reinterpret_cast(nativeEvent)); - }); - Event::defineEvent(EVENT_PAN, [](ExecutionContext* context, void* nativeEvent) -> EventInstance* { - return new GestureEventInstance(GestureEvent::instance(context), reinterpret_cast(nativeEvent)); - }); - Event::defineEvent(EVENT_LONG_PRESS, [](ExecutionContext* context, void* nativeEvent) -> EventInstance* { - return new GestureEventInstance(GestureEvent::instance(context), reinterpret_cast(nativeEvent)); - }); - Event::defineEvent(EVENT_SCALE, [](ExecutionContext* context, void* nativeEvent) -> EventInstance* { - return new GestureEventInstance(GestureEvent::instance(context), reinterpret_cast(nativeEvent)); - }); - Event::defineEvent( - EVENT_CLICK, [](ExecutionContext* context, void* nativeEvent) -> EventInstance* { return new MouseEventInstance(MouseEvent::instance(context), reinterpret_cast(nativeEvent)); }); - Event::defineEvent(EVENT_CANCEL, [](ExecutionContext* context, void* nativeEvent) -> EventInstance* { - return new MouseEventInstance(MouseEvent::instance(context), reinterpret_cast(nativeEvent)); - }); - Event::defineEvent(EVENT_POPSTATE, [](ExecutionContext* context, void* nativeEvent) -> EventInstance* { - return new PopStateEventInstance(PopStateEvent::instance(context), reinterpret_cast(nativeEvent)); - }); +// Event::defineEvent( +// EVENT_INPUT, [](ExecutionContext* context, void* nativeEvent) -> EventInstance* { return new InputEventInstance(InputEvent::instance(context), reinterpret_cast(nativeEvent)); }); +// Event::defineEvent(EVENT_MEDIA_ERROR, [](ExecutionContext* context, void* nativeEvent) -> EventInstance* { +// return new MediaErrorEventInstance(MediaErrorEvent::instance(context), reinterpret_cast(nativeEvent)); +// }); +// Event::defineEvent(EVENT_MESSAGE, [](ExecutionContext* context, void* nativeEvent) -> EventInstance* { +// return new MessageEventInstance(MessageEvent::instance(context), reinterpret_cast(nativeEvent)); +// }); +// Event::defineEvent( +// EVENT_CLOSE, [](ExecutionContext* context, void* nativeEvent) -> EventInstance* { return new CloseEventInstance(CloseEvent::instance(context), reinterpret_cast(nativeEvent)); }); +// Event::defineEvent(EVENT_INTERSECTION_CHANGE, [](ExecutionContext* context, void* nativeEvent) -> EventInstance* { +// return new IntersectionChangeEventInstance(IntersectionChangeEvent::instance(context), reinterpret_cast(nativeEvent)); +// }); +// Event::defineEvent(EVENT_TOUCH_START, [](ExecutionContext* context, void* nativeEvent) -> EventInstance* { +// return new TouchEventInstance(TouchEvent::instance(context), reinterpret_cast(nativeEvent)); +// }); +// Event::defineEvent(EVENT_TOUCH_END, [](ExecutionContext* context, void* nativeEvent) -> EventInstance* { +// return new TouchEventInstance(TouchEvent::instance(context), reinterpret_cast(nativeEvent)); +// }); +// Event::defineEvent(EVENT_TOUCH_MOVE, [](ExecutionContext* context, void* nativeEvent) -> EventInstance* { +// return new TouchEventInstance(TouchEvent::instance(context), reinterpret_cast(nativeEvent)); +// }); +// Event::defineEvent(EVENT_TOUCH_CANCEL, [](ExecutionContext* context, void* nativeEvent) -> EventInstance* { +// return new TouchEventInstance(TouchEvent::instance(context), reinterpret_cast(nativeEvent)); +// }); +// Event::defineEvent(EVENT_SWIPE, [](ExecutionContext* context, void* nativeEvent) -> EventInstance* { +// return new GestureEventInstance(GestureEvent::instance(context), reinterpret_cast(nativeEvent)); +// }); +// Event::defineEvent(EVENT_PAN, [](ExecutionContext* context, void* nativeEvent) -> EventInstance* { +// return new GestureEventInstance(GestureEvent::instance(context), reinterpret_cast(nativeEvent)); +// }); +// Event::defineEvent(EVENT_LONG_PRESS, [](ExecutionContext* context, void* nativeEvent) -> EventInstance* { +// return new GestureEventInstance(GestureEvent::instance(context), reinterpret_cast(nativeEvent)); +// }); +// Event::defineEvent(EVENT_SCALE, [](ExecutionContext* context, void* nativeEvent) -> EventInstance* { +// return new GestureEventInstance(GestureEvent::instance(context), reinterpret_cast(nativeEvent)); +// }); +// Event::defineEvent( +// EVENT_CLICK, [](ExecutionContext* context, void* nativeEvent) -> EventInstance* { return new MouseEventInstance(MouseEvent::instance(context), reinterpret_cast(nativeEvent)); }); +// Event::defineEvent(EVENT_CANCEL, [](ExecutionContext* context, void* nativeEvent) -> EventInstance* { +// return new MouseEventInstance(MouseEvent::instance(context), reinterpret_cast(nativeEvent)); +// }); +// Event::defineEvent(EVENT_POPSTATE, [](ExecutionContext* context, void* nativeEvent) -> EventInstance* { +// return new PopStateEventInstance(PopStateEvent::instance(context), reinterpret_cast(nativeEvent)); +// }); } } -JSClassID Document::classId() { - return kDocumentClassID; -} - -JSValue Document::instanceConstructor(JSContext* ctx, JSValue func_obj, JSValue this_val, int argc, JSValue* argv) { - auto* instance = new DocumentInstance(this); - return instance->jsObject; -} - -JSValue Document::createEvent(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { +IMPL_FUNCTION(Document, createEvent)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { if (argc < 1) { return JS_ThrowTypeError(ctx, "Failed to argumentCount: 1 argument required, but only 0 present."); } @@ -153,7 +153,7 @@ JSValue Document::createEvent(JSContext* ctx, JSValue this_val, int argc, JSValu std::unique_ptr nativeEventType = jsValueToNativeString(ctx, eventTypeValue); auto nativeEvent = new NativeEvent{nativeEventType.release()}; - auto document = static_cast(JS_GetOpaque(this_val, Document::classId())); + auto document = static_cast(JS_GetOpaque(this_val, Document::classId)); auto e = Event::buildEventInstance(eventType, document->context(), nativeEvent, false); return e->jsObject; } else { @@ -161,7 +161,7 @@ JSValue Document::createEvent(JSContext* ctx, JSValue this_val, int argc, JSValu } } -JSValue Document::createElement(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { +IMPL_FUNCTION(Document, createElement)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { if (argc < 1) { return JS_ThrowTypeError(ctx, "Failed to createElement: 1 argument required, but only 0 present."); } @@ -171,7 +171,7 @@ JSValue Document::createElement(JSContext* ctx, JSValue this_val, int argc, JSVa return JS_ThrowTypeError(ctx, "Failed to createElement: tagName should be a string."); } - auto document = static_cast(JS_GetOpaque(this_val, Document::classId())); + auto document = static_cast(JS_GetOpaque(this_val, Document::classId)); auto* context = static_cast(JS_GetContextOpaque(ctx)); std::string tagName = jsValueToStdString(ctx, tagNameValue); JSValue constructor = static_cast(document->prototype())->getElementConstructor(document->m_context, tagName); @@ -180,33 +180,33 @@ JSValue Document::createElement(JSContext* ctx, JSValue this_val, int argc, JSVa return element; } -JSValue Document::createTextNode(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { +IMPL_FUNCTION(Document, createTextNode)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { if (argc != 1) { return JS_ThrowTypeError(ctx, "Failed to execute 'createTextNode' on 'Document': 1 argument required, but only 0 present."); } - auto* document = static_cast(JS_GetOpaque(this_val, Document::classId())); + auto* document = static_cast(JS_GetOpaque(this_val, Document::classId)); JSValue textNode = JS_CallConstructor(ctx, TextNode::instance(document->m_context)->jsObject, argc, argv); return textNode; } -JSValue Document::createDocumentFragment(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { - auto* document = static_cast(JS_GetOpaque(this_val, Document::classId())); +IMPL_FUNCTION(Document, createDocumentFragment)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { + auto* document = static_cast(JS_GetOpaque(this_val, Document::classId)); return JS_CallConstructor(ctx, DocumentFragment::instance(document->m_context)->jsObject, 0, nullptr); } -JSValue Document::createComment(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { - auto* document = static_cast(JS_GetOpaque(this_val, Document::classId())); +IMPL_FUNCTION(Document, createComment)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { + auto* document = static_cast(JS_GetOpaque(this_val, Document::classId)); JSValue commentNode = JS_CallConstructor(ctx, Comment::instance(document->m_context)->jsObject, argc, argv); return commentNode; } -JSValue Document::getElementById(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { +IMPL_FUNCTION(Document, getElementById)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { if (argc < 1) { return JS_ThrowTypeError(ctx, "Uncaught TypeError: Failed to execute 'getElementById' on 'Document': 1 argument required, but only 0 present."); } - auto* document = static_cast(JS_GetOpaque(this_val, Document::classId())); + auto* document = static_cast(JS_GetOpaque(this_val, Document::classId)); JSValue idValue = argv[0]; if (!JS_IsString(idValue)) @@ -240,14 +240,14 @@ JSValue Document::getElementsByTagName(JSContext* ctx, JSValue this_val, int arg "but only 0 present."); } - auto* document = static_cast(JS_GetOpaque(this_val, Document::classId())); + auto* document = static_cast(JS_GetOpaque(this_val, Document::classId)); JSValue tagNameValue = argv[0]; std::string tagName = jsValueToStdString(ctx, tagNameValue); std::transform(tagName.begin(), tagName.end(), tagName.begin(), ::toupper); std::vector elements; - traverseNode(document, [tagName, &elements](NodeInstance* node) { + traverseNode(document, [tagName, &elements](Node* node) { if (node->nodeType == NodeType::ELEMENT_NODE) { auto* element = static_cast(node); if (element->tagName() == tagName || tagName == "*") { @@ -274,11 +274,11 @@ JSValue Document::getElementsByClassName(JSContext* ctx, JSValue this_val, int a return JS_ThrowTypeError(ctx, "Uncaught TypeError: Failed to execute 'getElementsByClassName' on 'Document': 1 argument required, but only 0 present."); } - auto* document = static_cast(JS_GetOpaque(this_val, Document::classId())); + auto* document = static_cast(JS_GetOpaque(this_val, Document::classId)); std::string className = jsValueToStdString(ctx, argv[0]); std::vector elements; - traverseNode(document, [ctx, className, &elements](NodeInstance* node) { + traverseNode(document, [ctx, className, &elements](Node* node) { if (node->nodeType == NodeType::ELEMENT_NODE) { auto element = reinterpret_cast(node); if (element->classNames()->containsAll(className)) { @@ -319,10 +319,10 @@ IMPL_PROPERTY_GETTER(Document, nodeName)(JSContext* ctx, JSValue this_val, int a } IMPL_PROPERTY_GETTER(Document, all)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { - auto* document = static_cast(JS_GetOpaque(this_val, Document::classId())); + auto* document = static_cast(JS_GetOpaque(this_val, Document::classId)); auto all = new AllCollection(document->m_context); - traverseNode(document, [&all](NodeInstance* node) { + traverseNode(document, [&all](Node* node) { all->internalAdd(node, nullptr); return false; }); @@ -332,23 +332,23 @@ IMPL_PROPERTY_GETTER(Document, all)(JSContext* ctx, JSValue this_val, int argc, // document.documentElement IMPL_PROPERTY_GETTER(Document, documentElement)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { - auto* document = static_cast(JS_GetOpaque(this_val, Document::classId())); - ElementInstance* documentElement = document->getDocumentElement(); + auto* document = static_cast(JS_GetOpaque(this_val, Document::classId)); + Element* documentElement = document->getDocumentElement(); return documentElement == nullptr ? JS_NULL : documentElement->jsObject; } // document.head IMPL_PROPERTY_GETTER(Document, head)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { - auto* document = static_cast(JS_GetOpaque(this_val, Document::classId())); - ElementInstance* documentElement = document->getDocumentElement(); + auto* document = static_cast(JS_GetOpaque(this_val, Document::classId)); + Element* documentElement = document->getDocumentElement(); int32_t len = arrayGetLength(ctx, documentElement->childNodes); JSValue head = JS_NULL; if (documentElement != nullptr) { for (int i = 0; i < len; i++) { JSValue v = JS_GetPropertyUint32(ctx, documentElement->childNodes, i); - auto* nodeInstance = static_cast(JS_GetOpaque(v, Node::classId(v))); + auto* nodeInstance = static_cast(JS_GetOpaque(v, Node::classId(v))); if (nodeInstance->nodeType == NodeType::ELEMENT_NODE) { - auto* elementInstance = static_cast(nodeInstance); + auto* elementInstance = static_cast(nodeInstance); if (elementInstance->tagName() == "HEAD") { head = elementInstance->jsObject; break; @@ -365,8 +365,8 @@ IMPL_PROPERTY_GETTER(Document, head)(JSContext* ctx, JSValue this_val, int argc, // document.body: https://html.spec.whatwg.org/multipage/dom.html#dom-document-body-dev IMPL_PROPERTY_GETTER(Document, body)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { - auto* document = static_cast(JS_GetOpaque(this_val, Document::classId())); - ElementInstance* documentElement = document->getDocumentElement(); + auto* document = static_cast(JS_GetOpaque(this_val, Document::classId)); + Element* documentElement = document->getDocumentElement(); JSValue body = JS_NULL; if (documentElement != nullptr) { @@ -375,9 +375,9 @@ IMPL_PROPERTY_GETTER(Document, body)(JSContext* ctx, JSValue this_val, int argc, // is either a body element or a frameset element, or null if there is no such element. for (int i = 0; i < len; i++) { JSValue v = JS_GetPropertyUint32(ctx, documentElement->childNodes, i); - auto* nodeInstance = static_cast(JS_GetOpaque(v, Node::classId(v))); + auto* nodeInstance = static_cast(JS_GetOpaque(v, Node::classId(v))); if (nodeInstance->nodeType == NodeType::ELEMENT_NODE) { - auto* elementInstance = static_cast(nodeInstance); + auto* elementInstance = static_cast(nodeInstance); if (elementInstance->tagName() == "BODY") { body = elementInstance->jsObject; break; @@ -393,8 +393,8 @@ IMPL_PROPERTY_GETTER(Document, body)(JSContext* ctx, JSValue this_val, int argc, // The body property is settable, setting a new body on a document will effectively remove all // the current children of the existing element. IMPL_PROPERTY_SETTER(Document, body)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { - auto* document = static_cast(JS_GetOpaque(this_val, Document::classId())); - ElementInstance* documentElement = document->getDocumentElement(); + auto* document = static_cast(JS_GetOpaque(this_val, Document::classId)); + Element* documentElement = document->getDocumentElement(); // If there is no document element, throw a Exception. if (documentElement == nullptr) { return JS_ThrowInternalError(ctx, "No document element exists"); @@ -403,7 +403,7 @@ IMPL_PROPERTY_SETTER(Document, body)(JSContext* ctx, JSValue this_val, int argc, JSValue newBody = argv[0]; // If the body element is not null, then replace the body element with the new value within the body element's parent and return. if (JS_IsInstanceOf(ctx, newBody, Element::instance(document->m_context)->jsObject)) { - auto* newElementInstance = static_cast(JS_GetOpaque(newBody, Element::classId())); + auto* newElementInstance = static_cast(JS_GetOpaque(newBody, Element::classId())); // If the new value is not a body element, then throw a Exception. if (newElementInstance->tagName() == "BODY") { JSValue oldBody = JS_GetPropertyStr(ctx, document->jsObject, "body"); @@ -414,7 +414,7 @@ IMPL_PROPERTY_SETTER(Document, body)(JSContext* ctx, JSValue this_val, int argc, documentElement->internalAppendChild(newElementInstance); } else { // Otherwise, replace the body element with the new value within the body element's parent. - auto* oldElementInstance = static_cast(JS_GetOpaque(oldBody, Element::classId())); + auto* oldElementInstance = static_cast(JS_GetOpaque(oldBody, Element::classId())); documentElement->internalReplaceChild(newElementInstance, oldElementInstance); } } @@ -433,14 +433,14 @@ IMPL_PROPERTY_SETTER(Document, body)(JSContext* ctx, JSValue this_val, int argc, // document.children IMPL_PROPERTY_GETTER(Document, children)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { - auto* document = static_cast(JS_GetOpaque(this_val, Document::classId())); + auto* document = static_cast(JS_GetOpaque(this_val, Document::classId)); JSValue array = JS_NewArray(ctx); JSValue pushMethod = JS_GetPropertyStr(ctx, array, "push"); int32_t len = arrayGetLength(ctx, document->childNodes); for (int i = 0; i < len; i++) { JSValue v = JS_GetPropertyUint32(ctx, document->childNodes, i); - auto* instance = static_cast(JS_GetOpaque(v, Node::classId(v))); + auto* instance = static_cast(JS_GetOpaque(v, Node::classId(v))); if (instance->nodeType == NodeType::ELEMENT_NODE) { JSValue arguments[] = {v}; JS_Call(ctx, pushMethod, array, 1, arguments); @@ -453,12 +453,12 @@ IMPL_PROPERTY_GETTER(Document, children)(JSContext* ctx, JSValue this_val, int a } IMPL_PROPERTY_GETTER(Document, cookie)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { - auto* document = static_cast(JS_GetOpaque(this_val, Document::classId())); + auto* document = static_cast(JS_GetOpaque(this_val, Document::classId)); std::string cookie = document->m_cookie->getCookie(); return JS_NewString(ctx, cookie.c_str()); } IMPL_PROPERTY_SETTER(Document, cookie)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { - auto* document = static_cast(JS_GetOpaque(this_val, Document::classId())); + auto* document = static_cast(JS_GetOpaque(this_val, Document::classId)); std::string value = jsValueToStdString(ctx, argv[0]); document->m_cookie->setCookie(value); return JS_NULL; @@ -516,7 +516,7 @@ void DocumentCookie::setCookie(std::string& cookieStr) { cookiePairs[key] = value; } -DocumentInstance::DocumentInstance(Document* document) : NodeInstance(document, NodeType::DOCUMENT_NODE, Document::classId(), "document") { +Document::Document(Document* document) : Node(document, NodeType::DOCUMENT_NODE, Document::classId, "document") { m_context->m_document = this; m_document = this; m_cookie = std::make_unique(); @@ -529,16 +529,9 @@ DocumentInstance::DocumentInstance(Document* document) : NodeInstance(document, #endif } -DocumentInstance::~DocumentInstance() { - // Atom string should keep alive in memory to make sure same string have the corresponding id. - // Only freed after document finalized. - for (auto& entry : m_elementMapById) { - JS_FreeAtomRT(m_context->runtime(), entry.first); - // Note: someone may be curious why there are no JS_FreeValueRT() call in this finalize callbacks. - // m_elementMapById's value are all elements, which are JavaScript objects. Will be freed by GC at marking phase. - } +Document::~Document() { } -void DocumentInstance::removeElementById(JSAtom id, ElementInstance* element) { +void Document::removeElementById(JSAtom id, Element* element) { if (m_elementMapById.count(id) > 0) { auto& list = m_elementMapById[id]; auto idx = std::find(list.begin(), list.end(), element); @@ -547,9 +540,9 @@ void DocumentInstance::removeElementById(JSAtom id, ElementInstance* element) { JS_FreeValue(m_ctx, element->jsObject); } } -void DocumentInstance::addElementById(JSAtom id, ElementInstance* element) { +void Document::addElementById(JSAtom id, Element* element) { if (m_elementMapById.count(id) == 0) { - m_elementMapById[id] = std::vector(); + m_elementMapById[id] = std::vector(); JS_DupAtom(m_ctx, id); } @@ -562,14 +555,14 @@ void DocumentInstance::addElementById(JSAtom id, ElementInstance* element) { } } -ElementInstance* DocumentInstance::getDocumentElement() { +Element* Document::getDocumentElement() { int32_t len = arrayGetLength(m_ctx, childNodes); for (int i = 0; i < len; i++) { JSValue v = JS_GetPropertyUint32(m_ctx, childNodes, i); - auto* instance = static_cast(JS_GetOpaque(v, Node::classId(v))); + auto* instance = static_cast(JS_GetOpaque(v, Node::classId(v))); if (instance->nodeType == NodeType::ELEMENT_NODE) { - return static_cast(instance); + return static_cast(instance); } JS_FreeValue(m_ctx, v); } @@ -577,16 +570,16 @@ ElementInstance* DocumentInstance::getDocumentElement() { return nullptr; } -int32_t DocumentInstance::requestAnimationFrame(FrameCallback* frameCallback) { +int32_t Document::requestAnimationFrame(FrameCallback* frameCallback) { return m_scriptAnimationController->registerFrameCallback(frameCallback); } -void DocumentInstance::cancelAnimationFrame(uint32_t callbackId) { +void Document::cancelAnimationFrame(uint32_t callbackId) { m_scriptAnimationController->cancelFrameCallback(callbackId); } -void DocumentInstance::trace(JSRuntime* rt, JSValue val, JS_MarkFunc* mark_func) { - NodeInstance::trace(rt, val, mark_func); +void Document::trace(JSRuntime* rt, JSValue val, JS_MarkFunc* mark_func) const { + Node::trace(rt, val, mark_func); // Trace scriptAnimationController if (m_scriptAnimationController != nullptr) { JS_MarkValue(rt, m_scriptAnimationController->toQuickJS(), mark_func); @@ -594,9 +587,21 @@ void DocumentInstance::trace(JSRuntime* rt, JSValue val, JS_MarkFunc* mark_func) // Trace elementByIdMaps for (auto& entry : m_elementMapById) { for (auto& value : entry.second) { - JS_MarkValue(rt, value->jsObject, mark_func); + JS_MarkValue(rt, value->toQuickJS(), mark_func); } } } +void Document::dispose() const { + Node::dispose(); + + // Atom string should keep alive in memory to make sure same string have the corresponding id. + // Only freed after document finalized. + for (auto& entry : m_elementMapById) { + JS_FreeAtomRT(m_runtime, entry.first); + // Note: someone may be curious why there are no JS_FreeValueRT() call in this finalize callbacks. + // m_elementMapById's value are all elements, which are JavaScript objects. Will be freed by GC at marking phase. + } +} + } // namespace kraken::binding::qjs diff --git a/bridge/bindings/qjs/dom/document.h b/bridge/bindings/qjs/dom/document.h index 161ec71502..b83cc42b17 100644 --- a/bridge/bindings/qjs/dom/document.h +++ b/bridge/bindings/qjs/dom/document.h @@ -15,36 +15,37 @@ namespace kraken::binding::qjs { void bindDocument(std::unique_ptr& context); -using TraverseHandler = std::function; +using TraverseHandler = std::function; -void traverseNode(NodeInstance* node, TraverseHandler handler); +void traverseNode(Node* node, TraverseHandler handler); -class Document : public Node { - public: - static JSClassID kDocumentClassID; - - Document() = delete; - Document(ExecutionContext* context); - - static JSClassID classId(); +class DocumentCookie { +public: + DocumentCookie() = default; - JSValue instanceConstructor(JSContext* ctx, JSValue func_obj, JSValue this_val, int argc, JSValue* argv) override; + std::string getCookie(); + void setCookie(std::string& str); - OBJECT_INSTANCE(Document); +private: + std::unordered_map cookiePairs; +}; - static JSValue createEvent(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); - static JSValue createElement(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); - static JSValue createTextNode(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); - static JSValue createDocumentFragment(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); - static JSValue createComment(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); - static JSValue getElementById(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); - static JSValue getElementsByTagName(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); - static JSValue getElementsByClassName(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); - JSValue getElementConstructor(ExecutionContext* context, const std::string& tagName); - bool isCustomElement(const std::string& tagName); +class Document : public Node { + public: + static JSClassID classId; + Document* create(JSContext* ctx); + explicit Document(); + + DEFINE_FUNCTION(createEvent); + DEFINE_FUNCTION(createElement); + DEFINE_FUNCTION(createTextNode); + DEFINE_FUNCTION(createDocumentFragment); + DEFINE_FUNCTION(createComment); + DEFINE_FUNCTION(getElementById); + DEFINE_FUNCTION(getElementsByTagName); + DEFINE_FUNCTION(getElementsByClassName); - private: DEFINE_PROTOTYPE_READONLY_PROPERTY(nodeName); DEFINE_PROTOTYPE_READONLY_PROPERTY(all); DEFINE_PROTOTYPE_READONLY_PROPERTY(documentElement); @@ -54,58 +55,48 @@ class Document : public Node { DEFINE_PROTOTYPE_PROPERTY(cookie); DEFINE_PROTOTYPE_PROPERTY(body); - DEFINE_PROTOTYPE_FUNCTION(createEvent, 1); - DEFINE_PROTOTYPE_FUNCTION(createElement, 1); - DEFINE_PROTOTYPE_FUNCTION(createDocumentFragment, 0); - DEFINE_PROTOTYPE_FUNCTION(createTextNode, 1); - DEFINE_PROTOTYPE_FUNCTION(createComment, 1); - DEFINE_PROTOTYPE_FUNCTION(getElementById, 1); - DEFINE_PROTOTYPE_FUNCTION(getElementsByTagName, 1); - DEFINE_PROTOTYPE_FUNCTION(getElementsByClassName, 1); + JSValue getElementConstructor(ExecutionContext* context, const std::string& tagName); + bool isCustomElement(const std::string& tagName); - void defineElement(const std::string& tagName, Element* constructor); + int32_t requestAnimationFrame(FrameCallback* frameCallback); + void cancelAnimationFrame(uint32_t callbackId); + void trace(JSRuntime* rt, JSValue val, JS_MarkFunc* mark_func) const override; + void dispose() const override; + + private: + + void removeElementById(JSAtom id, Element* element); + void addElementById(JSAtom id, Element* element); + Element* getDocumentElement(); + std::unordered_map> m_elementMapById; + Element* m_documentElement{nullptr}; + std::unique_ptr m_cookie; + + ScriptAnimationController* m_scriptAnimationController; +// DEFINE_PROTOTYPE_FUNCTION(createEvent, 1); +// DEFINE_PROTOTYPE_FUNCTION(createElement, 1); +// DEFINE_PROTOTYPE_FUNCTION(createDocumentFragment, 0); +// DEFINE_PROTOTYPE_FUNCTION(createTextNode, 1); +// DEFINE_PROTOTYPE_FUNCTION(createComment, 1); +// DEFINE_PROTOTYPE_FUNCTION(getElementById, 1); +// DEFINE_PROTOTYPE_FUNCTION(getElementsByTagName, 1); +// DEFINE_PROTOTYPE_FUNCTION(getElementsByClassName, 1); - friend DocumentInstance; + void defineElement(const std::string& tagName, Element* constructor); bool event_registered{false}; bool document_registered{false}; std::unordered_map elementConstructorMap; }; -class DocumentCookie { - public: - DocumentCookie() = default; - - std::string getCookie(); - void setCookie(std::string& str); - - private: - std::unordered_map cookiePairs; +auto documentCreator = [](JSContext* ctx, JSValueConst func_obj, JSValueConst this_val, int argc, JSValueConst* argv, int flags) -> JSValue { + return JS_ThrowTypeError(ctx, "Illegal constructor"); }; -class DocumentInstance : public NodeInstance { - public: - DocumentInstance() = delete; - explicit DocumentInstance(Document* document); - ~DocumentInstance(); - - int32_t requestAnimationFrame(FrameCallback* frameCallback); - void cancelAnimationFrame(uint32_t callbackId); - void trace(JSRuntime* rt, JSValue val, JS_MarkFunc* mark_func) override; - - private: - void removeElementById(JSAtom id, ElementInstance* element); - void addElementById(JSAtom id, ElementInstance* element); - ElementInstance* getDocumentElement(); - std::unordered_map> m_elementMapById; - ElementInstance* m_documentElement{nullptr}; - std::unique_ptr m_cookie; - - ScriptAnimationController* m_scriptAnimationController; - - friend Document; - friend ElementInstance; - friend ExecutionContext; +const WrapperTypeInfo documentTypeInfo = { + "Document", + &nodeTypeInfo, + documentCreator }; } // namespace kraken::binding::qjs diff --git a/bridge/bindings/qjs/dom/document_fragment.cc b/bridge/bindings/qjs/dom/document_fragment.cc index 1ebe5a0754..6b14777bba 100644 --- a/bridge/bindings/qjs/dom/document_fragment.cc +++ b/bridge/bindings/qjs/dom/document_fragment.cc @@ -10,29 +10,28 @@ namespace kraken::binding::qjs { void bindDocumentFragment(std::unique_ptr& context) { - auto* constructor = DocumentFragment::instance(context.get()); - context->defineGlobalProperty("DocumentFragment", constructor->jsObject); + JSValue classObject = context->contextData()->constructorForType(&documentFragmentInfo); + context->defineGlobalProperty("DocumentFragment", classObject); } -std::once_flag kDocumentFragmentFlag; +JSValue DocumentFragment::constructor(ExecutionContext* context) { + return context->contextData()->constructorForType(&documentFragmentInfo); +} -JSClassID DocumentFragment::kDocumentFragmentID{0}; +DocumentFragment * DocumentFragment::create(JSContext* ctx) { + auto* context = static_cast(JS_GetContextOpaque(ctx)); + JSValue prototype = context->contextData()->prototypeForType(&documentFragmentInfo); + auto* documentFragment = makeGarbageCollected()->initialize(ctx, &classId); -DocumentFragment::DocumentFragment(ExecutionContext* context) : Node(context) { - std::call_once(kDocumentFragmentFlag, []() { JS_NewClassID(&kDocumentFragmentID); }); - JS_SetPrototype(m_ctx, m_prototypeObject, Node::instance(m_context)->prototype()); -} + // Let documentFragment instance inherit Document prototype methods. + JS_SetPrototype(ctx, documentFragment->toQuickJS(), prototype); -JSClassID DocumentFragment::classId() { - return kDocumentFragmentID; + return documentFragment; } -JSValue DocumentFragment::instanceConstructor(JSContext* ctx, JSValue func_obj, JSValue this_val, int argc, JSValue* argv) { - return (new DocumentFragmentInstance(this))->jsObject; +DocumentFragment::DocumentFragment() { + setNodeFlag(DocumentFragment::NodeFlag::IsDocumentFragment); + context()->uiCommandBuffer()->addCommand(eventTargetId(), UICommand::createDocumentFragment, nativeEventTarget); } -DocumentFragmentInstance::DocumentFragmentInstance(DocumentFragment* fragment) : NodeInstance(fragment, NodeType::DOCUMENT_FRAGMENT_NODE, DocumentFragment::classId(), "DocumentFragment") { - setNodeFlag(DocumentFragmentInstance::NodeFlag::IsDocumentFragment); - m_context->uiCommandBuffer()->addCommand(m_eventTargetId, UICommand::createDocumentFragment, nativeEventTarget); -} } // namespace kraken::binding::qjs diff --git a/bridge/bindings/qjs/dom/document_fragment.h b/bridge/bindings/qjs/dom/document_fragment.h index 95f74303d5..a2bdcf8791 100644 --- a/bridge/bindings/qjs/dom/document_fragment.h +++ b/bridge/bindings/qjs/dom/document_fragment.h @@ -14,21 +14,25 @@ void bindDocumentFragment(std::unique_ptr& context); class DocumentFragment : public Node { public: - static JSClassID kDocumentFragmentID; - static JSClassID classId(); - - DocumentFragment() = delete; - explicit DocumentFragment(ExecutionContext* context); - - JSValue instanceConstructor(JSContext* ctx, JSValue func_obj, JSValue this_val, int argc, JSValue* argv) override; + static JSClassID classId; + // Return the constructor class object of DocumentFragment. + static JSValue constructor(ExecutionContext* context); + DocumentFragment* create(JSContext* ctx); + DocumentFragment(); + + private: + friend Node; +}; - OBJECT_INSTANCE(DocumentFragment); +auto documentFragmentCreator = [](JSContext* ctx, JSValueConst func_obj, JSValueConst this_val, int argc, JSValueConst* argv, int flags) -> JSValue { + auto* eventTarget = EventTarget::create(ctx); + return eventTarget->toQuickJS(); }; -class DocumentFragmentInstance : public NodeInstance { - public: - DocumentFragmentInstance() = delete; - DocumentFragmentInstance(DocumentFragment* fragment); +const WrapperTypeInfo documentFragmentInfo = { + "DocumentFragment", + &nodeTypeInfo, + documentFragmentCreator }; } // namespace kraken::binding::qjs diff --git a/bridge/bindings/qjs/dom/element.cc b/bridge/bindings/qjs/dom/element.cc index cfe5376cbb..3957b66b91 100644 --- a/bridge/bindings/qjs/dom/element.cc +++ b/bridge/bindings/qjs/dom/element.cc @@ -17,18 +17,58 @@ namespace kraken::binding::qjs { -std::once_flag kElementInitOnceFlag; - void bindElement(std::unique_ptr& context) { - auto* constructor = Element::instance(context.get()); - // auto* domRectConstructor = BoundingClientRect - context->defineGlobalProperty("Element", constructor->jsObject); - context->defineGlobalProperty("HTMLElement", JS_DupValue(context->ctx(), constructor->jsObject)); + auto* contextData = context->contextData(); + JSValue classObject = contextData->constructorForType(&elementTypeInfo); + JSValue prototypeObject = contextData->prototypeForType(&elementTypeInfo); + + // Install methods on prototype. + INSTALL_FUNCTION(Element, prototypeObject, getBoundingClientRect, 0); + INSTALL_FUNCTION(Element, prototypeObject, hasAttribute, 1); + INSTALL_FUNCTION(Element, prototypeObject, setAttribute, 2); + INSTALL_FUNCTION(Element, prototypeObject, getAttribute, 2); + INSTALL_FUNCTION(Element, prototypeObject, removeAttribute, 1); + INSTALL_FUNCTION(Element, prototypeObject, toBlob, 0); + INSTALL_FUNCTION(Element, prototypeObject, click, 2); + INSTALL_FUNCTION(Element, prototypeObject, scroll, 2); + // ScrollTo is same as scroll which reuse scroll functions. Macro expand is not support here. + installFunctionProperty(context.get(), prototypeObject, "scrollTo", Element::m_scroll_, 1); + INSTALL_FUNCTION(Element, prototypeObject, scrollBy, 2); + + // Install Getter and Setter properties. + // Install readonly properties. + INSTALL_READONLY_PROPERTY(Element, prototypeObject, nodeName); + INSTALL_READONLY_PROPERTY(Element, prototypeObject, tagName); + INSTALL_READONLY_PROPERTY(Element, prototypeObject, offsetLeft); + INSTALL_READONLY_PROPERTY(Element, prototypeObject, offsetTop); + INSTALL_READONLY_PROPERTY(Element, prototypeObject, offsetWidth); + INSTALL_READONLY_PROPERTY(Element, prototypeObject, offsetHeight); + INSTALL_READONLY_PROPERTY(Element, prototypeObject, clientWidth); + INSTALL_READONLY_PROPERTY(Element, prototypeObject, clientHeight); + INSTALL_READONLY_PROPERTY(Element, prototypeObject, clientTop); + INSTALL_READONLY_PROPERTY(Element, prototypeObject, clientLeft); + INSTALL_READONLY_PROPERTY(Element, prototypeObject, scrollHeight); + INSTALL_READONLY_PROPERTY(Element, prototypeObject, scrollWidth); + INSTALL_READONLY_PROPERTY(Element, prototypeObject, firstElementChild); + INSTALL_READONLY_PROPERTY(Element, prototypeObject, lastElementChild); + INSTALL_READONLY_PROPERTY(Element, prototypeObject, children); + INSTALL_READONLY_PROPERTY(Element, prototypeObject, attributes); + + // Install properties. + INSTALL_PROPERTY(Element, prototypeObject, className); + INSTALL_PROPERTY(Element, prototypeObject, innerHTML); + INSTALL_PROPERTY(Element, prototypeObject, outerHTML); + INSTALL_PROPERTY(Element, prototypeObject, scrollTop); + INSTALL_PROPERTY(Element, prototypeObject, scrollLeft); + + context->defineGlobalProperty("Element", classObject); + context->defineGlobalProperty("HTMLElement", JS_DupValue(context->ctx(), classObject)); } bool isJavaScriptExtensionElementInstance(ExecutionContext* context, JSValue instance) { - if (JS_IsInstanceOf(context->ctx(), instance, Element::instance(context)->jsObject)) { - auto* elementInstance = static_cast(JS_GetOpaque(instance, Element::classId())); + JSValue classObject = context->contextData()->constructorForType(&elementTypeInfo); + if (JS_IsInstanceOf(context->ctx(), instance, classObject)) { + auto* elementInstance = static_cast(JS_GetOpaque(instance, Element::classId())); std::string tagName = elementInstance->getRegisteredTagName(); // Special case for kraken official plugins. @@ -44,17 +84,6 @@ bool isJavaScriptExtensionElementInstance(ExecutionContext* context, JSValue ins return false; } -JSClassID Element::kElementClassId{0}; - -Element::Element(ExecutionContext* context) : Node(context, "Element") { - std::call_once(kElementInitOnceFlag, []() { JS_NewClassID(&kElementClassId); }); - JS_SetPrototype(m_ctx, m_prototypeObject, Node::instance(m_context)->prototype()); -} - -JSClassID Element::classId() { - return kElementClassId; -} - JSClassID ElementAttributes::classId{0}; JSValue ElementAttributes::getAttribute(const std::string& name) { bool numberIndex = isNumberIndex(name); @@ -138,29 +167,40 @@ void ElementAttributes::trace(JSRuntime* rt, JSValue val, JS_MarkFunc* mark_func } } -JSValue Element::instanceConstructor(JSContext* ctx, JSValue func_obj, JSValue this_val, int argc, JSValue* argv) { - if (argc == 0) - return JS_ThrowTypeError(ctx, "Illegal constructor"); - JSValue tagName = argv[0]; - - if (!JS_IsString(tagName)) { - return JS_ThrowTypeError(ctx, "Illegal constructor"); - } +JSClassID Element::classId{0}; +Element* Element::create(JSContext* ctx) { auto* context = static_cast(JS_GetContextOpaque(ctx)); - std::string name = jsValueToStdString(ctx, tagName); - - auto* Document = Document::instance(context); - if (Document->isCustomElement(name)) { - return JS_CallConstructor(ctx, Document->getElementConstructor(context, name), argc, argv); - } - - auto* element = new ElementInstance(this, name, true); - return element->jsObject; -} + JSValue prototype = context->contextData()->prototypeForType(&elementTypeInfo); + auto* element = makeGarbageCollected()->initialize(ctx, &classId); + // Let eventTarget instance inherit EventTarget prototype methods. + JS_SetPrototype(ctx, element->toQuickJS(), prototype); + return element; +} + +//JSValue Element::instanceConstructor(JSContext* ctx, JSValue func_obj, JSValue this_val, int argc, JSValue* argv) { +// if (argc == 0) +// return JS_ThrowTypeError(ctx, "Illegal constructor"); +// JSValue tagName = argv[0]; +// +// if (!JS_IsString(tagName)) { +// return JS_ThrowTypeError(ctx, "Illegal constructor"); +// } +// +// auto* context = static_cast(JS_GetContextOpaque(ctx)); +// std::string name = jsValueToStdString(ctx, tagName); +// +// auto* Document = Document::instance(context); +// if (Document->isCustomElement(name)) { +// return JS_CallConstructor(ctx, Document->getElementConstructor(context, name), argc, argv); +// } +// +// auto* element = new Element(this, name, true); +// return element->jsObject; +//} JSValue Element::getBoundingClientRect(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { - auto element = static_cast(JS_GetOpaque(this_val, Element::classId())); + auto element = static_cast(JS_GetOpaque(this_val, Element::classId())); getDartMethod()->flushUICommand(); return element->callNativeMethods("getBoundingClientRect", 0, nullptr); } @@ -176,7 +216,7 @@ JSValue Element::hasAttribute(JSContext* ctx, JSValue this_val, int argc, JSValu return JS_ThrowTypeError(ctx, "Failed to execute 'setAttribute' on 'Element': name attribute is not valid."); } - auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); + auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); auto* attributes = element->m_attributes; const char* cname = JS_ToCString(ctx, nameValue); @@ -200,7 +240,7 @@ JSValue Element::setAttribute(JSContext* ctx, JSValue this_val, int argc, JSValu return JS_ThrowTypeError(ctx, "Failed to execute 'setAttribute' on 'Element': name attribute is not valid."); } - auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); + auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); std::string name = jsValueToStdString(ctx, nameValue); std::transform(name.begin(), name.end(), name.begin(), ::tolower); @@ -241,7 +281,7 @@ JSValue Element::getAttribute(JSContext* ctx, JSValue this_val, int argc, JSValu return JS_ThrowTypeError(ctx, "Failed to execute 'setAttribute' on 'Element': name attribute is not valid."); } - auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); + auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); std::string name = jsValueToStdString(ctx, nameValue); auto* attributes = element->m_attributes; @@ -264,7 +304,7 @@ JSValue Element::removeAttribute(JSContext* ctx, JSValue this_val, int argc, JSV return JS_ThrowTypeError(ctx, "Failed to execute 'removeAttribute' on 'Element': name attribute is not valid."); } - auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); + auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); std::string name = jsValueToStdString(ctx, nameValue); auto* attributes = element->m_attributes; @@ -298,7 +338,7 @@ JSValue Element::toBlob(JSContext* ctx, JSValue this_val, int argc, JSValue* arg return JS_ThrowTypeError(ctx, "Failed to export blob: dart method (toBlob) is not registered."); } - auto* element = reinterpret_cast(JS_GetOpaque(this_val, Element::classId())); + auto* element = reinterpret_cast(JS_GetOpaque(this_val, Element::classId())); getDartMethod()->flushUICommand(); auto blobCallback = [](void* callbackContext, int32_t contextId, const char* error, uint8_t* bytes, int32_t length) { @@ -360,10 +400,10 @@ JSValue Element::toBlob(JSContext* ctx, JSValue this_val, int argc, JSValue* arg JSValue Element::click(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { #if FLUTTER_BACKEND getDartMethod()->flushUICommand(); - auto element = static_cast(JS_GetOpaque(this_val, Element::classId())); + auto element = static_cast(JS_GetOpaque(this_val, Element::classId())); return element->callNativeMethods("click", 0, nullptr); #elif UNIT_TEST - auto element = static_cast(JS_GetOpaque(this_val, Element::classId())); + auto element = static_cast(JS_GetOpaque(this_val, Element::classId())); TEST_dispatchEvent(element, "click"); return JS_UNDEFINED; #else @@ -373,36 +413,36 @@ JSValue Element::click(JSContext* ctx, JSValue this_val, int argc, JSValue* argv JSValue Element::scroll(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { getDartMethod()->flushUICommand(); - auto element = static_cast(JS_GetOpaque(this_val, Element::classId())); + auto element = static_cast(JS_GetOpaque(this_val, Element::classId())); NativeValue arguments[] = {jsValueToNativeValue(ctx, argv[0]), jsValueToNativeValue(ctx, argv[1])}; return element->callNativeMethods("scroll", 2, arguments); } JSValue Element::scrollBy(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { getDartMethod()->flushUICommand(); - auto element = static_cast(JS_GetOpaque(this_val, Element::classId())); + auto element = static_cast(JS_GetOpaque(this_val, Element::classId())); NativeValue arguments[] = {jsValueToNativeValue(ctx, argv[0]), jsValueToNativeValue(ctx, argv[1])}; return element->callNativeMethods("scrollBy", 2, arguments); } IMPL_PROPERTY_GETTER(Element, nodeName)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { - auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); + auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); std::string tagName = element->tagName(); return JS_NewString(ctx, tagName.c_str()); } IMPL_PROPERTY_GETTER(Element, tagName)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { - auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); + auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); std::string tagName = element->tagName(); return JS_NewString(ctx, tagName.c_str()); } IMPL_PROPERTY_GETTER(Element, className)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { - auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); + auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); return element->m_attributes->getAttribute("class"); } IMPL_PROPERTY_SETTER(Element, className)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { - auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); + auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); element->m_attributes->setAttribute("class", argv[0]); std::unique_ptr args_01 = stringToNativeString("class"); std::unique_ptr args_02 = jsValueToNativeString(ctx, argv[0]); @@ -414,103 +454,103 @@ enum class ViewModuleProperty { offsetTop, offsetLeft, offsetWidth, offsetHeight IMPL_PROPERTY_GETTER(Element, offsetLeft)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { getDartMethod()->flushUICommand(); - auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); + auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); NativeValue args[] = {Native_NewInt32(static_cast(ViewModuleProperty::offsetLeft))}; return element->callNativeMethods("getViewModuleProperty", 1, args); } IMPL_PROPERTY_GETTER(Element, offsetTop)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { getDartMethod()->flushUICommand(); - auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); + auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); NativeValue args[] = {Native_NewInt32(static_cast(ViewModuleProperty::offsetTop))}; return element->callNativeMethods("getViewModuleProperty", 1, args); } IMPL_PROPERTY_GETTER(Element, offsetWidth)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { getDartMethod()->flushUICommand(); - auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); + auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); NativeValue args[] = {Native_NewInt32(static_cast(ViewModuleProperty::offsetWidth))}; return element->callNativeMethods("getViewModuleProperty", 1, args); } IMPL_PROPERTY_GETTER(Element, offsetHeight)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { getDartMethod()->flushUICommand(); - auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); + auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); NativeValue args[] = {Native_NewInt32(static_cast(ViewModuleProperty::offsetHeight))}; return element->callNativeMethods("getViewModuleProperty", 1, args); } IMPL_PROPERTY_GETTER(Element, clientWidth)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { getDartMethod()->flushUICommand(); - auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); + auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); NativeValue args[] = {Native_NewInt32(static_cast(ViewModuleProperty::clientWidth))}; return element->callNativeMethods("getViewModuleProperty", 1, args); } IMPL_PROPERTY_GETTER(Element, clientHeight)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { getDartMethod()->flushUICommand(); - auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); + auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); NativeValue args[] = {Native_NewInt32(static_cast(ViewModuleProperty::clientHeight))}; return element->callNativeMethods("getViewModuleProperty", 1, args); } IMPL_PROPERTY_GETTER(Element, clientTop)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { getDartMethod()->flushUICommand(); - auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); + auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); NativeValue args[] = {Native_NewInt32(static_cast(ViewModuleProperty::clientTop))}; return element->callNativeMethods("getViewModuleProperty", 1, args); } IMPL_PROPERTY_GETTER(Element, clientLeft)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { getDartMethod()->flushUICommand(); - auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); + auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); NativeValue args[] = {Native_NewInt32(static_cast(ViewModuleProperty::clientLeft))}; return element->callNativeMethods("getViewModuleProperty", 1, args); } IMPL_PROPERTY_GETTER(Element, scrollTop)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { getDartMethod()->flushUICommand(); - auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); + auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); NativeValue args[] = {Native_NewInt32(static_cast(ViewModuleProperty::scrollTop))}; return element->callNativeMethods("getViewModuleProperty", 1, args); } IMPL_PROPERTY_SETTER(Element, scrollTop)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { getDartMethod()->flushUICommand(); - auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); + auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); NativeValue args[] = {Native_NewInt32(static_cast(ViewModuleProperty::scrollTop)), jsValueToNativeValue(ctx, argv[0])}; return element->callNativeMethods("setViewModuleProperty", 2, args); } IMPL_PROPERTY_GETTER(Element, scrollLeft)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { getDartMethod()->flushUICommand(); - auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); + auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); NativeValue args[] = {Native_NewInt32(static_cast(ViewModuleProperty::scrollLeft))}; return element->callNativeMethods("getViewModuleProperty", 1, args); } IMPL_PROPERTY_SETTER(Element, scrollLeft)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { getDartMethod()->flushUICommand(); - auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); + auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); NativeValue args[] = {Native_NewInt32(static_cast(ViewModuleProperty::scrollLeft)), jsValueToNativeValue(ctx, argv[0])}; return element->callNativeMethods("setViewModuleProperty", 2, args); } IMPL_PROPERTY_GETTER(Element, scrollHeight)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { getDartMethod()->flushUICommand(); - auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); + auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); NativeValue args[] = {Native_NewInt32(static_cast(ViewModuleProperty::scrollHeight))}; return element->callNativeMethods("getViewModuleProperty", 1, args); } IMPL_PROPERTY_GETTER(Element, scrollWidth)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { getDartMethod()->flushUICommand(); - auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); + auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); NativeValue args[] = {Native_NewInt32(static_cast(ViewModuleProperty::scrollWidth))}; return element->callNativeMethods("getViewModuleProperty", 1, args); } // Definition for firstElementChild IMPL_PROPERTY_GETTER(Element, firstElementChild)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { - auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); + auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); int32_t len = arrayGetLength(ctx, element->childNodes); for (int i = 0; i < len; i++) { @@ -527,7 +567,7 @@ IMPL_PROPERTY_GETTER(Element, firstElementChild)(JSContext* ctx, JSValue this_va // Definition for lastElementChild IMPL_PROPERTY_GETTER(Element, lastElementChild)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { - auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); + auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); int32_t len = arrayGetLength(ctx, element->childNodes); for (int i = len - 1; i >= 0; i--) { @@ -543,7 +583,7 @@ IMPL_PROPERTY_GETTER(Element, lastElementChild)(JSContext* ctx, JSValue this_val } IMPL_PROPERTY_GETTER(Element, children)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { - auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); + auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); JSValue array = JS_NewArray(ctx); JSValue pushMethod = JS_GetPropertyStr(ctx, array, "push"); @@ -565,16 +605,16 @@ IMPL_PROPERTY_GETTER(Element, children)(JSContext* ctx, JSValue this_val, int ar } IMPL_PROPERTY_GETTER(Element, attributes)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { - auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); + auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); return JS_DupValue(ctx, element->m_attributes->toQuickJS()); } IMPL_PROPERTY_GETTER(Element, innerHTML)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { - auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); + auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); return JS_NewString(ctx, element->innerHTML().c_str()); } IMPL_PROPERTY_SETTER(Element, innerHTML)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { - auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); + auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); const char* chtml = JS_ToCString(ctx, argv[0]); if (element->hasNodeFlag(NodeInstance::NodeFlag::IsTemplateElement)) { @@ -589,20 +629,20 @@ IMPL_PROPERTY_SETTER(Element, innerHTML)(JSContext* ctx, JSValue this_val, int a } IMPL_PROPERTY_GETTER(Element, outerHTML)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { - auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); + auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); return JS_NewString(ctx, element->outerHTML().c_str()); } IMPL_PROPERTY_SETTER(Element, outerHTML)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { return JS_NULL; } -JSClassID ElementInstance::classID() { +JSClassID Element::classID() { return Element::classId(); } -ElementInstance::~ElementInstance() {} +Element::~Element() {} -JSValue ElementInstance::internalGetTextContent() { +JSValue Element::internalGetTextContent() { JSValue array = JS_NewArray(m_ctx); JSValue pushMethod = JS_GetPropertyStr(m_ctx, array, "push"); @@ -629,7 +669,7 @@ JSValue ElementInstance::internalGetTextContent() { return returnValue; } -void ElementInstance::internalSetTextContent(JSValue content) { +void Element::internalSetTextContent(JSValue content) { internalClearChild(); JSValue textNodeValue = JS_CallConstructor(m_ctx, TextNode::instance(m_context)->jsObject, 1, &content); @@ -638,7 +678,7 @@ void ElementInstance::internalSetTextContent(JSValue content) { JS_FreeValue(m_ctx, textNodeValue); } -std::shared_ptr ElementInstance::classNames() { +std::shared_ptr Element::classNames() { return m_attributes->className(); } @@ -692,17 +732,17 @@ bool SpaceSplitString::containsAll(std::string s) { return flag; } -std::string ElementInstance::tagName() { +std::string Element::tagName() { std::string tagName = std::string(m_tagName); std::transform(tagName.begin(), tagName.end(), tagName.begin(), ::toupper); return tagName; } -std::string ElementInstance::getRegisteredTagName() { +std::string Element::getRegisteredTagName() { return m_tagName; } -std::string ElementInstance::outerHTML() { +std::string Element::outerHTML() { std::string s = "<" + getRegisteredTagName(); // Read attributes @@ -726,7 +766,7 @@ std::string ElementInstance::outerHTML() { return s; } -std::string ElementInstance::innerHTML() { +std::string Element::innerHTML() { std::string s; // If Element is TemplateElement, the innerHTML content is the content of documentFragment. @@ -745,7 +785,7 @@ std::string ElementInstance::innerHTML() { JSValue c = JS_GetPropertyUint32(m_ctx, parent->childNodes, i); auto* node = static_cast(JS_GetOpaque(c, Node::classId(c))); if (node->nodeType == NodeType::ELEMENT_NODE) { - s += reinterpret_cast(node)->outerHTML(); + s += reinterpret_cast(node)->outerHTML(); } else if (node->nodeType == NodeType::TEXT_NODE) { s += reinterpret_cast(node)->toString(); } @@ -755,12 +795,12 @@ std::string ElementInstance::innerHTML() { return s; } -void ElementInstance::_notifyNodeRemoved(NodeInstance* insertionNode) { +void Element::_notifyNodeRemoved(NodeInstance* insertionNode) { if (insertionNode->isConnected()) { traverseNode(this, [](NodeInstance* node) { auto* Element = Element::instance(node->m_context); if (node->prototype() == Element) { - auto element = reinterpret_cast(node); + auto element = reinterpret_cast(node); element->_notifyChildRemoved(); } @@ -769,7 +809,7 @@ void ElementInstance::_notifyNodeRemoved(NodeInstance* insertionNode) { } } -void ElementInstance::_notifyChildRemoved() { +void Element::_notifyChildRemoved() { std::string prop = "id"; if (m_attributes->hasAttribute(prop)) { JSValue idValue = m_attributes->getAttribute(prop); @@ -780,12 +820,12 @@ void ElementInstance::_notifyChildRemoved() { } } -void ElementInstance::_notifyNodeInsert(NodeInstance* insertNode) { +void Element::_notifyNodeInsert(NodeInstance* insertNode) { if (insertNode->isConnected()) { traverseNode(this, [](NodeInstance* node) { auto* Element = Element::instance(node->m_context); if (node->prototype() == Element) { - auto element = reinterpret_cast(node); + auto element = reinterpret_cast(node); element->_notifyChildInsert(); } @@ -794,7 +834,7 @@ void ElementInstance::_notifyNodeInsert(NodeInstance* insertNode) { } } -void ElementInstance::_notifyChildInsert() { +void Element::_notifyChildInsert() { std::string prop = "id"; if (m_attributes->hasAttribute(prop)) { JSValue idValue = m_attributes->getAttribute(prop); @@ -805,13 +845,13 @@ void ElementInstance::_notifyChildInsert() { } } -void ElementInstance::_didModifyAttribute(std::string& name, JSValue oldId, JSValue newId) { +void Element::_didModifyAttribute(std::string& name, JSValue oldId, JSValue newId) { if (name == "id") { _beforeUpdateId(oldId, newId); } } -void ElementInstance::_beforeUpdateId(JSValue oldIdValue, JSValue newIdValue) { +void Element::_beforeUpdateId(JSValue oldIdValue, JSValue newIdValue) { JSAtom oldId = JS_ValueToAtom(m_ctx, oldIdValue); JSAtom newId = JS_ValueToAtom(m_ctx, newIdValue); @@ -833,14 +873,14 @@ void ElementInstance::_beforeUpdateId(JSValue oldIdValue, JSValue newIdValue) { JS_FreeAtom(m_ctx, newId); } -void ElementInstance::trace(JSRuntime* rt, JSValue val, JS_MarkFunc* mark_func) { +void Element::trace(JSRuntime* rt, JSValue val, JS_MarkFunc* mark_func) { if (m_attributes != nullptr) { JS_MarkValue(rt, m_attributes->toQuickJS(), mark_func); } NodeInstance::trace(rt, val, mark_func); } -ElementInstance::ElementInstance(Element* element, std::string tagName, bool shouldAddUICommand) +Element::Element(Element* element, std::string tagName, bool shouldAddUICommand) : m_tagName(tagName), NodeInstance(element, NodeType::ELEMENT_NODE, Element::classId(), exoticMethods, "Element") { m_attributes = makeGarbageCollected()->initialize(m_ctx, &ElementAttributes::classId); JSValue arguments[] = {jsObject}; @@ -855,9 +895,9 @@ ElementInstance::ElementInstance(Element* element, std::string tagName, bool sho } } -JSClassExoticMethods ElementInstance::exoticMethods{nullptr, nullptr, nullptr, nullptr, hasProperty, getProperty, setProperty}; +JSClassExoticMethods Element::exoticMethods{nullptr, nullptr, nullptr, nullptr, hasProperty, getProperty, setProperty}; -StyleDeclarationInstance* ElementInstance::style() { +StyleDeclarationInstance* Element::style() { return m_style; } diff --git a/bridge/bindings/qjs/dom/element.h b/bridge/bindings/qjs/dom/element.h index e94295155d..28577ee00b 100644 --- a/bridge/bindings/qjs/dom/element.h +++ b/bridge/bindings/qjs/dom/element.h @@ -16,11 +16,9 @@ namespace kraken::binding::qjs { void bindElement(std::unique_ptr& context); -class ElementInstance; - class Element; -using ElementCreator = ElementInstance* (*)(Element* element, std::string tagName); +using ElementCreator = Element* (*)(Element* element, std::string tagName); struct NativeBoundingClientRect { double x; @@ -74,27 +72,19 @@ bool isJavaScriptExtensionElementInstance(ExecutionContext* context, JSValue ins class Element : public Node { public: - static JSClassID kElementClassId; - Element() = delete; - explicit Element(ExecutionContext* context); - - static JSClassID classId(); - - JSValue instanceConstructor(JSContext* ctx, JSValue func_obj, JSValue this_val, int argc, JSValue* argv) override; - - static JSValue getBoundingClientRect(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); - static JSValue hasAttribute(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); - static JSValue setAttribute(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); - static JSValue getAttribute(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); - static JSValue removeAttribute(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); - static JSValue toBlob(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); - static JSValue click(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); - static JSValue scroll(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); - static JSValue scrollBy(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); - - OBJECT_INSTANCE(Element); + static JSClassID classId; + static Element* create(JSContext* ctx); + + DEFINE_FUNCTION(getBoundingClientRect); + DEFINE_FUNCTION(hasAttribute); + DEFINE_FUNCTION(setAttribute); + DEFINE_FUNCTION(getAttribute); + DEFINE_FUNCTION(removeAttribute); + DEFINE_FUNCTION(toBlob); + DEFINE_FUNCTION(click); + DEFINE_FUNCTION(scroll); + DEFINE_FUNCTION(scrollBy); - private: DEFINE_PROTOTYPE_READONLY_PROPERTY(nodeName); DEFINE_PROTOTYPE_READONLY_PROPERTY(tagName); DEFINE_PROTOTYPE_READONLY_PROPERTY(offsetLeft); @@ -118,29 +108,6 @@ class Element : public Node { DEFINE_PROTOTYPE_PROPERTY(scrollTop); DEFINE_PROTOTYPE_PROPERTY(scrollLeft); - DEFINE_PROTOTYPE_FUNCTION(getBoundingClientRect, 0); - DEFINE_PROTOTYPE_FUNCTION(hasAttribute, 1); - DEFINE_PROTOTYPE_FUNCTION(setAttribute, 2); - DEFINE_PROTOTYPE_FUNCTION(getAttribute, 2); - DEFINE_PROTOTYPE_FUNCTION(removeAttribute, 1); - DEFINE_PROTOTYPE_FUNCTION(toBlob, 0); - DEFINE_PROTOTYPE_FUNCTION(click, 2); - DEFINE_PROTOTYPE_FUNCTION(scroll, 2); - // ScrollTo is same as scroll which reuse scroll functions. Macro expand is not support here. - ObjectFunction m_scrollTo{m_context, m_prototypeObject, "scrollTo", scroll, 2}; - DEFINE_PROTOTYPE_FUNCTION(scrollBy, 2); - friend ElementInstance; -}; - -struct PersistElement { - ElementInstance* element; - list_head link; -}; - -class ElementInstance : public NodeInstance { - public: - ElementInstance() = delete; - ~ElementInstance(); JSValue internalGetTextContent() override; void internalSetTextContent(JSValue content) override; @@ -151,37 +118,63 @@ class ElementInstance : public NodeInstance { std::string innerHTML(); StyleDeclarationInstance* style(); - static inline JSClassID classID(); + void trace(JSRuntime* rt, JSValue val, JS_MarkFunc* mark_func) const override; + void dispose() const override; protected: - explicit ElementInstance(Element* element, std::string tagName, bool shouldAddUICommand); - - void trace(JSRuntime* rt, JSValue val, JS_MarkFunc* mark_func) override; - + StyleDeclarationInstance* m_style{nullptr}; + ElementAttributes* m_attributes{nullptr}; private: - void _notifyNodeRemoved(NodeInstance* node) override; + std::string m_tagName; + void _notifyNodeRemoved(Node* node) override; void _notifyChildRemoved(); - void _notifyNodeInsert(NodeInstance* insertNode) override; + void _notifyNodeInsert(Node* insertNode) override; void _notifyChildInsert(); void _didModifyAttribute(std::string& name, JSValue oldId, JSValue newId); void _beforeUpdateId(JSValue oldIdValue, JSValue newIdValue); - std::string m_tagName; - friend Element; - friend NodeInstance; - friend Node; - friend DocumentInstance; - StyleDeclarationInstance* m_style{nullptr}; - ElementAttributes* m_attributes{nullptr}; - static JSClassExoticMethods exoticMethods; + friend class Node; }; -class BoundingClientRect : public HostObject { +struct PersistElement { + Element* element; + list_head link; +}; + +auto elementCreator = [](JSContext* ctx, JSValueConst func_obj, JSValueConst this_val, int argc, JSValueConst* argv, int flags) -> JSValue { + if (argc == 0) { + return JS_ThrowTypeError(ctx, "Illegal constructor"); + } + JSValue tagName = argv[0]; + + if (!JS_IsString(tagName)) { + return JS_ThrowTypeError(ctx, "Illegal constructor"); + } + + auto* context = static_cast(JS_GetContextOpaque(ctx)); + std::string name = jsValueToStdString(ctx, tagName); + + Element* element = Element::create(ctx); + + auto* document = context->document(); +// auto* Document = Document::instance(context); +// if (Document->isCustomElement(name)) { +// return JS_CallConstructor(ctx, Document->getElementConstructor(context, name), argc, argv); +// } +// +// auto* element = new Element(this, name, true); +// return element->jsObject; +}; + +const WrapperTypeInfo elementTypeInfo = {"Element", &nodeTypeInfo, elementCreator}; + +class BoundingClientRect : public GarbageCollected { public: BoundingClientRect() = delete; - explicit BoundingClientRect(ExecutionContext* context, NativeBoundingClientRect* nativeBoundingClientRect) - : HostObject(context, "BoundingClientRect"), m_nativeBoundingClientRect(nativeBoundingClientRect){}; + explicit BoundingClientRect(ExecutionContext* context, NativeBoundingClientRect* nativeBoundingClientRect) : GarbageCollected(), m_nativeBoundingClientRect(nativeBoundingClientRect){}; + + const char* getHumanReadableName() const override { return "BoundingClientRect"; } private: DEFINE_READONLY_PROPERTY(x); diff --git a/bridge/bindings/qjs/dom/event_target.cc b/bridge/bindings/qjs/dom/event_target.cc index 4e31c96b0b..b5958c687b 100644 --- a/bridge/bindings/qjs/dom/event_target.cc +++ b/bridge/bindings/qjs/dom/event_target.cc @@ -21,23 +21,23 @@ static std::atomic globalEventTargetId{0}; void bindEventTarget(std::unique_ptr& context) { auto* contextData = context->contextData(); - JSValue classObject = contextData->constructorForType(&eventTargetTypeInfo); + JSValue constructor = contextData->constructorForType(&eventTargetTypeInfo); JSValue prototypeObject = contextData->prototypeForType(&eventTargetTypeInfo); - installFunctionProperty(context.get(), prototypeObject, "addEventListener", EventTarget::addEventListener, 3); - installFunctionProperty(context.get(), prototypeObject, "removeEventListener", EventTarget::removeEventListener, 2); - installFunctionProperty(context.get(), prototypeObject, "dispatchEvent", EventTarget::dispatchEvent, 1); + INSTALL_FUNCTION(EventTarget, prototypeObject, addEventListener, 3); + INSTALL_FUNCTION(EventTarget, prototypeObject, removeEventListener, 2); + INSTALL_FUNCTION(EventTarget, prototypeObject, dispatchEvent, 1); // Set globalThis and Window's prototype to EventTarget's prototype to support EventTarget methods in global. - JS_SetPrototype(context->ctx(), context->global(), classObject); - context->defineGlobalProperty("EventTarget", classObject); + JS_SetPrototype(context->ctx(), context->global(), constructor); + context->defineGlobalProperty("EventTarget", constructor); } EventTarget::EventTarget() : GarbageCollected() { m_eventTargetId = globalEventTargetId++; } -JSValue EventTarget::addEventListener(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { +IMPL_FUNCTION(EventTarget, addEventListener)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { if (argc < 2) { return JS_ThrowTypeError(ctx, "Failed to addEventListener: type and listener are required."); } @@ -75,7 +75,7 @@ JSValue EventTarget::addEventListener(JSContext* ctx, JSValue this_val, int argc return JS_UNDEFINED; } -JSValue EventTarget::removeEventListener(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { +IMPL_FUNCTION(EventTarget, removeEventListener)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { if (argc < 2) { return JS_ThrowTypeError(ctx, "Failed to removeEventListener: at least type and listener are required."); } @@ -116,7 +116,7 @@ JSValue EventTarget::removeEventListener(JSContext* ctx, JSValue this_val, int a return JS_UNDEFINED; } -JSValue EventTarget::dispatchEvent(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { +IMPL_FUNCTION(EventTarget, dispatchEvent)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { if (argc != 1) { return JS_ThrowTypeError(ctx, "Failed to dispatchEvent: first arguments should be an event object"); } @@ -132,7 +132,14 @@ JSValue EventTarget::dispatchEvent(JSContext* ctx, JSValue this_val, int argc, J } EventTarget* EventTarget::create(JSContext* ctx) { - return makeGarbageCollected()->initialize(ctx, &EventTarget::classId, nullptr); + auto* context = static_cast(JS_GetContextOpaque(ctx)); + JSValue prototype = context->contextData()->prototypeForType(&eventTargetTypeInfo); + auto* eventTarget = makeGarbageCollected()->initialize(ctx, &EventTarget::classId, nullptr); + + // Let eventTarget instance inherit EventTarget prototype methods. + JS_SetPrototype(ctx, eventTarget->toQuickJS(), prototype); + + return eventTarget; } bool EventTarget::dispatchEvent(EventInstance* event) { diff --git a/bridge/bindings/qjs/dom/event_target.h b/bridge/bindings/qjs/dom/event_target.h index fd6064c816..f293bfb42a 100644 --- a/bridge/bindings/qjs/dom/event_target.h +++ b/bridge/bindings/qjs/dom/event_target.h @@ -59,15 +59,16 @@ class EventTarget : public GarbageCollected { EventTarget(); static JSClassID classId; static EventTarget* create(JSContext* ctx); - static JSValue addEventListener(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); - static JSValue removeEventListener(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); - static JSValue dispatchEvent(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); + + DEFINE_FUNCTION(addEventListener); + DEFINE_FUNCTION(removeEventListener); + DEFINE_FUNCTION(dispatchEvent); void trace(JSRuntime* rt, JSValue val, JS_MarkFunc* mark_func) const override; void dispose() const override; virtual bool dispatchEvent(EventInstance* event); - inline int32_t eventTargetId() const { return m_eventTargetId; } + FORCE_INLINE int32_t eventTargetId() const { return m_eventTargetId; } protected: JSValue callNativeMethods(const char* method, int32_t argc, NativeValue* argv); @@ -100,18 +101,10 @@ class EventTarget : public GarbageCollected { // property are not defined by Object.defineProperty or setProperty. // We store there values in here. EventTargetProperties m_properties{this->m_ctx}; - }; auto eventTargetCreator = [](JSContext* ctx, JSValueConst func_obj, JSValueConst this_val, int argc, JSValueConst* argv, int flags) -> JSValue { - auto* type = static_cast(JS_GetOpaque(func_obj, JSValueGetClassId(func_obj))); auto* eventTarget = EventTarget::create(ctx); - auto* context = static_cast(JS_GetContextOpaque(ctx)); - JSValue prototype = context->contextData()->prototypeForType(type); - - // Let eventTarget instance inherit EventTarget prototype methods. - JS_SetPrototype(ctx, eventTarget->toQuickJS(), prototype); - return eventTarget->toQuickJS(); }; diff --git a/bridge/bindings/qjs/dom/node.cc b/bridge/bindings/qjs/dom/node.cc index 63cfc06efb..2f3c5fc468 100644 --- a/bridge/bindings/qjs/dom/node.cc +++ b/bridge/bindings/qjs/dom/node.cc @@ -15,30 +15,37 @@ namespace kraken::binding::qjs { void bindNode(std::unique_ptr& context) { - auto* constructor = Node::instance(context.get()); - context->defineGlobalProperty("Node", constructor->jsObject); -} + auto* contextData = context->contextData(); + JSValue constructor = Node::constructor(context.get()); + JSValue prototype = Node::prototype(context.get()); + + // Install methods to Node.prototype. + INSTALL_FUNCTION(Node, prototype, cloneNode, 1); + INSTALL_FUNCTION(Node, prototype, appendChild, 1); + INSTALL_FUNCTION(Node, prototype, remove, 0); + INSTALL_FUNCTION(Node, prototype, removeChild, 1); + INSTALL_FUNCTION(Node, prototype, insertBefore, 2); + INSTALL_FUNCTION(Node, prototype, replaceChild, 2); -JSValue Node::instanceConstructor(JSContext* ctx, JSValue func_obj, JSValue this_val, int argc, JSValue* argv) { - return JS_ThrowTypeError(ctx, "Illegal constructor"); + context->defineGlobalProperty("Node", constructor); } -JSClassID Node::classId() { - assert_m(false, "classId is not implemented"); - return 0; +JSValue Node::constructor(ExecutionContext* context) { + return context->contextData()->constructorForType(&nodeTypeInfo); } -JSClassID Node::classId(JSValue& value) { - JSClassID classId = JSValueGetClassId(value); - if (classId == Element::classId() || classId == Document::classId() || classId == TextNode::classId() || classId == Comment::classId() || classId == DocumentFragment::classId()) { - return classId; - } +JSValue Node::prototype(ExecutionContext* context) { + return context->contextData()->prototypeForType(&nodeTypeInfo); +} - return 0; +Node* Node::create(JSContext* ctx) { + return nullptr; } -JSValue Node::cloneNode(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { - auto selfInstance = static_cast(JS_GetOpaque(this_val, Node::classId(this_val))); +JSClassID Node::classId{0}; + +IMPL_FUNCTION(Node, cloneNode)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { + auto self = static_cast(JS_GetOpaque(this_val, JSValueGetClassId(this_val))); JSValue deepValue; if (argc < 1) { @@ -52,38 +59,38 @@ JSValue Node::cloneNode(JSContext* ctx, JSValue this_val, int argc, JSValue* arg } bool deep = JS_ToBool(ctx, deepValue); - if (selfInstance->nodeType == NodeType::ELEMENT_NODE) { - JSValue newElement = copyNodeValue(ctx, selfInstance); - auto newElementInstance = static_cast(JS_GetOpaque(newElement, Node::classId(newElement))); + if (self->nodeType == NodeType::ELEMENT_NODE) { + JSValue newElementValue = copyNodeValue(ctx, self); + auto* newElement = static_cast(JS_GetOpaque(newElementValue, JSValueGetClassId(newElementValue))); if (deep) { - traverseCloneNode(ctx, selfInstance, newElementInstance); + traverseCloneNode(ctx, self, newElement); } - return newElementInstance->jsObject; - } else if (selfInstance->nodeType == NodeType::TEXT_NODE) { - auto textNode = static_cast(selfInstance); - JSValue newTextNode = copyNodeValue(ctx, static_cast(textNode)); + return newElement->jsObject; + } else if (self->nodeType == NodeType::TEXT_NODE) { + auto textNode = static_cast(self); + JSValue newTextNode = copyNodeValue(ctx, static_cast(textNode)); return newTextNode; - } else if (selfInstance->nodeType == NodeType::DOCUMENT_FRAGMENT_NODE) { - JSValue newFragment = JS_CallConstructor(ctx, DocumentFragment::instance(selfInstance->m_context)->jsObject, 0, nullptr); - auto* newFragmentInstance = static_cast(JS_GetOpaque(newFragment, Node::classId(newFragment))); + } else if (self->nodeType == NodeType::DOCUMENT_FRAGMENT_NODE) { + JSValue newFragmentValue = JS_CallConstructor(ctx, DocumentFragment::constructor(self->context()), 0, nullptr); + auto* newFragment = static_cast(JS_GetOpaque(newFragmentValue, JSValueGetClassId(newFragmentValue))); if (deep) { - traverseCloneNode(ctx, selfInstance, newFragmentInstance); + traverseCloneNode(ctx, self, newFragment); } - return newFragment; + return newFragmentValue; } return JS_NULL; } -JSValue Node::appendChild(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { +IMPL_FUNCTION(Node, appendChild)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { if (argc != 1) { return JS_ThrowTypeError(ctx, "Failed to execute 'appendChild' on 'Node': first argument is required."); } - auto selfInstance = static_cast(JS_GetOpaque(this_val, Node::classId(this_val))); - if (selfInstance == nullptr) + auto self = static_cast(JS_GetOpaque(this_val, JSValueGetClassId(this_val))); + if (self == nullptr) return JS_ThrowTypeError(ctx, "this object is not a instance of Node."); JSValue nodeValue = argv[0]; @@ -91,39 +98,38 @@ JSValue Node::appendChild(JSContext* ctx, JSValue this_val, int argc, JSValue* a return JS_ThrowTypeError(ctx, "Failed to execute 'appendChild' on 'Node': first arguments should be an Node type."); } - auto* nodeInstance = static_cast(JS_GetOpaque(nodeValue, Node::classId(nodeValue))); + auto* node = static_cast(JS_GetOpaque(nodeValue, JSValueGetClassId(nodeValue))); - if (nodeInstance == nullptr || nodeInstance->document() != selfInstance->document()) { + if (node == nullptr || node->ownerDocument() != self->ownerDocument()) { return JS_ThrowTypeError(ctx, "Failed to execute 'appendChild' on 'Node': first arguments should be an Node type."); } - if (nodeInstance == selfInstance) { + if (node == self) { return JS_ThrowTypeError(ctx, "Failed to execute 'appendChild' on 'Node': The new child element contains the parent."); } - if (nodeInstance->hasNodeFlag(NodeInstance::NodeFlag::IsDocumentFragment)) { - size_t len = arrayGetLength(ctx, nodeInstance->childNodes); + if (node->hasNodeFlag(Node::NodeFlag::IsDocumentFragment)) { + size_t len = arrayGetLength(ctx, node->childNodes); for (int i = 0; i < len; i++) { - JSValue n = JS_GetPropertyUint32(ctx, nodeInstance->childNodes, i); - auto* node = static_cast(JS_GetOpaque(n, Node::classId(n))); - selfInstance->internalAppendChild(node); + JSValue n = JS_GetPropertyUint32(ctx, node->childNodes, i); + self->internalAppendChild(static_cast(JS_GetOpaque(n, JSValueGetClassId(n)))); JS_FreeValue(ctx, n); } - JS_SetPropertyStr(ctx, nodeInstance->childNodes, "length", JS_NewUint32(ctx, 0)); + JS_SetPropertyStr(ctx, node->childNodes, "length", JS_NewUint32(ctx, 0)); } else { - selfInstance->ensureDetached(nodeInstance); - selfInstance->internalAppendChild(nodeInstance); + self->ensureDetached(node); + self->internalAppendChild(node); } - return JS_DupValue(ctx, nodeInstance->jsObject); + return JS_DupValue(ctx, node->jsObject); } -JSValue Node::remove(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { - auto selfInstance = static_cast(JS_GetOpaque(this_val, Node::classId(this_val))); - selfInstance->internalRemove(); +IMPL_FUNCTION(Node, remove)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { + auto self = static_cast(JS_GetOpaque(this_val, JSValueGetClassId(this_val))); + self->internalRemove(); return JS_UNDEFINED; } -JSValue Node::removeChild(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { +IMPL_FUNCTION(Node, removeChild)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { if (argc < 1) { return JS_ThrowTypeError(ctx, "Uncaught TypeError: Failed to execute 'removeChild' on 'Node': 1 arguments required"); } @@ -134,18 +140,18 @@ JSValue Node::removeChild(JSContext* ctx, JSValue this_val, int argc, JSValue* a return JS_ThrowTypeError(ctx, "Uncaught TypeError: Failed to execute 'removeChild' on 'Node': 1st arguments is not object"); } - auto selfInstance = static_cast(JS_GetOpaque(this_val, Node::classId(this_val))); - auto nodeInstance = static_cast(JS_GetOpaque(nodeValue, Node::classId(nodeValue))); + auto self = static_cast(JS_GetOpaque(this_val, JSValueGetClassId(this_val))); + auto node = static_cast(JS_GetOpaque(nodeValue, JSValueGetClassId(nodeValue))); - if (nodeInstance == nullptr || nodeInstance->document() != selfInstance->document()) { + if (node == nullptr || node->ownerDocument() != self->ownerDocument()) { return JS_ThrowTypeError(ctx, "Failed to execute 'removeChild' on 'Node': 1st arguments is not a Node object."); } - auto removedNode = selfInstance->internalRemoveChild(nodeInstance); + auto removedNode = self->internalRemoveChild(node); return JS_DupValue(ctx, removedNode->jsObject); } -JSValue Node::insertBefore(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { +IMPL_FUNCTION(Node, insertBefore)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { if (argc < 2) { return JS_ThrowTypeError(ctx, "Failed to execute 'insertBefore' on 'Node': 2 arguments is required."); } @@ -157,41 +163,40 @@ JSValue Node::insertBefore(JSContext* ctx, JSValue this_val, int argc, JSValue* return JS_ThrowTypeError(ctx, "Failed to execute 'insertBefore' on 'Node': the node element is not object."); } - NodeInstance* referenceInstance = nullptr; + Node* reference = nullptr; if (JS_IsObject(referenceNodeValue)) { - referenceInstance = static_cast(JS_GetOpaque(referenceNodeValue, Node::classId(referenceNodeValue))); + reference = static_cast(JS_GetOpaque(referenceNodeValue, JSValueGetClassId(referenceNodeValue))); } else if (!JS_IsNull(referenceNodeValue)) { return JS_ThrowTypeError(ctx, "TypeError: Failed to execute 'insertBefore' on 'Node': parameter 2 is not of type 'Node'"); } - auto selfInstance = static_cast(JS_GetOpaque(this_val, Node::classId(this_val))); - auto nodeInstance = static_cast(JS_GetOpaque(nodeValue, Node::classId(nodeValue))); + auto self = static_cast(JS_GetOpaque(this_val, JSValueGetClassId(this_val))); + auto node = static_cast(JS_GetOpaque(nodeValue, JSValueGetClassId(nodeValue))); - if (nodeInstance == nullptr || nodeInstance->document() != selfInstance->document()) { + if (node == nullptr || node->ownerDocument() != self->ownerDocument()) { return JS_ThrowTypeError(ctx, "Failed to execute 'insertBefore' on 'Node': parameter 1 is not of type 'Node'"); } - if (nodeInstance->hasNodeFlag(NodeInstance::NodeFlag::IsDocumentFragment)) { - size_t len = arrayGetLength(ctx, nodeInstance->childNodes); + if (node->hasNodeFlag(Node::NodeFlag::IsDocumentFragment)) { + size_t len = arrayGetLength(ctx, node->childNodes); for (int i = 0; i < len; i++) { - JSValue n = JS_GetPropertyUint32(ctx, nodeInstance->childNodes, i); - auto* node = static_cast(JS_GetOpaque(n, Node::classId(n))); - selfInstance->internalInsertBefore(node, referenceInstance); + JSValue n = JS_GetPropertyUint32(ctx, node->childNodes, i); + self->internalInsertBefore(static_cast(JS_GetOpaque(n, JSValueGetClassId(n))), reference); JS_FreeValue(ctx, n); } // Clear fragment childNodes reference. - JS_SetPropertyStr(ctx, nodeInstance->childNodes, "length", JS_NewUint32(ctx, 0)); + JS_SetPropertyStr(ctx, node->childNodes, "length", JS_NewUint32(ctx, 0)); } else { - selfInstance->ensureDetached(nodeInstance); - selfInstance->internalInsertBefore(nodeInstance, referenceInstance); + self->ensureDetached(node); + self->internalInsertBefore(node, reference); } return JS_NULL; } -JSValue Node::replaceChild(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { +IMPL_FUNCTION(Node, replaceChild)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { if (argc < 2) { return JS_ThrowTypeError(ctx, "Uncaught TypeError: Failed to execute 'replaceChild' on 'Node': 2 arguments required"); } @@ -207,66 +212,66 @@ JSValue Node::replaceChild(JSContext* ctx, JSValue this_val, int argc, JSValue* return JS_ThrowTypeError(ctx, "Uncaught TypeError: Failed to execute 'replaceChild' on 'Node': 2 arguments is not object."); } - auto selfInstance = static_cast(JS_GetOpaque(this_val, Node::classId(this_val))); - auto newChildInstance = static_cast(JS_GetOpaque(newChildValue, Node::classId(newChildValue))); - auto oldChildInstance = static_cast(JS_GetOpaque(oldChildValue, Node::classId(oldChildValue))); + auto self = static_cast(JS_GetOpaque(this_val, JSValueGetClassId(this_val))); + auto newChild = static_cast(JS_GetOpaque(newChildValue, JSValueGetClassId(newChildValue))); + auto oldChild = static_cast(JS_GetOpaque(oldChildValue, JSValueGetClassId(oldChildValue))); - if (oldChildInstance == nullptr || JS_VALUE_GET_PTR(oldChildInstance->parentNode) != JS_VALUE_GET_PTR(selfInstance->jsObject) || oldChildInstance->document() != selfInstance->document()) { + if (oldChild == nullptr || JS_VALUE_GET_PTR(oldChild->parentNode) != JS_VALUE_GET_PTR(self->jsObject) || oldChild->ownerDocument() != self->ownerDocument()) { return JS_ThrowTypeError(ctx, "Failed to execute 'replaceChild' on 'Node': The node to be replaced is not a child of this node."); } - if (newChildInstance == nullptr || newChildInstance->document() != selfInstance->document()) { + if (newChild == nullptr || newChild->ownerDocument() != self->ownerDocument()) { return JS_ThrowTypeError(ctx, "Failed to execute 'replaceChild' on 'Node': The new node is not a type of node."); } - if (newChildInstance->hasNodeFlag(NodeInstance::NodeFlag::IsDocumentFragment)) { - size_t len = arrayGetLength(ctx, newChildInstance->childNodes); + if (newChild->hasNodeFlag(Node::NodeFlag::IsDocumentFragment)) { + size_t len = arrayGetLength(ctx, newChild->childNodes); for (int i = 0; i < len; i++) { - JSValue n = JS_GetPropertyUint32(ctx, newChildInstance->childNodes, i); - auto* node = static_cast(JS_GetOpaque(n, Node::classId(n))); - selfInstance->internalInsertBefore(node, oldChildInstance); + JSValue n = JS_GetPropertyUint32(ctx, newChild->childNodes, i); + auto* node = static_cast(JS_GetOpaque(n, JSValueGetClassId(n))); + self->internalInsertBefore(node, oldChild); JS_FreeValue(ctx, n); } - selfInstance->internalRemoveChild(oldChildInstance); + self->internalRemoveChild(oldChild); // Clear fragment childNodes reference. - JS_SetPropertyStr(ctx, newChildInstance->childNodes, "length", JS_NewUint32(ctx, 0)); + JS_SetPropertyStr(ctx, newChild->childNodes, "length", JS_NewUint32(ctx, 0)); } else { - selfInstance->ensureDetached(newChildInstance); - selfInstance->internalReplaceChild(newChildInstance, oldChildInstance); + self->ensureDetached(newChild); + self->internalReplaceChild(newChild, oldChild); } - return JS_DupValue(ctx, oldChildInstance->jsObject); + return JS_DupValue(ctx, oldChild->jsObject); } -void Node::traverseCloneNode(JSContext* ctx, NodeInstance* baseNode, NodeInstance* targetNode) { +void Node::traverseCloneNode(JSContext* ctx, Node* baseNode, Node* targetNode) { int32_t len = arrayGetLength(ctx, baseNode->childNodes); for (int i = 0; i < len; i++) { JSValue n = JS_GetPropertyUint32(ctx, baseNode->childNodes, i); - auto* node = static_cast(JS_GetOpaque(n, Node::classId(n))); - JSValue newNode = copyNodeValue(ctx, node); - auto newNodeInstance = static_cast(JS_GetOpaque(newNode, Node::classId(newNode))); - targetNode->ensureDetached(newNodeInstance); - targetNode->internalAppendChild(newNodeInstance); + auto* node = static_cast(JS_GetOpaque(n, JSValueGetClassId(n))); + JSValue newNodeValue = copyNodeValue(ctx, node); + auto newNode = static_cast(JS_GetOpaque(newNodeValue, JSValueGetClassId(newNodeValue))); + targetNode->ensureDetached(newNode); + targetNode->internalAppendChild(newNode); // element node needs recursive child nodes. if (node->nodeType == NodeType::ELEMENT_NODE) { - traverseCloneNode(ctx, node, newNodeInstance); + traverseCloneNode(ctx, node, newNode); } - JS_FreeValue(ctx, newNode); + JS_FreeValue(ctx, newNodeValue); JS_FreeValue(ctx, n); } } -JSValue Node::copyNodeValue(JSContext* ctx, NodeInstance* node) { +JSValue Node::copyNodeValue(JSContext* ctx, Node* node) { if (node->nodeType == NodeType::ELEMENT_NODE) { - auto* element = reinterpret_cast(node); + auto* element = reinterpret_cast(node); /* createElement */ std::string tagName = element->getRegisteredTagName(); - JSValue tagNameValue = JS_NewString(element->m_ctx, tagName.c_str()); + JSValue tagNameValue = JS_NewString(element->ctx(), tagName.c_str()); JSValue arguments[] = {tagNameValue}; - JSValue newElementValue = JS_CallConstructor(element->context()->ctx(), Element::instance(element->context())->jsObject, 1, arguments); + JSValue newElementValue = JS_CallConstructor(element->context()->ctx(), element->context()->contextData()->constructorForType(&elementTypeInfo), 1, arguments); JS_FreeValue(ctx, tagNameValue); - auto* newElement = static_cast(JS_GetOpaque(newElementValue, Node::classId(newElementValue))); + auto* newElement = static_cast(JS_GetOpaque(newElementValue, JSValueGetClassId(newElementValue))); /* copy attributes */ newElement->m_attributes->copyWith(element->m_attributes); @@ -275,18 +280,18 @@ JSValue Node::copyNodeValue(JSContext* ctx, NodeInstance* node) { newElement->m_style->copyWith(element->m_style); /* copy properties */ - ElementInstance::copyNodeProperties(newElement, element); + EventTarget::copyNodeProperties(newElement, element); - std::string newNodeEventTargetId = std::to_string(newElement->m_eventTargetId); + std::string newNodeEventTargetId = std::to_string(newElement->eventTargetId()); std::unique_ptr args_01 = stringToNativeString(newNodeEventTargetId); - element->m_context->uiCommandBuffer()->addCommand(element->m_eventTargetId, UICommand::cloneNode, *args_01, nullptr); + element->context()->uiCommandBuffer()->addCommand(element->eventTargetId(), UICommand::cloneNode, *args_01, nullptr); return newElement->jsObject; } else if (node->nodeType == TEXT_NODE) { - auto* textNode = reinterpret_cast(node); + auto* textNode = reinterpret_cast(node); JSValue textContent = textNode->internalGetTextContent(); JSValue arguments[] = {textContent}; - JSValue result = JS_CallConstructor(ctx, TextNode::instance(textNode->m_context)->jsObject, 1, arguments); + JSValue result = JS_CallConstructor(ctx, TextNode::constructor(textNode->context()), 1, arguments); JS_FreeValue(ctx, textContent); return result; } @@ -294,172 +299,172 @@ JSValue Node::copyNodeValue(JSContext* ctx, NodeInstance* node) { } IMPL_PROPERTY_GETTER(Node, isConnected)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { - auto* nodeInstance = static_cast(JS_GetOpaque(this_val, Node::classId(this_val))); - return JS_NewBool(ctx, nodeInstance->isConnected()); + auto* node = static_cast(JS_GetOpaque(this_val, JSValueGetClassId(this_val))); + return JS_NewBool(ctx, node->isConnected()); } IMPL_PROPERTY_GETTER(Node, ownerDocument)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { - auto* nodeInstance = static_cast(JS_GetOpaque(this_val, Node::classId(this_val))); - return JS_DupValue(ctx, nodeInstance->m_document->jsObject); + auto* node = static_cast(JS_GetOpaque(this_val, JSValueGetClassId(this_val))); + return JS_DupValue(ctx, node->ownerDocument()->jsObject); } IMPL_PROPERTY_GETTER(Node, firstChild)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { - auto* nodeInstance = static_cast(JS_GetOpaque(this_val, Node::classId(this_val))); - auto* instance = nodeInstance->firstChild(); + auto* node = static_cast(JS_GetOpaque(this_val, JSValueGetClassId(this_val))); + auto* instance = node->firstChild(); return instance != nullptr ? instance->jsObject : JS_NULL; } IMPL_PROPERTY_GETTER(Node, lastChild)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { - auto* nodeInstance = static_cast(JS_GetOpaque(this_val, Node::classId(this_val))); - auto* instance = nodeInstance->lastChild(); + auto* node = static_cast(JS_GetOpaque(this_val, JSValueGetClassId(this_val))); + auto* instance = node->lastChild(); return instance != nullptr ? instance->jsObject : JS_NULL; } IMPL_PROPERTY_GETTER(Node, parentNode)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { - auto* nodeInstance = static_cast(JS_GetOpaque(this_val, Node::classId(this_val))); - return JS_DupValue(ctx, nodeInstance->parentNode); + auto* node = static_cast(JS_GetOpaque(this_val, JSValueGetClassId(this_val))); + return JS_DupValue(ctx, node->parentNode); } IMPL_PROPERTY_GETTER(Node, previousSibling)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { - auto* nodeInstance = static_cast(JS_GetOpaque(this_val, Node::classId(this_val))); - auto* instance = nodeInstance->previousSibling(); + auto* node = static_cast(JS_GetOpaque(this_val, JSValueGetClassId(this_val))); + auto* instance = node->previousSibling(); return instance != nullptr ? instance->jsObject : JS_NULL; } IMPL_PROPERTY_GETTER(Node, nextSibling)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { - auto* nodeInstance = static_cast(JS_GetOpaque(this_val, Node::classId(this_val))); - auto* instance = nodeInstance->nextSibling(); + auto* node = static_cast(JS_GetOpaque(this_val, JSValueGetClassId(this_val))); + auto* instance = node->nextSibling(); return instance != nullptr ? instance->jsObject : JS_NULL; } IMPL_PROPERTY_GETTER(Node, nodeType)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { - auto* nodeInstance = static_cast(JS_GetOpaque(this_val, Node::classId(this_val))); - return JS_NewUint32(ctx, nodeInstance->nodeType); + auto* node = static_cast(JS_GetOpaque(this_val, JSValueGetClassId(this_val))); + return JS_NewUint32(ctx, node->nodeType); } IMPL_PROPERTY_GETTER(Node, textContent)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { - auto* nodeInstance = static_cast(JS_GetOpaque(this_val, Node::classId(this_val))); - return nodeInstance->internalGetTextContent(); + auto* node = static_cast(JS_GetOpaque(this_val, JSValueGetClassId(this_val))); + return node->internalGetTextContent(); } IMPL_PROPERTY_SETTER(Node, textContent)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { - auto* nodeInstance = static_cast(JS_GetOpaque(this_val, Node::classId(this_val))); - nodeInstance->internalSetTextContent(argv[0]); + auto* node = static_cast(JS_GetOpaque(this_val, JSValueGetClassId(this_val))); + node->internalSetTextContent(argv[0]); return JS_NULL; } -bool NodeInstance::isConnected() { - bool _isConnected = this == document(); - auto parent = static_cast(JS_GetOpaque(parentNode, Node::classId(parentNode))); +bool Node::isConnected() { + bool _isConnected = this == ownerDocument(); + auto parent = static_cast(JS_GetOpaque(parentNode, JSValueGetClassId(parentNode))); while (parent != nullptr && !_isConnected) { - _isConnected = parent == document(); + _isConnected = parent == ownerDocument(); JSValue parentParentNode = parent->parentNode; - parent = static_cast(JS_GetOpaque(parentParentNode, Node::classId(parentParentNode))); + parent = static_cast(JS_GetOpaque(parentParentNode, JSValueGetClassId(parentParentNode))); } return _isConnected; } -DocumentInstance* NodeInstance::ownerDocument() { +Document* Node::ownerDocument() { if (nodeType == NodeType::DOCUMENT_NODE) { return nullptr; } - return document(); + return context()->document(); } -NodeInstance* NodeInstance::firstChild() { +Node* Node::firstChild() { int32_t len = arrayGetLength(m_ctx, childNodes); if (len == 0) { return nullptr; } JSValue result = JS_GetPropertyUint32(m_ctx, childNodes, 0); - return static_cast(JS_GetOpaque(result, Node::classId(result))); + return static_cast(JS_GetOpaque(result, JSValueGetClassId(result))); } -NodeInstance* NodeInstance::lastChild() { +Node* Node::lastChild() { int32_t len = arrayGetLength(m_ctx, childNodes); if (len == 0) { return nullptr; } JSValue result = JS_GetPropertyUint32(m_ctx, childNodes, len - 1); - return static_cast(JS_GetOpaque(result, Node::classId(result))); + return static_cast(JS_GetOpaque(result, JSValueGetClassId(result))); } -NodeInstance* NodeInstance::previousSibling() { +Node* Node::previousSibling() { if (JS_IsNull(parentNode)) return nullptr; - auto* parent = static_cast(JS_GetOpaque(parentNode, Node::classId(parentNode))); + auto* parent = static_cast(JS_GetOpaque(parentNode, JSValueGetClassId(parentNode))); auto parentChildNodes = parent->childNodes; int32_t idx = arrayFindIdx(m_ctx, parentChildNodes, jsObject); int32_t parentChildNodeLen = arrayGetLength(m_ctx, parentChildNodes); if (idx - 1 < parentChildNodeLen) { JSValue result = JS_GetPropertyUint32(m_ctx, parentChildNodes, idx - 1); - return static_cast(JS_GetOpaque(result, Node::classId(result))); + return static_cast(JS_GetOpaque(result, JSValueGetClassId(result))); } return nullptr; } -NodeInstance* NodeInstance::nextSibling() { +Node* Node::nextSibling() { if (JS_IsNull(parentNode)) return nullptr; - auto* parent = static_cast(JS_GetOpaque(parentNode, Node::classId(parentNode))); + auto* parent = static_cast(JS_GetOpaque(parentNode, JSValueGetClassId(parentNode))); auto parentChildNodes = parent->childNodes; int32_t idx = arrayFindIdx(m_ctx, parentChildNodes, jsObject); int32_t parentChildNodeLen = arrayGetLength(m_ctx, parentChildNodes); if (idx + 1 < parentChildNodeLen) { JSValue result = JS_GetPropertyUint32(m_ctx, parentChildNodes, idx + 1); - return static_cast(JS_GetOpaque(result, Node::classId(result))); + return static_cast(JS_GetOpaque(result, JSValueGetClassId(result))); } return nullptr; } -void NodeInstance::internalAppendChild(NodeInstance* node) { +void Node::internalAppendChild(Node* node) { arrayPushValue(m_ctx, childNodes, node->jsObject); node->setParentNode(this); node->_notifyNodeInsert(this); - std::string nodeEventTargetId = std::to_string(node->m_eventTargetId); + std::string nodeEventTargetId = std::to_string(node->eventTargetId()); std::string position = std::string("beforeend"); std::unique_ptr args_01 = stringToNativeString(nodeEventTargetId); std::unique_ptr args_02 = stringToNativeString(position); - m_context->uiCommandBuffer()->addCommand(m_eventTargetId, UICommand::insertAdjacentNode, *args_01, *args_02, nullptr); + context()->uiCommandBuffer()->addCommand(eventTargetId(), UICommand::insertAdjacentNode, *args_01, *args_02, nullptr); } -void NodeInstance::internalRemove() { +void Node::internalRemove() { if (JS_IsNull(parentNode)) return; - auto* parent = static_cast(JS_GetOpaque(parentNode, Node::classId(parentNode))); + auto* parent = static_cast(JS_GetOpaque(parentNode, JSValueGetClassId(parentNode))); parent->internalRemoveChild(this); } -void NodeInstance::internalClearChild() { +void Node::internalClearChild() { int32_t len = arrayGetLength(m_ctx, childNodes); for (int i = 0; i < len; i++) { JSValue v = JS_GetPropertyUint32(m_ctx, childNodes, i); - auto* node = static_cast(JS_GetOpaque(v, Node::classId(v))); + auto* node = static_cast(JS_GetOpaque(v, JSValueGetClassId(v))); node->removeParentNode(); node->_notifyNodeRemoved(this); - node->m_context->uiCommandBuffer()->addCommand(node->m_eventTargetId, UICommand::removeNode, nullptr); + node->context()->uiCommandBuffer()->addCommand(node->eventTargetId(), UICommand::removeNode, nullptr); JS_FreeValue(m_ctx, v); } JS_SetPropertyStr(m_ctx, childNodes, "length", JS_NewUint32(m_ctx, 0)); } -NodeInstance* NodeInstance::internalRemoveChild(NodeInstance* node) { +Node* Node::internalRemoveChild(Node* node) { int32_t idx = arrayFindIdx(m_ctx, childNodes, node->jsObject); if (idx != -1) { arraySpliceValue(m_ctx, childNodes, idx, 1); node->removeParentNode(); node->_notifyNodeRemoved(this); - node->m_context->uiCommandBuffer()->addCommand(node->m_eventTargetId, UICommand::removeNode, nullptr); + node->context()->uiCommandBuffer()->addCommand(node->eventTargetId(), UICommand::removeNode, nullptr); } return node; } -JSValue NodeInstance::internalInsertBefore(NodeInstance* node, NodeInstance* referenceNode) { +JSValue Node::internalInsertBefore(Node* node, Node* referenceNode) { if (referenceNode == nullptr) { internalAppendChild(node); } else { @@ -468,7 +473,7 @@ JSValue NodeInstance::internalInsertBefore(NodeInstance* node, NodeInstance* ref } auto parentNodeValue = referenceNode->parentNode; - auto* parent = static_cast(JS_GetOpaque(parentNodeValue, Node::classId(parentNodeValue))); + auto* parent = static_cast(JS_GetOpaque(parentNodeValue, JSValueGetClassId(parentNodeValue))); if (parent != nullptr) { JSValue parentChildNodes = parent->childNodes; int32_t idx = arrayFindIdx(m_ctx, parentChildNodes, referenceNode->jsObject); @@ -481,23 +486,23 @@ JSValue NodeInstance::internalInsertBefore(NodeInstance* node, NodeInstance* ref node->setParentNode(parent); node->_notifyNodeInsert(parent); - std::string nodeEventTargetId = std::to_string(node->m_eventTargetId); + std::string nodeEventTargetId = std::to_string(node->eventTargetId()); std::string position = std::string("beforebegin"); std::unique_ptr args_01 = stringToNativeString(nodeEventTargetId); std::unique_ptr args_02 = stringToNativeString(position); - m_context->uiCommandBuffer()->addCommand(referenceNode->m_eventTargetId, UICommand::insertAdjacentNode, *args_01, *args_02, nullptr); + context()->uiCommandBuffer()->addCommand(referenceNode->eventTargetId(), UICommand::insertAdjacentNode, *args_01, *args_02, nullptr); } } return JS_NULL; } -JSValue NodeInstance::internalGetTextContent() { +JSValue Node::internalGetTextContent() { return JS_NULL; } -void NodeInstance::internalSetTextContent(JSValue content) {} -JSValue NodeInstance::internalReplaceChild(NodeInstance* newChild, NodeInstance* oldChild) { +void Node::internalSetTextContent(JSValue content) {} +JSValue Node::internalReplaceChild(Node* newChild, Node* oldChild) { assert_m(JS_IsNull(newChild->parentNode), "ReplaceChild Error: newChild was not detached."); oldChild->removeParentNode(); @@ -513,20 +518,20 @@ JSValue NodeInstance::internalReplaceChild(NodeInstance* newChild, NodeInstance* oldChild->_notifyNodeRemoved(this); newChild->_notifyNodeInsert(this); - std::string newChildEventTargetId = std::to_string(newChild->m_eventTargetId); + std::string newChildEventTargetId = std::to_string(newChild->eventTargetId()); std::string position = std::string("afterend"); std::unique_ptr args_01 = stringToNativeString(newChildEventTargetId); std::unique_ptr args_02 = stringToNativeString(position); - m_context->uiCommandBuffer()->addCommand(oldChild->m_eventTargetId, UICommand::insertAdjacentNode, *args_01, *args_02, nullptr); + context()->uiCommandBuffer()->addCommand(oldChild->eventTargetId(), UICommand::insertAdjacentNode, *args_01, *args_02, nullptr); - m_context->uiCommandBuffer()->addCommand(oldChild->m_eventTargetId, UICommand::removeNode, nullptr); + context()->uiCommandBuffer()->addCommand(oldChild->eventTargetId(), UICommand::removeNode, nullptr); return oldChild->jsObject; } -void NodeInstance::setParentNode(NodeInstance* parent) { +void Node::setParentNode(Node* parent) { if (!JS_IsNull(parentNode)) { JS_FreeValue(m_ctx, parentNode); } @@ -534,7 +539,7 @@ void NodeInstance::setParentNode(NodeInstance* parent) { parentNode = JS_DupValue(m_ctx, parent->jsObject); } -void NodeInstance::removeParentNode() { +void Node::removeParentNode() { if (!JS_IsNull(parentNode)) { JS_FreeValue(m_ctx, parentNode); } @@ -542,19 +547,18 @@ void NodeInstance::removeParentNode() { parentNode = JS_NULL; } -NodeInstance::~NodeInstance() {} -void NodeInstance::refer() { +void Node::refer() { JS_DupValue(m_ctx, jsObject); - list_add_tail(&nodeLink.link, &m_context->node_job_list); + list_add_tail(&nodeLink.link, &context()->node_job_list); } -void NodeInstance::unrefer() { +void Node::unrefer() { list_del(&nodeLink.link); JS_FreeValue(m_ctx, jsObject); } -void NodeInstance::_notifyNodeRemoved(NodeInstance* node) {} -void NodeInstance::_notifyNodeInsert(NodeInstance* node) {} -void NodeInstance::ensureDetached(NodeInstance* node) { - auto* nodeParent = static_cast(JS_GetOpaque(node->parentNode, Node::classId(node->parentNode))); +void Node::_notifyNodeRemoved(Node* node) {} +void Node::_notifyNodeInsert(Node* node) {} +void Node::ensureDetached(Node* node) { + auto* nodeParent = static_cast(JS_GetOpaque(node->parentNode, JSValueGetClassId(node->parentNode))); if (nodeParent != nullptr) { int32_t idx = arrayFindIdx(m_ctx, nodeParent->childNodes, node->jsObject); @@ -566,12 +570,14 @@ void NodeInstance::ensureDetached(NodeInstance* node) { } } -void NodeInstance::trace(JSRuntime* rt, JSValue val, JS_MarkFunc* mark_func) { - EventTargetInstance::trace(rt, val, mark_func); +void Node::trace(JSRuntime* rt, JSValue val, JS_MarkFunc* mark_func) const { + EventTarget::trace(rt, val, mark_func); // Should check object is already inited before gc mark. if (JS_IsObject(parentNode)) JS_MarkValue(rt, parentNode, mark_func); } +void Node::dispose() const {} + } // namespace kraken::binding::qjs diff --git a/bridge/bindings/qjs/dom/node.h b/bridge/bindings/qjs/dom/node.h index 9f388f04fc..ddc1b43a71 100644 --- a/bridge/bindings/qjs/dom/node.h +++ b/bridge/bindings/qjs/dom/node.h @@ -13,14 +13,17 @@ namespace kraken::binding::qjs { +class Element; +class Document; +class DocumentFragment; + void bindNode(std::unique_ptr& context); enum NodeType { ELEMENT_NODE = 1, TEXT_NODE = 3, COMMENT_NODE = 8, DOCUMENT_NODE = 9, DOCUMENT_TYPE_NODE = 10, DOCUMENT_FRAGMENT_NODE = 11 }; class Node; -class ElementInstance; -class DocumentInstance; -class TextNodeInstance; +class TextNode; +class Document; struct NodeJob { Node* nodeInstance; @@ -29,16 +32,28 @@ struct NodeJob { class Node : public EventTarget { public: - static JSClassID classId(); - - static JSClassID classId(JSValue& value); - - static JSValue cloneNode(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); - static JSValue appendChild(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); - static JSValue remove(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); - static JSValue removeChild(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); - static JSValue insertBefore(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); - static JSValue replaceChild(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); + static JSClassID classId; + static JSValue constructor(ExecutionContext* context); + static JSValue prototype(ExecutionContext* context); + static Node* create(JSContext* ctx); + + DEFINE_FUNCTION(cloneNode); + DEFINE_FUNCTION(appendChild); + DEFINE_FUNCTION(remove); + DEFINE_FUNCTION(removeChild); + DEFINE_FUNCTION(insertBefore); + DEFINE_FUNCTION(replaceChild); + + DEFINE_PROTOTYPE_PROPERTY(textContent); + + DEFINE_PROTOTYPE_READONLY_PROPERTY(isConnected); + DEFINE_PROTOTYPE_READONLY_PROPERTY(ownerDocument); + DEFINE_PROTOTYPE_READONLY_PROPERTY(firstChild); + DEFINE_PROTOTYPE_READONLY_PROPERTY(lastChild); + DEFINE_PROTOTYPE_READONLY_PROPERTY(parentNode); + DEFINE_PROTOTYPE_READONLY_PROPERTY(previousSibling); + DEFINE_PROTOTYPE_READONLY_PROPERTY(nextSibling); + DEFINE_PROTOTYPE_READONLY_PROPERTY(nodeType); enum class NodeFlag : uint32_t { IsDocumentFragment = 1 << 0, IsTemplateElement = 1 << 1 }; mutable std::set m_nodeFlags; @@ -47,68 +62,54 @@ class Node : public EventTarget { void removeNodeFlag(NodeFlag flag) const { m_nodeFlags.erase(flag); } bool isConnected(); - DocumentInstance* ownerDocument(); - NodeInstance* firstChild(); - NodeInstance* lastChild(); - NodeInstance* previousSibling(); - NodeInstance* nextSibling(); - void internalAppendChild(NodeInstance* node); - void internalRemove(); - void internalClearChild(); - NodeInstance* internalRemoveChild(NodeInstance* node); - JSValue internalInsertBefore(NodeInstance* node, NodeInstance* referenceNode); - virtual JSValue internalGetTextContent(); - virtual void internalSetTextContent(JSValue content); - JSValue internalReplaceChild(NodeInstance* newChild, NodeInstance* oldChild); - + Document* ownerDocument(); + Node* firstChild(); + Node* lastChild(); + Node* previousSibling(); + Node* nextSibling(); - void setParentNode(NodeInstance* parent); + void setParentNode(Node* parent); void removeParentNode(); NodeType nodeType; JSValue parentNode{JS_NULL}; JSValue childNodes{JS_NewArray(m_ctx)}; + void trace(JSRuntime* rt, JSValue val, JS_MarkFunc* mark_func) const override; + void dispose() const override; protected: NodeJob nodeLink{this}; void refer(); void unrefer(); - inline DocumentInstance* document() { return m_document; } - - virtual void _notifyNodeRemoved(NodeInstance* node); - virtual void _notifyNodeInsert(NodeInstance* node); - - void trace(JSRuntime* rt, JSValue val, JS_MarkFunc* mark_func) const override; + void internalAppendChild(Node* node); + void internalRemove(); + void internalClearChild(); + Node* internalRemoveChild(Node* node); + JSValue internalInsertBefore(Node* node, Node* referenceNode); + virtual JSValue internalGetTextContent(); + virtual void internalSetTextContent(JSValue content); + JSValue internalReplaceChild(Node* newChild, Node* oldChild); + virtual void _notifyNodeRemoved(Node* node); + virtual void _notifyNodeInsert(Node* node); private: -// DEFINE_PROTOTYPE_PROPERTY(textContent); -// -// DEFINE_PROTOTYPE_READONLY_PROPERTY(isConnected); -// DEFINE_PROTOTYPE_READONLY_PROPERTY(ownerDocument); -// DEFINE_PROTOTYPE_READONLY_PROPERTY(firstChild); -// DEFINE_PROTOTYPE_READONLY_PROPERTY(lastChild); -// DEFINE_PROTOTYPE_READONLY_PROPERTY(parentNode); -// DEFINE_PROTOTYPE_READONLY_PROPERTY(previousSibling); -// DEFINE_PROTOTYPE_READONLY_PROPERTY(nextSibling); -// DEFINE_PROTOTYPE_READONLY_PROPERTY(nodeType); -// -// DEFINE_PROTOTYPE_FUNCTION(cloneNode, 1); -// DEFINE_PROTOTYPE_FUNCTION(appendChild, 1); -// DEFINE_PROTOTYPE_FUNCTION(remove, 0); -// DEFINE_PROTOTYPE_FUNCTION(removeChild, 1); -// DEFINE_PROTOTYPE_FUNCTION(insertBefore, 2); -// DEFINE_PROTOTYPE_FUNCTION(replaceChild, 2); - - DocumentInstance* m_document{nullptr}; ObjectProperty m_childNodes{context(), jsObject, "childNodes", childNodes}; - void ensureDetached(NodeInstance* node); + void ensureDetached(Node* node); + + static void traverseCloneNode(JSContext* ctx, Node* baseNode, Node* targetNode); + static JSValue copyNodeValue(JSContext* ctx, Node* node); +}; + +auto nodeCreator = [](JSContext* ctx, JSValueConst func_obj, JSValueConst this_val, int argc, JSValueConst* argv, int flags) -> JSValue { + return JS_ThrowTypeError(ctx, "Illegal constructor"); +}; - static void traverseCloneNode(JSContext* ctx, NodeInstance* baseNode, NodeInstance* targetNode); - static JSValue copyNodeValue(JSContext* ctx, NodeInstance* node); - friend ElementInstance; - friend TextNodeInstance; +const WrapperTypeInfo nodeTypeInfo = { + "Node", + &eventTargetTypeInfo, + nodeCreator }; } // namespace kraken::binding::qjs diff --git a/bridge/bindings/qjs/dom/text_node.cc b/bridge/bindings/qjs/dom/text_node.cc index 29c162b37e..0ee6065437 100644 --- a/bridge/bindings/qjs/dom/text_node.cc +++ b/bridge/bindings/qjs/dom/text_node.cc @@ -12,46 +12,55 @@ namespace kraken::binding::qjs { std::once_flag kTextNodeInitFlag; void bindTextNode(std::unique_ptr& context) { - auto* constructor = TextNode::instance(context.get()); - context->defineGlobalProperty("Text", constructor->jsObject); + JSValue constructor = TextNode::constructor(context.get()); + JSValue prototype = TextNode::prototype(context.get()); + + // Install readonly properties. + INSTALL_READONLY_PROPERTY(TextNode, prototype, nodeName); + + // Install properties. + INSTALL_PROPERTY(TextNode, prototype, data); + INSTALL_PROPERTY(TextNode, prototype, nodeValue); + + context->defineGlobalProperty("Text", constructor); } -JSClassID TextNode::kTextNodeClassId{0}; +JSClassID TextNode::classId{0}; -TextNode::TextNode(ExecutionContext* context) : Node(context, "TextNode") { - std::call_once(kTextNodeInitFlag, []() { JS_NewClassID(&kTextNodeClassId); }); - JS_SetPrototype(m_ctx, m_prototypeObject, Node::instance(m_context)->prototype()); +JSValue TextNode::constructor(ExecutionContext* context) { + return context->contextData()->constructorForType(&textNodeType); } -JSValue TextNode::instanceConstructor(JSContext* ctx, JSValue func_obj, JSValue this_val, int argc, JSValue* argv) { - JSValue textContent = JS_NULL; - if (argc == 1) { - textContent = argv[0]; - } +JSValue TextNode::prototype(ExecutionContext* context) { + return context->contextData()->prototypeForType(&textNodeType); +} - return (new TextNodeInstance(this, textContent))->jsObject; +TextNode* TextNode::create(JSContext* ctx, JSValue textContent) { + return makeGarbageCollected(textContent)->initialize(ctx, &classId); } -JSClassID TextNode::classId() { - return kTextNodeClassId; +TextNode::TextNode(JSValueConst textContent) { + m_data = jsValueToStdString(m_ctx, textContent); + std::unique_ptr args_01 = stringToNativeString(m_data); + context()->uiCommandBuffer()->addCommand(eventTargetId(), UICommand::createTextNode, *args_01, nativeEventTarget); } IMPL_PROPERTY_GETTER(TextNode, data)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { - auto* textNode = static_cast(JS_GetOpaque(this_val, TextNode::classId())); + auto* textNode = static_cast(JS_GetOpaque(this_val, TextNode::classId)); return JS_NewString(ctx, textNode->m_data.c_str()); } IMPL_PROPERTY_SETTER(TextNode, data)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { - auto* textNode = static_cast(JS_GetOpaque(this_val, TextNode::classId())); + auto* textNode = static_cast(JS_GetOpaque(this_val, TextNode::classId)); textNode->internalSetTextContent(argv[0]); return JS_NULL; } IMPL_PROPERTY_GETTER(TextNode, nodeValue)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { - auto* textNode = static_cast(JS_GetOpaque(this_val, TextNode::classId())); + auto* textNode = static_cast(JS_GetOpaque(this_val, TextNode::classId)); return JS_NewString(ctx, textNode->m_data.c_str()); } IMPL_PROPERTY_SETTER(TextNode, nodeValue)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) { - auto* textNode = static_cast(JS_GetOpaque(this_val, TextNode::classId())); + auto* textNode = static_cast(JS_GetOpaque(this_val, TextNode::classId)); textNode->internalSetTextContent(argv[0]); return JS_NULL; } @@ -60,27 +69,19 @@ IMPL_PROPERTY_GETTER(TextNode, nodeName)(JSContext* ctx, JSValue this_val, int a return JS_NewString(ctx, "#text"); } -TextNodeInstance::TextNodeInstance(TextNode* textNode, JSValue text) : NodeInstance(textNode, NodeType::TEXT_NODE, TextNode::classId(), "TextNode") { - m_data = jsValueToStdString(m_ctx, text); - std::unique_ptr args_01 = stringToNativeString(m_data); - m_context->uiCommandBuffer()->addCommand(m_eventTargetId, UICommand::createTextNode, *args_01, nativeEventTarget); -} - -TextNodeInstance::~TextNodeInstance() {} - -std::string TextNodeInstance::toString() { +std::string TextNode::toString() { return m_data; } -JSValue TextNodeInstance::internalGetTextContent() { +JSValue TextNode::internalGetTextContent() { return JS_NewString(m_ctx, m_data.c_str()); } -void TextNodeInstance::internalSetTextContent(JSValue content) { +void TextNode::internalSetTextContent(JSValue content) { m_data = jsValueToStdString(m_ctx, content); std::string key = "data"; std::unique_ptr args_01 = stringToNativeString(key); std::unique_ptr args_02 = jsValueToNativeString(m_ctx, content); - m_context->uiCommandBuffer()->addCommand(m_eventTargetId, UICommand::setProperty, *args_01, *args_02, nullptr); + context()->uiCommandBuffer()->addCommand(eventTargetId(), UICommand::setProperty, *args_01, *args_02, nullptr); } } // namespace kraken::binding::qjs diff --git a/bridge/bindings/qjs/dom/text_node.h b/bridge/bindings/qjs/dom/text_node.h index 1591831e96..2dea9ab6d4 100644 --- a/bridge/bindings/qjs/dom/text_node.h +++ b/bridge/bindings/qjs/dom/text_node.h @@ -16,40 +16,43 @@ void bindTextNode(std::unique_ptr& context); class TextNode : public Node { public: - static JSClassID kTextNodeClassId; - static JSClassID classId(); - TextNode() = delete; - explicit TextNode(ExecutionContext* context); + static JSClassID classId; + static JSValue constructor(ExecutionContext* context); + static JSValue prototype(ExecutionContext* context); + static TextNode* create(JSContext* ctx, JSValue textContent); - OBJECT_INSTANCE(TextNode); + TextNode() = delete; + explicit TextNode(JSValueConst textContent); - JSValue instanceConstructor(JSContext* ctx, JSValue func_obj, JSValue this_val, int argc, JSValue* argv) override; + std::string toString(); - private: DEFINE_PROTOTYPE_READONLY_PROPERTY(nodeName); DEFINE_PROTOTYPE_PROPERTY(data); DEFINE_PROTOTYPE_PROPERTY(nodeValue); - friend TextNodeInstance; -}; - -class TextNodeInstance : public NodeInstance { - public: - TextNodeInstance() = delete; - explicit TextNodeInstance(TextNode* textNode, JSValue textData); - ~TextNodeInstance(); - - std::string toString(); - private: + protected: JSValue internalGetTextContent() override; void internalSetTextContent(JSValue content) override; - friend TextNode; - friend Node; - std::string m_data; }; +auto textNodeCreator = [](JSContext* ctx, JSValueConst func_obj, JSValueConst this_val, int argc, JSValueConst* argv, int flags) -> JSValue { + JSValue textContent = JS_NULL; + if (argc == 1) { + textContent = argv[0]; + } + + TextNode* textNode = TextNode::create(ctx, textContent); + return textNode->toQuickJS(); +}; + +const WrapperTypeInfo textNodeType = { + "TextNode", + &nodeTypeInfo, + textNodeCreator +}; + } // namespace kraken::binding::qjs #endif // KRAKENBRIDGE_TEXT_NODE_H diff --git a/bridge/bindings/qjs/executing_context.h b/bridge/bindings/qjs/executing_context.h index 4afaecc98a..936f9025d6 100644 --- a/bridge/bindings/qjs/executing_context.h +++ b/bridge/bindings/qjs/executing_context.h @@ -31,7 +31,6 @@ namespace kraken::binding::qjs { static std::once_flag kinitJSClassIDFlag; -class DocumentInstance; class ExecutionContext; struct DOMTimerCallbackContext; @@ -155,6 +154,22 @@ static JSValue handleCallThisOnProxy(JSContext* ctx, JSValueConst this_val, int return result; } +class ObjectProperty { + KRAKEN_DISALLOW_COPY_ASSIGN_AND_MOVE(ObjectProperty); +public: + ObjectProperty() = delete; + + // Define an property on object with a JSValue. + explicit ObjectProperty(ExecutionContext* context, JSValueConst thisObject, const char* property, JSValue value) : m_value(value) { + JS_DefinePropertyValueStr(context->ctx(), thisObject, property, value, JS_PROP_ENUMERABLE); + } + + JSValue value() const { return m_value; } + +private: + JSValue m_value{JS_NULL}; +}; + // Property define helpers void installFunctionProperty(ExecutionContext* context, JSValueConst thisObject, const char* functionName, JSCFunction function, int argc); void installPropertyGetterSetter(ExecutionContext* context, JSValueConst thisObject, const char* property, JSCFunction getterFunction, JSCFunction setterFunction); diff --git a/bridge/bindings/qjs/js_context_macros.h b/bridge/bindings/qjs/js_context_macros.h index 4351a14b2d..e514f28cbe 100644 --- a/bridge/bindings/qjs/js_context_macros.h +++ b/bridge/bindings/qjs/js_context_macros.h @@ -6,14 +6,6 @@ #ifndef KRAKENBRIDGE_JS_CONTEXT_MACROS_H #define KRAKENBRIDGE_JS_CONTEXT_MACROS_H -#define OBJECT_INSTANCE(NAME) \ - static NAME* instance(ExecutionContext* context) { \ - if (context->constructorMap.count(#NAME) == 0) { \ - context->constructorMap[#NAME] = static_cast(new NAME(context)); \ - } \ - return static_cast(context->constructorMap[#NAME]); \ - } - #define QJS_GLOBAL_BINDING_FUNCTION(context, function, name, argc) \ { \ JSValue f = JS_NewCFunction(context->ctx(), function, name, argc); \