netsurf/content/handlers/javascript/duktape/Element.bnd
Daniel Silverstone 2325062ff1
Element: support innerHTML
To get us further along the JavaScript pathway, support the
getter and setter for innerHTML.  The getter always returns
an empty string for now, but the setter works.

Signed-off-by: Daniel Silverstone <dsilvers@digital-scurf.org>
2020-02-21 18:02:57 +00:00

517 lines
12 KiB
Plaintext

/* document binding for browser using duktape and libdom
*
* Copyright 2015 Vincent Sanders <vince@netsurf-browser.org>
*
* This file is part of NetSurf, http://www.netsurf-browser.org/
*
* Released under the terms of the MIT License,
* http://www.opensource.org/licenses/mit-license
*/
class Element {
prologue %{
#include <utils/corestrings.h>
#include <dom/bindings/hubbub/parser.h>
%};
};
init Element(struct dom_element *element::node);
getter Element::firstElementChild()
%{
dom_node *element;
dom_exception exc;
dom_node_type node_type;
dom_node *next_node;
exc = dom_node_get_first_child(((node_private_t*)priv)->node, &element);
if (exc != DOM_NO_ERR) {
return 0;
}
while (element != NULL) {
exc = dom_node_get_node_type(element, &node_type);
if ((exc == DOM_NO_ERR) && (node_type == DOM_ELEMENT_NODE)) {
/* found it */
break;
}
exc = dom_node_get_next_sibling(element, &next_node);
dom_node_unref(element);
if (exc == DOM_NO_ERR) {
element = next_node;
} else {
element = NULL;
}
}
if (dukky_push_node(ctx, (dom_node *)element) == false) {
dom_node_unref(element);
return 0;
}
dom_node_unref(element);
return 1;
%}
getter Element::lastElementChild()
%{
dom_node *element;
dom_exception exc;
dom_node_type node_type;
dom_node *next_node;
exc = dom_node_get_last_child(((node_private_t*)priv)->node, &element);
if (exc != DOM_NO_ERR) {
return 0;
}
while (element != NULL) {
exc = dom_node_get_node_type(element, &node_type);
if ((exc == DOM_NO_ERR) && (node_type == DOM_ELEMENT_NODE)) {
/* found it */
break;
}
exc = dom_node_get_previous_sibling(element, &next_node);
dom_node_unref(element);
if (exc == DOM_NO_ERR) {
element = next_node;
} else {
element = NULL;
}
}
if (dukky_push_node(ctx, (dom_node *)element) == false) {
dom_node_unref(element);
return 0;
}
dom_node_unref(element);
return 1;
%}
getter Element::previousElementSibling()
%{
dom_node *element;
dom_exception exc;
dom_node_type node_type;
dom_node *sib_node;
exc = dom_node_get_previous_sibling(((node_private_t *)priv)->node, &element);
if (exc != DOM_NO_ERR) {
return 0;
}
while (element != NULL) {
exc = dom_node_get_node_type(element, &node_type);
if ((exc == DOM_NO_ERR) && (node_type == DOM_ELEMENT_NODE)) {
/* found it */
break;
}
exc = dom_node_get_previous_sibling(element, &sib_node);
dom_node_unref(element);
if (exc == DOM_NO_ERR) {
element = sib_node;
} else {
element = NULL;
}
}
if (dukky_push_node(ctx, (dom_node *)element) == false) {
dom_node_unref(element);
return 0;
}
dom_node_unref(element);
return 1;
%}
getter Element::nextElementSibling()
%{
dom_node *element;
dom_exception exc;
dom_node_type node_type;
dom_node *sib_node;
exc = dom_node_get_next_sibling(((node_private_t *)priv)->node, &element);
if (exc != DOM_NO_ERR) {
return 0;
}
while (element != NULL) {
exc = dom_node_get_node_type(element, &node_type);
if ((exc == DOM_NO_ERR) && (node_type == DOM_ELEMENT_NODE)) {
/* found it */
break;
}
exc = dom_node_get_next_sibling(element, &sib_node);
dom_node_unref(element);
if (exc == DOM_NO_ERR) {
element = sib_node;
} else {
element = NULL;
}
}
if (dukky_push_node(ctx, (dom_node *)element) == false) {
dom_node_unref(element);
return 0;
}
dom_node_unref(element);
return 1;
%}
getter Element::childElementCount()
%{
dom_node *element;
dom_exception exc;
dom_node_type node_type;
dom_node *next_node;
duk_uint_t jsret = 0;
exc = dom_node_get_first_child(((node_private_t *)priv)->node, &element);
if (exc != DOM_NO_ERR) {
return 0;
}
while (element != NULL) {
exc = dom_node_get_node_type(element, &node_type);
if ((exc == DOM_NO_ERR) && (node_type == DOM_ELEMENT_NODE)) {
jsret += 1;
}
exc = dom_node_get_next_sibling(element, &next_node);
dom_node_unref(element);
if (exc == DOM_NO_ERR) {
element = next_node;
} else {
element = NULL;
}
}
NSLOG(netsurf, INFO, "I found %u of them", jsret);
duk_push_uint(ctx, jsret);
return 1;
%}
method Element::getElementsByTagName ()
%{
dom_nodelist *nlist = NULL;
dom_exception exc;
dom_string *tagname;
duk_size_t len;
const char *str = duk_to_lstring(ctx, 0, &len);
exc = dom_string_create((const uint8_t *)str, len, &tagname);
if (exc != DOM_NO_ERR) return 0;
exc = dom_element_get_elements_by_tag_name(priv->parent.node,
tagname, &nlist);
dom_string_unref(tagname);
if (exc != DOM_NO_ERR) return 0;
dukky_push_generics(ctx, "makeListProxy");
duk_push_pointer(ctx, nlist);
dukky_create_object(ctx, PROTO_NAME(NODELIST), 1);
dom_nodelist_unref(nlist);
if (dukky_pcall(ctx, 1, false) != 0) {
NSLOG(dukky, DEBUG, "Unable to construct nodelist?");
return 0; /* coerced to undefined */
}
return 1;
%}
getter Element::id ()
%{
dom_string *idstr = NULL;
dom_exception exc;
exc = dom_element_get_attribute(priv->parent.node,
corestring_dom_id,
&idstr);
if (exc != DOM_NO_ERR) return 0;
if (idstr == NULL) {
duk_push_lstring(ctx, "", 0);
} else {
duk_push_lstring(ctx, dom_string_data(idstr),
dom_string_length(idstr));
dom_string_unref(idstr);
}
return 1;
%}
setter Element::id ()
%{
dom_string *idstr = NULL;
dom_exception exc;
duk_size_t slen;
const char *s = duk_safe_to_lstring(ctx, 0, &slen);
exc = dom_string_create((const uint8_t *)s, slen, &idstr);
if (exc != DOM_NO_ERR) return 0;
exc = dom_element_set_attribute(priv->parent.node,
corestring_dom_id,
idstr);
dom_string_unref(idstr);
if (exc != DOM_NO_ERR) return 0;
return 0;
%}
method Element::removeAttribute()
%{
dom_string *attr = NULL;
dom_exception exc;
duk_size_t slen;
const char *s = duk_safe_to_lstring(ctx, 0, &slen);
exc = dom_string_create((const uint8_t *)s, slen, &attr);
if (exc != DOM_NO_ERR) return 0;
exc = dom_element_remove_attribute(priv->parent.node, attr);
dom_string_unref(attr);
if (exc != DOM_NO_ERR) return 0;
return 0;
%}
method Element::getAttribute()
%{
dom_string *attr_name = NULL;
dom_string *attr_value = NULL;
dom_exception exc;
duk_size_t slen;
const char *s = duk_safe_to_lstring(ctx, 0, &slen);
exc = dom_string_create((const uint8_t *)s, slen, &attr_name);
duk_pop(ctx);
if (exc != DOM_NO_ERR) {
return 0;
}
exc = dom_element_get_attribute(priv->parent.node,
attr_name, &attr_value);
dom_string_unref(attr_name);
if (exc != DOM_NO_ERR) {
return 0;
}
if (attr_value == NULL) {
duk_push_null(ctx);
} else {
duk_push_lstring(ctx, dom_string_data(attr_value),
dom_string_length(attr_value));
dom_string_unref(attr_value);
}
return 1;
%}
method Element::setAttribute()
%{
dom_exception exc;
dom_string *attr_str, *value_str;
duk_size_t attr_len, value_len;
const char *attr = duk_safe_to_lstring(ctx, 0, &attr_len);
const char *value = duk_safe_to_lstring(ctx, 1, &value_len);
exc = dom_string_create((const uint8_t *)attr, attr_len, &attr_str);
if (exc != DOM_NO_ERR) return 0;
exc = dom_string_create((const uint8_t *)value, value_len, &value_str);
if (exc != DOM_NO_ERR) {
dom_string_unref(attr_str);
return 0;
}
exc = dom_element_set_attribute(priv->parent.node,
attr_str, value_str);
dom_string_unref(attr_str);
dom_string_unref(value_str);
if (exc != DOM_NO_ERR) return 0;
return 0;
%}
method Element::hasAttribute()
%{
dom_string *attr_name = NULL;
dom_exception exc;
duk_size_t slen;
bool res;
const char *s = duk_safe_to_lstring(ctx, 0, &slen);
exc = dom_string_create((const uint8_t *)s, slen, &attr_name);
duk_pop(ctx);
if (exc != DOM_NO_ERR) {
return 0;
}
exc = dom_element_has_attribute(priv->parent.node,
attr_name, &res);
dom_string_unref(attr_name);
if (exc != DOM_NO_ERR) {
return 0;
}
duk_push_boolean(ctx, res);
return 1;
%}
getter Element::className ()
%{
dom_string *classstr = NULL;
dom_exception exc;
exc = dom_element_get_attribute(priv->parent.node,
corestring_dom_class,
&classstr);
if (exc != DOM_NO_ERR) return 0;
if (classstr == NULL) {
duk_push_lstring(ctx, "", 0);
} else {
duk_push_lstring(ctx, dom_string_data(classstr),
dom_string_length(classstr));
dom_string_unref(classstr);
}
return 1;
%}
setter Element::className ()
%{
dom_string *classstr = NULL;
dom_exception exc;
duk_size_t slen;
const char *s = duk_safe_to_lstring(ctx, 0, &slen);
exc = dom_string_create((const uint8_t *)s, slen, &classstr);
if (exc != DOM_NO_ERR) return 0;
exc = dom_element_set_attribute(priv->parent.node,
corestring_dom_class,
classstr);
dom_string_unref(classstr);
if (exc != DOM_NO_ERR) return 0;
return 0;
%}
getter Element::innerHTML()
%{
duk_push_lstring(ctx, "", 0);
return 1;
%}
setter Element::innerHTML()
%{
duk_size_t size;
const char *s = duk_safe_to_lstring(ctx, 0, &size);
dom_hubbub_parser_params parse_params;
dom_hubbub_error error;
dom_hubbub_parser *parser = NULL;
struct dom_document *doc = NULL;
struct dom_document_fragment *fragment = NULL;
dom_exception exc;
struct dom_node *child = NULL, *html = NULL, *body = NULL;
struct dom_nodelist *bodies = NULL;
exc = dom_node_get_owner_document(priv->parent.node, &doc);
if (exc != DOM_NO_ERR) goto out;
parse_params.enc = "UTF-8";
parse_params.fix_enc = true;
parse_params.enable_script = false;
parse_params.msg = NULL;
parse_params.script = NULL;
parse_params.ctx = NULL;
parse_params.daf = NULL;
error = dom_hubbub_fragment_parser_create(&parse_params,
doc,
&parser,
&fragment);
if (error != DOM_HUBBUB_OK) {
NSLOG(netsurf, ERROR, "Unable to create fragment parser!");
goto out;
}
error = dom_hubbub_parser_parse_chunk(parser, (const uint8_t*)s, size);
if (error != DOM_HUBBUB_OK) {
NSLOG(netsurf, ERROR, "Unable to parse HTML chunk");
goto out;
}
error = dom_hubbub_parser_completed(parser);
if (error != DOM_HUBBUB_OK) {
NSLOG(netsurf, ERROR, "Unable to complete parser");
goto out;
}
/* Parse is finished, transfer contents of fragment into node */
/* 1. empty this node */
exc = dom_node_get_first_child(priv->parent.node, &child);
if (exc != DOM_NO_ERR) goto out;
while (child != NULL) {
struct dom_node *cref;
exc = dom_node_remove_child(priv->parent.node, child, &cref);
if (exc != DOM_NO_ERR) goto out;
dom_node_unref(child);
child = NULL;
dom_node_unref(cref);
exc = dom_node_get_first_child(priv->parent.node, &child);
if (exc != DOM_NO_ERR) goto out;
}
/* 2. the first child in the fragment will be an HTML element
* because that's how hubbub works, walk through that to the body
* element hubbub will have created, we want to migrate that element's
* children into ourself.
*/
exc = dom_node_get_first_child(fragment, &html);
if (exc != DOM_NO_ERR) goto out;
/* We can then ask that HTML element to give us its body */
exc = dom_element_get_elements_by_tag_name(html, corestring_dom_BODY, &bodies);
if (exc != DOM_NO_ERR) goto out;
/* And now we can get the body which will be the zeroth body */
exc = dom_nodelist_item(bodies, 0, &body);
if (exc != DOM_NO_ERR) goto out;
/* 3. Migrate the children */
exc = dom_node_get_first_child(body, &child);
if (exc != DOM_NO_ERR) goto out;
while (child != NULL) {
struct dom_node *cref;
exc = dom_node_remove_child(body, child, &cref);
if (exc != DOM_NO_ERR) goto out;
dom_node_unref(cref);
exc = dom_node_append_child(priv->parent.node, child, &cref);
if (exc != DOM_NO_ERR) goto out;
dom_node_unref(cref);
dom_node_unref(child);
child = NULL;
exc = dom_node_get_first_child(body, &child);
if (exc != DOM_NO_ERR) goto out;
}
out:
if (parser != NULL) {
dom_hubbub_parser_destroy(parser);
}
if (doc != NULL) {
dom_node_unref(doc);
}
if (fragment != NULL) {
dom_node_unref(fragment);
}
if (child != NULL) {
dom_node_unref(child);
}
if (html != NULL) {
dom_node_unref(html);
}
if (bodies != NULL) {
dom_nodelist_unref(bodies);
}
if (body != NULL) {
dom_node_unref(body);
}
return 0;
%}