You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
583 lines
14 KiB
583 lines
14 KiB
/* 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
|
|
*/
|
|
|
|
prologue Document()
|
|
%{
|
|
#include "utils/corestrings.h"
|
|
#include "utils/libdom.h"
|
|
#include "utils/utils.h"
|
|
#include "content/hlcache.h"
|
|
#include "html/private.h"
|
|
#include "content/urldb.h"
|
|
|
|
#define HANDLER_MAGIC MAGIC(HANDLER_MAP)
|
|
%}
|
|
|
|
|
|
init Document(struct dom_document *document::node);
|
|
|
|
method Document::write()
|
|
%{
|
|
struct html_content *htmlc;
|
|
duk_size_t text_len;
|
|
dom_exception err;
|
|
const char *text;
|
|
|
|
for (int i = 0; i < duk_get_top(ctx); ++i) {
|
|
duk_safe_to_string(ctx, i);
|
|
}
|
|
duk_concat(ctx, duk_get_top(ctx));
|
|
text = duk_safe_to_lstring(ctx, 0, &text_len);
|
|
NSLOG(netsurf, DEBUG, "Writing %*s", (int)text_len, text);
|
|
|
|
err = dom_node_get_user_data(priv->parent.node,
|
|
corestring_dom___ns_key_html_content_data,
|
|
&htmlc);
|
|
if ((err != DOM_NO_ERR) || (htmlc == NULL)) {
|
|
NSLOG(netsurf, INFO,
|
|
"error getting htmlc. parent node:%p htmlc:%p",
|
|
priv->parent.node, htmlc);
|
|
return 0;
|
|
} else if (htmlc->parser == NULL) {
|
|
NSLOG(netsurf, INFO, "error; no parser for htmlc: %p", htmlc);
|
|
return 0;
|
|
}
|
|
|
|
dom_hubbub_parser_insert_chunk(htmlc->parser,
|
|
(uint8_t *)text, text_len);
|
|
|
|
return 0;
|
|
%}
|
|
|
|
method Document::writeln()
|
|
%{
|
|
const char nl[] = "\n";
|
|
struct html_content *htmlc;
|
|
duk_size_t text_len;
|
|
const char *text;
|
|
dom_exception err;
|
|
|
|
for (int i = 0; i < duk_get_top(ctx); ++i) {
|
|
duk_safe_to_string(ctx, i);
|
|
}
|
|
duk_concat(ctx, duk_get_top(ctx));
|
|
text = duk_safe_to_lstring(ctx, 0, &text_len);
|
|
|
|
NSLOG(netsurf, DEBUG, "Writeln %*s", (int)text_len, text);
|
|
err = dom_node_get_user_data(priv->parent.node,
|
|
corestring_dom___ns_key_html_content_data,
|
|
&htmlc);
|
|
if ((err != DOM_NO_ERR) || (htmlc == NULL)) {
|
|
NSLOG(netsurf, INFO,
|
|
"error getting htmlc. parent node:%p htmlc:%p",
|
|
priv->parent.node, htmlc);
|
|
return 0;
|
|
} else if (htmlc->parser == NULL) {
|
|
NSLOG(netsurf, INFO, "error; no parser for htmlc: %p", htmlc);
|
|
return 0;
|
|
}
|
|
|
|
dom_hubbub_parser_insert_chunk(htmlc->parser, (uint8_t *)text, text_len);
|
|
dom_hubbub_parser_insert_chunk(htmlc->parser, (uint8_t *)nl, SLEN(nl));
|
|
|
|
return 0;
|
|
%}
|
|
|
|
method Document::createTextNode()
|
|
%{
|
|
dom_node *newnode;
|
|
dom_exception err;
|
|
duk_size_t text_len;
|
|
const char *text = duk_safe_to_lstring(ctx, 0, &text_len);
|
|
dom_string *text_str;
|
|
|
|
err = dom_string_create((const uint8_t*)text, text_len, &text_str);
|
|
if (err != DOM_NO_ERR) return 0; /* coerced to undefined */
|
|
|
|
err = dom_document_create_text_node(priv->parent.node,
|
|
text_str,
|
|
&newnode);
|
|
if (err != DOM_NO_ERR) {
|
|
dom_string_unref(text_str);
|
|
return 0; /* coerced to undefined */
|
|
}
|
|
|
|
dom_string_unref(text_str);
|
|
|
|
dukky_push_node(ctx, newnode);
|
|
|
|
dom_node_unref(newnode);
|
|
|
|
return 1;
|
|
%}
|
|
|
|
method Document::createElement()
|
|
%{
|
|
dom_node *newnode;
|
|
dom_exception err;
|
|
duk_size_t text_len;
|
|
const char *text = duk_safe_to_lstring(ctx, 0, &text_len);
|
|
dom_string *text_str;
|
|
|
|
err = dom_string_create((const uint8_t*)text, text_len, &text_str);
|
|
if (err != DOM_NO_ERR) return 0; /* coerced to undefined */
|
|
|
|
err = dom_document_create_element_ns(priv->parent.node,
|
|
corestring_dom_html_namespace,
|
|
text_str,
|
|
&newnode);
|
|
if (err != DOM_NO_ERR) {
|
|
dom_string_unref(text_str);
|
|
return 0; /* coerced to undefined */
|
|
}
|
|
|
|
dom_string_unref(text_str);
|
|
|
|
dukky_push_node(ctx, newnode);
|
|
|
|
dom_node_unref(newnode);
|
|
|
|
return 1;
|
|
%}
|
|
|
|
method Document::createElementNS()
|
|
%{
|
|
dom_node *newnode;
|
|
dom_exception err;
|
|
duk_size_t text_len;
|
|
duk_size_t ns_len;
|
|
const char *ns = duk_safe_to_lstring(ctx, 0, &ns_len);
|
|
const char *text = duk_safe_to_lstring(ctx, 0, &text_len);
|
|
dom_string *text_str;
|
|
dom_string *ns_str;
|
|
|
|
err = dom_string_create((const uint8_t*)ns, ns_len, &ns_str);
|
|
if (err != DOM_NO_ERR) return 0; /* coerced to undefined */
|
|
|
|
err = dom_string_create((const uint8_t*)text, text_len, &text_str);
|
|
if (err != DOM_NO_ERR) {
|
|
dom_string_unref(ns_str);
|
|
return 0; /* coerced to undefined */
|
|
}
|
|
|
|
err = dom_document_create_element_ns(priv->parent.node,
|
|
ns_str,
|
|
text_str,
|
|
&newnode);
|
|
if (err != DOM_NO_ERR) {
|
|
dom_string_unref(ns_str);
|
|
dom_string_unref(text_str);
|
|
return 0; /* coerced to undefined */
|
|
}
|
|
|
|
dom_string_unref(text_str);
|
|
dom_string_unref(ns_str);
|
|
|
|
dukky_push_node(ctx, newnode);
|
|
|
|
dom_node_unref(newnode);
|
|
|
|
return 1;
|
|
%}
|
|
|
|
method Document::createDocumentFragment()
|
|
%{
|
|
struct dom_document_fragment *frag = NULL;
|
|
dom_exception err;
|
|
|
|
err = dom_document_create_document_fragment(priv->parent.node, &frag);
|
|
|
|
if (err != DOM_NO_ERR) return 0; /* coerced to undefined */
|
|
|
|
dukky_push_node(ctx, (dom_node *)frag);
|
|
|
|
dom_node_unref(frag); /* The pushed node holds the reference now */
|
|
|
|
return 1;
|
|
%}
|
|
|
|
getter Document::head()
|
|
%{
|
|
struct dom_nodelist *nodes;
|
|
struct dom_node *retnode;
|
|
dom_exception err;
|
|
err = dom_document_get_elements_by_tag_name(priv->parent.node,
|
|
corestring_dom_HEAD,
|
|
&nodes);
|
|
if (err != DOM_NO_ERR) return 0; /* coerced to undefined */
|
|
|
|
err = dom_nodelist_item(nodes, 0, &retnode);
|
|
|
|
if (err != DOM_NO_ERR) {
|
|
dom_nodelist_unref(nodes);
|
|
return 0; /* coerced to undefined */
|
|
}
|
|
|
|
dom_nodelist_unref(nodes);
|
|
|
|
if (retnode == NULL) return 0; /* coerced to undefined */
|
|
|
|
dukky_push_node(ctx, retnode);
|
|
|
|
dom_node_unref(retnode);
|
|
|
|
return 1;
|
|
%}
|
|
|
|
getter Document::body()
|
|
%{
|
|
struct dom_nodelist *nodes;
|
|
struct dom_node *retnode;
|
|
dom_exception err;
|
|
err = dom_document_get_elements_by_tag_name(priv->parent.node,
|
|
corestring_dom_BODY,
|
|
&nodes);
|
|
if (err != DOM_NO_ERR) {
|
|
return 0; /* coerced to undefined */
|
|
}
|
|
|
|
err = dom_nodelist_item(nodes, 0, &retnode);
|
|
|
|
if (err != DOM_NO_ERR) {
|
|
dom_nodelist_unref(nodes);
|
|
return 0; /* coerced to undefined */
|
|
}
|
|
|
|
dom_nodelist_unref(nodes);
|
|
|
|
if (retnode != NULL) {
|
|
dukky_push_node(ctx, retnode);
|
|
|
|
dom_node_unref(retnode);
|
|
|
|
return 1;
|
|
}
|
|
|
|
return 0; /* coerced to undefined */
|
|
%}
|
|
|
|
getter Document::location()
|
|
%{
|
|
/* retrieve the location object from the root object (window) */
|
|
duk_push_global_object(ctx);
|
|
duk_get_prop_string(ctx, -1, "location");
|
|
if (duk_is_undefined(ctx, -1)) {
|
|
duk_pop(ctx);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
%}
|
|
|
|
|
|
method Document::getElementById()
|
|
%{
|
|
dom_string *elementId_dom;
|
|
dom_element *element;
|
|
dom_exception exc;
|
|
duk_size_t text_len;
|
|
const char *text = duk_safe_to_lstring(ctx, 0, &text_len);
|
|
|
|
exc = dom_string_create((uint8_t*)text, text_len, &elementId_dom);
|
|
if (exc != DOM_NO_ERR) {
|
|
return 0;
|
|
}
|
|
|
|
exc = dom_document_get_element_by_id(((node_private_t *)priv)->node,
|
|
elementId_dom, &element);
|
|
dom_string_unref(elementId_dom);
|
|
if (exc != DOM_NO_ERR) {
|
|
return 0;
|
|
}
|
|
|
|
if (element != NULL) {
|
|
dukky_push_node(ctx, (dom_node *)element);
|
|
dom_node_unref(element);
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
%}
|
|
|
|
getter Document::documentElement()
|
|
%{
|
|
dom_exception exc;
|
|
dom_element *element;
|
|
|
|
exc = dom_document_get_document_element(((node_private_t *)priv)->node,
|
|
&element);
|
|
if (exc != DOM_NO_ERR) {
|
|
return 0;
|
|
}
|
|
|
|
if (element == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
dukky_push_node(ctx, (dom_node *)element);
|
|
dom_node_unref(element);
|
|
|
|
return 1;
|
|
|
|
%}
|
|
|
|
method Document::getElementsByTagName()
|
|
%{
|
|
dom_nodelist *nodes;
|
|
dom_exception err;
|
|
duk_size_t text_len;
|
|
const char *text = duk_safe_to_lstring(ctx, 0, &text_len);
|
|
dom_string *tag;
|
|
|
|
err = dom_string_create((uint8_t*)text, text_len, &tag);
|
|
|
|
if (err != DOM_NO_ERR) return 0; /* coerced to undefined */
|
|
|
|
err = dom_document_get_elements_by_tag_name(((node_private_t *)priv)->node,
|
|
tag, &nodes);
|
|
dom_string_unref(tag);
|
|
if (err != DOM_NO_ERR) return 0; /* coerced to undefined */
|
|
|
|
if (nodes == NULL) return 0; /* coerced to undefined */
|
|
|
|
dukky_push_generics(ctx, "makeListProxy");
|
|
|
|
duk_push_pointer(ctx, nodes);
|
|
dukky_create_object(ctx, PROTO_NAME(NODELIST), 1);
|
|
dom_nodelist_unref(nodes);
|
|
|
|
if (dukky_pcall(ctx, 1, false) != 0) {
|
|
NSLOG(dukky, DEBUG, "Unable to construct nodelist?");
|
|
return 0; /* coerced to undefined */
|
|
}
|
|
|
|
return 1; /* The Proxy(NodeList) wrapper */
|
|
%}
|
|
|
|
getter Document::cookie()
|
|
%{
|
|
char *cookie_str;
|
|
struct html_content *htmlc;
|
|
dom_exception err;
|
|
|
|
err = dom_node_get_user_data(priv->parent.node,
|
|
corestring_dom___ns_key_html_content_data,
|
|
&htmlc);
|
|
if ((err == DOM_NO_ERR) && (htmlc != NULL)) {
|
|
cookie_str = urldb_get_cookie(llcache_handle_get_url(htmlc->base.llcache), false);
|
|
if (cookie_str != NULL) {
|
|
duk_push_string(ctx, cookie_str);
|
|
free(cookie_str);
|
|
} else {
|
|
duk_push_string(ctx, "");
|
|
}
|
|
return 1;
|
|
} else {
|
|
NSLOG(netsurf, INFO,
|
|
"error getting htmlc. parent node:%p htmlc:%p",
|
|
priv->parent.node, htmlc);
|
|
}
|
|
return 0;
|
|
%}
|
|
|
|
setter Document::cookie()
|
|
%{
|
|
struct html_content *htmlc;
|
|
dom_exception err;
|
|
|
|
const char * cookie_str = duk_safe_to_string(ctx, 0);
|
|
|
|
err = dom_node_get_user_data(priv->parent.node,
|
|
corestring_dom___ns_key_html_content_data,
|
|
&htmlc);
|
|
if ((err == DOM_NO_ERR) && (htmlc != NULL)) {
|
|
/* At this point we need to get the given cookie string parsed
|
|
* and inserted into the urldb
|
|
*/
|
|
bool ok = urldb_set_cookie(cookie_str, /* The cookie string to set */
|
|
/* The location to set the cookie for */
|
|
llcache_handle_get_url(htmlc->base.llcache),
|
|
NULL); /* The referer, which we trust */
|
|
if (!ok) {
|
|
NSLOG(netsurf, DEEPDEBUG, "unable to set cookie: %s", cookie_str);
|
|
/* However there's no useful way to signal that to JS */
|
|
}
|
|
} else {
|
|
NSLOG(netsurf, INFO,
|
|
"error getting htmlc. parent node:%p htmlc:%p",
|
|
priv->parent.node, htmlc);
|
|
}
|
|
return 0;
|
|
%}
|
|
|
|
method Document::createEvent ()
|
|
%{
|
|
/* Create a new event, mark it untrusted since it's new from JS */
|
|
dom_event *evt = NULL;
|
|
dom_exception exc;
|
|
|
|
exc = dom_event_create(&evt);
|
|
if (exc != DOM_NO_ERR) return 0;
|
|
exc = dom_event_set_is_trusted(evt, false);
|
|
if (exc != DOM_NO_ERR) { dom_event_unref(evt); return 0; }
|
|
|
|
dukky_push_event(ctx, evt);
|
|
|
|
dom_event_unref(evt);
|
|
return 1;
|
|
%}
|
|
|
|
getter Document::implementation ()
|
|
%{
|
|
/* Create a new DOMImplementation instance */
|
|
duk_push_this(ctx);
|
|
duk_get_prop_string(ctx, -1, MAGIC(DOMImplementation));
|
|
if (duk_is_undefined(ctx, -1)) {
|
|
duk_pop(ctx);
|
|
|
|
if (dukky_create_object(ctx,
|
|
PROTO_NAME(DOMIMPLEMENTATION),
|
|
0) != DUK_EXEC_SUCCESS) {
|
|
return duk_error(ctx,
|
|
DUK_ERR_ERROR,
|
|
"Unable to create dom implementation object");
|
|
}
|
|
duk_dup(ctx, -1);
|
|
duk_put_prop_string(ctx, -3, MAGIC(DOMImplementation));
|
|
}
|
|
return 1;
|
|
%}
|
|
|
|
getter Document::onabort();
|
|
setter Document::onabort();
|
|
getter Document::onautocompleteerror();
|
|
setter Document::onautocompleteerror();
|
|
getter Document::onautocomplete();
|
|
setter Document::onautocomplete();
|
|
getter Document::onblur();
|
|
setter Document::onblur();
|
|
getter Document::oncancel();
|
|
setter Document::oncancel();
|
|
getter Document::oncanplaythrough();
|
|
setter Document::oncanplaythrough();
|
|
getter Document::oncanplay();
|
|
setter Document::oncanplay();
|
|
getter Document::onchange();
|
|
setter Document::onchange();
|
|
getter Document::onclick();
|
|
setter Document::onclick();
|
|
getter Document::onclose();
|
|
setter Document::onclose();
|
|
getter Document::oncontextmenu();
|
|
setter Document::oncontextmenu();
|
|
getter Document::oncuechange();
|
|
setter Document::oncuechange();
|
|
getter Document::ondblclick();
|
|
setter Document::ondblclick();
|
|
getter Document::ondragend();
|
|
setter Document::ondragend();
|
|
getter Document::ondragenter();
|
|
setter Document::ondragenter();
|
|
getter Document::ondragexit();
|
|
setter Document::ondragexit();
|
|
getter Document::ondragleave();
|
|
setter Document::ondragleave();
|
|
getter Document::ondragover();
|
|
setter Document::ondragover();
|
|
getter Document::ondragstart();
|
|
setter Document::ondragstart();
|
|
getter Document::ondrag();
|
|
setter Document::ondrag();
|
|
getter Document::ondrop();
|
|
setter Document::ondrop();
|
|
getter Document::ondurationchange();
|
|
setter Document::ondurationchange();
|
|
getter Document::onemptied();
|
|
setter Document::onemptied();
|
|
getter Document::onended();
|
|
setter Document::onended();
|
|
getter Document::onfocus();
|
|
setter Document::onfocus();
|
|
getter Document::oninput();
|
|
setter Document::oninput();
|
|
getter Document::oninvalid();
|
|
setter Document::oninvalid();
|
|
getter Document::onkeydown();
|
|
setter Document::onkeydown();
|
|
getter Document::onkeypress();
|
|
setter Document::onkeypress();
|
|
getter Document::onkeyup();
|
|
setter Document::onkeyup();
|
|
getter Document::onloadeddata();
|
|
setter Document::onloadeddata();
|
|
getter Document::onloadedmetadata();
|
|
setter Document::onloadedmetadata();
|
|
getter Document::onloadstart();
|
|
setter Document::onloadstart();
|
|
getter Document::onload();
|
|
setter Document::onload();
|
|
getter Document::onmousedown();
|
|
setter Document::onmousedown();
|
|
getter Document::onmouseenter();
|
|
setter Document::onmouseenter();
|
|
getter Document::onmouseleave();
|
|
setter Document::onmouseleave();
|
|
getter Document::onmousemove();
|
|
setter Document::onmousemove();
|
|
getter Document::onmouseout();
|
|
setter Document::onmouseout();
|
|
getter Document::onmouseover();
|
|
setter Document::onmouseover();
|
|
getter Document::onmouseup();
|
|
setter Document::onmouseup();
|
|
getter Document::onpause();
|
|
setter Document::onpause();
|
|
getter Document::onplaying();
|
|
setter Document::onplaying();
|
|
getter Document::onplay();
|
|
setter Document::onplay();
|
|
getter Document::onprogress();
|
|
setter Document::onprogress();
|
|
getter Document::onratechange();
|
|
setter Document::onratechange();
|
|
getter Document::onreadystatechange();
|
|
setter Document::onreadystatechange();
|
|
getter Document::onreset();
|
|
setter Document::onreset();
|
|
getter Document::onresize();
|
|
setter Document::onresize();
|
|
getter Document::onscroll();
|
|
setter Document::onscroll();
|
|
getter Document::onseeked();
|
|
setter Document::onseeked();
|
|
getter Document::onseeking();
|
|
setter Document::onseeking();
|
|
getter Document::onselect();
|
|
setter Document::onselect();
|
|
getter Document::onshow();
|
|
setter Document::onshow();
|
|
getter Document::onsort();
|
|
setter Document::onsort();
|
|
getter Document::onstalled();
|
|
setter Document::onstalled();
|
|
getter Document::onsubmit();
|
|
setter Document::onsubmit();
|
|
getter Document::onsuspend();
|
|
setter Document::onsuspend();
|
|
getter Document::ontimeupdate();
|
|
setter Document::ontimeupdate();
|
|
getter Document::ontoggle();
|
|
setter Document::ontoggle();
|
|
getter Document::onvolumechange();
|
|
setter Document::onvolumechange();
|
|
getter Document::onwaiting();
|
|
setter Document::onwaiting();
|
|
getter Document::onwheel();
|
|
setter Document::onwheel();
|