diff --git a/src/ecmascript/mujs.cpp b/src/ecmascript/mujs.cpp index 801cdbf86..7c55a2c7b 100644 --- a/src/ecmascript/mujs.cpp +++ b/src/ecmascript/mujs.cpp @@ -26,8 +26,11 @@ #include "ecmascript/ecmascript.h" #include "ecmascript/mujs.h" #include "ecmascript/mujs/console.h" +#include "ecmascript/mujs/document.h" +#include "ecmascript/mujs/element.h" #include "ecmascript/mujs/history.h" #include "ecmascript/mujs/localstorage.h" +#include "ecmascript/mujs/location.h" #include "ecmascript/mujs/navigator.h" #include "ecmascript/mujs/screen.h" #include "ecmascript/mujs/unibar.h" @@ -89,6 +92,9 @@ mujs_get_interpreter(struct ecmascript_interpreter *interpreter) mjs_history_init(J); mjs_console_init(J); mjs_localstorage_init(J); + mjs_element_init(J); + mjs_document_init(J); + mjs_location_init(J); return J; #if 0 diff --git a/src/ecmascript/mujs/attr.cpp b/src/ecmascript/mujs/attr.cpp new file mode 100644 index 000000000..5e6b9f8e1 --- /dev/null +++ b/src/ecmascript/mujs/attr.cpp @@ -0,0 +1,136 @@ +/* The MuJS attr object implementation. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "elinks.h" + +#include "bfu/dialog.h" +#include "cache/cache.h" +#include "cookies/cookies.h" +#include "dialogs/menu.h" +#include "dialogs/status.h" +#include "document/html/frames.h" +#include "document/document.h" +#include "document/forms.h" +#include "document/view.h" +#include "ecmascript/ecmascript.h" +#include "ecmascript/mujs.h" +#include "ecmascript/mujs/attr.h" +#include "intl/libintl.h" +#include "main/select.h" +#include "osdep/newwin.h" +#include "osdep/sysname.h" +#include "protocol/http/http.h" +#include "protocol/uri.h" +#include "session/history.h" +#include "session/location.h" +#include "session/session.h" +#include "session/task.h" +#include "terminal/tab.h" +#include "terminal/terminal.h" +#include "util/conv.h" +#include "util/memory.h" +#include "util/string.h" +#include "viewer/text/draw.h" +#include "viewer/text/form.h" +#include "viewer/text/link.h" +#include "viewer/text/vs.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +static void +mjs_attr_get_property_name(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct view_state *vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + xmlpp::AttributeNode *attr = static_cast(js_touserdata(J, 0, "attr")); + + if (!attr) { + js_pushnull(J); + return; + } + xmlpp::ustring v = attr->get_name(); + js_pushstring(J, v.c_str()); +} + +static void +mjs_attr_get_property_value(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct view_state *vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + + xmlpp::AttributeNode *attr = static_cast(js_touserdata(J, 0, "attr")); + + if (!attr) { + js_pushnull(J); + return; + } + + xmlpp::ustring v = attr->get_value(); + js_pushstring(J, v.c_str()); +} + +static void +mjs_attr_toString(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + js_pushstring(J, "[attr object]"); +} + +static std::map map_attrs; + +static +void mjs_attr_finalizer(js_State *J, void *node) +{ + map_attrs.erase(node); +} + +void +mjs_push_attr(js_State *J, void *node) +{ + js_newobject(J); + { + js_newuserdata(J, "attr", node, mjs_attr_finalizer); + addmethod(J, "toString", mjs_attr_toString, 0); + addproperty(J, "name", mjs_attr_get_property_name, NULL); + addproperty(J, "value", mjs_attr_get_property_value, NULL); + } +} diff --git a/src/ecmascript/mujs/attr.h b/src/ecmascript/mujs/attr.h new file mode 100644 index 000000000..ef74c8f79 --- /dev/null +++ b/src/ecmascript/mujs/attr.h @@ -0,0 +1,8 @@ +#ifndef EL__ECMASCRIPT_MUJS_ATTR_H +#define EL__ECMASCRIPT_MUJS_ATTR_H + +#include + +void mjs_push_attr(js_State *J, void *node); + +#endif diff --git a/src/ecmascript/mujs/attributes.cpp b/src/ecmascript/mujs/attributes.cpp new file mode 100644 index 000000000..d34f1816f --- /dev/null +++ b/src/ecmascript/mujs/attributes.cpp @@ -0,0 +1,238 @@ +/* The MuJS attributes object implementation. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "elinks.h" + +#include "bfu/dialog.h" +#include "cache/cache.h" +#include "cookies/cookies.h" +#include "dialogs/menu.h" +#include "dialogs/status.h" +#include "document/html/frames.h" +#include "document/document.h" +#include "document/forms.h" +#include "document/view.h" +#include "ecmascript/ecmascript.h" +#include "ecmascript/mujs.h" +#include "ecmascript/mujs/attr.h" +#include "ecmascript/mujs/attributes.h" +#include "intl/libintl.h" +#include "main/select.h" +#include "osdep/newwin.h" +#include "osdep/sysname.h" +#include "protocol/http/http.h" +#include "protocol/uri.h" +#include "session/history.h" +#include "session/location.h" +#include "session/session.h" +#include "session/task.h" +#include "terminal/tab.h" +#include "terminal/terminal.h" +#include "util/conv.h" +#include "util/memory.h" +#include "util/string.h" +#include "viewer/text/draw.h" +#include "viewer/text/form.h" +#include "viewer/text/link.h" +#include "viewer/text/vs.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +static std::map map_attributes; +static std::map map_rev_attributes; + +static void +mjs_attributes_set_items(js_State *J, void *node) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + assert(interpreter); + + xmlpp::Element::AttributeList *al = static_cast(node); + + if (!al) { + return; + } + + auto it = al->begin(); + auto end = al->end(); + int i = 0; + + for (;it != end; ++it, ++i) { + xmlpp::Attribute *attr = *it; + + if (!attr) { + continue; + } +// TODO Check it + mjs_push_attr(J, attr); + js_dup(J); + js_setindex(J, -2, i); + + xmlpp::ustring name = attr->get_name(); + + if (name != "" && name != "item" && name != "namedItem") { + js_setproperty(J, -2, name.c_str()); + } + js_pop(J, 1); + } +} + +static void +mjs_attributes_get_property_length(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct view_state *vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + xmlpp::Element::AttributeList *al = static_cast(js_touserdata(J, 0, "attribute")); + + if (!al) { + js_pushnumber(J, 0); + return; + } + js_pushnumber(J, al->size()); +} + +static void +mjs_push_attributes_item2(js_State *J, int idx) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + xmlpp::Element::AttributeList *al = static_cast(js_touserdata(J, 0, "attribute")); + + if (!al) { + js_pushundefined(J); + return; + } + + auto it = al->begin(); + auto end = al->end(); + int i = 0; + + for (;it != end; it++, i++) { + if (i != idx) { + continue; + } + xmlpp::Attribute *attr = *it; + mjs_push_attr(J, attr); + return; + } + js_pushundefined(J); +} + +static void +mjs_attributes_item(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + int index = js_toint32(J, 1); + + mjs_push_attributes_item2(J, index); +} + +static void +mjs_push_attributes_namedItem2(js_State *J, const char *str) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + xmlpp::Element::AttributeList *al = static_cast(js_touserdata(J, 0, "attribute")); + + if (!al) { + js_pushundefined(J); + return; + } + + xmlpp::ustring name = str; + + auto it = al->begin(); + auto end = al->end(); + + for (; it != end; ++it) { + auto attr = dynamic_cast(*it); + + if (!attr) { + continue; + } + + if (name == attr->get_name()) { + mjs_push_attr(J, attr); + return; + } + } + js_pushundefined(J); +} + +static void +mjs_attributes_getNamedItem(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + const char *str = js_tostring(J, 1); + + mjs_push_attributes_namedItem2(J, str); +} + +static void +mjs_attributes_toString(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + js_pushstring(J, "[attributes object]"); +} + +static void +mjs_attributes_finalizer(js_State *J, void *node) +{ + map_attributes.erase(node); +} + +void +mjs_push_attributes(js_State *J, void *node) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + js_newobject(J); + { + js_newuserdata(J, "attribute", node, mjs_attributes_finalizer); + addmethod(J, "item", mjs_attributes_item, 1); + addmethod(J, "getNamedItem", mjs_attributes_getNamedItem, 1); + addmethod(J, "toString", mjs_attributes_toString, 0); + addproperty(J, "length", mjs_attributes_get_property_length, NULL); + + mjs_attributes_set_items(J, node); + } + map_attributes[node] = node; +} diff --git a/src/ecmascript/mujs/attributes.h b/src/ecmascript/mujs/attributes.h new file mode 100644 index 000000000..860468556 --- /dev/null +++ b/src/ecmascript/mujs/attributes.h @@ -0,0 +1,8 @@ +#ifndef EL__ECMASCRIPT_MUJS_ATTRIBUTES_H +#define EL__ECMASCRIPT_MUJS_ATTRIBUTES_H + +#include + +void mjs_push_attributes(js_State *J, void *node); + +#endif diff --git a/src/ecmascript/mujs/collection.cpp b/src/ecmascript/mujs/collection.cpp new file mode 100644 index 000000000..74a1e126a --- /dev/null +++ b/src/ecmascript/mujs/collection.cpp @@ -0,0 +1,231 @@ +/* The MuJS html collection object implementation. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "elinks.h" + +#include "bfu/dialog.h" +#include "cache/cache.h" +#include "cookies/cookies.h" +#include "dialogs/menu.h" +#include "dialogs/status.h" +#include "document/html/frames.h" +#include "document/document.h" +#include "document/forms.h" +#include "document/view.h" +#include "ecmascript/ecmascript.h" +#include "ecmascript/mujs.h" +#include "ecmascript/mujs/collection.h" +#include "ecmascript/mujs/element.h" +#include "intl/libintl.h" +#include "main/select.h" +#include "osdep/newwin.h" +#include "osdep/sysname.h" +#include "protocol/http/http.h" +#include "protocol/uri.h" +#include "session/history.h" +#include "session/location.h" +#include "session/session.h" +#include "session/task.h" +#include "terminal/tab.h" +#include "terminal/terminal.h" +#include "util/conv.h" +#include "util/memory.h" +#include "util/string.h" +#include "viewer/text/draw.h" +#include "viewer/text/form.h" +#include "viewer/text/link.h" +#include "viewer/text/vs.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +static std::map map_collections; +static std::map map_rev_collections; + +static void +mjs_htmlCollection_get_property_length(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + xmlpp::Node::NodeSet *ns = static_cast(js_touserdata(J, 0, "collection")); + + if (!ns) { + js_pushnumber(J, 0); + return; + } + js_pushnumber(J, ns->size()); +} + +static void +mjs_push_htmlCollection_item2(js_State *J, int idx) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + xmlpp::Node::NodeSet *ns = static_cast(js_touserdata(J, 0, "collection")); + + if (!ns) { + js_pushundefined(J); + return; + } + + xmlpp::Element *element; + + try { + element = dynamic_cast(ns->at(idx)); + } catch (std::out_of_range &e) { + js_pushundefined(J); + return; + } + + if (!element) { + js_pushundefined(J); + return; + } + mjs_push_element(J, element); +} + +static void +mjs_htmlCollection_item(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + int index = js_toint32(J, 1); + + mjs_push_htmlCollection_item2(J, index); +} + +static void +mjs_push_htmlCollection_namedItem2(js_State *J, const char *str) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + xmlpp::Node::NodeSet *ns = static_cast(js_touserdata(J, 0, "collection")); + + if (!ns) { + js_pushundefined(J); + return; + } + + xmlpp::ustring name = str; + + auto it = ns->begin(); + auto end = ns->end(); + + for (; it != end; ++it) { + auto element = dynamic_cast(*it); + + if (!element) { + continue; + } + + if (name == element->get_attribute_value("id") + || name == element->get_attribute_value("name")) { + mjs_push_element(J, element); + return; + } + } + js_pushundefined(J); +} + +static void +mjs_htmlCollection_namedItem(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + const char *str = js_tostring(J, 1); + + mjs_push_htmlCollection_namedItem2(J, str); +} + +static void +mjs_htmlCollection_set_items(js_State *J, void *node) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + int counter = 0; + + xmlpp::Node::NodeSet *ns = static_cast(js_touserdata(J, 0, "collection")); + + if (!ns) { + return; + } + + xmlpp::Element *element; + + while (1) { + try { + element = dynamic_cast(ns->at(counter)); + } catch (std::out_of_range &e) { return;} + + if (!element) { + return; + } + mjs_push_element(J, element); + js_dup(J); + js_setindex(J, -2, counter); + + xmlpp::ustring name = element->get_attribute_value("id"); + if (name == "") { + name = element->get_attribute_value("name"); + } + if (name != "" && name != "item" && name != "namedItem") { + js_setproperty(J, -2, name.c_str()); + } + js_pop(J, 1); + counter++; + } +} + +static void +mjs_htmlCollection_toString(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + js_pushstring(J, "[htmlCollection object]"); +} + +static void +mjs_htmlCollection_finalizer(js_State *J, void *node) +{ + map_collections.erase(node); +} + +void +mjs_push_collection(js_State *J, void *node) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + js_newobject(J); + { + js_newuserdata(J, "collection", node, mjs_htmlCollection_finalizer); + addmethod(J, "item", mjs_htmlCollection_item, 1); + addmethod(J, "namedItem", mjs_htmlCollection_namedItem, 1); + addmethod(J, "toString", mjs_htmlCollection_toString, 0); + addproperty(J, "length", mjs_htmlCollection_get_property_length, NULL); + mjs_htmlCollection_set_items(J, node); + } + map_collections[node] = node; +} diff --git a/src/ecmascript/mujs/collection.h b/src/ecmascript/mujs/collection.h new file mode 100644 index 000000000..b82bd99c6 --- /dev/null +++ b/src/ecmascript/mujs/collection.h @@ -0,0 +1,8 @@ +#ifndef EL__ECMASCRIPT_MUJS_COLLECTION_H +#define EL__ECMASCRIPT_MUJS_COLLECTION_H + +#include + +void mjs_push_collection(js_State *J, void *node); + +#endif diff --git a/src/ecmascript/mujs/document.cpp b/src/ecmascript/mujs/document.cpp new file mode 100644 index 000000000..b7630c313 --- /dev/null +++ b/src/ecmascript/mujs/document.cpp @@ -0,0 +1,1598 @@ +/* The MuJS document object implementation. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "elinks.h" + +#include "ecmascript/ecmascript.h" + +#include "bfu/dialog.h" +#include "cache/cache.h" +#include "cookies/cookies.h" +#include "dialogs/menu.h" +#include "dialogs/status.h" +#include "document/html/frames.h" +#include "document/document.h" +#include "document/forms.h" +#include "document/view.h" +#include "ecmascript/css2xpath.h" +#include "ecmascript/ecmascript.h" +#include "ecmascript/mujs.h" +#include "ecmascript/mujs/collection.h" +#include "ecmascript/mujs/form.h" +#include "ecmascript/mujs/forms.h" +#include "ecmascript/mujs/implementation.h" +#include "ecmascript/mujs/location.h" +#include "ecmascript/mujs/document.h" +#include "ecmascript/mujs/element.h" +#include "ecmascript/mujs/nodelist.h" +#include "ecmascript/mujs/window.h" +#include "intl/libintl.h" +#include "main/select.h" +#include "osdep/newwin.h" +#include "osdep/sysname.h" +#include "protocol/http/http.h" +#include "protocol/uri.h" +#include "session/history.h" +#include "session/location.h" +#include "session/session.h" +#include "session/task.h" +#include "terminal/tab.h" +#include "terminal/terminal.h" +#include "util/conv.h" +#include "util/memory.h" +#include "util/string.h" +#include "viewer/text/draw.h" +#include "viewer/text/form.h" +#include "viewer/text/link.h" +#include "viewer/text/vs.h" + +#include +#include +#include + +#include +#include +#include + +static xmlpp::Document emptyDoc; +static void mjs_push_doctype(js_State *J, void *node); + +static void +mjs_document_get_property_anchors(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct document_view *doc_view = interpreter->vs->doc_view; + struct document *document = doc_view->document; + + if (!document->dom) { + document->dom = document_parse(document); + } + + if (!document->dom) { + js_pushnull(J); + return; + } + + xmlpp::Document *docu = (xmlpp::Document *)document->dom; + xmlpp::Element* root = (xmlpp::Element *)docu->get_root_node(); + + xmlpp::ustring xpath = "//a"; + xmlpp::Node::NodeSet *elements = new(std::nothrow) xmlpp::Node::NodeSet; + + if (!elements) { + js_pushnull(J); + return; + } + + *elements = root->find(xpath); + + if (elements->size() == 0) { + js_pushnull(J); + return; + } + mjs_push_collection(J, elements); +} + +static void +mjs_document_get_property_baseURI(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct view_state *vs; + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + + char *str = get_uri_string(vs->uri, URI_BASE); + + if (!str) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + js_pushstring(J, str); + mem_free(str); +} + +static void +mjs_document_get_property_body(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct document_view *doc_view = interpreter->vs->doc_view; + struct document *document = doc_view->document; + + if (!document->dom) { + document->dom = document_parse(document); + } + + if (!document->dom) { + js_pushnull(J); + return; + } + + xmlpp::Document *docu = (xmlpp::Document *)document->dom; + xmlpp::Element* root = (xmlpp::Element *)docu->get_root_node(); + + xmlpp::ustring xpath = "//body"; + xmlpp::Node::NodeSet elements = root->find(xpath); + + if (elements.size() == 0) { + js_pushnull(J); + return; + } + auto element = elements[0]; + mjs_push_element(J, element); +} + +static void +mjs_document_set_property_body(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + // TODO + js_pushundefined(J); +} + +#ifdef CONFIG_COOKIES +static void +mjs_document_get_property_cookie(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct view_state *vs; + struct string *cookies; + vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + cookies = send_cookies_js(vs->uri); + + if (cookies) { + static char cookiestr[1024]; + + strncpy(cookiestr, cookies->source, 1023); + done_string(cookies); + js_pushstring(J, cookiestr); + return; + + } else { + js_pushstring(J, ""); + return; + } +} + +static void +mjs_document_set_property_cookie(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct view_state *vs; + vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + const char *text = js_tostring(J, 1); + + if (!text) { + js_pushnull(J); + return; + } + char *str = stracpy(text); + if (str) { + set_cookie(vs->uri, str); + mem_free(str); + } + js_pushundefined(J); +} + +#endif + +static void +mjs_document_get_property_charset(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct document_view *doc_view = interpreter->vs->doc_view; + struct document *document = doc_view->document; + + if (!document->dom) { + document->dom = document_parse(document); + } + + if (!document->dom) { + js_pushnull(J); + return; + } + + xmlpp::Document* docu = (xmlpp::Document *)document->dom; + xmlpp::ustring encoding = docu->get_encoding(); + + if (encoding == "") { + encoding = "utf-8"; + } + js_pushstring(J, encoding.c_str()); +} + +static void +mjs_document_get_property_childNodes(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct view_state *vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + struct document *document = vs->doc_view->document; + + if (!document->dom) { + document->dom = document_parse(document); + } + + if (!document->dom) { + js_pushnull(J); + return; + } + + xmlpp::Document *docu = (xmlpp::Document *)document->dom; + xmlpp::Element* root = (xmlpp::Element *)docu->get_root_node(); + + if (!root) { + js_pushnull(J); + return; + } + + xmlpp::Node::NodeList *nodes = new(std::nothrow) xmlpp::Node::NodeList; + + if (!nodes) { + js_pushnull(J); + return; + } + + *nodes = root->get_children(); + if (nodes->empty()) { + delete nodes; + js_pushnull(J); + return; + } + mjs_push_nodelist(J, nodes); +} + +static void +mjs_document_get_property_doctype(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct document_view *doc_view = interpreter->vs->doc_view; + struct document *document = doc_view->document; + + if (!document->dom) { + document->dom = document_parse(document); + } + + if (!document->dom) { + js_pushnull(J); + return; + } + + xmlpp::Document* docu = (xmlpp::Document *)document->dom; + xmlpp::Dtd *dtd = docu->get_internal_subset(); + + if (!dtd) { + js_pushnull(J); + return; + } + mjs_push_doctype(J, dtd); +} + +static void +mjs_document_get_property_documentElement(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct document_view *doc_view = interpreter->vs->doc_view; + struct document *document = doc_view->document; + + if (!document->dom) { + document->dom = document_parse(document); + } + + if (!document->dom) { + js_pushnull(J); + return; + } + + xmlpp::Document *docu = (xmlpp::Document *)document->dom; + xmlpp::Element* root = (xmlpp::Element *)docu->get_root_node(); + + xmlpp::ustring xpath = "//html"; + xmlpp::Node::NodeSet elements = root->find(xpath); + + if (elements.size() == 0) { + js_pushnull(J); + return; + } + auto element = elements[0]; + mjs_push_element(J, element); +} + +static void +mjs_document_get_property_documentURI(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct view_state *vs; + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + vs = interpreter->vs; + + if (!vs) { + js_pushnull(J); + return; + } + + char *str = get_uri_string(vs->uri, URI_BASE); + + if (!str) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + js_pushstring(J, str); + mem_free(str); +} + +static void +mjs_document_get_property_domain(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct view_state *vs; + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + + char *str = get_uri_string(vs->uri, URI_HOST); + + if (!str) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + js_pushstring(J, str); + mem_free(str); +} + +static void +mjs_document_get_property_forms(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct document_view *doc_view = interpreter->vs->doc_view; + struct document *document = doc_view->document; + + if (!document->dom) { + document->dom = document_parse(document); + } + + if (!document->dom) { + js_pushnull(J); + return; + } + + if (!document->forms_nodeset) { + document->forms_nodeset = new(std::nothrow) xmlpp::Node::NodeSet; + } + + if (!document->forms_nodeset) { + js_pushnull(J); + return; + } + + xmlpp::Document *docu = (xmlpp::Document *)document->dom; + xmlpp::Element* root = (xmlpp::Element *)docu->get_root_node(); + xmlpp::ustring xpath = "//form"; + xmlpp::Node::NodeSet *elements = static_cast(document->forms_nodeset); + *elements = root->find(xpath); + + if (elements->size() == 0) { + js_pushnull(J); + return; + } + mjs_push_forms(J, elements); +} + +static void +mjs_document_get_property_head(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct document_view *doc_view = interpreter->vs->doc_view; + struct document *document = doc_view->document; + + if (!document->dom) { + document->dom = document_parse(document); + } + + if (!document->dom) { + js_pushnull(J); + return; + } + xmlpp::Document *docu = (xmlpp::Document *)document->dom; + xmlpp::Element* root = (xmlpp::Element *)docu->get_root_node(); + + xmlpp::ustring xpath = "//head"; + xmlpp::Node::NodeSet elements = root->find(xpath); + + if (elements.size() == 0) { + js_pushnull(J); + return; + } + auto element = elements[0]; + mjs_push_element(J, element); +} + +static void +mjs_document_get_property_images(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct document_view *doc_view = interpreter->vs->doc_view; + struct document *document = doc_view->document; + + if (!document->dom) { + document->dom = document_parse(document); + } + + if (!document->dom) { + js_pushnull(J); + return; + } + xmlpp::Document *docu = (xmlpp::Document *)document->dom; + xmlpp::Element* root = (xmlpp::Element *)docu->get_root_node(); + + xmlpp::ustring xpath = "//img"; + xmlpp::Node::NodeSet *elements = new(std::nothrow) xmlpp::Node::NodeSet; + + if (!elements) { + js_pushnull(J); + return; + } + + *elements = root->find(xpath); + + if (elements->size() == 0) { + js_pushnull(J); + return; + } + mjs_push_collection(J, elements); +} + +static void +mjs_document_get_property_implementation(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct document_view *doc_view = interpreter->vs->doc_view; + struct document *document = doc_view->document; + + if (!document->dom) { + document->dom = document_parse(document); + } + + if (!document->dom) { + js_pushnull(J); + return; + } + mjs_push_implementation(J); +} + +static void +mjs_document_get_property_links(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct document_view *doc_view = interpreter->vs->doc_view; + struct document *document = doc_view->document; + + if (!document->dom) { + document->dom = document_parse(document); + } + + if (!document->dom) { + js_pushnull(J); + return; + } + xmlpp::Document *docu = (xmlpp::Document *)document->dom; + xmlpp::Element* root = (xmlpp::Element *)docu->get_root_node(); + + xmlpp::ustring xpath = "//a[@href]|//area[@href]"; + xmlpp::Node::NodeSet *elements = new(std::nothrow) xmlpp::Node::NodeSet; + + if (!elements) { + js_pushnull(J); + return; + } + + *elements = root->find(xpath); + + if (elements->size() == 0) { + js_pushnull(J); + return; + } + mjs_push_collection(J, elements); +} + +static void +mjs_document_get_property_location(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + + mjs_push_location(J); +} + +static void +mjs_document_get_property_nodeType(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + js_pushnumber(J, 9); +} + +static void +mjs_document_set_property_location(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct view_state *vs; + struct document_view *doc_view; + vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + doc_view = vs->doc_view; + const char *url = js_tostring(J, 1); + + if (!url) { + js_pushnull(J); + return; + } + location_goto_const(doc_view, url); + js_pushundefined(J); +} + +static void +mjs_document_get_property_referrer(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct view_state *vs; + struct document_view *doc_view; + struct document *document; + struct session *ses; + vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + doc_view = vs->doc_view; + document = doc_view->document; + ses = doc_view->session; + + switch (get_opt_int("protocol.http.referer.policy", NULL)) { + case REFERER_NONE: + /* oh well */ + js_pushundefined(J); + return; + + case REFERER_FAKE: + js_pushstring(J, get_opt_str("protocol.http.referer.fake", NULL)); + return; + + case REFERER_TRUE: + /* XXX: Encode as in add_url_to_httset_prop_string(&prop, ) ? --pasky */ + if (ses->referrer) { + char *str = get_uri_string(ses->referrer, URI_HTTP_REFERRER); + + if (str) { + js_pushstring(J, str); + mem_free(str); + return; + } else { + js_pushundefined(J); + return; + } + } + break; + + case REFERER_SAME_URL: + char *str = get_uri_string(document->uri, URI_HTTP_REFERRER); + + if (str) { + js_pushstring(J, str); + mem_free(str); + return; + } else { + js_pushundefined(J); + return; + } + break; + } + js_pushundefined(J); +} + +static void +mjs_document_get_property_scripts(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct document_view *doc_view = interpreter->vs->doc_view; + struct document *document = doc_view->document; + + if (!document->dom) { + document->dom = document_parse(document); + } + + if (!document->dom) { + js_pushnull(J); + return; + } + + xmlpp::Document *docu = (xmlpp::Document *)document->dom; + xmlpp::Element* root = (xmlpp::Element *)docu->get_root_node(); + + xmlpp::ustring xpath = "//script"; + xmlpp::Node::NodeSet *elements = new(std::nothrow) xmlpp::Node::NodeSet; + + if (!elements) { + js_pushnull(J); + return; + } + + *elements = root->find(xpath); + + if (elements->size() == 0) { + js_pushnull(J); + return; + } + mjs_push_collection(J, elements); +} + +static void +mjs_document_get_property_title(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct view_state *vs; + struct document_view *doc_view; + struct document *document; + + vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + doc_view = vs->doc_view; + document = doc_view->document; + js_pushstring(J, document->title); +} + +static void +mjs_document_set_property_title(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct view_state *vs; + struct document_view *doc_view; + struct document *document; + vs = interpreter->vs; + + if (!vs || !vs->doc_view) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + doc_view = vs->doc_view; + document = doc_view->document; + + const char *str = js_tostring(J, 1); + char *string; + + if (!str) { + js_pushnull(J); + return; + } + string = stracpy(str); + mem_free_set(&document->title, string); + print_screen_status(doc_view->session); + js_pushundefined(J); +} + +static void +mjs_document_get_property_url(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct view_state *vs; + struct document_view *doc_view; + struct document *document; + vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + doc_view = vs->doc_view; + document = doc_view->document; + char *str = get_uri_string(document->uri, URI_ORIGINAL); + + if (str) { + js_pushstring(J, str); + mem_free(str); + return; + } else { + js_pushundefined(J); + return; + } +} + +static void +mjs_document_set_property_url(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct view_state *vs; + struct document_view *doc_view; + vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + doc_view = vs->doc_view; + const char *url = js_tostring(J, 1); + + if (!url) { + js_pushnull(J); + return; + } + location_goto_const(doc_view, url); + js_pushundefined(J); +} + +static void +mjs_document_write_do(js_State *J, int newline) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct string code; + + if (!init_string(&code)) { + js_pushnull(J); + return; + } + int argc = js_getlength(J, 0); + + if (argc >= 1) + { + for (int i = 0; i < argc; ++i) + { + const char *str = js_tostring(J, i+1); + + if (str) { + add_to_string(&code, str); + } + } + + if (newline) + { + add_to_string(&code, "\n"); + } + } + //DBG("%s",code.source); + + /* XXX: I don't know about you, but I have *ENOUGH* of those 'Undefined + * function' errors, I want to see just the useful ones. So just + * lighting a led and going away, no muss, no fuss. --pasky */ + /* TODO: Perhaps we can introduce ecmascript.error_report_unsupported + * -> "Show information about the document using some valid, + * nevertheless unsupported methods/properties." --pasky too */ + + struct document_view *doc_view = interpreter->vs->doc_view; + struct document *document; + document = doc_view->document; + struct cache_entry *cached = doc_view->document->cached; + cached = doc_view->document->cached; + struct fragment *f = get_cache_fragment(cached); + + if (f && f->length) + { + if (false && document->ecmascript_counter==0) + { + add_fragment(cached,0,code.source,code.length); + } else { + add_fragment(cached,f->length,code.source,code.length); + } + document->ecmascript_counter++; + } + +#ifdef CONFIG_LEDS + set_led_value(interpreter->vs->doc_view->session->status.ecmascript_led, 'J'); +#endif + + done_string(&code); + js_pushundefined(J); +} + +/* @document_funcs{"write"} */ +static void +mjs_document_write(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + mjs_document_write_do(J, 0); +} + +/* @document_funcs{"writeln"} */ +static void +mjs_document_writeln(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + mjs_document_write_do(J, 1); +} + +/* @document_funcs{"replace"} */ +static void +mjs_document_replace(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct document_view *doc_view = interpreter->vs->doc_view; + struct document *document; + document = doc_view->document; + + struct string needle; + struct string heystack; + + if (!init_string(&needle)) { + js_pushnull(J); + return; + } + if (!init_string(&heystack)) { + done_string(&needle); + js_pushnull(J); + return; + } + + const char *str = js_tostring(J, 1); + + if (str) { + add_to_string(&needle, str); + } + + str = js_tostring(J, 2); + + if (str) { + add_to_string(&heystack, str); + } + //DBG("doc replace %s %s\n", needle.source, heystack.source); + + struct cache_entry *cached = doc_view->document->cached; + struct fragment *f = get_cache_fragment(cached); + + if (f && f->length) + { + struct string f_data; + if (init_string(&f_data)) { + add_bytes_to_string(&f_data, f->data, f->length); + + struct string nu_str; + if (init_string(&nu_str)) { + string_replace(&nu_str, &f_data, &needle, &heystack); + delete_entry_content(cached); + /* TBD: somehow better rerender the document + * now it's places on the session level in doc_loading_callback */ + add_fragment(cached, 0, nu_str.source, nu_str.length); + normalize_cache_entry(cached, nu_str.length); + document->ecmascript_counter++; + done_string(&nu_str); + } + //DBG("doc replace %s %s\n", needle.source, heystack.source); + done_string(&f_data); + } + } + + done_string(&needle); + done_string(&heystack); + + js_pushundefined(J); +} + +static void +mjs_document_createComment(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + xmlpp::Element* emptyRoot = (xmlpp::Element *)emptyDoc.get_root_node(); + + if (!emptyRoot) { + emptyDoc.create_root_node("root"); + } + + emptyRoot = (xmlpp::Element *)emptyDoc.get_root_node(); + + if (!emptyRoot) { + js_pushnull(J); + return; + } + const char *str = js_tostring(J, 1); + + if (!str) { + js_pushnull(J); + return; + } + xmlpp::ustring text = str; + xmlpp::CommentNode *comment = emptyRoot->add_child_comment(text); + + if (!comment) { + js_pushnull(J); + return; + } + mjs_push_element(J, comment); +} + +static void +mjs_document_createDocumentFragment(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct document_view *doc_view = interpreter->vs->doc_view; + struct document *document = doc_view->document; + + if (!document->dom) { + document->dom = document_parse(document); + } + + if (!document->dom) { + js_pushnull(J); + return; + } + + xmlpp::Document *doc2 = static_cast(document->dom); + xmlDoc *docu = doc2->cobj(); + xmlNode *xmlnode = xmlNewDocFragment(docu); + + if (!xmlnode) { + js_pushnull(J); + return; + } + + xmlpp::Node *node = new(std::nothrow) xmlpp::Node(xmlnode); + + if (!node) { + js_pushnull(J); + return; + } + mjs_push_element(J, node); +} + +static void +mjs_document_createElement(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + xmlpp::Element* emptyRoot = (xmlpp::Element *)emptyDoc.get_root_node(); + + if (!emptyRoot) { + emptyDoc.create_root_node("root"); + } + + emptyRoot = (xmlpp::Element *)emptyDoc.get_root_node(); + + if (!emptyRoot) { + js_pushnull(J); + return; + } + const char *str = js_tostring(J, 1); + + if (!str) { + js_pushnull(J); + return; + } + xmlpp::ustring text = str; + xmlpp::Element *elem = emptyRoot->add_child_element(text); + + if (!elem) { + js_pushnull(J); + return; + } + mjs_push_element(J, elem); +} + +static void +mjs_document_createTextNode(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + xmlpp::Element* emptyRoot = (xmlpp::Element *)emptyDoc.get_root_node(); + + if (!emptyRoot) { + emptyDoc.create_root_node("root"); + } + + emptyRoot = (xmlpp::Element *)emptyDoc.get_root_node(); + + if (!emptyRoot) { + js_pushnull(J); + return; + } + const char *str = js_tostring(J, 1); + + if (!str) { + js_pushnull(J); + return; + } + xmlpp::ustring text = str; + xmlpp::TextNode *textNode = emptyRoot->add_child_text(text); + + if (!textNode) { + js_pushnull(J); + return; + } + mjs_push_element(J, textNode); +} + +static void +mjs_document_getElementById(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct document_view *doc_view = interpreter->vs->doc_view; + struct document *document = doc_view->document; + + if (!document->dom) { + document->dom = document_parse(document); + } + + if (!document->dom) { + js_pushnull(J); + return; + } + + xmlpp::Document *docu = (xmlpp::Document *)document->dom; + xmlpp::Element* root = (xmlpp::Element *)docu->get_root_node(); + const char *str = js_tostring(J, 1); + + if (!str) { + js_pushnull(J); + return; + } + xmlpp::ustring id = str; + xmlpp::ustring xpath = "//*[@id=\""; + xpath += id; + xpath += "\"]"; + + auto elements = root->find(xpath); + + if (elements.size() == 0) { + js_pushnull(J); + return; + } + auto node = elements[0]; + mjs_push_element(J, node); +} + +static void +mjs_document_getElementsByClassName(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct document_view *doc_view = interpreter->vs->doc_view; + struct document *document = doc_view->document; + + if (!document->dom) { + document->dom = document_parse(document); + } + + if (!document->dom) { + js_pushnull(J); + return; + } + + xmlpp::Document *docu = (xmlpp::Document *)document->dom; + xmlpp::Element* root = (xmlpp::Element *)docu->get_root_node(); + + const char *str = js_tostring(J, 1); + + if (!str) { + js_pushnull(J); + return; + } + xmlpp::ustring id = str; + xmlpp::ustring xpath = "//*[@class=\""; + xpath += id; + xpath += "\"]"; + xmlpp::Node::NodeSet *elements = new(std::nothrow) xmlpp::Node::NodeSet; + + if (!elements) { + js_pushnull(J); + return; + } + *elements = root->find(xpath); + mjs_push_collection(J, elements); +} + +static void +mjs_document_getElementsByName(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct document_view *doc_view = interpreter->vs->doc_view; + struct document *document = doc_view->document; + + if (!document->dom) { + document->dom = document_parse(document); + } + + if (!document->dom) { + js_pushnull(J); + return; + } + + xmlpp::Document *docu = (xmlpp::Document *)document->dom; + xmlpp::Element* root = (xmlpp::Element *)docu->get_root_node(); + + const char *str = js_tostring(J, 1); + + if (!str) { + js_pushnull(J); + return; + } + xmlpp::ustring id = str; + xmlpp::ustring xpath = "//*[@id=\""; + xpath += id; + xpath += "\"]|//*[@name=\""; + xpath += id; + xpath += "\"]"; + xmlpp::Node::NodeSet *elements = new(std::nothrow) xmlpp::Node::NodeSet; + + if (!elements) { + js_pushnull(J); + return; + } + *elements = root->find(xpath); + mjs_push_collection(J, elements); +} + +static void +mjs_document_getElementsByTagName(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct document_view *doc_view = interpreter->vs->doc_view; + struct document *document = doc_view->document; + + if (!document->dom) { + document->dom = document_parse(document); + } + + if (!document->dom) { + js_pushnull(J); + return; + } + xmlpp::Document *docu = (xmlpp::Document *)document->dom; + xmlpp::Element* root = (xmlpp::Element *)docu->get_root_node(); + const char *str = js_tostring(J, 1); + + if (!str) { + js_pushnull(J); + return; + } + xmlpp::ustring id = str; + std::transform(id.begin(), id.end(), id.begin(), ::tolower); + xmlpp::ustring xpath = "//"; + xpath += id; + xmlpp::Node::NodeSet *elements = new(std::nothrow) xmlpp::Node::NodeSet; + + if (!elements) { + js_pushnull(J); + return; + } + *elements = root->find(xpath); + mjs_push_collection(J, elements); +} + +static void +mjs_document_querySelector(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct document_view *doc_view = interpreter->vs->doc_view; + struct document *document = doc_view->document; + + if (!document->dom) { + document->dom = document_parse(document); + } + + if (!document->dom) { + js_pushnull(J); + return; + } + + xmlpp::Document *docu = (xmlpp::Document *)document->dom; + xmlpp::Element* root = (xmlpp::Element *)docu->get_root_node(); + const char *str = js_tostring(J, 1); + + if (!str) { + js_pushnull(J); + return; + } + xmlpp::ustring css = str; + xmlpp::ustring xpath = css2xpath(css); + + xmlpp::Node::NodeSet elements; + + try { + elements = root->find(xpath); + } catch (xmlpp::exception &e) { + js_pushnull(J); + return; + } + + if (elements.size() == 0) { + js_pushnull(J); + return; + } + auto node = elements[0]; + mjs_push_element(J, node); +} + +static void +mjs_document_querySelectorAll(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct document_view *doc_view = interpreter->vs->doc_view; + struct document *document = doc_view->document; + + if (!document->dom) { + document->dom = document_parse(document); + } + + if (!document->dom) { + js_pushnull(J); + return; + } + + xmlpp::Document *docu = (xmlpp::Document *)document->dom; + xmlpp::Element* root = (xmlpp::Element *)docu->get_root_node(); + const char *str = js_tostring(J, 1); + + if (!str) { + js_pushnull(J); + return; + } + xmlpp::ustring css = str; + xmlpp::ustring xpath = css2xpath(css); + xmlpp::Node::NodeSet *elements = new(std::nothrow) xmlpp::Node::NodeSet; + + if (!elements) { + js_pushnull(J); + return; + } + + try { + *elements = root->find(xpath); + } catch (xmlpp::exception &e) { + } + mjs_push_collection(J, elements); +} + + +static void +mjs_doctype_get_property_name(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + xmlpp::Dtd *dtd = static_cast(js_touserdata(J, 0, "doctype")); + + if (!dtd) { + js_pushnull(J); + return; + } + xmlpp::ustring v = dtd->get_name(); + js_pushstring(J, v.c_str()); +} + +static void +mjs_doctype_get_property_publicId(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + xmlpp::Dtd *dtd = static_cast(js_touserdata(J, 0, "doctype")); + + if (!dtd) { + js_pushnull(J); + return; + } + xmlpp::ustring v = dtd->get_external_id(); + js_pushstring(J, v.c_str()); +} + +static void +mjs_doctype_get_property_systemId(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + xmlpp::Dtd *dtd = static_cast(js_touserdata(J, 0, "doctype")); + + if (!dtd) { + js_pushnull(J); + return; + } + xmlpp::ustring v = dtd->get_system_id(); + js_pushstring(J, v.c_str()); +} + +static void +mjs_document_toString(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + js_pushstring(J, "[document object]"); +} + +int +mjs_document_init(js_State *J) +{ + js_newobject(J); + { + addmethod(J, "createComment", mjs_document_createComment, 1); + addmethod(J, "createDocumentFragment",mjs_document_createDocumentFragment, 0); + addmethod(J, "createElement", mjs_document_createElement, 1); + addmethod(J, "createTextNode", mjs_document_createTextNode, 1); + addmethod(J, "write", mjs_document_write, 1); + addmethod(J, "writeln", mjs_document_writeln, 1); + addmethod(J, "replace", mjs_document_replace, 2); + addmethod(J, "getElementById", mjs_document_getElementById, 1); + addmethod(J, "getElementsByClassName", mjs_document_getElementsByClassName, 1); + addmethod(J, "getElementsByName", mjs_document_getElementsByName, 1); + addmethod(J, "getElementsByTagName", mjs_document_getElementsByTagName, 1); + addmethod(J, "querySelector", mjs_document_querySelector, 1); + addmethod(J, "querySelectorAll", mjs_document_querySelectorAll, 1); + addmethod(J, "toString", mjs_document_toString, 0); + + addproperty(J, "anchors", mjs_document_get_property_anchors, NULL); + addproperty(J, "baseURI", mjs_document_get_property_baseURI, NULL); + addproperty(J, "body", mjs_document_get_property_body, mjs_document_set_property_body); +#ifdef CONFIG_COOKIES + addproperty(J, "cookie", mjs_document_get_property_cookie, mjs_document_set_property_cookie); +#endif + addproperty(J, "charset", mjs_document_get_property_charset, NULL); + addproperty(J, "characterSet", mjs_document_get_property_charset, NULL); + addproperty(J, "childNodes", mjs_document_get_property_childNodes, NULL); + addproperty(J, "doctype", mjs_document_get_property_doctype, NULL); + addproperty(J, "documentElement", mjs_document_get_property_documentElement, NULL); + addproperty(J, "documentURI", mjs_document_get_property_documentURI, NULL); + addproperty(J, "domain", mjs_document_get_property_domain, NULL); + addproperty(J, "forms", mjs_document_get_property_forms, NULL); + addproperty(J, "head", mjs_document_get_property_head, NULL); + addproperty(J, "images", mjs_document_get_property_images, NULL); + addproperty(J, "implementation", mjs_document_get_property_implementation, NULL); + addproperty(J, "inputEncoding", mjs_document_get_property_charset, NULL); + addproperty(J, "links", mjs_document_get_property_links, NULL); + addproperty(J, "location", mjs_document_get_property_location, mjs_document_set_property_location); + addproperty(J, "nodeType", mjs_document_get_property_nodeType, NULL); + addproperty(J, "referrer", mjs_document_get_property_referrer, NULL); + addproperty(J, "scripts", mjs_document_get_property_scripts, NULL); + addproperty(J, "title", mjs_document_get_property_title, mjs_document_set_property_title); /* TODO: Charset? */ + addproperty(J, "URL", mjs_document_get_property_url, mjs_document_set_property_url); + + } + js_defglobal(J, "document", JS_DONTENUM); + + return 0; +} + +static void +mjs_doctype_toString(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + js_pushstring(J, "[doctype object]"); +} + +static std::map map_doctypes; + +static void +mjs_doctype_finalizer(js_State *J, void *node) +{ + map_doctypes.erase(node); +} + +static void +mjs_push_doctype(js_State *J, void *node) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + js_newobject(J); + { + js_newuserdata(J, "doctype", node, mjs_doctype_finalizer); + addmethod(J, "toString", mjs_doctype_toString, 0); + addproperty(J, "name", mjs_doctype_get_property_name, NULL); + addproperty(J, "publicId", mjs_doctype_get_property_publicId, NULL); + addproperty(J, "systemId", mjs_doctype_get_property_systemId, NULL); + } + map_doctypes[node] = node; +} + +void +mjs_push_document(js_State *J, void *doc) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + mjs_document_init(J); + js_newuserdata(J, "document", doc, NULL); +} diff --git a/src/ecmascript/mujs/document.h b/src/ecmascript/mujs/document.h new file mode 100644 index 000000000..2802647b9 --- /dev/null +++ b/src/ecmascript/mujs/document.h @@ -0,0 +1,9 @@ +#ifndef EL__ECMASCRIPT_MUJS_DOCUMENT_H +#define EL__ECMASCRIPT_MUJS_DOCUMENT_H + +#include + +void mjs_push_document(js_State *J, void *doc); +int mjs_document_init(js_State *J); + +#endif diff --git a/src/ecmascript/mujs/element.cpp b/src/ecmascript/mujs/element.cpp new file mode 100644 index 000000000..6dd091df0 --- /dev/null +++ b/src/ecmascript/mujs/element.cpp @@ -0,0 +1,1619 @@ +/* The QuickJS html element objects implementation. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "elinks.h" + +#include "bfu/dialog.h" +#include "cache/cache.h" +#include "cookies/cookies.h" +#include "dialogs/menu.h" +#include "dialogs/status.h" +#include "document/html/frames.h" +#include "document/document.h" +#include "document/forms.h" +#include "document/view.h" +#include "ecmascript/css2xpath.h" +#include "ecmascript/ecmascript.h" +#include "ecmascript/mujs.h" +#include "ecmascript/mujs/attr.h" +#include "ecmascript/mujs/attributes.h" +#include "ecmascript/mujs/collection.h" +#include "ecmascript/mujs/document.h" +#include "ecmascript/mujs/element.h" +#include "ecmascript/mujs/nodelist.h" +#include "ecmascript/mujs/window.h" +#include "intl/libintl.h" +#include "main/select.h" +#include "osdep/newwin.h" +#include "osdep/sysname.h" +#include "protocol/http/http.h" +#include "protocol/uri.h" +#include "session/history.h" +#include "session/location.h" +#include "session/session.h" +#include "session/task.h" +#include "terminal/tab.h" +#include "terminal/terminal.h" +#include "util/conv.h" +#include "util/memory.h" +#include "util/string.h" +#include "viewer/text/draw.h" +#include "viewer/text/form.h" +#include "viewer/text/link.h" +#include "viewer/text/vs.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +static void +mjs_element_get_property_attributes(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + xmlpp::Element *el = static_cast(js_touserdata(J, 0, "element")); + + if (!el) { + js_pushnull(J); + return; + } + xmlpp::Element::AttributeList *attrs = new(std::nothrow) xmlpp::Element::AttributeList; + + if (!attrs) { + js_pushnull(J); + return; + } + *attrs = el->get_attributes(); + + mjs_push_attributes(J, attrs); +// return getAttributes(ctx, attrs); +} + +static void +mjs_element_get_property_children(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + xmlpp::Element *el = static_cast(js_touserdata(J, 0, "element")); + + if (!el) { + js_pushnull(J); + return; + } + + auto nodes = el->get_children(); + if (nodes.empty()) { + js_pushnull(J); + return; + } + + xmlpp::Node::NodeSet *list = new(std::nothrow) xmlpp::Node::NodeSet; + + if (!list) { + js_pushnull(J); + return; + } + + auto it = nodes.begin(); + auto end = nodes.end(); + + for (; it != end; ++it) { + const auto element = dynamic_cast(*it); + + if (element) { + list->push_back(reinterpret_cast(element)); + } + } + + if (list->empty()) { + delete list; + js_pushnull(J); + return; + } + + mjs_push_collection(J, list); + +// return getCollection(ctx, list); +} + +static void +mjs_element_get_property_childElementCount(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + xmlpp::Element *el = static_cast(js_touserdata(J, 0, "element")); + + if (!el) { + js_pushnull(J); + return; + } + + int res = el->get_children().size(); + + js_pushnumber(J, res); +} + +static void +mjs_element_get_property_childNodes(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + xmlpp::Element *el = static_cast(js_touserdata(J, 0, "element")); + + if (!el) { + js_pushnull(J); + return; + } + + xmlpp::Node::NodeList *nodes = new(std::nothrow) xmlpp::Node::NodeList; + + if (!nodes) { + js_pushnull(J); + return; + } + + *nodes = el->get_children(); + if (nodes->empty()) { + delete nodes; + js_pushnull(J); + return; + } + + mjs_push_nodelist(J, nodes); +// return getNodeList(ctx, nodes); +} + +static void +mjs_element_get_property_className(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + xmlpp::Element *el = static_cast(js_touserdata(J, 0, "element")); + + if (!el) { + js_pushnull(J); + return; + } + xmlpp::ustring v = el->get_attribute_value("class"); + js_pushstring(J, v.c_str()); +// JSValue r = JS_NewStringLen(ctx, v.c_str(), v.length()); +// RETURN_JS(r); +} + +static void +mjs_element_get_property_dir(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + xmlpp::Element *el = static_cast(js_touserdata(J, 0, "element")); + + if (!el) { + js_pushnull(J); + return; + } + + xmlpp::ustring v = el->get_attribute_value("dir"); + + if (v != "auto" && v != "ltr" && v != "rtl") { + v = ""; + } + js_pushstring(J, v.c_str()); +} + +static void +mjs_element_get_property_firstChild(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + xmlpp::Element *el = static_cast(js_touserdata(J, 0, "element")); + + if (!el) { + js_pushnull(J); + return; + } + + auto node = el->get_first_child(); + + if (!node) { + js_pushnull(J); + return; + } + mjs_push_element(J, node); +// return getElement(ctx, node); +} + +static void +mjs_element_get_property_firstElementChild(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + xmlpp::Element *el = static_cast(js_touserdata(J, 0, "element")); + + if (!el) { + js_pushnull(J); + return; + } + + auto nodes = el->get_children(); + if (nodes.empty()) { + js_pushnull(J); + return; + } + + auto it = nodes.begin(); + auto end = nodes.end(); + + for (; it != end; ++it) { + auto element = dynamic_cast(*it); + + if (element) { + mjs_push_element(J, element); + return; + //return getElement(ctx, element); + } + } + js_pushnull(J); +} + +static void +mjs_element_get_property_id(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + xmlpp::Element *el = static_cast(js_touserdata(J, 0, "element")); + + if (!el) { + js_pushnull(J); + return; + } + xmlpp::ustring v = el->get_attribute_value("id"); + js_pushstring(J, v.c_str()); +} + +static void +mjs_element_get_property_lang(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + xmlpp::Element *el = static_cast(js_touserdata(J, 0, "element")); + + if (!el) { + js_pushnull(J); + return; + } + xmlpp::ustring v = el->get_attribute_value("lang"); + js_pushstring(J, v.c_str()); +} + +static void +mjs_element_get_property_lastChild(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + xmlpp::Element *el = static_cast(js_touserdata(J, 0, "element")); + + if (!el) { + js_pushnull(J); + return; + } + auto nodes = el->get_children(); + + if (nodes.empty()) { + js_pushnull(J); + return; + } + mjs_push_element(J, *(nodes.rbegin())); +} + +static void +mjs_element_get_property_lastElementChild(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + xmlpp::Element *el = static_cast(js_touserdata(J, 0, "element")); + + if (!el) { + js_pushnull(J); + return; + } + auto nodes = el->get_children(); + + if (nodes.empty()) { + js_pushnull(J); + return; + } + + auto it = nodes.rbegin(); + auto end = nodes.rend(); + + for (; it != end; ++it) { + auto element = dynamic_cast(*it); + + if (element) { + mjs_push_element(J, element); + return; + } + } + js_pushnull(J); +} + +static void +mjs_element_get_property_nextElementSibling(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + xmlpp::Element *el = static_cast(js_touserdata(J, 0, "element")); + + if (!el) { + js_pushnull(J); + return; + } + xmlpp::Node *node = el; + + while (true) { + node = node->get_next_sibling(); + + if (!node) { + js_pushnull(J); + return; + } + xmlpp::Element *next = dynamic_cast(node); + + if (next) { + mjs_push_element(J, next); + return; + } + } + js_pushnull(J); +} + +static void +mjs_element_get_property_nodeName(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + xmlpp::Node *node = static_cast(js_touserdata(J, 0, "element")); + + xmlpp::ustring v; + + if (!node) { + js_pushstring(J, ""); + return; + } + auto el = dynamic_cast(node); + + if (el) { + v = el->get_name(); + std::transform(v.begin(), v.end(), v.begin(), ::toupper); + } else { + auto el = dynamic_cast(node); + if (el) { + v = el->get_name(); + } else if (dynamic_cast(node)) { + v = "#text"; + } else if (dynamic_cast(node)) { + v = "#comment"; + } + } + js_pushstring(J, v.c_str()); +} + +static void +mjs_element_get_property_nodeType(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + xmlpp::Node *node = static_cast(js_touserdata(J, 0, "element")); + + if (!node) { + js_pushnull(J); + return; + } + + int ret = 8; + + if (dynamic_cast(node)) { + ret = 1; + } else if (dynamic_cast(node)) { + ret = 2; + } else if (dynamic_cast(node)) { + ret = 3; + } else if (dynamic_cast(node)) { + ret = 8; + } + js_pushnumber(J, ret); +} + +static void +mjs_element_get_property_nodeValue(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + xmlpp::Node *node = static_cast(js_touserdata(J, 0, "element")); + + if (!node) { + js_pushnull(J); + return; + } + + if (dynamic_cast(node)) { + js_pushnull(J); + return; + } + + auto el = dynamic_cast(node); + + if (el) { + xmlpp::ustring v = el->get_value(); + js_pushstring(J, v.c_str()); + return; + } + + auto el2 = dynamic_cast(node); + + if (el2) { + xmlpp::ustring v = el2->get_content(); + js_pushstring(J, v.c_str()); + return; + } + + auto el3 = dynamic_cast(node); + + if (el3) { + xmlpp::ustring v = el3->get_content(); + js_pushstring(J, v.c_str()); + return; + } + js_pushundefined(J); +} + +static void +mjs_element_get_property_nextSibling(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + xmlpp::Element *el = static_cast(js_touserdata(J, 0, "element")); + + if (!el) { + js_pushnull(J); + return; + } + + auto node = el->get_next_sibling(); + + if (!node) { + js_pushnull(J); + return; + } + mjs_push_element(J, node); +} + +static void +mjs_element_get_property_ownerDocument(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + + mjs_push_document(J, interpreter); +} + +static void +mjs_element_get_property_parentElement(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + xmlpp::Element *el = static_cast(js_touserdata(J, 0, "element")); + + if (!el) { + js_pushnull(J); + return; + } + + auto node = dynamic_cast(el->get_parent()); + + if (!node) { + js_pushnull(J); + return; + } + mjs_push_element(J, node); +} + +static void +mjs_element_get_property_parentNode(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + xmlpp::Element *el = static_cast(js_touserdata(J, 0, "element")); + + if (!el) { + js_pushnull(J); + return; + } + auto node = el->get_parent(); + + if (!node) { + js_pushnull(J); + return; + } + mjs_push_element(J, node); +} + +static void +mjs_element_get_property_previousElementSibling(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + xmlpp::Element *el = static_cast(js_touserdata(J, 0, "element")); + + if (!el) { + js_pushnull(J); + return; + } + xmlpp::Node *node = el; + + while (true) { + node = node->get_previous_sibling(); + + if (!node) { + js_pushnull(J); + return; + } + xmlpp::Element *next = dynamic_cast(node); + + if (next) { + mjs_push_element(J, next); + return; + } + } + js_pushnull(J); +} + +static void +mjs_element_get_property_previousSibling(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + xmlpp::Element *el = static_cast(js_touserdata(J, 0, "element")); + + if (!el) { + js_pushnull(J); + return; + } + auto node = el->get_previous_sibling(); + + if (!node) { + js_pushnull(J); + return; + } + mjs_push_element(J, node); +} + +static void +mjs_element_get_property_tagName(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + xmlpp::Element *el = static_cast(js_touserdata(J, 0, "element")); + + if (!el) { + js_pushnull(J); + return; + } + xmlpp::ustring v = el->get_name(); + std::transform(v.begin(), v.end(), v.begin(), ::toupper); + js_pushstring(J, v.c_str()); +} + +static void +mjs_element_get_property_title(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + xmlpp::Element *el = static_cast(js_touserdata(J, 0, "element")); + + if (!el) { + js_pushnull(J); + return; + } + xmlpp::ustring v = el->get_attribute_value("title"); + js_pushstring(J, v.c_str()); +} + +static void +dump_element(struct string *buf, xmlpp::Element *element, bool toSort = false) +{ + add_char_to_string(buf, '<'); + add_to_string(buf, element->get_name().c_str()); + auto attrs = element->get_attributes(); + if (toSort) { + attrs.sort([](const xmlpp::Attribute *a1, const xmlpp::Attribute *a2) + { + if (a1->get_name() == a2->get_name()) { + return a1->get_value() < a2->get_value(); + } + return a1->get_name() < a2->get_name(); + }); + } + auto it = attrs.begin(); + auto end = attrs.end(); + for (;it != end; ++it) { + add_char_to_string(buf, ' '); + add_to_string(buf, (*it)->get_name().c_str()); + add_char_to_string(buf, '='); + add_char_to_string(buf, '"'); + add_to_string(buf, (*it)->get_value().c_str()); + add_char_to_string(buf, '"'); + } + add_char_to_string(buf, '>'); +} + +void +walk_tree(struct string *buf, void *nod, bool start, bool toSortAttrs) +{ + xmlpp::Node *node = static_cast(nod); + + if (!start) { + const auto textNode = dynamic_cast(node); + + if (textNode) { + add_bytes_to_string(buf, textNode->get_content().c_str(), textNode->get_content().length()); + } else { + auto element = dynamic_cast(node); + + if (element) { + dump_element(buf, element, toSortAttrs); + } + } + } + + auto childs = node->get_children(); + auto it = childs.begin(); + auto end = childs.end(); + + for (; it != end; ++it) { + walk_tree(buf, *it, false, toSortAttrs); + } + + if (!start) { + const auto element = dynamic_cast(node); + if (element) { + add_to_string(buf, "get_name().c_str()); + add_char_to_string(buf, '>'); + } + } +} + +static void +walk_tree_content(struct string *buf, xmlpp::Node *node) +{ + const auto nodeText = dynamic_cast(node); + + if (nodeText) { + add_bytes_to_string(buf, nodeText->get_content().c_str(), nodeText->get_content().length()); + } + + auto childs = node->get_children(); + auto it = childs.begin(); + auto end = childs.end(); + + for (; it != end; ++it) { + walk_tree_content(buf, *it); + } +} + +static void +mjs_element_get_property_innerHtml(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + xmlpp::Element *el = static_cast(js_touserdata(J, 0, "element")); + + if (!el) { + js_pushnull(J); + return; + } + struct string buf; + if (!init_string(&buf)) { + js_pushnull(J); + return; + } + walk_tree(&buf, el); + js_pushstring(J, buf.source); + done_string(&buf); +} + +static void +mjs_element_get_property_outerHtml(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + xmlpp::Element *el = static_cast(js_touserdata(J, 0, "element")); + + if (!el) { + js_pushnull(J); + return; + } + struct string buf; + if (!init_string(&buf)) { + js_pushnull(J); + return; + } + walk_tree(&buf, el, false); + js_pushstring(J, buf.source); + done_string(&buf); +} + +static void +mjs_element_get_property_textContent(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + xmlpp::Element *el = static_cast(js_touserdata(J, 0, "element")); + + if (!el) { + js_pushnull(J); + return; + } + struct string buf; + if (!init_string(&buf)) { + js_pushnull(J); + return; + } + walk_tree_content(&buf, el); + js_pushstring(J, buf.source); + done_string(&buf); +} + +static void +mjs_element_set_property_className(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + const char *val = js_tostring(J, 1); + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + assert(interpreter); + xmlpp::Element *el = static_cast(js_touserdata(J, 0, "element")); + + if (!el) { + js_pushundefined(J); + return; + } + xmlpp::ustring value = val; + el->set_attribute("class", value); + interpreter->changed = true; + js_pushundefined(J); +} + +static void +mjs_element_set_property_dir(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + const char *val = js_tostring(J, 1); + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + assert(interpreter); + xmlpp::Element *el = static_cast(js_touserdata(J, 0, "element")); + + if (!el) { + js_pushundefined(J); + return; + } + xmlpp::ustring value = val; + + if (value == "ltr" || value == "rtl" || value == "auto") { + el->set_attribute("dir", value); + interpreter->changed = true; + } + js_pushundefined(J); +} + +static void +mjs_element_set_property_id(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + const char *val = js_tostring(J, 1); + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + assert(interpreter); + xmlpp::Element *el = static_cast(js_touserdata(J, 0, "element")); + + if (!el) { + js_pushundefined(J); + return; + } + xmlpp::ustring value = val; + el->set_attribute("id", value); + interpreter->changed = true; + js_pushundefined(J); +} + +static void +mjs_element_set_property_innerHtml(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + const char *val = js_tostring(J, 1); + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + xmlpp::Element *el = static_cast(js_touserdata(J, 0, "element")); + + if (!el) { + js_pushundefined(J); + return; + } + auto children = el->get_children(); + auto it = children.begin(); + auto end = children.end(); + + for (;it != end; ++it) { + xmlpp::Node::remove_node(*it); + } + xmlpp::ustring text = ""; + text += val; + text += ""; + + xmlDoc* doc = htmlReadDoc((xmlChar*)text.c_str(), NULL, NULL, HTML_PARSE_RECOVER | HTML_PARSE_NOERROR | HTML_PARSE_NOWARNING); + // Encapsulate raw libxml document in a libxml++ wrapper + xmlpp::Document doc1(doc); + + auto root = doc1.get_root_node(); + auto root1 = root->find("//root")[0]; + auto children2 = root1->get_children(); + auto it2 = children2.begin(); + auto end2 = children2.end(); + for (; it2 != end2; ++it2) { + el->import_node(*it2); + } + interpreter->changed = true; + + js_pushundefined(J); +} + +static void +mjs_element_set_property_innerText(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + const char *val = js_tostring(J, 1); + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + xmlpp::Element *el = static_cast(js_touserdata(J, 0, "element")); + + if (!el) { + js_pushundefined(J); + return; + } + auto children = el->get_children(); + auto it = children.begin(); + auto end = children.end(); + + for (;it != end; ++it) { + xmlpp::Node::remove_node(*it); + } + el->add_child_text(val); + interpreter->changed = true; + + js_pushundefined(J); +} + +static void +mjs_element_set_property_lang(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + const char *str = js_tostring(J, 1); + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + xmlpp::Element *el = static_cast(js_touserdata(J, 0, "element")); + + if (!el) { + js_pushundefined(J); + return; + } + xmlpp::ustring value = str; + el->set_attribute("lang", value); + interpreter->changed = true; + + js_pushundefined(J); +} + +static void +mjs_element_set_property_title(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + const char *str = js_tostring(J, 1); + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + xmlpp::Element *el = static_cast(js_touserdata(J, 0, "element")); + + if (!el) { + js_pushundefined(J); + return; + } + xmlpp::ustring value = str; + el->set_attribute("title", value); + interpreter->changed = true; + + js_pushundefined(J); +} + +// Common part of all add_child_element*() methods. +static xmlpp::Element* +el_add_child_element_common(xmlNode* child, xmlNode* node) +{ + if (!node) { + xmlFreeNode(child); + throw xmlpp::internal_error("Could not add child element node"); + } + xmlpp::Node::create_wrapper(node); + + return static_cast(node->_private); +} + +static void +check_contains(xmlpp::Node *node, xmlpp::Node *searched, bool *result_set, bool *result) +{ + if (*result_set) { + return; + } + + auto childs = node->get_children(); + auto it = childs.begin(); + auto end = childs.end(); + + for (; it != end; ++it) { + if (*it == searched) { + *result_set = true; + *result = true; + return; + } + check_contains(*it, searched, result_set, result); + } +} + +static void +mjs_element_appendChild(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + xmlpp::Element *el = static_cast(js_touserdata(J, 0, "element")); + + if (!el) { + js_pushnull(J); + return; + } + xmlpp::Node *el2 = static_cast(js_touserdata(J, 1, "element")); + el->import_node(el2); + interpreter->changed = true; + + mjs_push_element(J, el2); +} + +static void +mjs_element_cloneNode(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + xmlpp::Element *el = static_cast(js_touserdata(J, 0, "element")); + + if (!el) { + js_pushnull(J); + return; + } + struct document_view *doc_view = interpreter->vs->doc_view; + struct document *document = doc_view->document; + + xmlpp::Document *doc2 = static_cast(document->dom); + xmlDoc *docu = doc2->cobj(); + xmlNode *xmlnode = xmlNewDocFragment(docu); + + if (!xmlnode) { + js_pushnull(J); + return; + } + xmlpp::Node *node = new(std::nothrow) xmlpp::Node(xmlnode); + + if (!node) { + js_pushnull(J); + return; + } + + try { + xmlpp::Node *node2 = node->import_node(el, js_toboolean(J, 1)); + + if (!node2) { + js_pushnull(J); + return; + } + + mjs_push_element(J, node2); + return; + } catch (xmlpp::exception &e) { + js_pushnull(J); + return; + } +} + +static bool +isAncestor(xmlpp::Element *el, xmlpp::Node *node) +{ + while (node) { + if (el == node) { + return true; + } + node = node->get_parent(); + } + + return false; +} + +static void +mjs_element_closest(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + xmlpp::Element *el = static_cast(js_touserdata(J, 0, "element")); + + if (!el) { + js_pushnull(J); + return; + } + const char *str = js_tostring(J, 1); + xmlpp::ustring css = str; + xmlpp::ustring xpath = css2xpath(css); + + xmlpp::Node::NodeSet elements; + + try { + elements = el->find(xpath); + } catch (xmlpp::exception &e) { + js_pushnull(J); + return; + } + + if (elements.size() == 0) { + js_pushnull(J); + return; + } + + while (el) + { + for (auto node: elements) + { + if (isAncestor(el, node)) + { + mjs_push_element(J, node); + return; + } + } + el = el->get_parent(); + } + js_pushnull(J); +} + +static void +mjs_element_contains(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + xmlpp::Element *el = static_cast(js_touserdata(J, 0, "element")); + + if (!el) { + js_pushboolean(J, 0); + return; + } + xmlpp::Element *el2 = static_cast(js_touserdata(J, 1, "element")); + + if (!el2) { + js_pushboolean(J, 0); + return; + } + + bool result_set = false; + bool result = false; + + check_contains(el, el2, &result_set, &result); + + js_pushboolean(J, result); +} + +static void +mjs_element_getAttribute(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + xmlpp::Element *el = static_cast(js_touserdata(J, 0, "element")); + + if (!el) { + js_pushboolean(J, 0); + return; + } + const char *str = js_tostring(J, 1); + xmlpp::ustring v = str; + xmlpp::Attribute *attr = el->get_attribute(v); + + if (!attr) { + js_pushnull(J); + return; + } + xmlpp::ustring val = attr->get_value(); + js_pushstring(J, val.c_str()); +} + +static void +mjs_element_getAttributeNode(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + const char *str = js_tostring(J, 1); + + xmlpp::Element *el = static_cast(js_touserdata(J, 0, "element")); + + if (!el) { + js_pushundefined(J); + return; + } + + if (!str) { + js_pushnull(J); + return; + } + xmlpp::ustring v = str; + xmlpp::Attribute *attr = el->get_attribute(v); + + mjs_push_attr(J, attr); +} + +static void +mjs_element_hasAttribute(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + xmlpp::Element *el = static_cast(js_touserdata(J, 0, "element")); + + if (!el) { + js_pushboolean(J, 0); + return; + } + const char *str = js_tostring(J, 1); + xmlpp::ustring v = str; + xmlpp::Attribute *attr = el->get_attribute(v); + + js_pushboolean(J, (bool)attr); +} + +static void +mjs_element_hasAttributes(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + xmlpp::Element *el = static_cast(js_touserdata(J, 0, "element")); + + if (!el) { + js_pushboolean(J, 0); + return; + } + auto attrs = el->get_attributes(); + + js_pushboolean(J, (bool)attrs.size()); +} + +static void +mjs_element_hasChildNodes(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + xmlpp::Element *el = static_cast(js_touserdata(J, 0, "element")); + + if (!el) { + js_pushboolean(J, 0); + return; + } + auto children = el->get_children(); + + js_pushboolean(J, (bool)children.size()); +} + +static void +mjs_element_insertBefore(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + xmlpp::Element *el = static_cast(js_touserdata(J, 0, "element")); + + if (!el) { + js_pushundefined(J); + return; + } + xmlpp::Node *next_sibling = static_cast(js_touserdata(J, 2, "element")); + + if (!next_sibling) { + js_pushnull(J); + return; + } + + xmlpp::Node *child = static_cast(js_touserdata(J, 1, "element")); + auto node = xmlAddPrevSibling(next_sibling->cobj(), child->cobj()); + auto res = el_add_child_element_common(child->cobj(), node); + + interpreter->changed = true; + + mjs_push_element(J, res); +} + +static void +mjs_element_isEqualNode(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + xmlpp::Element *el = static_cast(js_touserdata(J, 0, "element")); + + if (!el) { + js_pushboolean(J, 0); + return; + } + + xmlpp::Element *el2 = static_cast(js_touserdata(J, 1, "element")); + + struct string first; + struct string second; + + if (!init_string(&first)) { + js_pushnull(J); + return; + } + if (!init_string(&second)) { + done_string(&first); + js_pushnull(J); + return; + } + + walk_tree(&first, el, false, true); + walk_tree(&second, el2, false, true); + + bool ret = !strcmp(first.source, second.source); + + done_string(&first); + done_string(&second); + + js_pushboolean(J, ret); +} + +static void +mjs_element_isSameNode(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + xmlpp::Element *el = static_cast(js_touserdata(J, 0, "element")); + + if (!el) { + js_pushboolean(J, 0); + return; + } + xmlpp::Element *el2 = static_cast(js_touserdata(J, 1, "element")); + + js_pushboolean(J, (el == el2)); +} + +static void +mjs_element_matches(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + xmlpp::Element *el = static_cast(js_touserdata(J, 0, "element")); + + if (!el) { + js_pushboolean(J, 0); + return; + } + const char *str = js_tostring(J, 1); + xmlpp::ustring css = str; + xmlpp::ustring xpath = css2xpath(css); + + xmlpp::Node::NodeSet elements; + + try { + elements = el->find(xpath); + } catch (xmlpp::exception &e) { + js_pushboolean(J, 0); + return; + } + + for (auto node: elements) { + if (node == el) { + js_pushboolean(J, 1); + return; + } + } + js_pushboolean(J, 0); +} + +static void +mjs_element_querySelector(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + xmlpp::Element *el = static_cast(js_touserdata(J, 0, "element")); + + if (!el) { + js_pushboolean(J, 0); + return; + } + const char *str = js_tostring(J, 1); + xmlpp::ustring css = str; + xmlpp::ustring xpath = css2xpath(css); + xmlpp::Node::NodeSet elements; + + try { + elements = el->find(xpath); + } catch (xmlpp::exception &e) { + js_pushnull(J); + return; + } + + for (auto node: elements) + { + if (isAncestor(el, node)) + { + mjs_push_element(J, node); + return; + } + } + js_pushnull(J); +} + +static void +mjs_element_querySelectorAll(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + xmlpp::Element *el = static_cast(js_touserdata(J, 0, "element")); + + if (!el) { + js_pushboolean(J, 0); + return; + } + const char *str = js_tostring(J, 1); + xmlpp::ustring css = str; + xmlpp::ustring xpath = css2xpath(css); + xmlpp::Node::NodeSet elements; + xmlpp::Node::NodeSet *res = new(std::nothrow) xmlpp::Node::NodeSet; + + if (!res) { + js_pushnull(J); + return; + } + + try { + elements = el->find(xpath); + } catch (xmlpp::exception &e) {} + + for (auto node: elements) + { + if (isAncestor(el, node)) { + res->push_back(node); + } + } + mjs_push_collection(J, res); +} + +static void +mjs_element_remove(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + xmlpp::Element *el = static_cast(js_touserdata(J, 0, "element")); + + if (!el) { + js_pushundefined(J); + return; + } + xmlpp::Node::remove_node(el); + interpreter->changed = true; + + js_pushundefined(J); +} + +static void +mjs_element_removeChild(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + xmlpp::Element *el = static_cast(js_touserdata(J, 0, "element")); + xmlpp::Element *el2 = static_cast(js_touserdata(J, 1, "element")); + + if (!el || !el2) { + js_pushnull(J); + return; + } + auto children = el->get_children(); + auto it = children.begin(); + auto end = children.end(); + + for (;it != end; ++it) { + if (*it == el2) { + xmlpp::Node::remove_node(el2); + interpreter->changed = true; + + mjs_push_element(J, el2); + return; + } + } + js_pushnull(J); +} + +static void +mjs_element_setAttribute(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + xmlpp::Element *el = static_cast(js_touserdata(J, 0, "element")); + + if (!el) { + js_pushundefined(J); + return; + } + const char *attr_c = js_tostring(J, 1); + const char *value_c = js_tostring(J, 2); + + xmlpp::ustring attr = attr_c; + xmlpp::ustring value = value_c; + el->set_attribute(attr, value); + interpreter->changed = true; + + js_pushundefined(J); +} + +static void +mjs_element_toString(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + js_pushstring(J, "[element object]"); +} + +static std::map map_elements; + +static void +mjs_element_finalizer(js_State *J, void *node) +{ + map_elements.erase(node); +} + +void +mjs_push_element(js_State *J, void *node) +{ + js_newobject(J); + { + js_newuserdata(J, "element", node, mjs_element_finalizer); + addmethod(J, "appendChild",mjs_element_appendChild, 1); + addmethod(J, "cloneNode", mjs_element_cloneNode, 1); + addmethod(J, "closest", mjs_element_closest, 1); + addmethod(J, "contains", mjs_element_contains, 1); + addmethod(J, "getAttribute", mjs_element_getAttribute, 1); + addmethod(J, "getAttributeNode", mjs_element_getAttributeNode, 1); + addmethod(J, "hasAttribute", mjs_element_hasAttribute, 1); + addmethod(J, "hasAttributes", mjs_element_hasAttributes, 0); + addmethod(J, "hasChildNodes", mjs_element_hasChildNodes, 0); + addmethod(J, "insertBefore", mjs_element_insertBefore, 2); + addmethod(J, "isEqualNode", mjs_element_isEqualNode, 1); + addmethod(J, "isSameNode", mjs_element_isSameNode, 1); + addmethod(J, "matches", mjs_element_matches, 1); + addmethod(J, "querySelector", mjs_element_querySelector, 1); + addmethod(J, "querySelectorAll", mjs_element_querySelectorAll, 1); + addmethod(J, "remove", mjs_element_remove, 0); + addmethod(J, "removeChild", mjs_element_removeChild, 1); + addmethod(J, "setAttribute", mjs_element_setAttribute, 2); + addmethod(J, "toString", mjs_element_toString, 0); + + addproperty(J, "attributes", mjs_element_get_property_attributes, NULL); + addproperty(J, "children", mjs_element_get_property_children, NULL); + addproperty(J, "childElementCount", mjs_element_get_property_childElementCount, NULL); + addproperty(J, "childNodes", mjs_element_get_property_childNodes, NULL); + addproperty(J, "className", mjs_element_get_property_className, mjs_element_set_property_className); + addproperty(J, "dir", mjs_element_get_property_dir, mjs_element_set_property_dir); + addproperty(J, "firstChild", mjs_element_get_property_firstChild, NULL); + addproperty(J, "firstElementChild", mjs_element_get_property_firstElementChild, NULL); + addproperty(J, "id", mjs_element_get_property_id, mjs_element_set_property_id); + addproperty(J, "innerHTML", mjs_element_get_property_innerHtml, mjs_element_set_property_innerHtml); + addproperty(J, "innerText", mjs_element_get_property_innerHtml, mjs_element_set_property_innerText); + addproperty(J, "lang", mjs_element_get_property_lang, mjs_element_set_property_lang); + addproperty(J, "lastChild", mjs_element_get_property_lastChild, NULL); + addproperty(J, "lastElementChild", mjs_element_get_property_lastElementChild, NULL); + addproperty(J, "nextElementSibling", mjs_element_get_property_nextElementSibling, NULL); + addproperty(J, "nextSibling", mjs_element_get_property_nextSibling, NULL); + addproperty(J, "nodeName", mjs_element_get_property_nodeName, NULL); + addproperty(J, "nodeType", mjs_element_get_property_nodeType, NULL); + addproperty(J, "nodeValue", mjs_element_get_property_nodeValue, NULL); + addproperty(J, "outerHTML", mjs_element_get_property_outerHtml, NULL); + addproperty(J, "ownerDocument", mjs_element_get_property_ownerDocument, NULL); + addproperty(J, "parentElement", mjs_element_get_property_parentElement, NULL); + addproperty(J, "parentNode", mjs_element_get_property_parentNode, NULL); + addproperty(J, "previousElementSibling", mjs_element_get_property_previousElementSibling, NULL); + addproperty(J, "previousSibling", mjs_element_get_property_previousSibling, NULL); + addproperty(J, "tagName", mjs_element_get_property_tagName, NULL); + addproperty(J, "textContent", mjs_element_get_property_textContent, NULL); + addproperty(J, "title", mjs_element_get_property_title, mjs_element_set_property_title); + } +} + +int +mjs_element_init(js_State *J) +{ + mjs_push_element(J, NULL); + js_defglobal(J, "Element", JS_DONTENUM); + + return 0; +} diff --git a/src/ecmascript/mujs/element.h b/src/ecmascript/mujs/element.h new file mode 100644 index 000000000..a1d14ea3b --- /dev/null +++ b/src/ecmascript/mujs/element.h @@ -0,0 +1,10 @@ +#ifndef EL__ECMASCRIPT_MUJS_ELEMENT_H +#define EL__ECMASCRIPT_MUJS_ELEMENT_H + +#include + +int mjs_element_init(js_State *J); +void mjs_push_element(js_State *J, void *node); +void walk_tree(struct string *buf, void *nod, bool start = true, bool toSortAttrs = false); + +#endif diff --git a/src/ecmascript/mujs/form.cpp b/src/ecmascript/mujs/form.cpp new file mode 100644 index 000000000..3576545f4 --- /dev/null +++ b/src/ecmascript/mujs/form.cpp @@ -0,0 +1,842 @@ +/* The MuJS form object implementation. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "elinks.h" + +#include "bfu/dialog.h" +#include "cache/cache.h" +#include "cookies/cookies.h" +#include "dialogs/menu.h" +#include "dialogs/status.h" +#include "document/html/frames.h" +#include "document/document.h" +#include "document/forms.h" +#include "document/view.h" +#include "ecmascript/ecmascript.h" +#include "ecmascript/mujs.h" +#include "ecmascript/mujs/document.h" +#include "ecmascript/mujs/form.h" +#include "ecmascript/mujs/forms.h" +#include "ecmascript/mujs/input.h" +#include "ecmascript/mujs/window.h" +#include "intl/libintl.h" +#include "main/select.h" +#include "osdep/newwin.h" +#include "osdep/sysname.h" +#include "protocol/http/http.h" +#include "protocol/uri.h" +#include "session/history.h" +#include "session/location.h" +#include "session/session.h" +#include "session/task.h" +#include "terminal/tab.h" +#include "terminal/terminal.h" +#include "util/conv.h" +#include "util/memory.h" +#include "util/string.h" +#include "viewer/text/draw.h" +#include "viewer/text/form.h" +#include "viewer/text/link.h" +#include "viewer/text/vs.h" + +#include +#include + +static std::map map_form_elements; +static std::map map_elements_form; +static std::map map_form; +static std::map map_rev_form; + +//void mjs_push_form_object(js_State *J, struct form *form); + +static void +mjs_push_form_control_object(js_State *J, + enum form_type type, struct form_state *fs) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + switch (type) { + case FC_TEXT: + case FC_PASSWORD: + case FC_FILE: + case FC_CHECKBOX: + case FC_RADIO: + case FC_SUBMIT: + case FC_IMAGE: + case FC_RESET: + case FC_BUTTON: + case FC_HIDDEN: + case FC_SELECT: + mjs_push_input_object(J, fs); + return; + + case FC_TEXTAREA: + /* TODO */ + js_pushnull(J); + return; + + default: + INTERNAL("Weird fc->type %d", type); + js_pushnull(J); + return; + } +} + +static void +mjs_form_set_items(js_State *J, void *node) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct view_state *vs; + struct document_view *doc_view; + struct document *document; + struct form_view *form_view; + struct form *form; + + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + vs = interpreter->vs; + doc_view = vs->doc_view; + document = doc_view->document; + + form_view = (struct form_view *)node; + if (!form_view) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + return; /* detached */ + } + form = find_form_by_form_view(document, form_view); + + int counter = 0; + struct el_form_control *fc; + foreach (fc, form->items) { + struct form_state *fs = find_form_state(doc_view, fc); + + if (!fs) { + continue; + } + mjs_push_form_control_object(J, fc->type, fs); + js_dup(J); + js_setindex(J, 0, counter); + + if (fc->id && strcmp(fc->id, "item") && strcmp(fc->id, "namedItem")) { + js_setproperty(J, 0, fc->id); + } else if (fc->name && strcmp(fc->name, "item") && strcmp(fc->name, "namedItem")) { + js_setproperty(J, 0, fc->name); + } else { + js_pop(J, 1); + } + counter++; + } +} + +static void +mjs_form_set_items2(js_State *J, void *node) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct view_state *vs; + struct document_view *doc_view; + struct form *form; + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + vs = interpreter->vs; + doc_view = vs->doc_view; + form = (struct form *)node; + + int counter = 0; + struct el_form_control *fc; + foreach (fc, form->items) { + struct form_state *fs = find_form_state(doc_view, fc); + + if (!fs) { + continue; + } + mjs_push_form_control_object(J, fc->type, fs); + js_dup(J); + js_setindex(J, 0, counter); + + if (fc->id && strcmp(fc->id, "item") && strcmp(fc->id, "namedItem")) { + js_setproperty(J, 0, fc->id); + } else if (fc->name && strcmp(fc->name, "item") && strcmp(fc->name, "namedItem")) { + js_setproperty(J, 0, fc->name); + } else { + js_pop(J, 1); + } + counter++; + } +} + +static void +mjs_form_elements_get_property_length(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct view_state *vs; + struct document_view *doc_view; + struct document *document; + struct form_view *form_view; + struct form *form; + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushundefined(J); + return; + } + + doc_view = vs->doc_view; + document = doc_view->document; + + form_view = (struct form_view *)js_touserdata(J, 0, "form_view"); + if (!form_view) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushundefined(J); + return; /* detached */ + } + form = find_form_by_form_view(document, form_view); + js_pushnumber(J, list_size(&form->items)); +} + +static void +mjs_form_elements_item2(js_State *J, int index) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct view_state *vs; + struct document_view *doc_view; + struct document *document; + struct form_view *form_view; + struct form *form; + struct el_form_control *fc; + int counter = -1; + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + vs = interpreter->vs; + doc_view = vs->doc_view; + document = doc_view->document; + form_view = (struct form_view *)js_touserdata(J, 0, "form_view"); + + if (!form_view) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; /* detached */ + } + form = find_form_by_form_view(document, form_view); + foreach (fc, form->items) { + counter++; + + if (counter == index) { + struct form_state *fs = find_form_state(doc_view, fc); + + if (fs) { + mjs_push_form_control_object(J, fc->type, fs); + return; + } + } + } + js_pushundefined(J); +} + +/* @form_elements_funcs{"item"} */ +static void +mjs_form_elements_item(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + int index = js_toint32(J, 1); + + mjs_form_elements_item2(J, index); +} + +static void +mjs_form_elements_namedItem2(js_State *J, const char *string) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct view_state *vs; + struct document_view *doc_view; + struct document *document; + struct form_view *form_view; + struct form *form; + struct el_form_control *fc; + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + + if (!*string) { + js_pushundefined(J); + return; + } + vs = interpreter->vs; + doc_view = vs->doc_view; + document = doc_view->document; + form_view = (struct form_view *)js_touserdata(J, 0, "form_view"); + + if (!form_view) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; /* detached */ + } + form = find_form_by_form_view(document, form_view); + + foreach (fc, form->items) { + if ((fc->id && !c_strcasecmp(string, fc->id)) + || (fc->name && !c_strcasecmp(string, fc->name))) { + struct form_state *fs = find_form_state(doc_view, fc); + + if (fs) { + mjs_push_form_control_object(J, fc->type, fs); + return; + } + } + } + js_pushundefined(J); +} + +/* @form_elements_funcs{"namedItem"} */ +static void +mjs_form_elements_namedItem(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + const char *str = js_tostring(J, 1); + + if (!str) { + js_pushnull(J); + return; + } + mjs_form_elements_namedItem2(J, str); +} + +static void +mjs_form_elements_toString(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + js_pushstring(J, "[form elements object]"); +} + +static void +mjs_form_get_property_action(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct view_state *vs; + struct form *form; + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushundefined(J); + return; + } + form = (struct form *)js_touserdata(J, 0, "form"); + assert(form); + + js_pushstring(J, form->action); +} + +static void +mjs_form_set_property_action(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct view_state *vs; + struct form *form; + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + vs = interpreter->vs; + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushundefined(J); + return; + } + form = (struct form *)js_touserdata(J, 0, "form"); + assert(form); + + const char *str = js_tostring(J, 1); + char *string; + + if (!str) { + js_pushnull(J); + return; + } + string = stracpy(str); + + if (form->action) { + ecmascript_set_action(&form->action, string); + } else { + mem_free_set(&form->action, string); + } + js_pushundefined(J); +} + +#if 0 +void +mjs_detach_form_view(struct form_view *fv) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + JSValue jsform = fv->ecmascript_obj; + + if (!JS_IsNull(jsform)) { + map_form_elements.erase(fv); + setOpaque(jsform, nullptr); + fv->ecmascript_obj = JS_NULL; + } +} +#endif + +static void +mjs_elements_finalizer(js_State *J, void *node) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct form_view *fv = (struct form_view *)node; + + map_form_elements.erase(fv); +} + +void +mjs_push_form_elements(js_State *J, struct form_view *fv) +{ + js_newobject(J); + { + js_newuserdata(J, "form_view", fv, mjs_elements_finalizer); + + addmethod(J, "item", mjs_form_elements_item, 1); + addmethod(J, "namedItem", mjs_form_elements_namedItem, 1); + addmethod(J, "toString", mjs_form_elements_toString, 0); + + addproperty(J, "length", mjs_form_elements_get_property_length, NULL); + + mjs_form_set_items(J, fv); + } + map_form_elements[fv] = fv; +} + +static void +mjs_form_get_property_elements(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct view_state *vs = interpreter->vs; + + struct form *form = (struct form *)js_touserdata(J, 0, "form"); + assert(form); + + struct form_view *fv = NULL; + bool found = false; + + foreach (fv, vs->forms) { + if (form->form_num == fv->form_num) { + found = true; + break; + } + } + + if (!found || !fv) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; /* detached */ + } + mjs_push_form_elements(J, fv); +} + +static void +mjs_form_get_property_encoding(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct view_state *vs; + struct form *form; + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + vs = interpreter->vs; + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + form = (struct form *)js_touserdata(J, 0, "form"); + assert(form); + + switch (form->method) { + case FORM_METHOD_GET: + case FORM_METHOD_POST: + js_pushstring(J, "application/x-www-form-urlencoded"); + return; + case FORM_METHOD_POST_MP: + js_pushstring(J, "multipart/form-data"); + return; + case FORM_METHOD_POST_TEXT_PLAIN: + js_pushstring(J, "text/plain"); + return; + } + js_pushundefined(J); +} + +/* @form_class.setProperty */ +static void +mjs_form_set_property_encoding(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct view_state *vs; + struct form *form; + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + vs = interpreter->vs; + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushundefined(J); + return; + } + form = (struct form *)js_touserdata(J, 0, "form"); + assert(form); + const char *str = js_tostring(J, 1); + + if (!str) { + js_pushnull(J); + return; + } + + if (!c_strcasecmp(str, "application/x-www-form-urlencoded")) { + form->method = form->method == FORM_METHOD_GET ? FORM_METHOD_GET + : FORM_METHOD_POST; + } else if (!c_strcasecmp(str, "multipart/form-data")) { + form->method = FORM_METHOD_POST_MP; + } else if (!c_strcasecmp(str, "text/plain")) { + form->method = FORM_METHOD_POST_TEXT_PLAIN; + } + js_pushundefined(J); +} + +static void +mjs_form_get_property_length(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct view_state *vs; + struct form *form; + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushundefined(J); + return; + } + form = (struct form *)js_touserdata(J, 0, "form"); + assert(form); + js_pushnumber(J, list_size(&form->items)); +} + +static void +mjs_form_get_property_method(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct view_state *vs; + struct form *form; + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushundefined(J); + return; + } + form = (struct form *)js_touserdata(J, 0, "form"); + assert(form); + + switch (form->method) { + case FORM_METHOD_GET: + js_pushstring(J, "GET"); + return; + + case FORM_METHOD_POST: + case FORM_METHOD_POST_MP: + case FORM_METHOD_POST_TEXT_PLAIN: + js_pushstring(J, "POST"); + return; + } + js_pushundefined(J); +} + +/* @form_class.setProperty */ +static void +mjs_form_set_property_method(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct view_state *vs; + struct form *form; + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushundefined(J); + return; + } + form = (struct form *)js_touserdata(J, 0, "form"); + assert(form); + const char *str = js_tostring(J, 1); + + if (!str) { + js_pushnull(J); + return; + } + + if (!c_strcasecmp(str, "GET")) { + form->method = FORM_METHOD_GET; + } else if (!c_strcasecmp(str, "POST")) { + form->method = FORM_METHOD_POST; + } + js_pushundefined(J); +} + +static void +mjs_form_get_property_name(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct view_state *vs; + struct form *form; + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushundefined(J); + return; + } + form = (struct form *)js_touserdata(J, 0, "form"); + assert(form); + js_pushstring(J, form->name); +} + +/* @form_class.setProperty */ +static void +mjs_form_set_property_name(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct view_state *vs; + struct form *form; + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushundefined(J); + return; + } + form = (struct form *)js_touserdata(J, 0, "form"); + assert(form); + + const char *str = js_tostring(J, 1); + + if (!str) { + js_pushnull(J); + return; + } + mem_free_set(&form->name, stracpy(str)); + js_pushundefined(J); +} + +static void +mjs_form_get_property_target(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct view_state *vs; + struct form *form; + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushundefined(J); + return; + } + form = (struct form *)js_touserdata(J, 0, "form"); + assert(form); + js_pushstring(J, form->target); +} + +static void +mjs_form_set_property_target(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct view_state *vs; + struct form *form; + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushundefined(J); + return; + } + form = (struct form *)js_touserdata(J, 0, "form"); + assert(form); + + const char *str = js_tostring(J, 1); + + if (!str) { + js_pushnull(J); + return; + } + mem_free_set(&form->target, stracpy(str)); + js_pushundefined(J); +} + +/* @form_funcs{"reset"} */ +static void +mjs_form_reset(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct view_state *vs; + struct document_view *doc_view; + struct form *form; + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + vs = interpreter->vs; + doc_view = vs->doc_view; + form = (struct form *)js_touserdata(J, 0, "form"); + assert(form); + + do_reset_form(doc_view, form); + draw_forms(doc_view->session->tab->term, doc_view); + + js_pushundefined(J); +} + +/* @form_funcs{"submit"} */ +static void +mjs_form_submit(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct view_state *vs; + struct document_view *doc_view; + struct session *ses; + struct form *form; + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + vs = interpreter->vs; + doc_view = vs->doc_view; + ses = doc_view->session; + + form = (struct form *)js_touserdata(J, 0, "form"); + assert(form); + submit_given_form(ses, doc_view, form, 0); + + js_pushundefined(J); +} + +static void +mjs_form_toString(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + js_pushstring(J, "[form object]"); +} + +static +void mjs_form_finalizer(js_State *J, void *node) +{ + struct form *form = (struct form *)node; + map_form.erase(form); +} + +void +mjs_push_form_object(js_State *J, struct form *form) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + js_newobject(J); + { + js_newuserdata(J, "form", form, mjs_form_finalizer); + + addmethod(J, "reset", mjs_form_reset, 0); + addmethod(J, "submit", mjs_form_submit, 0); + addmethod(J, "toString", mjs_form_toString, 0); + + addproperty(J, "action", mjs_form_get_property_action, mjs_form_set_property_action); + addproperty(J, "elements", mjs_form_get_property_elements, NULL); + addproperty(J, "encoding", mjs_form_get_property_encoding, mjs_form_set_property_encoding); + addproperty(J, "length", mjs_form_get_property_length, NULL); + addproperty(J, "method", mjs_form_get_property_method, mjs_form_set_property_method); + addproperty(J, "name", mjs_form_get_property_name, mjs_form_set_property_name); + addproperty(J, "target", mjs_form_get_property_target, mjs_form_set_property_target); + + mjs_form_set_items2(J, form); + } + map_form[form] = form; +} diff --git a/src/ecmascript/mujs/form.h b/src/ecmascript/mujs/form.h new file mode 100644 index 000000000..96e97fa6a --- /dev/null +++ b/src/ecmascript/mujs/form.h @@ -0,0 +1,10 @@ +#ifndef EL__ECMASCRIPT_MUJS_FORM_H +#define EL__ECMASCRIPT_MUJS_FORM_H + +#include + +struct form; + +void mjs_push_form_object(js_State *J, struct form *form); + +#endif diff --git a/src/ecmascript/mujs/forms.cpp b/src/ecmascript/mujs/forms.cpp new file mode 100644 index 000000000..cc3b8b771 --- /dev/null +++ b/src/ecmascript/mujs/forms.cpp @@ -0,0 +1,223 @@ +/* The MuJS forms implementation. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "elinks.h" + +#include "bfu/dialog.h" +#include "cache/cache.h" +#include "cookies/cookies.h" +#include "dialogs/menu.h" +#include "dialogs/status.h" +#include "document/html/frames.h" +#include "document/document.h" +#include "document/forms.h" +#include "document/view.h" +#include "ecmascript/ecmascript.h" +#include "ecmascript/mujs.h" +#include "ecmascript/mujs/document.h" +#include "ecmascript/mujs/form.h" +#include "ecmascript/mujs/forms.h" +#include "ecmascript/mujs/input.h" +#include "ecmascript/mujs/window.h" +#include "intl/libintl.h" +#include "main/select.h" +#include "osdep/newwin.h" +#include "osdep/sysname.h" +#include "protocol/http/http.h" +#include "protocol/uri.h" +#include "session/history.h" +#include "session/location.h" +#include "session/session.h" +#include "session/task.h" +#include "terminal/tab.h" +#include "terminal/terminal.h" +#include "util/conv.h" +#include "util/memory.h" +#include "util/string.h" +#include "viewer/text/draw.h" +#include "viewer/text/form.h" +#include "viewer/text/link.h" +#include "viewer/text/vs.h" + +#include +#include + +static std::map map_forms; +static std::map map_rev_forms; + +/* Find the form whose name is @name, which should normally be a + * string (but might not be). */ +static void +mjs_find_form_by_name(js_State *J, + struct document_view *doc_view, + const char *string) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct form *form; + + if (!*string) { + js_pushnull(J); + return; + } + + foreach (form, doc_view->document->forms) { + if (form->name && !c_strcasecmp(string, form->name)) { + mjs_push_form_object(J, form); + return; + } + } + js_pushnull(J); +} + +static void +mjs_forms_set_items(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + + struct view_state *vs; + struct document_view *doc_view; + + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + vs = interpreter->vs; + doc_view = vs->doc_view; + struct document *document = doc_view->document; + int counter = 0; + struct form_view *fv; + + foreach (fv, vs->forms) { + struct form *form = find_form_by_form_view(document, fv); + + mjs_push_form_object(J, form); + js_dup(J); + js_setindex(J, 0, counter); + + if (form->name && strcmp(form->name, "item") && strcmp(form->name, "namedItem")) { + js_setproperty(J, 0, form->name); + } else { + js_pop(J, 1); + } + counter++; + } +} + +static void +mjs_forms_get_property_length(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct view_state *vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushundefined(J); + return; + } + struct document_view *doc_view = vs->doc_view; + struct document *document = doc_view->document; + + js_pushnumber(J, list_size(&document->forms)); +} + +static void +mjs_forms_item2(js_State *J, int index) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct view_state *vs; + struct form_view *fv; + int counter = -1; + + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + + vs = interpreter->vs; + struct document_view *doc_view = vs->doc_view; + struct document *document = doc_view->document; + + foreach (fv, vs->forms) { + counter++; + if (counter == index) { + struct form *form = find_form_by_form_view(document, fv); + + mjs_push_form_object(J, form); + return; + } + } + js_pushundefined(J); +} + +/* @forms_funcs{"item"} */ +static void +mjs_forms_item(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + int index = js_toint32(J, 1);; + + mjs_forms_item2(J, index); +} + +/* @forms_funcs{"namedItem"} */ +static void +mjs_forms_namedItem(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct view_state *vs = interpreter->vs; + struct document_view *doc_view = vs->doc_view; + const char *str = js_tostring(J, 1);; + + if (!str) { + js_pushnull(J); + return; + } + mjs_find_form_by_name(J, doc_view, str); +} + +static void +mjs_forms_toString(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + js_pushstring(J, "[forms object]"); +} + +void +mjs_push_forms(js_State *J, void *node) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + + js_newobject(J); + { + js_newuserdata(J, "forms", node, NULL); + + addmethod(J, "item", mjs_forms_item, 1); + addmethod(J, "namedItem", mjs_forms_namedItem, 1); + addmethod(J, "toString", mjs_forms_toString, 0); + + addproperty(J, "length", mjs_forms_get_property_length, NULL); + mjs_forms_set_items(J); + } + map_forms[node] = node; +} diff --git a/src/ecmascript/mujs/forms.h b/src/ecmascript/mujs/forms.h new file mode 100644 index 000000000..fb104d6ee --- /dev/null +++ b/src/ecmascript/mujs/forms.h @@ -0,0 +1,11 @@ +#ifndef EL__ECMASCRIPT_MUJS_FORMS_H +#define EL__ECMASCRIPT_MUJS_FORMS_H + +#include + +struct form; + +void mjs_push_form_object(js_State *J, struct form *form); +void mjs_push_forms(js_State *J, void *node); + +#endif diff --git a/src/ecmascript/mujs/implementation.cpp b/src/ecmascript/mujs/implementation.cpp new file mode 100644 index 000000000..9ab4c3b50 --- /dev/null +++ b/src/ecmascript/mujs/implementation.cpp @@ -0,0 +1,75 @@ +/* The MuJS domimplementation object. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "elinks.h" + +#include "ecmascript/ecmascript.h" +#include "ecmascript/mujs.h" +#include "ecmascript/mujs/document.h" +#include "ecmascript/mujs/implementation.h" +#include "util/conv.h" + +#include +#include + +static void +mjs_implementation_createHTMLDocument(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + const char *title = js_tostring(J, 1); + + if (!title) { + js_pushnull(J); + return; + } + struct string str; + + if (!init_string(&str)) { + js_pushnull(J); + return; + } + add_to_string(&str, "\n"); + add_to_string(&str, title); + add_to_string(&str, ""); + + // Parse HTML and create a DOM tree + xmlDoc* doc = htmlReadDoc((xmlChar*)str.source, NULL, "utf-8", + HTML_PARSE_RECOVER | HTML_PARSE_NOERROR | HTML_PARSE_NOWARNING); + // Encapsulate raw libxml document in a libxml++ wrapper + xmlpp::Document *docu = new(std::nothrow) xmlpp::Document(doc); + done_string(&str); + + mjs_push_document(J, docu); +} + +static void +mjs_implementation_toString(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + js_pushstring(J, "[implementation object]"); +} + +void +mjs_push_implementation(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + + js_newobject(J); + { + addmethod(J, "createHTMLDocument", mjs_implementation_createHTMLDocument, 1); + addmethod(J, "toString", mjs_implementation_toString, 0); + } +} diff --git a/src/ecmascript/mujs/implementation.h b/src/ecmascript/mujs/implementation.h new file mode 100644 index 000000000..9c3838abd --- /dev/null +++ b/src/ecmascript/mujs/implementation.h @@ -0,0 +1,11 @@ +#ifndef EL__ECMASCRIPT_MUJS_IMPLEMENTATION_H +#define EL__ECMASCRIPT_MUJS_IMPLEMENTATION_H + +#include + +void mjs_push_implementation(js_State *J); + +#endif + + + diff --git a/src/ecmascript/mujs/input.cpp b/src/ecmascript/mujs/input.cpp new file mode 100644 index 000000000..cc92bbc3b --- /dev/null +++ b/src/ecmascript/mujs/input.cpp @@ -0,0 +1,1458 @@ +/* The MuJS input objects implementation. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "elinks.h" + +#include "bfu/dialog.h" +#include "cache/cache.h" +#include "cookies/cookies.h" +#include "dialogs/menu.h" +#include "dialogs/status.h" +#include "document/html/frames.h" +#include "document/document.h" +#include "document/forms.h" +#include "document/view.h" +#include "ecmascript/ecmascript.h" +#include "ecmascript/mujs.h" +#include "ecmascript/mujs/document.h" +#include "ecmascript/mujs/form.h" +#include "ecmascript/mujs/forms.h" +#include "ecmascript/mujs/input.h" +#include "ecmascript/mujs/window.h" +#include "intl/libintl.h" +#include "main/select.h" +#include "osdep/newwin.h" +#include "osdep/sysname.h" +#include "protocol/http/http.h" +#include "protocol/uri.h" +#include "session/history.h" +#include "session/location.h" +#include "session/session.h" +#include "session/task.h" +#include "terminal/tab.h" +#include "terminal/terminal.h" +#include "util/conv.h" +#include "util/memory.h" +#include "util/string.h" +#include "viewer/text/draw.h" +#include "viewer/text/form.h" +#include "viewer/text/link.h" +#include "viewer/text/vs.h" + +#include +#include + +static std::map map_inputs; + +/* Accordingly to the JS specs, each input type should own object. That'd be a + * huge PITA though, however DOM comes to the rescue and defines just a single + * HTMLInputElement. The difference could be spotted only by some clever tricky + * JS code, but I hope it doesn't matter anywhere. --pasky */ + +static struct form_state *mjs_input_get_form_state(js_State *J); + +#if 0 +static JSValue +unicode_to_value(JSContext *ctx, unicode_val_T u) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + JSValue str = JS_NewStringLen(ctx, " ", 8); + JSString *p = JS_VALUE_GET_STRING(str); + p->is_wide_char = 1; + + if (u <= 0xFFFF && !is_utf16_surrogate(u)) { + p->u.str16[0] = u; + p->len = 1; + return str; + } else if (needs_utf16_surrogates(u)) { + p->u.str16[0] = get_utf16_high_surrogate(u); + p->u.str16[1] = get_utf16_low_surrogate(u); + p->len = 2; + return str; + } else { + p->len = 1; + p->u.str16[0] = 0; + return str; + } +} + +static int +string_get(const JSString *p, int idx) +{ + return p->is_wide_char ? p->u.str16[idx] : p->u.str8[idx]; +} +#endif + +static char * +mjs_unicode_to_string(unicode_val_T v) +{ + return ""; +} + +/* Convert the string *@vp to an access key. Return 0 for no access + * key, UCS_NO_CHAR on error, or the access key otherwise. */ +static unicode_val_T +mjs_value_to_accesskey(const char *val) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + return UCS_NO_CHAR; +#if 0 + + + JSString *p = JS_VALUE_GET_STRING(val); + + size_t len; + char16_t chr[2]; + + len = p->len; + + /* This implementation ignores extra characters in the string. */ + if (len < 1) + return 0; /* which means no access key */ + chr[0] = string_get(p, 0); + + if (!is_utf16_surrogate(chr[0])) { + return chr[0]; + } + if (len >= 2) { + chr[1] = string_get(p, 1); + if (is_utf16_high_surrogate(chr[0]) + && is_utf16_low_surrogate(chr[1])) { + return join_utf16_surrogates(chr[0], chr[1]); + } + } + return UCS_NO_CHAR; /* which the caller will reject */ +#endif +} + +static void +mjs_input_get_property_accessKey(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct view_state *vs; + struct document_view *doc_view; + struct document *document; + struct form_state *fs; + struct el_form_control *fc; + int linknum; + struct link *link = NULL; + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + doc_view = vs->doc_view; + document = doc_view->document; + fs = mjs_input_get_form_state(J); + if (!fs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; /* detached */ + } + fc = find_form_control(document, fs); + + assert(fc); + assert(fc->form && fs); + + linknum = get_form_control_link(document, fc); + /* Hiddens have no link. */ + if (linknum >= 0) link = &document->links[linknum]; + + if (!link) { + js_pushundefined(J); + return; + } + + if (!link->accesskey) { + js_pushstring(J, ""); + return; + } else { + js_pushstring(J, mjs_unicode_to_string(link->accesskey)); + return; + } + js_pushundefined(J); +} + +static void +mjs_input_set_property_accessKey(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct view_state *vs; + struct document_view *doc_view; + struct document *document; + struct form_state *fs; + struct el_form_control *fc; + int linknum; + struct link *link = NULL; + unicode_val_T accesskey; + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + doc_view = vs->doc_view; + document = doc_view->document; + fs = mjs_input_get_form_state(J); + if (!fs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; /* detached */ + } + fc = find_form_control(document, fs); + + assert(fc); + assert(fc->form && fs); + + linknum = get_form_control_link(document, fc); + /* Hiddens have no link. */ + if (linknum >= 0) link = &document->links[linknum]; + + const char *val = js_tostring(J, 1); + + accesskey = mjs_value_to_accesskey(val); + + if (link) { + link->accesskey = accesskey; + } + js_pushundefined(J); +} + +static void +mjs_input_get_property_alt(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct view_state *vs; + struct document_view *doc_view; + struct document *document; + struct form_state *fs; + struct el_form_control *fc; + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + doc_view = vs->doc_view; + document = doc_view->document; + fs = mjs_input_get_form_state(J); + if (!fs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; /* detached */ + } + fc = find_form_control(document, fs); + + assert(fc); + assert(fc->form && fs); + js_pushstring(J, fc->alt); +} + +static void +mjs_input_set_property_alt(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct view_state *vs; + struct document_view *doc_view; + struct document *document; + struct form_state *fs; + struct el_form_control *fc; + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + doc_view = vs->doc_view; + document = doc_view->document; + fs = mjs_input_get_form_state(J); + if (!fs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; /* detached */ + } + fc = find_form_control(document, fs); + + assert(fc); + assert(fc->form && fs); + const char *str = js_tostring(J, 1); + char *string; + + if (!str) { + js_pushnull(J); + return; + } + string = stracpy(str); + mem_free_set(&fc->alt, string); + js_pushundefined(J); +} + +static void +mjs_input_get_property_checked(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct form_state *fs; + fs = mjs_input_get_form_state(J); + if (!fs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; /* detached */ + } + js_pushboolean(J, fs->state); +} + +static void +mjs_input_set_property_checked(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct view_state *vs; + struct document_view *doc_view; + struct document *document; + struct form_state *fs; + struct el_form_control *fc; + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + doc_view = vs->doc_view; + document = doc_view->document; + fs = mjs_input_get_form_state(J); + + if (!fs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; /* detached */ + } + fc = find_form_control(document, fs); + + assert(fc); + assert(fc->form && fs); + + if (fc->type != FC_CHECKBOX && fc->type != FC_RADIO) { + js_pushundefined(J); + return; + } + fs->state = js_toboolean(J, 1); + js_pushundefined(J); +} + +static void +mjs_input_get_property_defaultChecked(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct view_state *vs; + struct document_view *doc_view; + struct document *document; + struct form_state *fs; + struct el_form_control *fc; + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + doc_view = vs->doc_view; + document = doc_view->document; + fs = mjs_input_get_form_state(J); + if (!fs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; /* detached */ + } + fc = find_form_control(document, fs); + + assert(fc); + assert(fc->form && fs); + js_pushboolean(J, fc->default_state); +} + +static void +mjs_input_get_property_defaultValue(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct view_state *vs; + struct document_view *doc_view; + struct document *document; + struct form_state *fs; + struct el_form_control *fc; + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + doc_view = vs->doc_view; + document = doc_view->document; + fs = mjs_input_get_form_state(J); + + if (!fs) { + js_pushnull(J); + return; /* detached */ + } + fc = find_form_control(document, fs); + + assert(fc); + assert(fc->form && fs); + js_pushstring(J, fc->default_value); + /* FIXME (bug 805): convert from the charset of the document */ +} + +static void +mjs_input_get_property_disabled(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct view_state *vs; + struct document_view *doc_view; + struct document *document; + struct form_state *fs; + struct el_form_control *fc; + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + doc_view = vs->doc_view; + document = doc_view->document; + fs = mjs_input_get_form_state(J); + + if (!fs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; /* detached */ + } + fc = find_form_control(document, fs); + + assert(fc); + assert(fc->form && fs); + + /* FIXME: --pasky */ + js_pushboolean(J, fc->mode == FORM_MODE_DISABLED); +} + +static void +mjs_input_set_property_disabled(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct view_state *vs; + struct document_view *doc_view; + struct document *document; + struct form_state *fs; + struct el_form_control *fc; + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + doc_view = vs->doc_view; + document = doc_view->document; + fs = mjs_input_get_form_state(J); + + if (!fs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; /* detached */ + } + fc = find_form_control(document, fs); + + assert(fc); + assert(fc->form && fs); + int val = js_toboolean(J, 1); + + /* FIXME: --pasky */ + fc->mode = (val ? FORM_MODE_DISABLED + : fc->mode == FORM_MODE_READONLY ? FORM_MODE_READONLY + : FORM_MODE_NORMAL); + + js_pushundefined(J); +} + +static void +mjs_input_get_property_form(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + // TODO + js_pushundefined(J); +} + +static void +mjs_input_get_property_maxLength(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct view_state *vs; + struct document_view *doc_view; + struct document *document; + struct form_state *fs; + struct el_form_control *fc; + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + doc_view = vs->doc_view; + document = doc_view->document; + fs = mjs_input_get_form_state(J); + + if (!fs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; /* detached */ + } + fc = find_form_control(document, fs); + + assert(fc); + assert(fc->form && fs); + js_pushnumber(J, fc->maxlength); +} + +static void +mjs_input_set_property_maxLength(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct view_state *vs; + struct document_view *doc_view; + struct document *document; + struct form_state *fs; + struct el_form_control *fc; + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + doc_view = vs->doc_view; + document = doc_view->document; + fs = mjs_input_get_form_state(J); + + if (!fs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; /* detached */ + } + fc = find_form_control(document, fs); + + assert(fc); + assert(fc->form && fs); + fc->maxlength = js_toint32(J, 1); + js_pushundefined(J); +} + +static void +mjs_input_get_property_name(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct view_state *vs; + struct document_view *doc_view; + struct document *document; + struct form_state *fs; + struct el_form_control *fc; + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + doc_view = vs->doc_view; + document = doc_view->document; + fs = mjs_input_get_form_state(J); + + if (!fs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; /* detached */ + } + fc = find_form_control(document, fs); + + assert(fc); + assert(fc->form && fs); + js_pushstring(J, fc->name); +} + +/* @input_class.setProperty */ +static void +mjs_input_set_property_name(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct view_state *vs; + struct document_view *doc_view; + struct document *document; + struct form_state *fs; + struct el_form_control *fc; + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + doc_view = vs->doc_view; + document = doc_view->document; + fs = mjs_input_get_form_state(J); + + if (!fs) { + js_pushnull(J); + return; /* detached */ + } + fc = find_form_control(document, fs); + + assert(fc); + assert(fc->form && fs); + + const char *str = js_tostring(J, 1); + char *string; + + if (!str) { + js_pushnull(J); + return; + } + + string = stracpy(str); + mem_free_set(&fc->name, string); + js_pushundefined(J); +} + +static void +mjs_input_get_property_readonly(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct view_state *vs; + struct document_view *doc_view; + struct document *document; + struct form_state *fs; + struct el_form_control *fc; + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + doc_view = vs->doc_view; + document = doc_view->document; + fs = mjs_input_get_form_state(J); + + if (!fs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; /* detached */ + } + fc = find_form_control(document, fs); + + assert(fc); + assert(fc->form && fs); + js_pushboolean(J, fc->mode == FORM_MODE_READONLY); + /* FIXME: --pasky */ +} + +/* @input_class.setProperty */ +static void +mjs_input_set_property_readonly(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct view_state *vs; + struct document_view *doc_view; + struct document *document; + struct form_state *fs; + struct el_form_control *fc; + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + doc_view = vs->doc_view; + document = doc_view->document; + fs = mjs_input_get_form_state(J); + + if (!fs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; /* detached */ + } + fc = find_form_control(document, fs); + + assert(fc); + assert(fc->form && fs); + int val = js_toint32(J, 1); + + /* FIXME: --pasky */ + fc->mode = (val ? FORM_MODE_READONLY + : fc->mode == FORM_MODE_DISABLED ? FORM_MODE_DISABLED + : FORM_MODE_NORMAL); + + js_pushundefined(J); +} + +static void +mjs_input_get_property_selectedIndex(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct view_state *vs; + struct document_view *doc_view; + struct document *document; + struct form_state *fs; + struct el_form_control *fc; + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + doc_view = vs->doc_view; + document = doc_view->document; + fs = mjs_input_get_form_state(J); + + if (!fs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; /* detached */ + } + fc = find_form_control(document, fs); + + assert(fc); + assert(fc->form && fs); + + if (fc->type == FC_SELECT) { + js_pushnumber(J, fs->state); + return; + } else { + js_pushundefined(J); + return; + } +} + +/* @input_class.setProperty */ +static void +mjs_input_set_property_selectedIndex(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct view_state *vs; + struct document_view *doc_view; + struct document *document; + struct form_state *fs; + struct el_form_control *fc; + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + doc_view = vs->doc_view; + document = doc_view->document; + fs = mjs_input_get_form_state(J); + + if (!fs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; /* detached */ + } + fc = find_form_control(document, fs); + + assert(fc); + assert(fc->form && fs); + + if (fc->type == FC_SELECT) { + int item = js_toint32(J, 1); + + if (item >= 0 && item < fc->nvalues) { + fs->state = item; + mem_free_set(&fs->value, stracpy(fc->values[item])); + } + } + js_pushundefined(J); +} + +static void +mjs_input_get_property_size(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct view_state *vs; + struct document_view *doc_view; + struct document *document; + struct form_state *fs; + struct el_form_control *fc; + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + doc_view = vs->doc_view; + document = doc_view->document; + fs = mjs_input_get_form_state(J); + + if (!fs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; /* detached */ + } + fc = find_form_control(document, fs); + + assert(fc); + assert(fc->form && fs); + js_pushnumber(J, fc->size); +} + +static void +mjs_input_get_property_src(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct view_state *vs; + struct document_view *doc_view; + struct document *document; + struct form_state *fs; + struct el_form_control *fc; + int linknum; + struct link *link = NULL; + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + doc_view = vs->doc_view; + document = doc_view->document; + fs = mjs_input_get_form_state(J); + + if (!fs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; /* detached */ + } + fc = find_form_control(document, fs); + + assert(fc); + assert(fc->form && fs); + linknum = get_form_control_link(document, fc); + /* Hiddens have no link. */ + if (linknum >= 0) link = &document->links[linknum]; + + if (link && link->where_img) { + js_pushstring(J, link->where_img); + return; + } else { + js_pushundefined(J); + return; + } +} + +static void +mjs_input_set_property_src(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct view_state *vs; + struct document_view *doc_view; + struct document *document; + struct form_state *fs; + struct el_form_control *fc; + int linknum; + struct link *link = NULL; + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + vs = interpreter->vs; + + if (!vs) { + js_pushundefined(J); + return; + } + doc_view = vs->doc_view; + document = doc_view->document; + fs = mjs_input_get_form_state(J); + + if (!fs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; /* detached */ + } + fc = find_form_control(document, fs); + + assert(fc); + assert(fc->form && fs); + + linknum = get_form_control_link(document, fc); + /* Hiddens have no link. */ + if (linknum >= 0) link = &document->links[linknum]; + + if (link) { + const char *str = js_tostring(J, 1); + char *string; + + if (!str) { + js_pushnull(J); + return; + } + + string = stracpy(str); + mem_free_set(&link->where_img, string); + } + js_pushundefined(J); +} + +static void +mjs_input_get_property_tabIndex(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct view_state *vs; + struct document_view *doc_view; + struct document *document; + struct form_state *fs; + struct el_form_control *fc; + int linknum; + struct link *link = NULL; + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + doc_view = vs->doc_view; + document = doc_view->document; + fs = mjs_input_get_form_state(J); + + if (!fs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; /* detached */ + } + fc = find_form_control(document, fs); + + assert(fc); + assert(fc->form && fs); + + linknum = get_form_control_link(document, fc); + /* Hiddens have no link. */ + if (linknum >= 0) link = &document->links[linknum]; + + if (link) { + /* FIXME: This is WRONG. --pasky */ + js_pushnumber(J, link->number); + return; + } else { + js_pushundefined(J); + return; + } +} + +static void +mjs_input_get_property_type(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct view_state *vs; + struct document_view *doc_view; + struct document *document; + struct form_state *fs; + struct el_form_control *fc; + const char *s = NULL; + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + doc_view = vs->doc_view; + document = doc_view->document; + fs = mjs_input_get_form_state(J); + + if (!fs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; /* detached */ + } + fc = find_form_control(document, fs); + + assert(fc); + assert(fc->form && fs); + + switch (fc->type) { + case FC_TEXT: s = "text"; break; + case FC_PASSWORD: s = "password"; break; + case FC_FILE: s = "file"; break; + case FC_CHECKBOX: s = "checkbox"; break; + case FC_RADIO: s = "radio"; break; + case FC_SUBMIT: s = "submit"; break; + case FC_IMAGE: s = "image"; break; + case FC_RESET: s = "reset"; break; + case FC_BUTTON: s = "button"; break; + case FC_HIDDEN: s = "hidden"; break; + case FC_SELECT: s = "select"; break; + default: INTERNAL("input_get_property() upon a non-input item."); break; + } + + js_pushstring(J, s); +} + +static void +mjs_input_get_property_value(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct form_state *fs; + fs = mjs_input_get_form_state(J); + + if (!fs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; /* detached */ + } + js_pushstring(J, fs->value); +} + +static void +mjs_input_set_property_value(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct view_state *vs; + struct document_view *doc_view; + struct document *document; + struct form_state *fs; + struct el_form_control *fc; + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + doc_view = vs->doc_view; + document = doc_view->document; + fs = mjs_input_get_form_state(J); + + if (!fs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; /* detached */ + } + fc = find_form_control(document, fs); + + assert(fc); + assert(fc->form && fs); + + if (fc->type != FC_FILE) { + const char *str = js_tostring(J, 1); + char *string; + + if (!str) { + js_pushnull(J); + return; + } + + string = stracpy(str); + + mem_free_set(&fs->value, string); + if (fc->type == FC_TEXT || fc->type == FC_PASSWORD) + fs->state = strlen(fs->value); + } + js_pushundefined(J); +} + +static struct form_state * +mjs_input_get_form_state(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct form_state *fs = (struct form_state *)js_touserdata(J, 0, "input"); + + return fs; +} + +/* @input_funcs{"blur"} */ +static void +mjs_input_blur(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + /* We are a text-mode browser and there *always* has to be something + * selected. So we do nothing for now. (That was easy.) */ + js_pushundefined(J); +} + +/* @input_funcs{"click"} */ +static void +mjs_input_click(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct view_state *vs; + struct document_view *doc_view; + struct document *document; + struct session *ses; + struct form_state *fs; + struct el_form_control *fc; + int linknum; + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + vs = interpreter->vs; + doc_view = vs->doc_view; + document = doc_view->document; + ses = doc_view->session; + fs = mjs_input_get_form_state(J); + + if (!fs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; /* detached */ + } + + assert(fs); + fc = find_form_control(document, fs); + assert(fc); + + linknum = get_form_control_link(document, fc); + /* Hiddens have no link. */ + if (linknum < 0) { + js_pushundefined(J); + return; + } + + /* Restore old current_link afterwards? */ + jump_to_link_number(ses, doc_view, linknum); + if (enter(ses, doc_view, 0) == FRAME_EVENT_REFRESH) { + refresh_view(ses, doc_view, 0); + } else { + print_screen_status(ses); + } + js_pushundefined(J); +} + +/* @input_funcs{"focus"} */ +static void +mjs_input_focus(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct view_state *vs; + struct document_view *doc_view; + struct document *document; + struct session *ses; + struct form_state *fs; + struct el_form_control *fc; + int linknum; + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + vs = interpreter->vs; + doc_view = vs->doc_view; + document = doc_view->document; + ses = doc_view->session; + fs = mjs_input_get_form_state(J); + + if (!fs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; /* detached */ + } + + assert(fs); + fc = find_form_control(document, fs); + assert(fc); + + linknum = get_form_control_link(document, fc); + /* Hiddens have no link. */ + if (linknum < 0) { + js_pushundefined(J); + return; + } + + jump_to_link_number(ses, doc_view, linknum); + js_pushundefined(J); +} + +/* @input_funcs{"select"} */ +static void +mjs_input_select(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + /* We support no text selecting yet. So we do nothing for now. + * (That was easy, too.) */ + js_pushundefined(J); +} + +static void +mjs_input_toString(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + js_pushstring(J, "[input object]"); +} + + +#if 0 +void +quickjs_detach_form_state(struct form_state *fs) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + JSValue jsinput = fs->ecmascript_obj; + + if (!JS_IsNull(jsinput)) { + map_inputs.erase(fs); + JS_SetOpaque(jsinput, nullptr); + fs->ecmascript_obj = JS_NULL; + } +} + +void +quickjs_moved_form_state(struct form_state *fs) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + JSValue jsinput = fs->ecmascript_obj; + + if (!JS_IsNull(jsinput)) { + map_inputs.erase(fs); + JS_SetOpaque(jsinput, fs); + map_inputs[fs] = jsinput; + } +} +#endif + +static void +mjs_input_finalizer(js_State *J, void *node) +{ + struct form_state *fs = (struct form_state *)node; + map_inputs.erase(fs); +} + +void +mjs_push_input_object(js_State *J, struct form_state *fs) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + + js_newobject(J); + { + js_newuserdata(J, "input", fs, mjs_input_finalizer); + + addmethod(J, "blur", mjs_input_blur, 0); + addmethod(J, "click", mjs_input_click, 0); + addmethod(J, "focus", mjs_input_focus, 0); + addmethod(J, "select", mjs_input_select, 0); + addmethod(J, "toString", mjs_input_toString, 0); + + addproperty(J, "accessKey", mjs_input_get_property_accessKey, mjs_input_set_property_accessKey); + addproperty(J, "alt", mjs_input_get_property_alt, mjs_input_set_property_alt); + addproperty(J, "checked", mjs_input_get_property_checked, mjs_input_set_property_checked); + addproperty(J, "defaultChecked", mjs_input_get_property_defaultChecked, NULL); + addproperty(J, "defaultValue",mjs_input_get_property_defaultValue, NULL); + addproperty(J, "disabled", mjs_input_get_property_disabled, mjs_input_set_property_disabled); + addproperty(J, "form", mjs_input_get_property_form, NULL); + addproperty(J, "maxLength", mjs_input_get_property_maxLength, mjs_input_set_property_maxLength); + addproperty(J, "name", mjs_input_get_property_name, mjs_input_set_property_name); + addproperty(J, "readonly", mjs_input_get_property_readonly, mjs_input_set_property_readonly); + addproperty(J, "selectedIndex", mjs_input_get_property_selectedIndex, mjs_input_set_property_selectedIndex); + addproperty(J, "size", mjs_input_get_property_size, NULL); + addproperty(J, "src", mjs_input_get_property_src, mjs_input_set_property_src); + addproperty(J, "tabindex", mjs_input_get_property_tabIndex, NULL); + addproperty(J, "type", mjs_input_get_property_type, NULL); + addproperty(J, "value", mjs_input_get_property_value, mjs_input_set_property_value); + } + map_inputs[fs] = fs; +} diff --git a/src/ecmascript/mujs/input.h b/src/ecmascript/mujs/input.h new file mode 100644 index 000000000..8efa07542 --- /dev/null +++ b/src/ecmascript/mujs/input.h @@ -0,0 +1,10 @@ +#ifndef EL__ECMASCRIPT_MUJS_INPUT_H +#define EL__ECMASCRIPT_MUJS_INPUT_H + +#include + +struct form_state; + +void mjs_push_input_object(js_State *J, struct form_state *fs); + +#endif diff --git a/src/ecmascript/mujs/location.cpp b/src/ecmascript/mujs/location.cpp new file mode 100644 index 000000000..d0cb54761 --- /dev/null +++ b/src/ecmascript/mujs/location.cpp @@ -0,0 +1,588 @@ +/* The MuJS location object implementation. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "elinks.h" + +#include "bfu/dialog.h" +#include "cache/cache.h" +#include "cookies/cookies.h" +#include "dialogs/menu.h" +#include "dialogs/status.h" +#include "document/html/frames.h" +#include "document/document.h" +#include "document/forms.h" +#include "document/view.h" +#include "ecmascript/ecmascript.h" +#include "ecmascript/mujs.h" +#include "ecmascript/mujs/location.h" +#include "ecmascript/mujs/window.h" +#include "intl/libintl.h" +#include "main/select.h" +#include "osdep/newwin.h" +#include "osdep/sysname.h" +#include "protocol/http/http.h" +#include "protocol/uri.h" +#include "session/history.h" +#include "session/location.h" +#include "session/session.h" +#include "session/task.h" +#include "terminal/tab.h" +#include "terminal/terminal.h" +#include "util/conv.h" +#include "util/memory.h" +#include "util/string.h" +#include "viewer/text/draw.h" +#include "viewer/text/form.h" +#include "viewer/text/link.h" +#include "viewer/text/vs.h" + +static void +mjs_location_get_property_hash(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct view_state *vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + + struct string fragment; + if (!init_string(&fragment)) { + js_pushnull(J); + return; + } + + if (vs->uri->fragmentlen) { + add_bytes_to_string(&fragment, vs->uri->fragment, vs->uri->fragmentlen); + } + js_pushstring(J, fragment.source); + done_string(&fragment); +} + +static void +mjs_location_get_property_host(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct view_state *vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + + char *str = get_uri_string(vs->uri, URI_HOST_PORT); + + if (!str) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + js_pushstring(J, str); + mem_free(str); +} + +static void +mjs_location_get_property_hostname(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct view_state *vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + + char *str = get_uri_string(vs->uri, URI_HOST); + + if (!str) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + js_pushstring(J, str); + mem_free(str); +} + +static void +mjs_location_get_property_href(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct view_state *vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + + char *str = get_uri_string(vs->uri, URI_ORIGINAL); + + if (!str) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + js_pushstring(J, str); + mem_free(str); +} + +static void +mjs_location_get_property_origin(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct view_state *vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + + char *str = get_uri_string(vs->uri, URI_SERVER); + + if (!str) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + js_pushstring(J, str); + mem_free(str); +} + +static void +mjs_location_get_property_pathname(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct view_state *vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + + struct string pathname; + if (!init_string(&pathname)) { + js_pushnull(J); + return; + } + + const char *query = (const char *)memchr(vs->uri->data, '?', vs->uri->datalen); + int len = (query ? query - vs->uri->data : vs->uri->datalen); + + add_bytes_to_string(&pathname, vs->uri->data, len); + js_pushstring(J, pathname.source); + done_string(&pathname); +} + +static void +mjs_location_get_property_port(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct view_state *vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + + struct string port; + if (!init_string(&port)) { + js_pushnull(J); + return; + } + if (vs->uri->portlen) { + add_bytes_to_string(&port, vs->uri->port, vs->uri->portlen); + } + js_pushstring(J, port.source); + done_string(&port); +} + +static void +mjs_location_get_property_protocol(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct view_state *vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + + struct string proto; + if (!init_string(&proto)) { + js_pushnull(J); + return; + } + + /* Custom or unknown keep the URI untouched. */ + if (vs->uri->protocol == PROTOCOL_UNKNOWN) { + add_to_string(&proto, struri(vs->uri)); + } else { + add_bytes_to_string(&proto, vs->uri->string, vs->uri->protocollen); + add_char_to_string(&proto, ':'); + } + js_pushstring(J, proto.source); + done_string(&proto); +} + +static void +mjs_location_get_property_search(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct view_state *vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + + struct string search; + if (!init_string(&search)) { + js_pushnull(J); + return; + } + + const char *query = (const char *)memchr(vs->uri->data, '?', vs->uri->datalen); + + if (query) { + add_bytes_to_string(&search, query, strcspn(query, "#" POST_CHAR_S)); + } + js_pushstring(J, search.source); + done_string(&search); +} + +static void +mjs_location_set_property_hash(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct view_state *vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + const char *str = js_tostring(J, 1); + + if (!str) { + js_pushnull(J); + return; + } + location_goto_const(vs->doc_view, str); + js_pushundefined(J); +} + +static void +mjs_location_set_property_host(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct view_state *vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + const char *str = js_tostring(J, 1); + + if (!str) { + js_pushnull(J); + return; + } + location_goto_const(vs->doc_view, str); + js_pushundefined(J); +} + +static void +mjs_location_set_property_hostname(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct view_state *vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + const char *str = js_tostring(J, 1); + + if (!str) { + js_pushnull(J); + return; + } + location_goto_const(vs->doc_view, str); + js_pushundefined(J); +} + +static void +mjs_location_set_property_href(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct view_state *vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + const char *str = js_tostring(J, 1); + + if (!str) { + js_pushnull(J); + return; + } + location_goto_const(vs->doc_view, str); + js_pushundefined(J); +} + +static void +mjs_location_set_property_pathname(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct view_state *vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + const char *str = js_tostring(J, 1); + + if (!str) { + js_pushnull(J); + return; + } + location_goto_const(vs->doc_view, str); + js_pushundefined(J); +} + +static void +mjs_location_set_property_port(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct view_state *vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + const char *str = js_tostring(J, 1); + + if (!str) { + js_pushnull(J); + return; + } + location_goto_const(vs->doc_view, str); + js_pushundefined(J); +} + +static void +mjs_location_set_property_protocol(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct view_state *vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + const char *str = js_tostring(J, 1); + + if (!str) { + js_pushnull(J); + return; + } + location_goto_const(vs->doc_view, str); + js_pushundefined(J); +} + +static void +mjs_location_set_property_search(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct view_state *vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + const char *str = js_tostring(J, 1); + + if (!str) { + js_pushnull(J); + return; + } + location_goto_const(vs->doc_view, str); + js_pushundefined(J); +} + +static void +mjs_location_reload(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); + struct view_state *vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + js_pushnull(J); + return; + } + location_goto_const(vs->doc_view, ""); + js_pushundefined(J); +} + +/* @location_funcs{"toString"}, @location_funcs{"toLocaleString"} */ +static void +mjs_location_toString(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + mjs_location_get_property_href(J); +} + +int +mjs_location_init(js_State *J) +{ + js_newobject(J); + { + addmethod(J, "reload", mjs_location_reload, 0); + addmethod(J, "toString", mjs_location_toString, 0); + addmethod(J, "toLocaleString", mjs_location_toString, 0); + + addproperty(J, "hash", mjs_location_get_property_hash, mjs_location_set_property_hash); + addproperty(J, "host", mjs_location_get_property_host, mjs_location_set_property_host); + addproperty(J, "hostname", mjs_location_get_property_hostname, mjs_location_set_property_hostname); + addproperty(J, "href", mjs_location_get_property_href, mjs_location_set_property_href); + addproperty(J, "origin", mjs_location_get_property_origin, NULL); + addproperty(J, "pathname", mjs_location_get_property_pathname, mjs_location_set_property_pathname); + addproperty(J, "port", mjs_location_get_property_port, mjs_location_set_property_port); + addproperty(J, "protocol", mjs_location_get_property_protocol, mjs_location_set_property_protocol); + addproperty(J, "search", mjs_location_get_property_search, mjs_location_set_property_search); + } + js_defglobal(J, "location", JS_DONTENUM); + + return 0; +} + +void +mjs_push_location(js_State *J) +{ + mjs_location_init(J); +} diff --git a/src/ecmascript/mujs/location.h b/src/ecmascript/mujs/location.h new file mode 100644 index 000000000..fce51a97f --- /dev/null +++ b/src/ecmascript/mujs/location.h @@ -0,0 +1,9 @@ +#ifndef EL__ECMASCRIPT_MUJS_LOCATION_H +#define EL__ECMASCRIPT_MUJS_LOCATION_H + +#include + +int mjs_location_init(js_State *J); +void mjs_push_location(js_State *J); + +#endif diff --git a/src/ecmascript/mujs/meson.build b/src/ecmascript/mujs/meson.build index ad3148daa..971269b21 100644 --- a/src/ecmascript/mujs/meson.build +++ b/src/ecmascript/mujs/meson.build @@ -1,3 +1,4 @@ #srcs += files('attr.cpp', 'attributes.cpp', 'collection.cpp', 'console.cpp', 'document.cpp', 'element.cpp', 'form.cpp', 'forms.cpp', 'heartbeat.cpp', 'history.cpp', 'implementation.cpp', #'input.cpp', 'localstorage.cpp', 'location.cpp', 'navigator.cpp', 'nodelist.cpp', 'screen.cpp', 'unibar.cpp', 'window.cpp') -srcs += files('console.cpp', 'history.cpp', 'localstorage.cpp', 'navigator.cpp', 'screen.cpp', 'unibar.cpp', 'window.cpp') +srcs += files('attr.cpp', 'attributes.cpp', 'collection.cpp', 'console.cpp', 'document.cpp', 'element.cpp', 'form.cpp', 'forms.cpp', 'history.cpp', 'implementation.cpp', +'input.cpp', 'localstorage.cpp', 'location.cpp', 'navigator.cpp', 'nodelist.cpp', 'screen.cpp', 'unibar.cpp', 'window.cpp') diff --git a/src/ecmascript/mujs/nodelist.cpp b/src/ecmascript/mujs/nodelist.cpp new file mode 100644 index 000000000..30b6587f8 --- /dev/null +++ b/src/ecmascript/mujs/nodelist.cpp @@ -0,0 +1,174 @@ +/* The MuJS nodeList object implementation. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "elinks.h" + +#include "bfu/dialog.h" +#include "cache/cache.h" +#include "cookies/cookies.h" +#include "dialogs/menu.h" +#include "dialogs/status.h" +#include "document/html/frames.h" +#include "document/document.h" +#include "document/forms.h" +#include "document/view.h" +#include "ecmascript/ecmascript.h" +#include "ecmascript/mujs.h" +#include "ecmascript/mujs/element.h" +#include "ecmascript/mujs/nodelist.h" +#include "ecmascript/mujs/window.h" +#include "intl/libintl.h" +#include "main/select.h" +#include "osdep/newwin.h" +#include "osdep/sysname.h" +#include "protocol/http/http.h" +#include "protocol/uri.h" +#include "session/history.h" +#include "session/location.h" +#include "session/session.h" +#include "session/task.h" +#include "terminal/tab.h" +#include "terminal/terminal.h" +#include "util/conv.h" +#include "util/memory.h" +#include "util/string.h" +#include "viewer/text/draw.h" +#include "viewer/text/form.h" +#include "viewer/text/link.h" +#include "viewer/text/vs.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +static std::map map_nodelist; +static std::map map_rev_nodelist; + + +#if 0 +static JSValue +js_nodeList_get_property_length(JSContext *ctx, JSValueConst this_val) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + xmlpp::Node::NodeList *nl = static_cast(js_nodeList_GetOpaque(this_val)); + + if (!nl) { + return JS_NewInt32(ctx, 0); + } + + return JS_NewInt32(ctx, nl->size()); +} +#endif + +static void +mjs_push_nodeList_item2(js_State *J, int idx) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + xmlpp::Node::NodeList *nl = static_cast(js_touserdata(J, 0, "nodelist")); + + if (!nl) { + js_pushundefined(J); + return; + } + + xmlpp::Node *element = nullptr; + + auto it = nl->begin(); + auto end = nl->end(); + for (int i = 0; it != end; ++it, ++i) { + if (i == idx) { + element = *it; + break; + } + } + + if (!element) { + js_pushundefined(J); + return; + } + mjs_push_element(J, element); +} + +static void +mjs_nodeList_item(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + int index = js_toint32(J, 1); + + mjs_push_nodeList_item2(J, index); +} + +static void +mjs_nodeList_set_items(js_State *J, void *node) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + + xmlpp::Node::NodeList *nl = static_cast(node); + + if (!nl) { + return; + } + + auto it = nl->begin(); + auto end = nl->end(); + for (int i = 0; it != end; ++it, ++i) { + xmlpp::Node *element = *it; + + if (element) { + mjs_push_element(J, element); + js_setindex(J, 1, i); + } + } +} + +static void +mjs_nodeList_toString(js_State *J) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + js_pushstring(J, "[nodeList object]"); +} + +static void +mjs_nodeList_finalizer(js_State *J, void *node) +{ + map_nodelist.erase(node); +} + +static void +mjs_push_nodelist(js_State *J, void *node) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + js_newobject(J); + { + js_newuserdata(J, "nodelist", node, mjs_nodeList_finalizer); + addmethod(J, "item", mjs_nodeList_item, 1); + addmethod(J, "toString", mjs_nodeList_toString, 0); + mjs_nodeList_set_items(J, node); + } + map_nodelist[node] = node; +} diff --git a/src/ecmascript/mujs/nodelist.h b/src/ecmascript/mujs/nodelist.h new file mode 100644 index 000000000..cfc40d3fc --- /dev/null +++ b/src/ecmascript/mujs/nodelist.h @@ -0,0 +1,8 @@ +#ifndef EL__ECMASCRIPT_MUJS_NODELIST_H +#define EL__ECMASCRIPT_MUJS_NODELIST_H + +#include + +void mjs_push_nodelist(js_State *J, void *node); + +#endif