Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into feat/navigation_observer
Browse files Browse the repository at this point in the history
  • Loading branch information
andycall committed Dec 10, 2021
2 parents 6f5d3a0 + fdc1f62 commit 2e838a1
Show file tree
Hide file tree
Showing 162 changed files with 3,932 additions and 2,977 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,4 @@ By contributing to Kraken, you agree that your contributions will be licensed un
```shell
$ npm test
```
7 changes: 1 addition & 6 deletions bridge/bindings/qjs/bom/window.cc
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ JSClassID Window::classId() {
}

JSValue Window::open(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) {
JSValue url = argv[0];
auto window = static_cast<WindowInstance*>(JS_GetOpaque(this_val, Window::classId()));
NativeValue arguments[] = {jsValueToNativeValue(ctx, argv[0])};
return window->callNativeMethods("open", 1, arguments);
Expand All @@ -46,8 +45,6 @@ JSValue Window::scrollTo(QjsContext* ctx, JSValue this_val, int argc, JSValue* a
return window->callNativeMethods("scroll", 2, arguments);
}
JSValue Window::scrollBy(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) {
JSValue x = argv[0];
JSValue y = argv[1];
auto window = static_cast<WindowInstance*>(JS_GetOpaque(this_val, Window::classId()));
NativeValue arguments[] = {jsValueToNativeValue(ctx, argv[0]), jsValueToNativeValue(ctx, argv[1])};
return window->callNativeMethods("scrollBy", 2, arguments);
Expand Down Expand Up @@ -103,7 +100,7 @@ PROP_GETTER(Window, __location__)(QjsContext* ctx, JSValue this_val, int argc, J
auto* window = static_cast<WindowInstance*>(JS_GetOpaque(this_val, 1));
if (window == nullptr)
return JS_UNDEFINED;
return JS_DupValue(ctx, window->m_location->jsObject);
return JS_DupValue(ctx, window->m_location.value());
}
PROP_SETTER(Window, __location__)(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) {
return JS_NULL;
Expand Down Expand Up @@ -183,8 +180,6 @@ void WindowInstance::gcMark(JSRuntime* rt, JSValue val, JS_MarkFunc* mark_func)
EventTargetInstance::gcMark(rt, val, mark_func);

// Should check object is already inited before gc mark.
if (JS_IsObject(m_location->jsObject))
JS_MarkValue(rt, m_location->jsObject, mark_func);
if (JS_IsObject(onerror))
JS_MarkValue(rt, onerror, mark_func);
}
Expand Down
5 changes: 3 additions & 2 deletions bridge/bindings/qjs/bom/window.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,13 @@ class WindowInstance : public EventTargetInstance {
public:
WindowInstance() = delete;
explicit WindowInstance(Window* window);
~WindowInstance() { JS_FreeValue(m_ctx, onerror); }
~WindowInstance() {}

private:
void gcMark(JSRuntime* rt, JSValue val, JS_MarkFunc* mark_func) override;

Location* m_location{new Location(m_context)};
ObjectProperty m_location{m_context, instanceObject, "m_location", (new Location(m_context))->jsObject};
ObjectProperty m_onerror{m_context, instanceObject, "m_onerror", JS_NULL};
JSValue onerror{JS_NULL};
friend Window;
friend JSContext;
Expand Down
2 changes: 1 addition & 1 deletion bridge/bindings/qjs/dom/custom_event.cc
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ JSValue CustomEvent::initCustomEvent(QjsContext* ctx, JSValue this_val, int argc
}

JSValue typeValue = argv[0];
eventInstance->nativeEvent->type = jsValueToNativeString(ctx, typeValue);
eventInstance->nativeEvent->type = jsValueToNativeString(ctx, typeValue).release();

if (argc <= 2) {
bool canBubble = JS_ToBool(ctx, argv[1]);
Expand Down
213 changes: 182 additions & 31 deletions bridge/bindings/qjs/dom/document.cc
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,13 @@ Document::Document(JSContext* context) : Node(context, "Document") {
std::call_once(kDocumentInitOnceFlag, []() { JS_NewClassID(&kDocumentClassID); });
JS_SetPrototype(m_ctx, m_prototypeObject, Node::instance(m_context)->prototype());
if (!document_registered) {
Element::defineElement("img", ImageElement::instance(m_context));
Element::defineElement("a", AnchorElement::instance(m_context));
Element::defineElement("canvas", CanvasElement::instance(m_context));
Element::defineElement("input", InputElement::instance(m_context));
Element::defineElement("object", ObjectElement::instance(m_context));
Element::defineElement("script", ScriptElement::instance(m_context));
Element::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;
}

Expand Down Expand Up @@ -140,8 +140,8 @@ JSValue Document::createEvent(QjsContext* ctx, JSValue this_val, int argc, JSVal
JS_FreeCString(ctx, c_eventType);
std::string eventType = std::string(c_eventType);
if (eventType == "Event") {
NativeString* nativeEventType = jsValueToNativeString(ctx, eventTypeValue);
auto nativeEvent = new NativeEvent{nativeEventType};
std::unique_ptr<NativeString> nativeEventType = jsValueToNativeString(ctx, eventTypeValue);
auto nativeEvent = new NativeEvent{nativeEventType.release()};

auto document = static_cast<DocumentInstance*>(JS_GetOpaque(this_val, Document::classId()));
auto e = Event::buildEventInstance(eventType, document->context(), nativeEvent, false);
Expand All @@ -164,7 +164,7 @@ JSValue Document::createElement(QjsContext* ctx, JSValue this_val, int argc, JSV
auto document = static_cast<DocumentInstance*>(JS_GetOpaque(this_val, Document::classId()));
auto* context = static_cast<JSContext*>(JS_GetContextOpaque(ctx));
std::string tagName = jsValueToStdString(ctx, tagNameValue);
JSValue constructor = Element::getConstructor(document->m_context, tagName);
JSValue constructor = static_cast<Document*>(document->prototype())->getElementConstructor(document->m_context, tagName);

JSValue element = JS_CallConstructor(ctx, constructor, argc, argv);
return element;
Expand All @@ -179,15 +179,18 @@ JSValue Document::createTextNode(QjsContext* ctx, JSValue this_val, int argc, JS
JSValue textNode = JS_CallConstructor(ctx, TextNode::instance(document->m_context)->classObject, argc, argv);
return textNode;
}

JSValue Document::createDocumentFragment(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) {
auto* document = static_cast<DocumentInstance*>(JS_GetOpaque(this_val, Document::classId()));
return JS_CallConstructor(ctx, DocumentFragment::instance(document->m_context)->classObject, 0, nullptr);
}

JSValue Document::createComment(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) {
auto* document = static_cast<DocumentInstance*>(JS_GetOpaque(this_val, Document::classId()));
JSValue commentNode = JS_CallConstructor(ctx, Comment::instance(document->m_context)->classObject, argc, argv);
return commentNode;
}

JSValue Document::getElementById(QjsContext* 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.");
Expand Down Expand Up @@ -219,6 +222,7 @@ JSValue Document::getElementById(QjsContext* ctx, JSValue this_val, int argc, JS

return JS_NULL;
}

JSValue Document::getElementsByTagName(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) {
if (argc < 1) {
return JS_ThrowTypeError(ctx,
Expand All @@ -233,9 +237,9 @@ JSValue Document::getElementsByTagName(QjsContext* ctx, JSValue this_val, int ar

std::vector<ElementInstance*> elements;

traverseNode(document->m_documentElement, [tagName, &elements](NodeInstance* node) {
traverseNode(document, [tagName, &elements](NodeInstance* node) {
if (node->nodeType == NodeType::ELEMENT_NODE) {
auto element = static_cast<ElementInstance*>(node);
auto* element = static_cast<ElementInstance*>(node);
if (element->tagName() == tagName || tagName == "*") {
elements.emplace_back(element);
}
Expand Down Expand Up @@ -264,7 +268,7 @@ JSValue Document::getElementsByClassName(QjsContext* ctx, JSValue this_val, int
std::string className = jsValueToStdString(ctx, argv[0]);

std::vector<ElementInstance*> elements;
traverseNode(document->m_documentElement, [ctx, className, &elements](NodeInstance* node) {
traverseNode(document, [ctx, className, &elements](NodeInstance* node) {
if (node->nodeType == NodeType::ELEMENT_NODE) {
auto element = reinterpret_cast<ElementInstance*>(node);
if (element->classNames()->containsAll(className)) {
Expand All @@ -286,24 +290,174 @@ JSValue Document::getElementsByClassName(QjsContext* ctx, JSValue this_val, int
return array;
}

void Document::defineElement(const std::string& tagName, Element* constructor) {
elementConstructorMap[tagName] = constructor;
}

JSValue Document::getElementConstructor(JSContext* context, const std::string& tagName) {
if (elementConstructorMap.count(tagName) > 0)
return elementConstructorMap[tagName]->classObject;
return Element::instance(context)->classObject;
}

bool Document::isCustomElement(const std::string& tagName) {
return elementConstructorMap.count(tagName) > 0;
}

PROP_GETTER(DocumentInstance, nodeName)(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) {
return JS_NewString(ctx, "#document");
}

PROP_SETTER(DocumentInstance, nodeName)(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) {
return JS_NULL;
}

// document.documentElement
PROP_GETTER(DocumentInstance, documentElement)(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) {
auto* document = static_cast<DocumentInstance*>(JS_GetOpaque(this_val, Document::classId()));
ElementInstance* documentElement = document->getDocumentElement();
return documentElement == nullptr ? JS_NULL : documentElement->instanceObject;
}

PROP_SETTER(DocumentInstance, documentElement)(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) {
return JS_NULL;
}

// document.head
PROP_GETTER(DocumentInstance, head)(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) {
auto* document = static_cast<DocumentInstance*>(JS_GetOpaque(this_val, Document::classId()));
ElementInstance* 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<NodeInstance*>(JS_GetOpaque(v, Node::classId(v)));
if (nodeInstance->nodeType == NodeType::ELEMENT_NODE) {
auto* elementInstance = static_cast<ElementInstance*>(nodeInstance);
if (elementInstance->tagName() == "HEAD") {
head = elementInstance->instanceObject;
break;
}
}
JS_FreeValue(ctx, v);
}

JS_FreeValue(ctx, documentElement->instanceObject);
}

return head;
}

PROP_SETTER(DocumentInstance, head)(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) {
return JS_NULL;
}

// document.body: https://html.spec.whatwg.org/multipage/dom.html#dom-document-body-dev
PROP_GETTER(DocumentInstance, body)(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) {
auto* document = static_cast<DocumentInstance*>(JS_GetOpaque(this_val, Document::classId()));
ElementInstance* documentElement = document->getDocumentElement();
JSValue body = JS_NULL;

if (documentElement != nullptr) {
int32_t len = arrayGetLength(ctx, documentElement->childNodes);
// The body element of a document is the first of the html documentElement's children that
// 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<NodeInstance*>(JS_GetOpaque(v, Node::classId(v)));
if (nodeInstance->nodeType == NodeType::ELEMENT_NODE) {
auto* elementInstance = static_cast<ElementInstance*>(nodeInstance);
if (elementInstance->tagName() == "BODY") {
body = elementInstance->instanceObject;
break;
}
}
JS_FreeValue(ctx, v);
}
JS_FreeValue(ctx, documentElement->instanceObject);
}
return body;
}

// The body property is settable, setting a new body on a document will effectively remove all
// the current children of the existing <body> element.
PROP_SETTER(DocumentInstance, body)(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) {
auto* document = static_cast<DocumentInstance*>(JS_GetOpaque(this_val, Document::classId()));
ElementInstance* documentElement = document->getDocumentElement();
// If there is no document element, throw a Exception.
if (documentElement == nullptr) {
return JS_ThrowInternalError(ctx, "No document element exists");
}
JSValue result = JS_NULL;
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)->classObject)) {
auto* newElementInstance = static_cast<ElementInstance*>(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->instanceObject, "body");
if (JS_VALUE_GET_PTR(oldBody) != JS_VALUE_GET_PTR(newBody)) {
// If the new value is the same as the body element.
if (JS_IsNull(oldBody)) {
// The old body element is null, but there's a document element. Append the new value to the document element.
documentElement->internalAppendChild(newElementInstance);
} else {
// Otherwise, replace the body element with the new value within the body element's parent.
auto* oldElementInstance = static_cast<ElementInstance*>(JS_GetOpaque(oldBody, Element::classId()));
documentElement->internalReplaceChild(newElementInstance, oldElementInstance);
}
}
JS_FreeValue(ctx, oldBody);
result = JS_DupValue(ctx, newBody);
} else {
result = JS_ThrowTypeError(ctx, "The new body element must be a 'BODY' element");
}
} else {
result = JS_ThrowTypeError(ctx, "The 1st argument provided is either null, or an invalid HTMLElement");
}

JS_FreeValue(ctx, documentElement->instanceObject);
return result;
}

// document.children
PROP_GETTER(DocumentInstance, children)(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) {
auto* document = static_cast<DocumentInstance*>(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<NodeInstance*>(JS_GetOpaque(v, Node::classId(v)));
if (instance->nodeType == NodeType::ELEMENT_NODE) {
JSValue arguments[] = {v};
JS_Call(ctx, pushMethod, array, 1, arguments);
}
JS_FreeValue(ctx, v);
}

JS_FreeValue(ctx, pushMethod);
return array;
}

PROP_SETTER(DocumentInstance, children)(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) {
return JS_NULL;
}

PROP_GETTER(DocumentInstance, all)(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) {
auto* document = static_cast<DocumentInstance*>(JS_GetOpaque(this_val, Document::classId()));
auto all = new AllCollection(document->m_context);

traverseNode(document->m_documentElement, [&all](NodeInstance* node) {
traverseNode(document, [&all](NodeInstance* node) {
all->internalAdd(node, nullptr);
return false;
});

return all->jsObject;
}

PROP_SETTER(DocumentInstance, all)(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) {
return JS_NULL;
}
Expand All @@ -313,6 +467,7 @@ PROP_GETTER(DocumentInstance, cookie)(QjsContext* ctx, JSValue this_val, int arg
std::string cookie = document->m_cookie->getCookie();
return JS_NewString(ctx, cookie.c_str());
}

PROP_SETTER(DocumentInstance, cookie)(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) {
auto* document = static_cast<DocumentInstance*>(JS_GetOpaque(this_val, Document::classId()));
std::string value = jsValueToStdString(ctx, argv[0]);
Expand Down Expand Up @@ -377,22 +532,7 @@ DocumentInstance::DocumentInstance(Document* document) : NodeInstance(document,
m_instanceMap[Document::instance(m_context)] = this;
m_eventTargetId = DOCUMENT_TARGET_ID;

JSAtom htmlTagName = JS_NewAtom(m_ctx, "HTML");
JSValue htmlTagValue = JS_AtomToValue(m_ctx, htmlTagName);
JSValue htmlArgs[] = {htmlTagValue};
JSValue documentElementValue = JS_CallConstructor(m_ctx, Element::instance(m_context)->classObject, 1, htmlArgs);
m_documentElement = static_cast<ElementInstance*>(JS_GetOpaque(documentElementValue, Element::classId()));
m_documentElement->parentNode = JS_DupValue(m_ctx, instanceObject);

JSAtom documentElementTag = JS_NewAtom(m_ctx, "documentElement");
JS_SetProperty(m_ctx, instanceObject, documentElementTag, documentElementValue);

JS_FreeAtom(m_ctx, documentElementTag);
JS_FreeAtom(m_ctx, htmlTagName);
JS_FreeValue(m_ctx, htmlTagValue);

#if FLUTTER_BACKEND
getDartMethod()->initHTML(m_context->getContextId(), m_documentElement->nativeEventTarget);
getDartMethod()->initDocument(m_context->getContextId(), nativeEventTarget);
#endif
}
Expand Down Expand Up @@ -423,8 +563,19 @@ void DocumentInstance::addElementById(JSAtom id, ElementInstance* element) {
}
}

ElementInstance* DocumentInstance::documentElement() {
return m_documentElement;
ElementInstance* DocumentInstance::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<NodeInstance*>(JS_GetOpaque(v, Node::classId(v)));
if (instance->nodeType == NodeType::ELEMENT_NODE) {
return static_cast<ElementInstance*>(instance);
}
JS_FreeValue(m_ctx, v);
}

return nullptr;
}

} // namespace kraken::binding::qjs
Loading

0 comments on commit 2e838a1

Please sign in to comment.