/* document binding for browser using duktape and libdom * * Copyright 2015 Vincent Sanders * * 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 #include %}; }; 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; %} getter Element::attributes() %{ dom_exception exc; dom_namednodemap *nmap = NULL; duk_set_top(ctx, 0); duk_push_this(ctx); duk_get_prop_string(ctx, 0, MAGIC(attributes)); if (duk_is_undefined(ctx, -1)) { duk_pop(ctx); exc = dom_node_get_attributes(priv->parent.node, &nmap); if (exc != DOM_NO_ERR) return 0; dukky_push_generics(ctx, "makeNodeMapProxy"); duk_push_pointer(ctx, nmap); if (dukky_create_object(ctx, PROTO_NAME(NAMEDNODEMAP), 1) != DUK_EXEC_SUCCESS) { dom_namednodemap_unref(nmap); return 0; } dom_namednodemap_unref(nmap); if (dukky_pcall(ctx, 1, false) != 0) { NSLOG(dukky, DEBUG, "Unable to construct nodelist?"); return 0; /* coerced to undefined */ } duk_dup(ctx, -1); duk_put_prop_string(ctx, 0, MAGIC(attributes)); } return 1; %}