mirror of
https://github.com/rkd77/elinks.git
synced 2024-12-04 14:46:47 -05:00
[click] Added eventListener. It works for these two test cases.
This commit is contained in:
parent
3330427738
commit
d7f4f94a62
@ -82,6 +82,19 @@
|
||||
|
||||
static INIT_LIST_OF(struct document, format_cache);
|
||||
|
||||
const char *script_event_hook_name[] = {
|
||||
"click",
|
||||
"dblclick",
|
||||
"mouseover",
|
||||
"hover",
|
||||
"focus",
|
||||
"mouseout",
|
||||
"blur",
|
||||
"keydown",
|
||||
"keyup",
|
||||
NULL
|
||||
};
|
||||
|
||||
#ifdef HAVE_INET_NTOP
|
||||
/* DNS callback. */
|
||||
static void
|
||||
|
@ -90,6 +90,9 @@ enum script_event_hook_type {
|
||||
SEVHOOK_ONKEYUP
|
||||
};
|
||||
|
||||
/* keep in sync with above */
|
||||
extern const char *script_event_hook_name[];
|
||||
|
||||
struct script_event_hook {
|
||||
LIST_HEAD(struct script_event_hook);
|
||||
|
||||
@ -102,6 +105,9 @@ struct link {
|
||||
|
||||
enum link_type type;
|
||||
|
||||
#ifdef CONFIG_ECMASCRIPT
|
||||
int element_offset;
|
||||
#endif
|
||||
char *where;
|
||||
char *target;
|
||||
char *where_img;
|
||||
@ -214,6 +220,7 @@ struct document {
|
||||
LIST_OF(struct ecmascript_timeout) timeouts;
|
||||
int ecmascript_counter;
|
||||
void *dom;
|
||||
void *element_map;
|
||||
char *text;
|
||||
void *forms_nodeset;
|
||||
#endif
|
||||
|
@ -76,6 +76,8 @@ struct text_attrib {
|
||||
char *onblur;
|
||||
char *onkeydown;
|
||||
char *onkeyup;
|
||||
|
||||
char *top_name;
|
||||
};
|
||||
|
||||
/* This enum is pretty ugly, yes ;). */
|
||||
|
@ -141,13 +141,14 @@ init_form_control(enum form_type type, char *attr,
|
||||
|
||||
void
|
||||
html_button(struct html_context *html_context, char *a,
|
||||
char *xxx3, char *xxx4, char **xxx5)
|
||||
char *html, char *xxx4, char **xxx5)
|
||||
{
|
||||
char *al;
|
||||
struct el_form_control *fc;
|
||||
enum form_type type = FC_SUBMIT;
|
||||
int cp = html_context->doc_cp;
|
||||
|
||||
elformat.top_name = html_top->name;
|
||||
html_focusable(html_context, a);
|
||||
|
||||
al = get_attr_val(a, "type", cp);
|
||||
@ -192,9 +193,11 @@ html_input_format(struct html_context *html_context, char *a,
|
||||
struct el_form_control *fc)
|
||||
{
|
||||
put_chrs(html_context, " ", 1);
|
||||
char *top_name = html_top->name;
|
||||
html_stack_dup(html_context, ELEMENT_KILLABLE);
|
||||
html_focusable(html_context, a);
|
||||
elformat.form = fc;
|
||||
elformat.top_name = top_name;
|
||||
mem_free_if(elformat.title);
|
||||
elformat.title = get_attr_val(a, "title", html_context->doc_cp);
|
||||
switch (fc->type) {
|
||||
@ -354,6 +357,7 @@ do_html_select(char *attr, char *html,
|
||||
int i, max_width;
|
||||
int closing_tag;
|
||||
|
||||
elformat.top_name = html_top->name;
|
||||
html_focusable(html_context, attr);
|
||||
init_menu(&lnk_menu);
|
||||
|
||||
@ -529,6 +533,7 @@ do_html_select_multiple(struct html_context *html_context, char *a,
|
||||
char *al = get_attr_val(a, "name", html_context->doc_cp);
|
||||
|
||||
if (!al) return;
|
||||
elformat.top_name = html_top->name;
|
||||
html_focusable(html_context, a);
|
||||
html_top->type = ELEMENT_DONT_KILL;
|
||||
mem_free_set(&elformat.select, al);
|
||||
@ -636,6 +641,7 @@ html_textarea(struct html_context *html_context, char *attr,
|
||||
int cols, rows;
|
||||
int i;
|
||||
|
||||
elformat.top_name = html_top->name;
|
||||
html_focusable(html_context, attr);
|
||||
while (html < eof && (*html == '\n' || *html == '\r')) html++;
|
||||
p = html;
|
||||
|
@ -1047,6 +1047,7 @@ html_frame(struct html_context *html_context, char *a,
|
||||
if (!name) return;
|
||||
|
||||
if (!html_context->options->frames || !html_top->frameset) {
|
||||
elformat.top_name = html_top->name;
|
||||
html_focusable(html_context, a);
|
||||
put_link_line("Frame: ", name, url, "", html_context);
|
||||
|
||||
|
@ -94,6 +94,7 @@ html_a(struct html_context *html_context, char *a,
|
||||
mem_free_set(&elformat.title,
|
||||
get_attr_val(a, "title", html_context->doc_cp));
|
||||
|
||||
elformat.top_name = html_top->name;
|
||||
html_focusable(html_context, a);
|
||||
|
||||
} else {
|
||||
@ -205,6 +206,7 @@ put_image_label(char *a, char *label,
|
||||
/* This is not 100% appropriate for <img>, but well, accepting
|
||||
* accesskey and tabindex near <img> is just our little
|
||||
* extension to the standard. After all, it makes sense. */
|
||||
elformat.top_name = html_top->name;
|
||||
html_focusable(html_context, a);
|
||||
|
||||
saved_foreground = elformat.style.color.foreground;
|
||||
@ -381,7 +383,7 @@ html_source(struct html_context *html_context, char *a,
|
||||
mem_free_set(&title, get_image_filename_from_src(options->image_link.filename_maxlen, src));
|
||||
}
|
||||
}
|
||||
|
||||
elformat.top_name = html_top->name;
|
||||
html_focusable(html_context, a);
|
||||
|
||||
if (title && *title) {
|
||||
@ -433,7 +435,7 @@ html_applet(struct html_context *html_context, char *a,
|
||||
if (!code) return;
|
||||
|
||||
alt = get_attr_val(a, "alt", html_context->doc_cp);
|
||||
|
||||
elformat.top_name = html_top->name;
|
||||
html_focusable(html_context, a);
|
||||
|
||||
if (alt && *alt) {
|
||||
@ -459,6 +461,7 @@ html_audio(struct html_context *html_context, char *a,
|
||||
url = get_url_val(a, "src", html_context->doc_cp);
|
||||
if (!url) return;
|
||||
|
||||
elformat.top_name = html_top->name;
|
||||
html_focusable(html_context, a);
|
||||
|
||||
put_link_line("Audio: ", basename(url), url,
|
||||
@ -489,6 +492,7 @@ html_iframe_do(char *a, char *object_src,
|
||||
return;
|
||||
}
|
||||
|
||||
elformat.top_name = html_top->name;
|
||||
html_focusable(html_context, a);
|
||||
|
||||
if (html_context->options->iframes) {
|
||||
@ -574,6 +578,7 @@ html_object(struct html_context *html_context, char *a,
|
||||
|
||||
name = get_attr_val(a, "standby", html_context->doc_cp);
|
||||
|
||||
elformat.top_name = html_top->name;
|
||||
html_focusable(html_context, a);
|
||||
|
||||
if (name && *name) {
|
||||
@ -638,6 +643,7 @@ html_video(struct html_context *html_context, char *a,
|
||||
url = get_url_val(a, "src", html_context->doc_cp);
|
||||
if (!url) return;
|
||||
|
||||
elformat.top_name = html_top->name;
|
||||
html_focusable(html_context, a);
|
||||
|
||||
put_link_line("Video: ", basename(url), url,
|
||||
@ -944,6 +950,7 @@ html_link(struct html_context *html_context, char *a,
|
||||
if (!name) goto free_and_return;
|
||||
if (!init_string(&text)) goto free_and_return;
|
||||
|
||||
elformat.top_name = html_top->name;
|
||||
html_focusable(html_context, a);
|
||||
|
||||
if (link.title) {
|
||||
|
@ -1551,6 +1551,10 @@ new_link(struct html_context *html_context, const char *name, int namelen)
|
||||
? elformat.style.color.foreground
|
||||
: elformat.color.clink;
|
||||
|
||||
#ifdef CONFIG_ECMASCRIPT
|
||||
link->element_offset = elformat.top_name ? elformat.top_name - document->text : 0;
|
||||
#endif
|
||||
|
||||
init_link_event_hooks(html_context, link);
|
||||
|
||||
document->links_sorted = 0;
|
||||
|
@ -293,8 +293,8 @@ render_encoded_document(struct cache_entry *cached, struct document *document)
|
||||
&& (!c_strlcasecmp("text/gemini", 11, cached->content_type, -1)))
|
||||
render_gemini_document(cached, document, &buffer);
|
||||
else
|
||||
#ifdef CONFIG_XML
|
||||
if (false) render_xhtml_document(cached, document, &buffer);
|
||||
#if defined(CONFIG_XML) && defined(CONFIG_ECMASCRIPT)
|
||||
if (true) render_xhtml_document(cached, document, NULL);
|
||||
else
|
||||
#endif
|
||||
render_html_document(cached, document, &buffer);
|
||||
|
@ -38,6 +38,8 @@
|
||||
#include "util/string.h"
|
||||
|
||||
#include <libxml++/libxml++.h>
|
||||
#include <map>
|
||||
|
||||
|
||||
#if 0
|
||||
|
||||
@ -267,6 +269,65 @@ dump_dom_structure(struct source_renderer *renderer, void *nod, int depth)
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
dump_element(std::map<int, xmlpp::Element *> *mapa, struct string *buf, xmlpp::Element *element)
|
||||
{
|
||||
add_char_to_string(buf, '<');
|
||||
(*mapa)[buf->length] = element;
|
||||
|
||||
add_to_string(buf, element->get_name().c_str());
|
||||
auto attrs = element->get_attributes();
|
||||
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, '>');
|
||||
}
|
||||
|
||||
static void
|
||||
walk_tree(std::map<int, xmlpp::Element *> *mapa, struct string *buf, void *nod, bool start)
|
||||
{
|
||||
xmlpp::Node *node = static_cast<xmlpp::Node *>(nod);
|
||||
|
||||
if (!start) {
|
||||
const auto textNode = dynamic_cast<const xmlpp::ContentNode*>(node);
|
||||
|
||||
if (textNode) {
|
||||
add_bytes_to_string(buf, textNode->get_content().c_str(), textNode->get_content().length());
|
||||
} else {
|
||||
auto element = dynamic_cast<xmlpp::Element*>(node);
|
||||
|
||||
if (element) {
|
||||
dump_element(mapa, buf, element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto childs = node->get_children();
|
||||
auto it = childs.begin();
|
||||
auto end = childs.end();
|
||||
|
||||
for (; it != end; ++it) {
|
||||
walk_tree(mapa, buf, *it, false);
|
||||
}
|
||||
|
||||
if (!start) {
|
||||
const auto element = dynamic_cast<const xmlpp::Element*>(node);
|
||||
if (element) {
|
||||
add_to_string(buf, "</");
|
||||
add_to_string(buf, element->get_name().c_str());
|
||||
add_char_to_string(buf, '>');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
render_xhtml_document(struct cache_entry *cached, struct document *document, struct string *buffer)
|
||||
{
|
||||
@ -278,7 +339,6 @@ render_xhtml_document(struct cache_entry *cached, struct document *document, str
|
||||
render_html_document(cached, document, buffer);
|
||||
return;
|
||||
}
|
||||
|
||||
struct string head;
|
||||
|
||||
assert(cached && document);
|
||||
@ -300,19 +360,29 @@ render_xhtml_document(struct cache_entry *cached, struct document *document, str
|
||||
}
|
||||
|
||||
xmlpp::Document *doc = (xmlpp::Document *)document->dom;
|
||||
xmlpp::Element* root = (xmlpp::Element *)doc->get_root_node();
|
||||
|
||||
if (!buffer) {
|
||||
xmlpp::ustring text = doc->write_to_string_formatted();
|
||||
struct string tt;
|
||||
|
||||
if (!init_string(&tt)) {
|
||||
done_string(&head);
|
||||
return;
|
||||
}
|
||||
add_bytes_to_string(&tt, text.c_str(), text.size());
|
||||
std::map<int, xmlpp::Element *> *mapa = (std::map<int, xmlpp::Element *> *)document->element_map;
|
||||
|
||||
if (!mapa) {
|
||||
mapa = new std::map<int, xmlpp::Element *>;
|
||||
document->element_map = (void *)mapa;
|
||||
} else {
|
||||
mapa->clear();
|
||||
}
|
||||
|
||||
walk_tree(mapa, &tt, root, true);
|
||||
buffer = &tt;
|
||||
document->text = tt.source;
|
||||
}
|
||||
|
||||
if (add_to_head) {
|
||||
mem_free_set(&cached->head, head.source);
|
||||
}
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "ecmascript/spidermonkey/attributes.h"
|
||||
#include "ecmascript/spidermonkey/collection.h"
|
||||
#include "ecmascript/spidermonkey/element.h"
|
||||
#include "ecmascript/spidermonkey/heartbeat.h"
|
||||
#include "ecmascript/spidermonkey/nodelist.h"
|
||||
#include "ecmascript/spidermonkey/window.h"
|
||||
#include "intl/libintl.h"
|
||||
@ -97,6 +98,18 @@ static bool element_set_property_textContent(JSContext *ctx, unsigned int argc,
|
||||
static bool element_get_property_title(JSContext *ctx, unsigned int argc, JS::Value *vp);
|
||||
static bool element_set_property_title(JSContext *ctx, unsigned int argc, JS::Value *vp);
|
||||
|
||||
struct listener {
|
||||
LIST_HEAD(struct listener);
|
||||
char *typ;
|
||||
JS::RootedValue fun;
|
||||
};
|
||||
|
||||
struct element_private {
|
||||
LIST_OF(struct listener) listeners;
|
||||
struct ecmascript_interpreter *interpreter;
|
||||
JS::RootedObject thisval;
|
||||
};
|
||||
|
||||
static void element_finalize(JS::GCContext *op, JSObject *obj);
|
||||
|
||||
JSClassOps element_ops = {
|
||||
@ -114,7 +127,7 @@ JSClassOps element_ops = {
|
||||
|
||||
JSClass element_class = {
|
||||
"element",
|
||||
JSCLASS_HAS_RESERVED_SLOTS(1),
|
||||
JSCLASS_HAS_RESERVED_SLOTS(2),
|
||||
&element_ops
|
||||
};
|
||||
|
||||
@ -2310,6 +2323,7 @@ element_set_property_title(JSContext *ctx, unsigned int argc, JS::Value *vp)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool element_addEventListener(JSContext *ctx, unsigned int argc, JS::Value *rval);
|
||||
static bool element_appendChild(JSContext *ctx, unsigned int argc, JS::Value *rval);
|
||||
static bool element_cloneNode(JSContext *ctx, unsigned int argc, JS::Value *rval);
|
||||
static bool element_closest(JSContext *ctx, unsigned int argc, JS::Value *rval);
|
||||
@ -2327,9 +2341,11 @@ static bool element_querySelector(JSContext *ctx, unsigned int argc, JS::Value *
|
||||
static bool element_querySelectorAll(JSContext *ctx, unsigned int argc, JS::Value *rval);
|
||||
static bool element_remove(JSContext *ctx, unsigned int argc, JS::Value *rval);
|
||||
static bool element_removeChild(JSContext *ctx, unsigned int argc, JS::Value *rval);
|
||||
static bool element_removeEventListener(JSContext *ctx, unsigned int argc, JS::Value *rval);
|
||||
static bool element_setAttribute(JSContext *ctx, unsigned int argc, JS::Value *rval);
|
||||
|
||||
const spidermonkeyFunctionSpec element_funcs[] = {
|
||||
{ "addEventListener", element_addEventListener, 3 },
|
||||
{ "appendChild", element_appendChild, 1 },
|
||||
{ "cloneNode", element_cloneNode, 1 },
|
||||
{ "closest", element_closest, 1 },
|
||||
@ -2347,6 +2363,7 @@ const spidermonkeyFunctionSpec element_funcs[] = {
|
||||
{ "querySelectorAll", element_querySelectorAll, 1 },
|
||||
{ "remove", element_remove, 0 },
|
||||
{ "removeChild", element_removeChild, 1 },
|
||||
{ "removeEventListener", element_removeEventListener, 3 },
|
||||
{ "setAttribute", element_setAttribute, 2 },
|
||||
{ NULL }
|
||||
};
|
||||
@ -2385,6 +2402,137 @@ check_contains(xmlpp::Node *node, xmlpp::Node *searched, bool *result_set, bool
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
element_addEventListener(JSContext *ctx, unsigned int argc, JS::Value *rval)
|
||||
{
|
||||
#ifdef ECMASCRIPT_DEBUG
|
||||
fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__);
|
||||
#endif
|
||||
JS::Realm *comp = js::GetContextRealm(ctx);
|
||||
|
||||
if (!comp) {
|
||||
#ifdef ECMASCRIPT_DEBUG
|
||||
fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
JS::CallArgs args = CallArgsFromVp(argc, rval);
|
||||
JS::RootedObject hobj(ctx, &args.thisv().toObject());
|
||||
|
||||
struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)JS::GetRealmPrivate(comp);
|
||||
|
||||
if (!JS_InstanceOf(ctx, hobj, &element_class, NULL)) {
|
||||
#ifdef ECMASCRIPT_DEBUG
|
||||
fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
xmlpp::Element *el = JS::GetMaybePtrFromReservedSlot<xmlpp::Element>(hobj, 0);
|
||||
struct element_private *el_private = JS::GetMaybePtrFromReservedSlot<struct element_private>(hobj, 1);
|
||||
|
||||
if (!el || !el_private) {
|
||||
args.rval().setNull();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (argc < 2) {
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
char *method = jsval_to_string(ctx, args[0]);
|
||||
JS::RootedValue fun(ctx, args[1]);
|
||||
|
||||
struct listener *l;
|
||||
|
||||
foreach(l, el_private->listeners) {
|
||||
if (strcmp(l->typ, method)) {
|
||||
continue;
|
||||
}
|
||||
if (l->fun == fun) {
|
||||
args.rval().setUndefined();
|
||||
mem_free(method);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
struct listener *n = (struct listener *)mem_calloc(1, sizeof(*n));
|
||||
|
||||
if (n) {
|
||||
n->typ = method;
|
||||
n->fun = fun;
|
||||
add_to_list_end(el_private->listeners, n);
|
||||
}
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
element_removeEventListener(JSContext *ctx, unsigned int argc, JS::Value *rval)
|
||||
{
|
||||
#ifdef ECMASCRIPT_DEBUG
|
||||
fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__);
|
||||
#endif
|
||||
JS::Realm *comp = js::GetContextRealm(ctx);
|
||||
|
||||
if (!comp) {
|
||||
#ifdef ECMASCRIPT_DEBUG
|
||||
fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
JS::CallArgs args = CallArgsFromVp(argc, rval);
|
||||
JS::RootedObject hobj(ctx, &args.thisv().toObject());
|
||||
|
||||
struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)JS::GetRealmPrivate(comp);
|
||||
|
||||
if (!JS_InstanceOf(ctx, hobj, &element_class, NULL)) {
|
||||
#ifdef ECMASCRIPT_DEBUG
|
||||
fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
xmlpp::Element *el = JS::GetMaybePtrFromReservedSlot<xmlpp::Element>(hobj, 0);
|
||||
struct element_private *el_private = JS::GetMaybePtrFromReservedSlot<struct element_private>(hobj, 1);
|
||||
|
||||
if (!el || !el_private) {
|
||||
args.rval().setNull();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (argc < 2) {
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
char *method = jsval_to_string(ctx, args[0]);
|
||||
|
||||
if (!method) {
|
||||
return false;
|
||||
}
|
||||
JS::RootedValue fun(ctx, args[1]);
|
||||
|
||||
struct listener *l;
|
||||
|
||||
foreach(l, el_private->listeners) {
|
||||
if (strcmp(l->typ, method)) {
|
||||
continue;
|
||||
}
|
||||
if (l->fun == fun) {
|
||||
del_from_list(l);
|
||||
mem_free_set(&l->typ, NULL);
|
||||
mem_free(l);
|
||||
mem_free(method);
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
mem_free(method);
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
element_appendChild(JSContext *ctx, unsigned int argc, JS::Value *rval)
|
||||
{
|
||||
@ -3316,14 +3464,34 @@ element_setAttribute(JSContext *ctx, unsigned int argc, JS::Value *rval)
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::map<void *, struct element_private *> map_privates;
|
||||
|
||||
JSObject *
|
||||
getElement(JSContext *ctx, void *node)
|
||||
{
|
||||
auto elem = map_privates.find(node);
|
||||
struct element_private *el_private = NULL;
|
||||
|
||||
if (elem != map_privates.end()) {
|
||||
el_private = elem->second;
|
||||
} else {
|
||||
el_private = (struct element_private *)mem_calloc(1, sizeof(*el_private));
|
||||
|
||||
if (!el_private) {
|
||||
return NULL;
|
||||
}
|
||||
init_list(el_private->listeners);
|
||||
}
|
||||
|
||||
JSObject *el = JS_NewObject(ctx, &element_class);
|
||||
|
||||
if (!el) {
|
||||
mem_free(el_private);
|
||||
return NULL;
|
||||
}
|
||||
JS::Realm *comp = js::GetContextRealm(ctx);
|
||||
struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)JS::GetRealmPrivate(comp);
|
||||
el_private->interpreter = interpreter;
|
||||
|
||||
JS::RootedObject r_el(ctx, el);
|
||||
|
||||
@ -3331,6 +3499,39 @@ getElement(JSContext *ctx, void *node)
|
||||
spidermonkey_DefineFunctions(ctx, el, element_funcs);
|
||||
|
||||
JS::SetReservedSlot(el, 0, JS::PrivateValue(node));
|
||||
JS::SetReservedSlot(el, 1, JS::PrivateValue(el_private));
|
||||
|
||||
el_private->thisval = r_el;
|
||||
map_privates[node] = el_private;
|
||||
|
||||
return el;
|
||||
}
|
||||
|
||||
void
|
||||
check_element_event(void *elem, const char *event_name)
|
||||
{
|
||||
auto el = map_privates.find(elem);
|
||||
|
||||
if (el == map_privates.end()) {
|
||||
return;
|
||||
}
|
||||
struct element_private *el_private = el->second;
|
||||
struct ecmascript_interpreter *interpreter = el_private->interpreter;
|
||||
JSContext *ctx = (JSContext *)interpreter->backend_data;
|
||||
JS::Realm *comp = JS::EnterRealm(ctx, (JSObject *)interpreter->ac);
|
||||
JS::RootedValue r_val(ctx);
|
||||
interpreter->heartbeat = add_heartbeat(interpreter);
|
||||
|
||||
struct listener *l;
|
||||
|
||||
foreach(l, el_private->listeners) {
|
||||
if (strcmp(l->typ, event_name)) {
|
||||
continue;
|
||||
}
|
||||
JS_CallFunctionValue(ctx, el_private->thisval, l->fun, JS::HandleValueArray::empty(), &r_val);
|
||||
}
|
||||
done_heartbeat(interpreter->heartbeat);
|
||||
JS::LeaveRealm(ctx, comp);
|
||||
|
||||
check_for_rerender(interpreter, event_name);
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
#ifndef EL__ECMASCRIPT_SPIDERMONKEY_ELEMENT_H
|
||||
#define EL__ECMASCRIPT_SPIDERMONKEY_ELEMENT_H
|
||||
|
||||
@ -11,4 +10,6 @@ JSObject *getElement(JSContext *ctx, void *node);
|
||||
|
||||
void walk_tree(struct string *buf, void *nod, bool start = true, bool toSortAttrs = false);
|
||||
|
||||
void check_element_event(void *elem, const char *event_name);
|
||||
|
||||
#endif
|
||||
|
@ -112,7 +112,6 @@ struct listener {
|
||||
JS::RootedValue fun;
|
||||
};
|
||||
|
||||
|
||||
struct classcomp {
|
||||
bool operator() (const std::string& lhs, const std::string& rhs) const
|
||||
{
|
||||
|
@ -20,6 +20,12 @@
|
||||
#include "document/options.h"
|
||||
#include "document/view.h"
|
||||
#include "ecmascript/ecmascript.h"
|
||||
|
||||
#ifdef CONFIG_ECMASCRIPT_SMJS
|
||||
#include "ecmascript/spidermonkey/element.h"
|
||||
#include <libxml++/libxml++.h>
|
||||
#endif
|
||||
|
||||
#include "intl/libintl.h"
|
||||
#include "main/object.h"
|
||||
#include "protocol/uri.h"
|
||||
@ -44,6 +50,7 @@
|
||||
#include "viewer/text/view.h"
|
||||
#include "viewer/text/vs.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
/* Perhaps some of these would be more fun to have in viewer/common/, dunno.
|
||||
* --pasky */
|
||||
@ -59,10 +66,21 @@ current_link_evhook(struct document_view *doc_view, enum script_event_hook_type
|
||||
assert(doc_view && doc_view->vs);
|
||||
link = get_current_link(doc_view);
|
||||
if (!link) return -1;
|
||||
if (!link->event_hooks) return -1;
|
||||
|
||||
if (!doc_view->vs->ecmascript) return -1;
|
||||
|
||||
std::map<int, xmlpp::Element *> *mapa = (std::map<int, xmlpp::Element *> *)doc_view->document->element_map;
|
||||
|
||||
if (mapa) {
|
||||
auto element = (*mapa).find(link->element_offset);
|
||||
|
||||
if (element != (*mapa).end()) {
|
||||
const char *event_name = script_event_hook_name[(int)type];
|
||||
check_element_event(element->second, event_name);
|
||||
}
|
||||
}
|
||||
|
||||
if (!link->event_hooks) return -1;
|
||||
|
||||
foreach (evhook, *link->event_hooks) {
|
||||
char *ret;
|
||||
|
||||
|
20
test/ecmascript/clickListener.html
Normal file
20
test/ecmascript/clickListener.html
Normal file
@ -0,0 +1,20 @@
|
||||
<html>
|
||||
<head>
|
||||
<script>
|
||||
function cli()
|
||||
{
|
||||
document.location.href='http://www.elinks.cz';
|
||||
}
|
||||
|
||||
function loader()
|
||||
{
|
||||
document.getElementById('c').addEventListener('click', cli);
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onload="loader()">
|
||||
<center>
|
||||
<input type="button" id="c" value="ELinks homepage">
|
||||
</center>
|
||||
</body>
|
||||
</html>
|
21
test/ecmascript/clickListener2.html
Normal file
21
test/ecmascript/clickListener2.html
Normal file
@ -0,0 +1,21 @@
|
||||
<html>
|
||||
<head>
|
||||
<script>
|
||||
function cli()
|
||||
{
|
||||
alert("document.location.href='http://www.elinks.cz';");
|
||||
}
|
||||
|
||||
function loader()
|
||||
{
|
||||
document.getElementById('c').addEventListener('click', function() { window.alert('aaaa'); });
|
||||
document.getElementById('c').addEventListener('click', cli);
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onload="loader()">
|
||||
<center>
|
||||
<button id="c" value="ELinks homepage">AAA</button>
|
||||
</center>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user