diff --git a/config/m4/see.m4 b/config/m4/see.m4 deleted file mode 100644 index 0c202ee4a..000000000 --- a/config/m4/see.m4 +++ /dev/null @@ -1,48 +0,0 @@ -AC_DEFUN([EL_CONFIG_SEE], -[ - -enable_see="no"; - -AC_ARG_WITH(see, [ --with-see enable Simple Ecmascript Engine (SEE) support], - [ if test "x$withval" != xno; then enable_see=yes; fi ]) - -# The following is probably bad, ugly and so on. Stolen from Guile's (1.4) -# SEE_FLAGS but I really don't want to require people to have Guile in order -# to compile CVS. Also, the macro seems to be really stupid regarding searching -# for Guile in $PATH etc. --pasky - -AC_MSG_CHECKING([for SEE]) - -if test "$enable_see" = "yes"; then - AC_MSG_RESULT(yes); - ## Based on the SEE_FLAGS macro. - - if test -d "$withval"; then - SEE_PATH="$withval:$PATH" - else - SEE_PATH="$PATH" - fi - - AC_PATH_PROG(SEE_CONFIG, libsee-config, no, $SEE_PATH) - - ## First, let's just see if we can find Guile at all. - if test "$SEE_CONFIG" != no; then - cf_result="yes"; - - SEE_LIBS="`$SEE_CONFIG --libs`" - SEE_CFLAGS="`$SEE_CONFIG --cppflags`" - LIBS="$SEE_LIBS $LIBS" - CPPFLAGS="$CPPFLAGS $SEE_CFLAGS" - EL_CONFIG(CONFIG_SEE, [SEE]) - AC_SUBST(SEE_CFLAGS) - else - if test -n "$withval" && test "x$withval" != xno; then - AC_MSG_ERROR([SEE not found]) - else - AC_MSG_WARN([SEE support disabled]) - fi - fi -else - AC_MSG_RESULT(no); -fi -]) diff --git a/configure.in b/configure.in index fb37328ba..b00e903de 100644 --- a/configure.in +++ b/configure.in @@ -724,12 +724,6 @@ dnl =================================================================== EL_CONFIG_RUBY -dnl =================================================================== -dnl Check for SEE, optional even if installed. -dnl =================================================================== - -EL_CONFIG_SEE - dnl =================================================================== dnl Setup global scripting dnl =================================================================== diff --git a/contrib/see/hooks.js b/contrib/see/hooks.js deleted file mode 100644 index e788d962e..000000000 --- a/contrib/see/hooks.js +++ /dev/null @@ -1,220 +0,0 @@ -/* Hooks for the ELinks SEE browser scripting - * - * Copyright (c) Jonas Fonseca, 2005 - */ - -function quit() -{ - // alert("quiting ... " + navigator.appVersion); -} - -/********************************************************************* - * goto_url(url, current_url) - *********************************************************************/ - -var goto_url_hooks = [] - -function goto_url(url, current_url) -{ - var context = { - url: url, - current_url: current_url ? current_url : "" - } - - for (var i = 0; i < goto_url_hooks.length; i++) - if (goto_url_hooks[i](context, current_url)) - break - - return context.url -} - -// Don't take localhost as directory name -function expand_localhost(context) -{ - if (context.url.match(/localhost/)) { - context.url = "http://" + context.url + "/" - return true - } - - return false -} - -goto_url_hooks.push(expand_localhost) - -// You can write smt like "gg" to goto URL dialog and it'll go to google.com. -// Note that this is obsoleted by the URI rewrite plugin. - -var dumbprefixes = { - arc: "http://web.archive.org/web/*/%c", - b: "http://babelfish.altavista.com/babelfish/tr", - bz: "http://bugzilla.elinks.or.cz", - bug: "http://bugzilla.elinks.or.cz", - d: "http://www.dict.org", - g: "http://www.google.com/", - gg: "http://www.google.com/", - go: "http://www.google.com/", - fm: "http://www.freshmeat.net/", - sf: "http://www.sourceforge.net/", - dbug: "http://bugs.debian.org/", - dpkg: "http://packages.debian.org/", - pycur: "http://www.python.org/doc/current/", - pydev: "http://www.python.org/dev/doc/devel/", - pyhelp: "http://starship.python.net/crew/theller/pyhelp.cgi", - pyvault: "http://www.vex.net/parnassus/", - e2: "http://www.everything2.org/", - sd: "http://www.slashdot.org/", - vhtml: "http://validator.w3.org/check?uri=%c", - vcss: "http://jigsaw.w3.org/css-validator/validator?uri=%c" -} - -function expand_dumbprefix(context, current_url) -{ - if (dumbprefixes[context.url]) { - context.url = dumbprefixes[context.url].replace(/%c/, current_url) - return true - } - - return false -} - -goto_url_hooks.push(expand_dumbprefix) - -function gmane(url) -{ - var match = url.match(/([^\s]+)\s+(.*)$/) - var group = match[1] - var words = match[2] - - if (!words) return null - - return "http://search.gmane.org/search.php?query=" + words + "&group=" + group -} - -function gitweb(base_url) -{ - return function (arguments) { - var url = base_url - var match = arguments.match(/^(search|summary|shortlog|log|commit|commitdiff|tree)(\s(.*))?/) - - if (match[1]) - url += ';a=' + match[1] - else - url += ';a=summary' - - if (match[1] == 'search' && match[3]) - url += ';s=' + escape(match[3]) - - return url - } -} - -function bugzilla (base_url) -{ - return function (arguments) { - if (!arguments || arguments == '') - return base_url - - if (arguments.match(/^[\d]+$/)) - return base_url + 'show_bug.cgi?id=' + arguments - - return base_url + 'buglist.cgi?short_desc_type=allwordssubstr' - + '&short_desc=' + escape(arguments) - } -} - - -var smartprefixes = { - arc: "http://web.archive.org/web/*/%s", - bug: bugzilla('http://bugzilla.elinks.or.cz/'), - cambridge: "http://dictionary.cambridge.org/results.asp?searchword=%s", - cliki: "http://www.cliki.net/admin/search?words: %s", - d: "http://www.dict.org/bin/Dict?Query: %s&Form=Dict1&Strategy=*&Database=*&submit=Submit+query", - dmoz: "http://search.dmoz.org/cgi-bin/search?search=%s", - foldoc: "http://wombat.doc.ic.ac.uk/foldoc/foldoc.cgi?%s", - g: "http://www.google.com/search?q=%s&btnG=Google+Search", - gd: "http://www.google.com/search?q=%s&cat=gwd/Top", - gg: "http://www.google.com/search?q=%s&btnG=Google+Search", - // Whose idea was it to use 'gg' for websearches? -- Miciah - //gg = "http://groups.google.com/groups?q=%s", - gi: "http://images.google.com/images?q=%s", - gitweb: gitweb("http://pasky.or.cz/gitweb.cgi?p=elinks.git"), - gmane: gmane, - gn: "http://news.google.com/news?q=%s", - go: "http://www.google.com/search?q=%s&btnG=Google+Search", - gwho: "http://www.googlism.com/?ism=%s&name=1", - gwhat: "http://www.googlism.com/?ism=%s&name=2", - gwhere: "http://www.googlism.com/?ism=%s&name=3", - gwhen: "http://www.googlism.com/?ism=%s&name=4", - fm: "http://www.freshmeat.net/search/?q=%s", - savannah: "http://savannah.nongnu.org/search/?words=%s&type_of_search=soft&exact=1", - sf: "http://sourceforge.net/search/?q=%s", - sfp: "http://sourceforge.net/projects/%s", - sd: "http://www.slashdot.org/search.pl?query=%s", - sdc: "http://www.slashdot.org/search.pl?query=%s&op=comments", - sdu: "http://www.slashdot.org/search.pl?query=%s&op=users", - sdp: "http://www.slashdot.org/search.pl?query=%s&op=polls", - sdj: "http://www.slashdot.org/search.pl?query=%s&op=journals", - dbug: "http://bugs.debian.org/%s", - dpkg: "http://packages.debian.org/%s", - emacs: "http://www.emacswiki.org/cgi-bin/wiki.pl?search=%s", - lyrics: "http://music.lycos.com/lyrics/results.asp?QT=L&QW=%s", - lxr: "http://lxr.linux.no/ident?i=%s", - leo: "http://dict.leo.org/?search=%s", - onelook: "http://onelook.com/?w=%s&ls=a", - py: "http://starship.python.net/crew/theller/pyhelp.cgi?keyword=%s&version=current", - pydev: "http://starship.python.net/crew/theller/pyhelp.cgi?keyword=%s&version=devel", - pyvault: "http://py.vaults.ca/apyllo.py?find=%s", - e2: "http://www.everything2.org/?node=%s", - encz: "http://www.slovnik.cz/bin/ecd?ecd_il=1&ecd_vcb=%s&ecd_trn=translate&ecd_trn_dir=0&ecd_lines=15&ecd_hptxt=0", - czen: "http://www.slovnik.cz/bin/ecd?ecd_il=1&ecd_vcb=%s&ecd_trn=translate&ecd_trn_dir=1&ecd_lines=15&ecd_hptxt=0", - dict: "http://dictionary.reference.com/search?q=%s", - thes: "http://thesaurus.reference.com/search?q=%s", - a: "http://acronymfinder.com/af-query.asp?String=exact&Acronym=%s", - imdb: "http://imdb.com/Find?%s", - mw: "http://www.m-w.com/cgi-bin/dictionary?book=Dictionary&va=%s", - mwt: "http://www.m-w.com/cgi-bin/thesaurus?book=Thesaurus&va=%s", - whatis: "http://uptime.netcraft.com/up/graph/?host=%s", - wiki: "http://www.wikipedia.org/w/wiki.phtml?search=%s", - wn: "http://www.cogsci.princeton.edu/cgi-bin/webwn1.7.1?stage=1&word=%s", - // rfc by number - rfc: "http://www.rfc-editor.org/rfc/rfc%s.txt", - // rfc search - rfcs: "http://www.rfc-editor.org/cgi-bin/rfcsearch.pl?searchwords=%s&format=http&abstract=abson&keywords=keyon&num=25", - cr: "http://www.rfc-editor.org/cgi-bin/rfcsearch.pl?searchwords=%s&format=http&abstract=abson&keywords=keyon&num=25", - // Internet Draft search - rfcid: "http://www.rfc-editor.org/cgi-bin/idsearch.pl?searchwords=%s&format=http&abstract=abson&keywords=keyon&num=25", - urbandict: "http://www.urbandictionary.com/define.php?term=%s", - id: "http://www.rfc-editor.org/cgi-bin/idsearch.pl?searchwords=%s&format=http&abstract=abson&keywords=keyon&num=25", - draft: "http://www.rfc-editor.org/cgi-bin/idsearch.pl?searchwords=%s&format=http&abstract=abson&keywords=keyon&num=25" -} - - -function expand_smartprefix(context, current_url) -{ - var match = context.url.match(/^([^:\s]+)(:|\s)\s*(.*)\s*$/) - - if (match && match[1] && match[3]) { - var nick = match[1] - var val = match[3] - - if (smartprefixes[nick]) { - if (typeof smartprefixes[nick] == 'string') { - context.url = smartprefixes[nick].replace(/%s/, escape(val)) - return true - - } else if (typeof smartprefixes[nick] == 'function') { - context.url = smartprefixes[nick](val) - return true - - } else { - alert('smartprefix "' + nick + '" has unsupported type "' + typeof smartprefixes[nick] + '".') - return false - } - } - } - - // Unmatched. - return false -} - -goto_url_hooks.push(expand_smartprefix) diff --git a/src/bfu/menu.c b/src/bfu/menu.c index 200ca6eb8..1b25f1cf1 100644 --- a/src/bfu/menu.c +++ b/src/bfu/menu.c @@ -254,80 +254,57 @@ count_menu_size(struct terminal *term, struct menu *menu) int_bounds(&menu->box.y, 0, height - my); } -static int -search_selectable(struct menu *menu, int pos, int dir) -{ - assert(pos >= 0 && pos < menu->size && (dir == 1 || dir == -1)); - if_assert_failed return -1; - - while (!mi_is_selectable(&menu->items[pos])) { - if (dir > 0 && pos == menu->size - 1) - return -1; - else if (dir < 0 && pos == 0) - return -1; - - pos += dir; - } - - return pos; -} - static void scroll_menu(struct menu *menu, int steps, int wrap) { - int height, scr_i, pos; + int height, scr_i, pos, start; int s = steps ? steps/abs(steps) : 1; /* Selectable item search direction. */ if (menu->size <= 0) { +no_item: /* Menu is empty. */ menu->selected = -1; menu->first = 0; return; } - /* Move by required steps and handle wraparound if needed. - * A step of zero can be used, indicating we want to select - * item corresponding to |menu->selected| value rather than - * moving by to a position relative to this value. - * We override search direction for selectable items if we encounter - * a limit, since it depends in which conditions this limit is - * attained. */ - menu->selected += steps; - if (menu->selected >= menu->size) { - if (wrap) { - menu->selected = 0; - s = 1; - } else { - menu->selected = int_max(0, menu->size - 1); - s = -1; - } - } else if (menu->selected < 0) { - if (wrap) { - menu->selected = int_max(0, menu->size - 1); - s = -1; - } else { - menu->selected = 0; - s = 1; + start = pos = menu->selected; + + if (!steps) steps = 1, --pos; + + while (steps) { + pos += s, steps -= s; + + while (1) { + if (start == pos) { + goto select_item; + } else if (pos >= menu->size && s == 1) { + if (wrap) { + pos = 0; + } else { + pos = menu->size - 1; + goto select_item; + } + } else if (pos < 0 && s == -1) { + if (wrap) { + pos = menu->size - 1; + } else { + pos = 0; + goto select_item; + } + } else if (!mi_is_selectable(&menu->items[pos])) { + pos += s; + } else { + break; + } + + if (start == -1) start = 0; } } - /* Current selected item may be an unselectable item, so we need to - * find first selectable item near to it. - * @s = 1 : ascending search. - * @s = -1: descending search. */ - - /* Search first selectable item in one direction. */ - pos = search_selectable(menu, menu->selected, s); - if (pos == -1) { - /* If not found, invert the search direction and try again. */ - pos = search_selectable(menu, menu->selected, -s); - } - - /* No selectable item found, just return. */ - if (pos == -1) { - menu->selected = -1; - menu->first = 0; - } +select_item: + if (!mi_is_selectable(&menu->items[pos])) + goto no_item; menu->selected = pos; diff --git a/src/document/dom/Makefile b/src/document/dom/Makefile index a95649c1b..db8415726 100644 --- a/src/document/dom/Makefile +++ b/src/document/dom/Makefile @@ -1,6 +1,6 @@ top_builddir=../../.. include $(top_builddir)/Makefile.config -OBJS = node.o renderer.o stack.o +OBJS = node.o renderer.o select.o stack.o include $(top_srcdir)/Makefile.lib diff --git a/src/document/dom/node.c b/src/document/dom/node.c index 54a796a8f..ee4c9ff90 100644 --- a/src/document/dom/node.c +++ b/src/document/dom/node.c @@ -94,7 +94,7 @@ del_from_dom_node_list(struct dom_node_list *list, struct dom_node *node) if (!list) return; - foreach_dom_node(i, entry, list) { + foreach_dom_node (list, entry, i) { size_t successors; if (entry != node) continue; @@ -115,7 +115,7 @@ done_dom_node_list(struct dom_node_list *list) assert(list); - foreach_dom_node (i, node, list) { + foreach_dom_node (list, node, i) { /* Avoid that the node start messing with the node list. */ done_dom_node_data(node); } diff --git a/src/document/dom/node.h b/src/document/dom/node.h index 5f74263a7..10a703816 100644 --- a/src/document/dom/node.h +++ b/src/document/dom/node.h @@ -214,11 +214,11 @@ struct dom_node_list { struct dom_node *entries[1]; }; -#define foreach_dom_node(i, node, list) \ +#define foreach_dom_node(list, node, i) \ for ((i) = 0; (i) < (list)->size; (i)++) \ if (((node) = (list)->entries[(i)])) -#define foreachback_dom_node(i, node, list) \ +#define foreachback_dom_node(list, node, i) \ for ((i) = (list)->size - 1; (i) > 0; (i)--) \ if (((node) = (list)->entries[(i)])) diff --git a/src/document/dom/select.c b/src/document/dom/select.c new file mode 100644 index 000000000..f5af0acbf --- /dev/null +++ b/src/document/dom/select.c @@ -0,0 +1,861 @@ +/* DOM node selection */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "elinks.h" + +#include "document/css/scanner.h" +#include "document/dom/dom.h" +#include "document/dom/node.h" +#include "document/dom/select.h" +#include "document/dom/stack.h" +#include "util/memory.h" +#include "util/scanner.h" +#include "util/string.h" + + +static enum dom_select_pseudo +get_dom_select_pseudo(struct scanner_token *token) +{ + static struct { + struct dom_string string; + enum dom_select_pseudo pseudo; + } pseudo_info[] = { + +#define INIT_DOM_SELECT_PSEUDO_STRING(str, type) \ + { INIT_DOM_STRING(str, -1), DOM_SELECT_PSEUDO_##type } + + INIT_DOM_SELECT_PSEUDO_STRING("first-line", FIRST_LINE), + INIT_DOM_SELECT_PSEUDO_STRING("first-letter", FIRST_LETTER), + INIT_DOM_SELECT_PSEUDO_STRING("selection", SELECTION), + INIT_DOM_SELECT_PSEUDO_STRING("after", AFTER), + INIT_DOM_SELECT_PSEUDO_STRING("before", BEFORE), + INIT_DOM_SELECT_PSEUDO_STRING("link", LINK), + INIT_DOM_SELECT_PSEUDO_STRING("visited", VISITED), + INIT_DOM_SELECT_PSEUDO_STRING("active", ACTIVE), + INIT_DOM_SELECT_PSEUDO_STRING("hover", HOVER), + INIT_DOM_SELECT_PSEUDO_STRING("focus", FOCUS), + INIT_DOM_SELECT_PSEUDO_STRING("target", TARGET), + INIT_DOM_SELECT_PSEUDO_STRING("enabled", ENABLED), + INIT_DOM_SELECT_PSEUDO_STRING("disabled", DISABLED), + INIT_DOM_SELECT_PSEUDO_STRING("checked", CHECKED), + INIT_DOM_SELECT_PSEUDO_STRING("indeterminate", INDETERMINATE), + + /* Content pseudo-classes: */ + + INIT_DOM_SELECT_PSEUDO_STRING("contains", CONTAINS), + + /* Structural pseudo-classes: */ + + INIT_DOM_SELECT_PSEUDO_STRING("nth-child", NTH_CHILD), + INIT_DOM_SELECT_PSEUDO_STRING("nth-last-child", NTH_LAST_CHILD), + INIT_DOM_SELECT_PSEUDO_STRING("first-child", FIRST_CHILD), + INIT_DOM_SELECT_PSEUDO_STRING("last-child", LAST_CHILD), + INIT_DOM_SELECT_PSEUDO_STRING("only-child", ONLY_CHILD), + + INIT_DOM_SELECT_PSEUDO_STRING("nth-of-type", NTH_TYPE), + INIT_DOM_SELECT_PSEUDO_STRING("nth-last-of-type",NTH_LAST_TYPE), + INIT_DOM_SELECT_PSEUDO_STRING("first-of-type", FIRST_TYPE), + INIT_DOM_SELECT_PSEUDO_STRING("last-of-type", LAST_TYPE), + INIT_DOM_SELECT_PSEUDO_STRING("only-of-type", ONLY_TYPE), + + INIT_DOM_SELECT_PSEUDO_STRING("root", ROOT), + INIT_DOM_SELECT_PSEUDO_STRING("empty", EMPTY), + +#undef INIT_DOM_SELECT_PSEUDO_STRING + + }; + struct dom_string string; + int i; + + set_dom_string(&string, token->string, token->length); + + for (i = 0; i < sizeof_array(pseudo_info); i++) + if (!dom_string_casecmp(&pseudo_info[i].string, &string)) + return pseudo_info[i].pseudo; + + return DOM_SELECT_PSEUDO_UNKNOWN; +} + + +static enum dom_exception_code +parse_dom_select_attribute(struct dom_select_node *sel, struct scanner *scanner) +{ + struct scanner_token *token = get_scanner_token(scanner); + + if (token->type != '[') + return DOM_ERR_INVALID_STATE; + + token = get_next_scanner_token(scanner); + if (!token || token->type != CSS_TOKEN_IDENT) + return DOM_ERR_SYNTAX; + + set_dom_string(&sel->node.string, token->string, token->length); + + token = get_next_scanner_token(scanner); + if (!token) return DOM_ERR_SYNTAX; + + switch (token->type) { + case ']': + sel->match.attribute |= DOM_SELECT_ATTRIBUTE_ANY; + return DOM_ERR_NONE; + + case CSS_TOKEN_SELECT_SPACE_LIST: + sel->match.attribute |= DOM_SELECT_ATTRIBUTE_SPACE_LIST; + break; + + case CSS_TOKEN_SELECT_HYPHEN_LIST: + sel->match.attribute |= DOM_SELECT_ATTRIBUTE_HYPHEN_LIST; + break; + + case CSS_TOKEN_SELECT_BEGIN: + sel->match.attribute |= DOM_SELECT_ATTRIBUTE_BEGIN; + break; + + case CSS_TOKEN_SELECT_END: + sel->match.attribute |= DOM_SELECT_ATTRIBUTE_END; + break; + + case CSS_TOKEN_SELECT_CONTAINS: + sel->match.attribute |= DOM_SELECT_ATTRIBUTE_CONTAINS; + break; + + default: + return DOM_ERR_SYNTAX; + } + + token = get_next_scanner_token(scanner); + if (!token) return DOM_ERR_SYNTAX; + + switch (token->type) { + case CSS_TOKEN_IDENT: + case CSS_TOKEN_STRING: + set_dom_string(&sel->node.data.attribute.value, token->string, token->length); + break; + + default: + return DOM_ERR_SYNTAX; + } + + token = get_next_scanner_token(scanner); + if (token && token->type == ']') + return DOM_ERR_NONE; + + return DOM_ERR_SYNTAX; +} + +/* Parse: + * + * 0n+1 / 1 + * 2n+0 / 2n + * 2n+1 + * -0n+2 + * -0n+1 / -1 + * 1n+0 / n+0 / n + * 0n+0 + */ +static enum dom_exception_code +parse_dom_select_nth_numeric(struct dom_select_nth_match *nth, + struct scanner_token *arg) +{ + size_t sign = 1; + size_t number; + + /* Parse negated value: -0n+1 */ + if (arg->string[0] == '-') { + arg->string++, arg->length--; + sign = -1; + if (arg->length == 0) + return DOM_ERR_SYNTAX; + } + + /* Parse -n or n */ + if (arg->string[0] == 'n') { + nth->step = sign; + arg->string++, arg->length--; + + } else if (isdigit(arg->string[0])) { + number = 0; + do { + size_t old_number = number; + + number *= 10; + if (old_number > number) + return DOM_ERR_NOT_SUPPORTED; + + number += arg->string[0] - '0'; + arg->string++, arg->length--; + + } while (arg->length > 0 && isdigit(arg->string[0])); + + if (arg->length > 0 && arg->string[0] == 'n') { + nth->step = number * sign; + arg->string++, arg->length--; + + } else { + nth->step = 0; + nth->index = number * sign; + return DOM_ERR_NONE; + } + } + + /* Parse the +... part of n+2 */ + + if (arg->length <= 1 || arg->string[0] != '+') + return DOM_ERR_NONE; + + arg->string++, arg->length--; + + /* Accept 2n+ */ + if (!isdigit(arg->string[0])) + return DOM_ERR_NONE; + + number = 0; + do { + size_t old_number = number; + + number *= 10; + if (old_number > number) + return DOM_ERR_NOT_SUPPORTED; + + number += arg->string[0] - '0'; + arg->string++, arg->length--; + + } while (arg->length > 0 && isdigit(arg->string[0])); + + nth->index = number * sign; + + return DOM_ERR_NONE; +} + +static enum dom_exception_code +parse_dom_select_nth_arg(struct dom_select_nth_match *nth, struct scanner *scanner) +{ + struct scanner_token *token = get_next_scanner_token(scanner); + struct scanner_token arg; + + if (!token || token->type != '(') + return DOM_ERR_SYNTAX; + + token = get_next_scanner_token(scanner); + if (!token || token->type == ')') + return DOM_ERR_SYNTAX; + + copy_struct(&arg, token); + + do { + /* Combine all the arg material to one token. */ + arg.length = (token->string + token->length) - arg.string; + + token = get_next_scanner_token(scanner); + if (!token) return DOM_ERR_SYNTAX; + + } while (token->type != ')'); + + if (scanner_token_contains(&arg, "even")) { + nth->step = 2; + nth->index = 0; + + } else if (scanner_token_contains(&arg, "odd")) { + nth->step = 2; + nth->index = 1; + + } else if (arg.length > 0) { + return parse_dom_select_nth_numeric(nth, &arg); + + return DOM_ERR_NONE; + } + + return DOM_ERR_NONE; +} + +static enum dom_exception_code +parse_dom_select_pseudo(struct dom_select *select, struct dom_select_node *sel, + struct scanner *scanner) +{ + struct scanner_token *token = get_scanner_token(scanner); + enum dom_select_pseudo pseudo; + enum dom_exception_code code; + + /* Skip double :'s in front of some pseudo's */ + do { + token = get_next_scanner_token(scanner); + } while (token && token->type == ':'); + + if (!token) return DOM_ERR_SYNTAX; + + pseudo = get_dom_select_pseudo(token); + switch (pseudo) { + case DOM_SELECT_PSEUDO_UNKNOWN: + return DOM_ERR_NOT_FOUND; + + case DOM_SELECT_PSEUDO_CONTAINS: + /* FIXME: E:contains("text") */ + break; + + case DOM_SELECT_PSEUDO_NTH_CHILD: + case DOM_SELECT_PSEUDO_NTH_LAST_CHILD: + code = parse_dom_select_nth_arg(&sel->nth_child, scanner); + if (code != DOM_ERR_NONE) + return code; + + sel->match.element |= DOM_SELECT_ELEMENT_NTH_CHILD; + break; + + case DOM_SELECT_PSEUDO_FIRST_CHILD: + sel->match.element |= DOM_SELECT_ELEMENT_NTH_CHILD; + set_dom_select_nth_match(&sel->nth_child, 0, 1); + break; + + case DOM_SELECT_PSEUDO_LAST_CHILD: + sel->match.element |= DOM_SELECT_ELEMENT_NTH_CHILD; + set_dom_select_nth_match(&sel->nth_child, 0, -1); + break; + + case DOM_SELECT_PSEUDO_ONLY_CHILD: + sel->match.element |= DOM_SELECT_ELEMENT_NTH_CHILD; + set_dom_select_nth_match(&sel->nth_child, 0, 0); + break; + + case DOM_SELECT_PSEUDO_NTH_TYPE: + case DOM_SELECT_PSEUDO_NTH_LAST_TYPE: + code = parse_dom_select_nth_arg(&sel->nth_type, scanner); + if (code != DOM_ERR_NONE) + return code; + + sel->match.element |= DOM_SELECT_ELEMENT_NTH_TYPE; + break; + + case DOM_SELECT_PSEUDO_FIRST_TYPE: + sel->match.element |= DOM_SELECT_ELEMENT_NTH_TYPE; + set_dom_select_nth_match(&sel->nth_type, 0, 1); + break; + + case DOM_SELECT_PSEUDO_LAST_TYPE: + sel->match.element |= DOM_SELECT_ELEMENT_NTH_TYPE; + set_dom_select_nth_match(&sel->nth_type, 0, -1); + break; + + case DOM_SELECT_PSEUDO_ONLY_TYPE: + sel->match.element |= DOM_SELECT_ELEMENT_NTH_TYPE; + set_dom_select_nth_match(&sel->nth_type, 0, 0); + break; + + case DOM_SELECT_PSEUDO_ROOT: + sel->match.element |= DOM_SELECT_ELEMENT_ROOT; + break; + + case DOM_SELECT_PSEUDO_EMPTY: + sel->match.element |= DOM_SELECT_ELEMENT_EMPTY; + break; + + default: + /* It's a bitflag! */ + select->pseudo |= pseudo; + } + + return DOM_ERR_NONE; +} + +#define get_element_relation(sel) \ + ((sel)->match.element & DOM_SELECT_RELATION_FLAGS) + +static enum dom_exception_code +parse_dom_select(struct dom_select *select, unsigned char *string, int length) +{ + struct dom_stack stack; + struct scanner scanner; + struct dom_select_node sel; + + init_scanner(&scanner, &css_scanner_info, string, string + length); + init_dom_stack(&stack, select, NULL, NULL, 0, 1); + + memset(&sel, 0, sizeof(sel)); + + while (scanner_has_tokens(&scanner)) { + struct scanner_token *token = get_scanner_token(&scanner); + enum dom_exception_code code; + struct dom_select_node *select_node; + + assert(token); + + if (token->type == '{' + || token->type == '}' + || token->type == ';' + || token->type == ',') + break; + + /* Examine the selector fragment */ + + switch (token->type) { + case CSS_TOKEN_IDENT: + sel.node.type = DOM_NODE_ELEMENT; + set_dom_string(&sel.node.string, token->string, token->length); + if (token->length == 1 && token->string[0] == '*') + sel.match.element |= DOM_SELECT_ELEMENT_UNIVERSAL; + break; + + case CSS_TOKEN_HASH: + case CSS_TOKEN_HEX_COLOR: + /* ID fragment */ + sel.node.type = DOM_NODE_ATTRIBUTE; + sel.match.attribute |= DOM_SELECT_ATTRIBUTE_ID; + /* Skip the leading '#'. */ + token->string++, token->length--; + break; + + case '[': + sel.node.type = DOM_NODE_ATTRIBUTE; + code = parse_dom_select_attribute(&sel, &scanner); + if (code != DOM_ERR_NONE) + return code; + break; + + case '.': + token = get_next_scanner_token(&scanner); + if (!token || token->type != CSS_TOKEN_IDENT) + return DOM_ERR_SYNTAX; + + sel.node.type = DOM_NODE_ATTRIBUTE; + sel.match.attribute |= DOM_SELECT_ATTRIBUTE_SPACE_LIST; + set_dom_string(&sel.node.string, "class", -1); + set_dom_string(&sel.node.data.attribute.value, token->string, token->length); + break; + + case ':': + code = parse_dom_select_pseudo(select, &sel, &scanner); + if (code != DOM_ERR_NONE) + return code; + break; + + case '>': + if (get_element_relation(&sel)) + return DOM_ERR_SYNTAX; + sel.match.element |= DOM_SELECT_RELATION_DIRECT_CHILD; + break; + + case '+': + if (get_element_relation(&sel)) + return DOM_ERR_SYNTAX; + sel.match.element |= DOM_SELECT_RELATION_DIRECT_ADJACENT; + break; + + case '~': + if (get_element_relation(&sel)) + return DOM_ERR_SYNTAX; + sel.match.element |= DOM_SELECT_RELATION_INDIRECT_ADJACENT; + break; + + default: + return DOM_ERR_SYNTAX; + } + + skip_scanner_token(&scanner); + + if (sel.node.type == DOM_NODE_UNKNOWN) + continue; + + WDBG("Adding %s: %.*s", (sel.node.type == DOM_NODE_ELEMENT) ? "element" : "attr", sel.node.string.length, sel.node.string.string); + /* FIXME */ + select_node = mem_calloc(1, sizeof(*select_node)); + copy_struct(select_node, &sel); + + if (select_node->node.parent) { + struct dom_node *node = &select_node->node; + struct dom_node *parent = node->parent; + struct dom_node_list **list = get_dom_node_list(parent, node); + int sort = (node->type == DOM_NODE_ATTRIBUTE); + int index; + + assertm(list, "Adding node to bad parent", + get_dom_node_type_name(node->type), + get_dom_node_type_name(parent->type)); + + index = *list && (*list)->size > 0 && sort + ? get_dom_node_map_index(*list, node) : -1; + + if (!add_to_dom_node_list(list, node, index)) { + done_dom_node(node); + return DOM_ERR_INVALID_STATE; + } + } else { + assert(!select->selector); + select->selector = select_node; + } + + memset(&sel, 0, sizeof(sel)); + sel.node.parent = &select_node->node; + } + + if (select->selector) + return DOM_ERR_NONE; + + WDBG("All has failed ..."); + + return DOM_ERR_INVALID_STATE; +} + +void +print_dom_select(struct dom_select *select) +{ + if (select->selector) { + struct dom_node *node = (struct dom_node *) select->selector; + + done_dom_node(node); + } + + mem_free(select); +} + +struct dom_select * +init_dom_select(enum dom_select_syntax syntax, + unsigned char *string, int length) +{ + struct dom_select *select = mem_calloc(1, sizeof(select)); + enum dom_exception_code code; + + code = parse_dom_select(select, string, length); + if (code == DOM_ERR_NONE) + return select; + + done_dom_select(select); + + return NULL; +} + +void +done_dom_select(struct dom_select *select) +{ + if (select->selector) { + struct dom_node *node = (struct dom_node *) select->selector; + + done_dom_node(node); + } + + mem_free(select); +} + + +struct dom_select_data { + struct dom_stack stack; + struct dom_select *select; + struct dom_node_list *list; +}; + +struct dom_select_state { + struct dom_node *node; +}; + + +static int +compare_element_type(struct dom_node *node1, struct dom_node *node2) +{ + /* Assuming the same document type */ + if (node1->data.element.type + && node2->data.element.type + && node1->data.element.type == node2->data.element.type) + return 0; + + return dom_string_casecmp(&node1->string, &node2->string); +} + +static struct dom_select_node * +get_child_dom_select_node(struct dom_select_node *selector, + enum dom_node_type type) +{ + struct dom_node_list *children = selector->node.data.element.children; + size_t index; + + if (!children) + return NULL; + + for (index = 0; is_dom_node_list_member(children, index); index++) { + struct dom_node *node = children->entries[index]; + + if (node && node->type == type) + return (struct dom_select_node *) node; + } + + return NULL; +} + + +#define has_attribute_match(selector, name) \ + ((selector)->match.attribute & (name)) + +static int +match_attribute_selectors(struct dom_select_node *base, struct dom_node *node) +{ + struct dom_node_list *attrs = node->data.element.map; + struct dom_node_list *selnodes = base->node.data.element.map; + struct dom_node *selnode; + size_t index; + + assert(base->node.type == DOM_NODE_ELEMENT + && node->type == DOM_NODE_ELEMENT); + + if (!selnodes) + return 1; + + if (!attrs) + return 0; + + foreach_dom_node (selnodes, selnode, index) { + struct dom_select_node *selector = (void *) selnode; + struct dom_node *attr; + struct dom_string *value; + struct dom_string *selvalue; + + if (has_attribute_match(selector, DOM_SELECT_ATTRIBUTE_ID)) { + size_t idindex; + + foreach_dom_node (attrs, attr, idindex) { + if (attr->data.attribute.id) + break; + } + + if (!is_dom_node_list_member(attrs, idindex)) + attr = NULL; + + } else { + attr = get_dom_node_map_entry(attrs, DOM_NODE_ATTRIBUTE, + selnode->data.attribute.type, + &selnode->string); + } + + if (!attr) + return 0; + + if (has_attribute_match(selector, DOM_SELECT_ATTRIBUTE_ANY)) + continue; + + value = &attr->data.attribute.value; + selvalue = &selnode->data.attribute.value; + + if (value->length < selvalue->length) + return 0; + + if (has_attribute_match(selector, DOM_SELECT_ATTRIBUTE_EXACT) + && dom_string_casecmp(value, selvalue)) + return 0; + + if (has_attribute_match(selector, DOM_SELECT_ATTRIBUTE_BEGIN)) + return 0; + + if (has_attribute_match(selector, DOM_SELECT_ATTRIBUTE_END)) + return 0; + + if (has_attribute_match(selector, DOM_SELECT_ATTRIBUTE_SPACE_LIST)) + return 0; + + if (has_attribute_match(selector, DOM_SELECT_ATTRIBUTE_HYPHEN_LIST)) + return 0; + + if (has_attribute_match(selector, DOM_SELECT_ATTRIBUTE_CONTAINS)) + return 0; + } + + return 1; +} + +#define has_element_match(selector, name) \ + ((selector)->match.element & (name)) + +static struct dom_node * +dom_select_push_element(struct dom_stack *stack, struct dom_node *node, void *data) +{ + struct dom_select_data *select_data = stack->data; + struct dom_stack_state *state; + int pos; + + WDBG("Push element %.*s.", node->string.length, node->string.string); + + foreach_dom_stack_state(&select_data->stack, state, pos) { + struct dom_select_node *selector = (void *) state->node; + + /* Match the node. */ + if (!has_element_match(selector, DOM_SELECT_ELEMENT_UNIVERSAL) + && compare_element_type(&selector->node, node)) + continue; + + switch (get_element_relation(selector)) { + case DOM_SELECT_RELATION_DIRECT_CHILD: /* E > F */ + /* node->parent */ + /* Check all states to see if node->parent is there + * and for the right reasons. */ + break; + + case DOM_SELECT_RELATION_DIRECT_ADJACENT: /* E + F */ + /* Get preceding node to see if it is on the stack. */ + break; + + case DOM_SELECT_RELATION_INDIRECT_ADJACENT: /* E ~ F */ + /* Check all states with same depth? */ + break; + + case DOM_SELECT_RELATION_DESCENDANT: /* E F */ + default: + break; + } + + /* Roots don't have parent nodes. */ + if (has_element_match(selector, DOM_SELECT_ELEMENT_ROOT) + && node->parent) + continue; + + if (has_element_match(selector, DOM_SELECT_ELEMENT_EMPTY) + && node->data.element.map->size > 0) + continue; + + if (has_element_match(selector, DOM_SELECT_ELEMENT_NTH_CHILD)) { + /* FIXME */ + continue; + } + + if (has_element_match(selector, DOM_SELECT_ELEMENT_NTH_TYPE)) { + /* FIXME */ + continue; + } + + /* Check attribute selectors. */ + if (selector->node.data.element.map + && !match_attribute_selectors(selector, node)) + continue; + + WDBG("Matched element: %.*s.", node->string.length, node->string.string); + /* This node is matched, so push the next selector node to + * match on the stack. */ + selector = get_child_dom_select_node(selector, DOM_NODE_ELEMENT); + if (selector) + push_dom_node(&select_data->stack, &selector->node); + } + + return node; +} + +static struct dom_node * +dom_select_pop_element(struct dom_stack *stack, struct dom_node *node, void *data) +{ + struct dom_select_data *select_data = stack->data; + struct dom_stack_state *state; + int index; + + WDBG("Pop element: %.*s", node->string.length, node->string.string); + stack = &select_data->stack; + + foreachback_dom_stack_state (stack, state, index) { + struct dom_select_node *selector = (void *) state->node; + struct dom_select_state *select_state; + + select_state = get_dom_stack_state_data(stack, state); + if (select_state->node == node) { + pop_dom_state(stack, state); + WDBG("Remove element."); + continue; + } + + /* Pop states that no longer lives up to a relation. */ + switch (get_element_relation(selector)) { + case DOM_SELECT_RELATION_DIRECT_CHILD: /* E > F */ + case DOM_SELECT_RELATION_DIRECT_ADJACENT: /* E + F */ + case DOM_SELECT_RELATION_INDIRECT_ADJACENT: /* E ~ F */ + case DOM_SELECT_RELATION_DESCENDANT: /* E F */ + default: + break; + } + } + + return node; +} + +static struct dom_node * +dom_select_push_text(struct dom_stack *stack, struct dom_node *node, void *data) +{ + struct dom_select_data *select_data = stack->data; + struct dom_stack_state *state = get_dom_stack_top(&select_data->stack); + struct dom_select_node *selector = (void *) state->node; + struct dom_select_node *text_sel = get_child_dom_select_node(selector, DOM_NODE_TEXT); + struct dom_string *text; + + WDBG("Text node: %d chars", node->string.length); + + if (!text_sel) + return node; + + text = &text_sel->node.string; + + switch (node->type) { + case DOM_NODE_TEXT: + case DOM_NODE_CDATA_SECTION: + case DOM_NODE_ENTITY_REFERENCE: + break; + default: + ERROR("Unhandled type"); + } + + return node; +} + + +dom_stack_callback_T dom_select_push_callbacks[DOM_NODES] = { + /* */ NULL, + /* DOM_NODE_ELEMENT */ dom_select_push_element, + /* DOM_NODE_ATTRIBUTE */ NULL, + /* DOM_NODE_TEXT */ dom_select_push_text, + /* DOM_NODE_CDATA_SECTION */ dom_select_push_text, + /* DOM_NODE_ENTITY_REFERENCE */ dom_select_push_text, + /* DOM_NODE_ENTITY */ NULL, + /* DOM_NODE_PROC_INSTRUCTION */ NULL, + /* DOM_NODE_COMMENT */ NULL, + /* DOM_NODE_DOCUMENT */ NULL, + /* DOM_NODE_DOCUMENT_TYPE */ NULL, + /* DOM_NODE_DOCUMENT_FRAGMENT */ NULL, + /* DOM_NODE_NOTATION */ NULL, +}; + +dom_stack_callback_T dom_select_pop_callbacks[DOM_NODES] = { + /* */ NULL, + /* DOM_NODE_ELEMENT */ dom_select_pop_element, + /* DOM_NODE_ATTRIBUTE */ NULL, + /* DOM_NODE_TEXT */ NULL, + /* DOM_NODE_CDATA_SECTION */ NULL, + /* DOM_NODE_ENTITY_REFERENCE */ NULL, + /* DOM_NODE_ENTITY */ NULL, + /* DOM_NODE_PROC_INSTRUCTION */ NULL, + /* DOM_NODE_COMMENT */ NULL, + /* DOM_NODE_DOCUMENT */ NULL, + /* DOM_NODE_DOCUMENT_TYPE */ NULL, + /* DOM_NODE_DOCUMENT_FRAGMENT */ NULL, + /* DOM_NODE_NOTATION */ NULL, +}; + +struct dom_node_list * +select_dom_nodes(struct dom_select *select, struct dom_node *root) +{ + struct dom_select_data select_data; + struct dom_stack stack; + size_t obj_size = sizeof(struct dom_select_state); + + memset(&select_data, 0, sizeof(select_data)); + + select_data.select = select;; + + init_dom_stack(&stack, &select_data, + dom_select_push_callbacks, + dom_select_pop_callbacks, 0, 1); + + init_dom_stack(&select_data.stack, &select_data, NULL, NULL, + obj_size, 1); + + if (push_dom_node(&select_data.stack, &select->selector->node)) { + get_dom_stack_top(&select_data.stack)->immutable = 1; + walk_dom_nodes(&stack, root); + } + + done_dom_stack(&select_data.stack); + done_dom_stack(&stack); + + return select_data.list; +} diff --git a/src/document/dom/select.h b/src/document/dom/select.h new file mode 100644 index 000000000..dedac216a --- /dev/null +++ b/src/document/dom/select.h @@ -0,0 +1,357 @@ +#ifndef EL__DOCUMENT_DOM_SELECT_H +#define EL__DOCUMENT_DOM_SELECT_H + +#include "document/dom/node.h" + + +/* FIXME: Namespaces; *|E */ + +enum dom_select_element_match { + /* Gives info about the relation required between two element nodes for + * them to match. This is also referred to as combinators. */ + /* The following are mutually exclusive and at least one must be set. + * DOM_SELECT_RELATION_DESCENDANT is the default. */ + + /* Matches any F descendant of E: E F */ + /* Bogus flag; it is an easy way to have a default. */ + DOM_SELECT_RELATION_DESCENDANT = 0, + /* Matches F being a direct child of E: E > F */ + DOM_SELECT_RELATION_DIRECT_CHILD = 1, + /* Matches F immediate preceded by E: E + F */ + DOM_SELECT_RELATION_DIRECT_ADJACENT = 2, + /* Matches F preceded by E: E ~ F */ + DOM_SELECT_RELATION_INDIRECT_ADJACENT = 4, + + DOM_SELECT_RELATION_FLAGS = DOM_SELECT_RELATION_DESCENDANT + | DOM_SELECT_RELATION_DIRECT_CHILD + | DOM_SELECT_RELATION_DIRECT_ADJACENT + | DOM_SELECT_RELATION_INDIRECT_ADJACENT, + + /* None of the following are mutual exclusive. They can co-exist + * although combining them might not make a lot of sense. */ + + /* Matches any element: * */ + DOM_SELECT_ELEMENT_UNIVERSAL = 8, + /* Matches the root node of the document: :root or // */ + DOM_SELECT_ELEMENT_ROOT = 16, + /* Matches the empty element (not even text): :empty */ + DOM_SELECT_ELEMENT_EMPTY = 32, + + /* Matches the some n-th child of its parent: :nth-child(n), etc. */ + DOM_SELECT_ELEMENT_NTH_CHILD = 64, + + /* Matches the some n-th sibling of its type: :nth-of-type(n), etc. */ + DOM_SELECT_ELEMENT_NTH_TYPE = 128, +}; + +/* The special CSS .bar class attribute syntax is represented as + * E[class="bar"]. The ID flag will match against any attribute with it's + * boolean id member set. XXX: These flags are ATM mutual exclusive. */ +enum dom_select_attribute_match { + /* Matches any set value: E[foo] */ + DOM_SELECT_ATTRIBUTE_ANY = 1, + /* Matches exact value "bar": E[foo="bar"] */ + DOM_SELECT_ATTRIBUTE_EXACT = 2, + /* Matches space seprated list "z bar bee": E[foo~="bar"] */ + DOM_SELECT_ATTRIBUTE_SPACE_LIST = 4, + /* Matches hyphen separated list "z-bar-bee": E[foo|="bar"] */ + DOM_SELECT_ATTRIBUTE_HYPHEN_LIST = 8, + /* Matches value begining; "bar-z-bee": E[foo^="bar"]*/ + DOM_SELECT_ATTRIBUTE_BEGIN = 16, + /* Matches value ending; "z-bee-bar": E[foo$="bar"] */ + DOM_SELECT_ATTRIBUTE_END = 32, + /* Matches value containing; "m33p/bar\++": E[foo*="bar"] */ + DOM_SELECT_ATTRIBUTE_CONTAINS = 64, + /* Matches exact ID attribute value "bar": #bar */ + DOM_SELECT_ATTRIBUTE_ID = 128, +}; + +/* Info about text matching is stored in a DOM text node. */ +enum dom_select_text_match { + /* Matches E containing substring "foo": E:contains("foo") */ + DOM_SELECT_TEXT_CONTAINS = 1, +}; + +/* Info about what nth child or type to match. The basic syntax is: + * + * n + * + * with a little syntactic sugar. + * + * Examples: + * + * 0n+1 / 1 is first child (same as :first-child) + * 2n+0 / 2n / even is all even children + * 2n+1 / odd is all odd children + * -0n+2 is the last two children + * -0n+1 / -1 is last child (same as :last-child) + * 1n+0 / n+0 / n is all elements of type + * 0n+0 is only element of type (a special internal syntax + * used when storing nth-info) + * + * That is, a zero step (0n) means exact indexing, and non-zero step + * means stepwise indexing. + */ +struct dom_select_nth_match { + size_t step; + size_t index; +}; + +#define set_dom_select_nth_match(nth, nthstep, nthindex) \ + do { (nth)->step = (nthstep); (nth)->index = (nthindex); } while(0) + +/* This is supposed to be a simple selector. However, this struct is also used + * for holding data for attribute matching and element text matching. */ +struct dom_select_node { + /* This holds the DOM node which has data about the node being matched. + * It can be either an element, attribute, and a text node. */ + /* XXX: Keep at the top. This is used for translating dom_node + * reference to dom_select_node. */ + struct dom_node node; + + /* Only meaningful for element nodes. */ + /* FIXME: Don't waste memory for non-element nodes. */ + struct dom_select_nth_match nth_child; + struct dom_select_nth_match nth_type; + + /* Flags, specifying how the matching should be done. */ + union { + enum dom_select_element_match element; + enum dom_select_attribute_match attribute; + enum dom_select_text_match text; + } match; +}; + + +enum dom_select_pseudo { + DOM_SELECT_PSEUDO_UNKNOWN = 0, + + /* Pseudo-elements: */ + + /* Matches first formatted line: ::first-line */ + DOM_SELECT_PSEUDO_FIRST_LINE = 1, + /* Matches first formatted letter: ::first-letter */ + DOM_SELECT_PSEUDO_FIRST_LETTER = 2, + /* Matches text selected by user: ::selection */ + DOM_SELECT_PSEUDO_SELECTION = 4, + /* Matches generated context after an element: ::after */ + DOM_SELECT_PSEUDO_AFTER = 8, + /* Matches generated content before an element: ::before */ + DOM_SELECT_PSEUDO_BEFORE = 16, + + /* Pseudo-attributes: */ + + /* Link pseudo-classes: */ + DOM_SELECT_PSEUDO_LINK = 32, /* :link */ + DOM_SELECT_PSEUDO_VISITED = 64, /* :visited */ + + /* User action pseudo-classes: */ + DOM_SELECT_PSEUDO_ACTIVE = 128, /* :active */ + DOM_SELECT_PSEUDO_HOVER = 256, /* :hover */ + DOM_SELECT_PSEUDO_FOCUS = 512, /* :focus */ + + /* Target pseudo-class: */ + DOM_SELECT_PSEUDO_TARGET = 1024, /* :target */ + + /* UI element states pseudo-classes: */ + DOM_SELECT_PSEUDO_ENABLED = 2048, /* :enabled */ + DOM_SELECT_PSEUDO_DISABLED = 4096, /* :disabled */ + DOM_SELECT_PSEUDO_CHECKED = 8192, /* :checked */ + DOM_SELECT_PSEUDO_INDETERMINATE = 16384, /* :indeterminate */ + + /* XXX: The following pseudo-classes are not kept in the pseudo member + * of the dom_select struct so they should not be bitfields. They are + * mostly for parsing purposes. */ + + DOM_SELECT_PSEUDO_CONTAINS = 10000, + + DOM_SELECT_PSEUDO_NTH_CHILD, + DOM_SELECT_PSEUDO_NTH_LAST_CHILD, + DOM_SELECT_PSEUDO_FIRST_CHILD, + DOM_SELECT_PSEUDO_LAST_CHILD, + DOM_SELECT_PSEUDO_ONLY_CHILD, + + DOM_SELECT_PSEUDO_NTH_TYPE, + DOM_SELECT_PSEUDO_NTH_LAST_TYPE, + DOM_SELECT_PSEUDO_FIRST_TYPE, + DOM_SELECT_PSEUDO_LAST_TYPE, + DOM_SELECT_PSEUDO_ONLY_TYPE, + + DOM_SELECT_PSEUDO_ROOT, + DOM_SELECT_PSEUDO_EMPTY, +}; + +struct dom_select { + struct dom_select_node *selector; + unsigned long specificity; + enum dom_select_pseudo pseudo; +}; + +enum dom_select_syntax { + DOM_SELECT_SYNTAX_CSS, /* Example: 'p a[id=node] a:hover */ + DOM_SELECT_SYNTAX_PATH, /* Example: '//rss/channel/item' */ +}; + +struct dom_select *init_dom_select(enum dom_select_syntax syntax, + unsigned char *string, int length); + +void done_dom_select(struct dom_select *select); + +struct dom_node_list * +select_dom_nodes(struct dom_select *select, struct dom_node *root); + +/* + * +------------------------------------------------------------------------------------+ + * | Pattern | Meaning | Type | Version | + * |-----------------------+------------------------------+-------------------+---------| + * | * | any element | Universal | 2 | + * | | | selector | | + * |-----------------------+------------------------------+-------------------+---------| + * | E | an element of type E | Type selector | 1 | + * |-----------------------+------------------------------+-------------------+---------| + * | E F | an F element descendant of | Descendant | 1 | + * | | an E element | combinator | | + * |-----------------------+------------------------------+-------------------+---------| + * | E > F | an F element child of an E | Child combinator | 2 | + * | | element | | | + * |-----------------------+------------------------------+-------------------+---------| + * | E + F | an F element immediately | Direct adjacent | 2 | + * | | preceded by an E element | combinator | | + * |-----------------------+------------------------------+-------------------+---------| + * | E ~ F | an F element preceded by an | Indirect adjacent | 3 | + * | | E element | combinator | | + * |-----------------------+------------------------------+-------------------+---------| + * | E:root | an E element, root of the | Structural | 3 | + * | | document | pseudo-classes | | + * |-----------------------+------------------------------+-------------------+---------| + * | | an E element that has no | Structural | | + * | E:empty | children (including text | pseudo-classes | 3 | + * | | nodes) | | | + * |-----------------------+------------------------------+-------------------+---------| + * | E:first-child | an E element, first child of | Structural | 2 | + * | | its parent | pseudo-classes | | + * |-----------------------+------------------------------+-------------------+---------| + * | E:last-child | an E element, last child of | Structural | 3 | + * | | its parent | pseudo-classes | | + * |-----------------------+------------------------------+-------------------+---------| + * | E:nth-child(n) | an E element, the n-th child | Structural | 3 | + * | | of its parent | pseudo-classes | | + * |-----------------------+------------------------------+-------------------+---------| + * | | an E element, the n-th child | Structural | | + * | E:nth-last-child(n) | of its parent, counting from | pseudo-classes | 3 | + * | | the last one | | | + * |-----------------------+------------------------------+-------------------+---------| + * | E:first-of-type | an E element, first sibling | Structural | 3 | + * | | of its type | pseudo-classes | | + * |-----------------------+------------------------------+-------------------+---------| + * | E:last-of-type | an E element, last sibling | Structural | 3 | + * | | of its type | pseudo-classes | | + * |-----------------------+------------------------------+-------------------+---------| + * | E:nth-of-type(n) | an E element, the n-th | Structural | 3 | + * | | sibling of its type | pseudo-classes | | + * |-----------------------+------------------------------+-------------------+---------| + * | | an E element, the n-th | Structural | | + * | E:nth-last-of-type(n) | sibling of its type, | pseudo-classes | 3 | + * | | counting from the last one | | | + * |-----------------------+------------------------------+-------------------+---------| + * | E:only-child | an E element, only child of | Structural | 3 | + * | | its parent | pseudo-classes | | + * |-----------------------+------------------------------+-------------------+---------| + * | E:only-of-type | an E element, only sibling | Structural | 3 | + * | | of its type | pseudo-classes | | + * |-----------------------+------------------------------+-------------------+---------| + * | | an E element being the | | | + * | E:link | source anchor of a hyperlink | The link | | + * | E:visited | of which the target is not | pseudo-classes | 1 | + * | | yet visited (:link) or | | | + * | | already visited (:visited) | | | + * |-----------------------+------------------------------+-------------------+---------| + * | E:active | an E element during certain | The user action | | + * | E:hover | user actions | pseudo-classes | 1 and 2 | + * | E:focus | | | | + * |-----------------------+------------------------------+-------------------+---------| + * | E:target | an E element being the | The target | 3 | + * | | target of the referring URI | pseudo-class | | + * |-----------------------+------------------------------+-------------------+---------| + * | | an element of type E in | | | + * | E:lang(fr) | language "fr" (the document | The :lang() | 2 | + * | FIXME | language specifies how | pseudo-class | | + * | | language is determined) | | | + * |-----------------------+------------------------------+-------------------+---------| + * | E:enabled | a user interface element E | The UI element | | + * | E:disabled | which is enabled or disabled | states | 3 | + * | | | pseudo-classes | | + * |-----------------------+------------------------------+-------------------+---------| + * | | a user interface element E | | | + * | E:checked | which is checked or in an | The UI element | | + * | E:indeterminate | indeterminate state (for | states | 3 | + * | | instance a radio-button or | pseudo-classes | | + * | | checkbox) | | | + * |-----------------------+------------------------------+-------------------+---------| + * | | an E element containing the | Content | | + * | E:contains("foo") | substring "foo" in its | pseudo-class | 3 | + * | | textual contents | | | + * |-----------------------+------------------------------+-------------------+---------| + * | E::first-line | the first formatted line of | The :first-line | 1 | + * | | an E element | pseudo-element | | + * |-----------------------+------------------------------+-------------------+---------| + * | E::first-letter | the first formatted letter | The :first-letter | 1 | + * | | of an E element | pseudo-element | | + * |-----------------------+------------------------------+-------------------+---------| + * | | the portion of an E element | The UI element | | + * | E::selection | that is currently | fragments | 3 | + * | | selected/highlighted by the | pseudo-elements | | + * | | user | | | + * |-----------------------+------------------------------+-------------------+---------| + * | E::before | generated content before an | The :before | 2 | + * | | E element | pseudo-element | | + * |-----------------------+------------------------------+-------------------+---------| + * | E::after | generated content after an E | The :after | 2 | + * | | element | pseudo-element | | + * |-----------------------+------------------------------+-------------------+---------| + * | | an E element whose class is | | | + * | E.warning | "warning" (the document | Class selectors | 1 | + * | | language specifies how class | | | + * | | is determined). | | | + * |-----------------------+------------------------------+-------------------+---------| + * | E#myid | an E element with ID equal | ID selectors | 1 | + * | | to "myid". | | | + * |-----------------------+------------------------------+-------------------+---------| + * | E[foo] | an E element with a "foo" | Attribute | 2 | + * | | attribute | selectors | | + * |-----------------------+------------------------------+-------------------+---------| + * | | an E element whose "foo" | Attribute | | + * | E[foo="bar"] | attribute value is exactly | selectors | 2 | + * | | equal to "bar" | | | + * |-----------------------+------------------------------+-------------------+---------| + * | | an E element whose "foo" | | | + * | | attribute value is a list of | Attribute | | + * | E[foo~="bar"] | space-separated values, one | selectors | 2 | + * | | of which is exactly equal to | | | + * | | "bar" | | | + * |-----------------------+------------------------------+-------------------+---------| + * | | an E element whose "foo" | | | + * | E[foo^="bar"] | attribute value begins | Attribute | 3 | + * | | exactly with the string | selectors | | + * | | "bar" | | | + * |-----------------------+------------------------------+-------------------+---------| + * | | an E element whose "foo" | Attribute | | + * | E[foo$="bar"] | attribute value ends exactly | selectors | 3 | + * | | with the string "bar" | | | + * |-----------------------+------------------------------+-------------------+---------| + * | | an E element whose "foo" | Attribute | | + * | E[foo*="bar"] | attribute value contains the | selectors | 3 | + * | | substring "bar" | | | + * |-----------------------+------------------------------+-------------------+---------| + * | | an E element whose | | | + * | | "hreflang" attribute has a | Attribute | | + * | E[hreflang|="en"] | hyphen-separated list of | selectors | 2 | + * | | values beginning (from the | | | + * | | left) with "en" | | | + * |-----------------------+------------------------------+-------------------+---------| + * | E:not(s) | an E element that does not | Negation | 3 | + * | FIXME | match simple selector s | pseudo-class | | + * +------------------------------------------------------------------------------------+ + */ + +#endif diff --git a/src/document/dom/stack.c b/src/document/dom/stack.c index f7904b606..510ee3742 100644 --- a/src/document/dom/stack.c +++ b/src/document/dom/stack.c @@ -137,10 +137,12 @@ do_pop_dom_node(struct dom_stack *stack, struct dom_stack_state *parent) struct dom_stack_state *state; dom_stack_callback_T callback; - assert(stack); - if (!dom_stack_has_parents(stack)) return 0; + assert(stack && !dom_stack_is_empty(stack)); state = get_dom_stack_top(stack); + if (state->immutable) + return 1; + callback = stack->pop_callbacks[state->node->type]; if (callback) { void *state_data = get_dom_stack_state_data(stack, state); @@ -169,7 +171,8 @@ void pop_dom_node(struct dom_stack *stack) { assert(stack); - if (!dom_stack_has_parents(stack)) return; + + if (dom_stack_is_empty(stack)) return; do_pop_dom_node(stack, get_dom_stack_parent(stack)); } @@ -180,7 +183,9 @@ pop_dom_nodes(struct dom_stack *stack, enum dom_node_type type, { struct dom_stack_state *state; - if (!dom_stack_has_parents(stack)) return; + assert(stack); + + if (dom_stack_is_empty(stack)) return; state = search_dom_stack(stack, type, string); if (state) @@ -193,11 +198,13 @@ pop_dom_state(struct dom_stack *stack, struct dom_stack_state *target) struct dom_stack_state *state; unsigned int pos; + assert(stack); + if (!target) return; - if (!dom_stack_has_parents(stack)) return; + if (dom_stack_is_empty(stack)) return; - foreachback_dom_state (stack, state, pos) { + foreachback_dom_stack_state (stack, state, pos) { if (do_pop_dom_node(stack, target)) break;; } @@ -210,7 +217,7 @@ walk_dom_nodes(struct dom_stack *stack, struct dom_node *root) push_dom_node(stack, root); - while (dom_stack_has_parents(stack)) { + while (!dom_stack_is_empty(stack)) { struct dom_stack_state *state = get_dom_stack_top(stack); struct dom_node_list *list = state->list; struct dom_node *node = state->node; diff --git a/src/document/dom/stack.h b/src/document/dom/stack.h index f3c624438..373cfe963 100644 --- a/src/document/dom/stack.h +++ b/src/document/dom/stack.h @@ -27,6 +27,9 @@ struct dom_stack_state { /* The depth of the state in the stack. This is amongst other things * used to get the state object data. */ unsigned int depth; + + /* Wether this stack state can be popped with pop_dom_*() family. */ + unsigned int immutable:1; }; /* The DOM stack is a convenient way to traverse DOM trees. Also it @@ -55,8 +58,8 @@ struct dom_stack { void *data; }; -#define dom_stack_has_parents(stack) \ - ((stack)->states && (stack)->depth > 0) +#define dom_stack_is_empty(stack) \ + (!(stack)->states || (stack)->depth == 0) static inline struct dom_stack_state * get_dom_stack_state(struct dom_stack *stack, int top_offset) @@ -74,13 +77,13 @@ get_dom_stack_state(struct dom_stack *stack, int top_offset) /* The state iterators do not include the bottom state */ -#define foreach_dom_state(stack, item, pos) \ - for ((pos) = 1; (pos) < (stack)->depth; (pos)++) \ - if (((item) = &(stack)->states[(pos)])) +#define foreach_dom_stack_state(stack, state, pos) \ + for ((pos) = 0; (pos) < (stack)->depth; (pos)++) \ + if (((state) = &(stack)->states[(pos)])) -#define foreachback_dom_state(stack, item, pos) \ - for ((pos) = (stack)->depth - 1; (pos) > 0; (pos)--) \ - if (((item) = &(stack)->states[(pos)])) +#define foreachback_dom_stack_state(stack, state, pos) \ + for ((pos) = (stack)->depth - 1; (pos) >= 0; (pos)--) \ + if (((state) = &(stack)->states[(pos)])) /* Dive through the stack states in search for the specified match. */ static inline struct dom_stack_state * @@ -91,7 +94,7 @@ search_dom_stack(struct dom_stack *stack, enum dom_node_type type, int pos; /* FIXME: Take node subtype and compare if non-zero or something. */ - foreachback_dom_state (stack, state, pos) { + foreachback_dom_stack_state (stack, state, pos) { struct dom_node *parent = state->node; if (parent->type == type diff --git a/src/document/sgml/parser.c b/src/document/sgml/parser.c index 34bfdfb9e..b63556b60 100644 --- a/src/document/sgml/parser.c +++ b/src/document/sgml/parser.c @@ -330,6 +330,8 @@ init_sgml_parser(enum sgml_parser_type type, void *data, struct uri *uri, return NULL; } + get_dom_stack_top(&parser->stack)->immutable = 1; + return parser; } diff --git a/src/scripting/Makefile b/src/scripting/Makefile index c3b3e4636..9d897eba5 100644 --- a/src/scripting/Makefile +++ b/src/scripting/Makefile @@ -7,7 +7,7 @@ SUBDIRS-$(CONFIG_PERL) += perl SUBDIRS-$(CONFIG_PYTHON) += python SUBDIRS-$(CONFIG_RUBY) += ruby SUBDIRS-$(CONFIG_SEE) += see -SUBDIRS-$(CONFIG_ECMASCRIPT) += smjs +SUBDIRS-$(CONFIG_SPIDERMONKEY) += smjs OBJS = scripting.o diff --git a/src/scripting/scripting.c b/src/scripting/scripting.c index 66202cce2..97ce79a44 100644 --- a/src/scripting/scripting.c +++ b/src/scripting/scripting.c @@ -21,7 +21,6 @@ #include "scripting/perl/perl.h" #include "scripting/python/python.h" #include "scripting/ruby/ruby.h" -#include "scripting/see/see.h" #include "scripting/smjs/smjs.h" @@ -78,9 +77,6 @@ static struct module *scripting_modules[] = { #ifdef CONFIG_RUBY &ruby_scripting_module, #endif -#ifdef CONFIG_SEE - &see_scripting_module, -#endif #ifdef CONFIG_ECMASCRIPT &smjs_scripting_module, #endif diff --git a/src/scripting/see/Makefile b/src/scripting/see/Makefile deleted file mode 100644 index 742158cf1..000000000 --- a/src/scripting/see/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -top_builddir=../../.. -include $(top_builddir)/Makefile.config - -INCLUDES += $(SEE_CFLAGS) - -OBJS = see.o core.o hooks.o interface.o - -include $(top_srcdir)/Makefile.lib diff --git a/src/scripting/see/core.c b/src/scripting/see/core.c deleted file mode 100644 index 491e75910..000000000 --- a/src/scripting/see/core.c +++ /dev/null @@ -1,139 +0,0 @@ -/* SEE interface (scripting engine) */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include - -#include "elinks.h" - -#include "bfu/dialog.h" -#include "config/home.h" -#include "intl/gettext/libintl.h" -#include "main/module.h" -#include "scripting/scripting.h" -#include "scripting/see/core.h" -#include "scripting/see/interface.h" -#include "scripting/see/see.h" -#include "util/error.h" -#include "util/string.h" - - -#define SEE_HOOKS_FILENAME "hooks.js" - -struct SEE_interpreter see_interpreter; -struct session *see_ses; - - -struct string * -convert_see_string(struct string *string, struct SEE_string *source) -{ - unsigned int pos; - - if (!init_string(string)) - return NULL; - - for (pos = 0; pos < source->length; pos++) { - add_char_to_string(string, (unsigned char) source->data[pos]); - } - - return string; -} - - -/* Error reporting. */ - -void -alert_see_error(struct session *ses, unsigned char *msg) -{ - report_scripting_error(&see_scripting_module, ses, msg); -} - - -static void -see_abort_handler(struct SEE_interpreter *see, const char *msg) -{ - ERROR((unsigned char *) msg); - /* TODO: reload scripts! */ -} - -static void -see_oom_handler(struct SEE_interpreter *see) -{ - /* XXX: Ignore! */ -} - -void -init_see(struct module *module) -{ - struct SEE_interpreter *see = &see_interpreter; - unsigned char *path; - FILE *file; - - SEE_abort = see_abort_handler; - SEE_mem_exhausted_hook = see_oom_handler; - - /* Initialise an interpreter */ - SEE_interpreter_init(see); - - /* Set up the ELinks interface. */ - init_see_interface(see); - - if (elinks_home) { - path = straconcat(elinks_home, SEE_HOOKS_FILENAME, NULL); - - } else { - path = stracpy(CONFDIR "/" SEE_HOOKS_FILENAME); - } - - if (!path) return; - - file = fopen(path, "r"); - if (file) { - struct SEE_value result; - struct SEE_input *input; - SEE_try_context_t try_context; - struct SEE_value *exception; - struct string error_msg; - - /* Load ~/.elinks/hooks.js into the interpreter. */ - input = SEE_input_file(see, file, path, NULL); - SEE_TRY(see, try_context) { - SEE_Global_eval(see, input, &result); - } - - SEE_INPUT_CLOSE(input); - - exception = SEE_CAUGHT(try_context); - if (exception && init_string(&error_msg)) { - SEE_try_context_t try_context2; - struct SEE_value value; - - SEE_TRY(see, try_context2) { - SEE_ToString(see, exception, &value); - convert_see_string(&error_msg, value.u.string); -#if 0 - if (ctxt.throw_file) - fprintf(stderr, " (thrown from %s:%d)\n", - ctxt.throw_file, ctxt.throw_line); -#endif - } - - WDBG("errors encountered while reading %s:\n%s", path, error_msg.source); - done_string(&error_msg); - - if (SEE_CAUGHT(try_context2)) { - WDBG("exception thrown while " - "printing exception"); -#if 0 - if (ctxt2.throw_file) - fprintf(stderr, " at %s:%d", - ctxt2.throw_file, ctxt2.throw_line); -#endif - } - } - } - - mem_free(path); -} diff --git a/src/scripting/see/core.h b/src/scripting/see/core.h deleted file mode 100644 index a3ee58b08..000000000 --- a/src/scripting/see/core.h +++ /dev/null @@ -1,20 +0,0 @@ - -#ifndef EL__SCRIPTING_SEE_CORE_H -#define EL__SCRIPTING_SEE_CORE_H - -#include - -struct module; -struct session; -struct string; - -extern struct SEE_interpreter see_interpreter; -extern struct session *see_ses; - -struct string *convert_see_string(struct string *string, struct SEE_string *source); -void alert_see_error(struct session *ses, unsigned char *msg); -void see_report_error(struct session *ses, int state); - -void init_see(struct module *module); - -#endif diff --git a/src/scripting/see/hooks.c b/src/scripting/see/hooks.c deleted file mode 100644 index ee4d9ee17..000000000 --- a/src/scripting/see/hooks.c +++ /dev/null @@ -1,259 +0,0 @@ -/* Ruby scripting hooks */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include - -#include "elinks.h" - -#include "cache/cache.h" -#include "main/event.h" -#include "protocol/uri.h" -#include "scripting/see/core.h" -#include "scripting/see/hooks.h" -#include "session/location.h" -#include "session/session.h" -#include "util/string.h" - - -/* The events that will trigger the functions below and what they are expected - * to do is explained in doc/events.txt */ - -static struct SEE_value * -call_see_hook(struct SEE_interpreter *see, struct session *ses, - unsigned char *name, - struct SEE_value *args[], int argc, - struct SEE_value *result) -{ - struct SEE_string *hook_name = SEE_string_sprintf(see, name); - struct SEE_value hook; - SEE_try_context_t try_context; - struct SEE_value *exception; - - SEE_OBJECT_GET(see, see->Global, hook_name, &hook); - - if (SEE_VALUE_GET_TYPE(&hook) != SEE_OBJECT - || !SEE_OBJECT_HAS_CALL(hook.u.object)) - return NULL; - - see_ses = ses; - - SEE_TRY(see, try_context) { - SEE_OBJECT_CALL(see, hook.u.object, NULL, argc, args, result); - } - - exception = SEE_CAUGHT(try_context); - if (exception) { - SEE_try_context_t try_context2; - struct SEE_value value; - - SEE_TRY(see, try_context2) { - struct string error_msg; - - SEE_ToString(see, exception, &value); - - if (init_string(&error_msg)) { - convert_see_string(&error_msg, value.u.string); - alert_see_error(ses, error_msg.source); - done_string(&error_msg); - } - } - - if (SEE_CAUGHT(try_context2)) { - alert_see_error(ses, "exception thrown while printing exception"); - } - - see_ses = NULL; - return NULL; - } - - see_ses = NULL; - return result; -} - - -static enum evhook_status -script_hook_goto_url(va_list ap, void *data) -{ - unsigned char **url = va_arg(ap, unsigned char **); - struct session *ses = va_arg(ap, struct session *); - struct SEE_interpreter *see = &see_interpreter; - struct SEE_value args_[2], *args[2] = { &args_[0], &args_[1] }; - struct SEE_value result; - - if (*url == NULL) - return EVENT_HOOK_STATUS_NEXT; - - see_ses = ses; - SEE_SET_STRING(args[0], SEE_string_sprintf(see, "%s", *url)); - - if (!ses || !have_location(ses)) { - SEE_SET_UNDEFINED(args[1]); - } else { - SEE_SET_STRING(args[1], - SEE_string_sprintf(see, "%s", struri(cur_loc(ses)->vs.uri))); - } - - if (!call_see_hook(see, ses, "goto_url", args, sizeof_array(args), &result)) - return EVENT_HOOK_STATUS_NEXT; - - switch (SEE_VALUE_GET_TYPE(&result)) { - case SEE_STRING: - { - struct string new_url; - - if (convert_see_string(&new_url, result.u.string)) - mem_free_set(url, new_url.source); - break; - } - case SEE_NULL: - break; - - default: - alert_see_error(ses, "goto_url_hook must return a string or null"); - } - - return EVENT_HOOK_STATUS_NEXT; -} - -static enum evhook_status -script_hook_follow_url(va_list ap, void *data) -{ - unsigned char **url = va_arg(ap, unsigned char **); - struct session *ses = va_arg(ap, struct session *); - struct SEE_interpreter *see = &see_interpreter; - struct SEE_value args_[1], *args[1] = { &args_[0] }; - struct SEE_value result; - - if (*url == NULL) - return EVENT_HOOK_STATUS_NEXT; - - SEE_SET_STRING(args[0], SEE_string_sprintf(see, "%s", *url)); - - if (!call_see_hook(see, ses, "follow_url", args, sizeof_array(args), &result)) - return EVENT_HOOK_STATUS_NEXT; - - switch (SEE_VALUE_GET_TYPE(&result)) { - case SEE_STRING: - { - struct string new_url; - - if (convert_see_string(&new_url, result.u.string)) - mem_free_set(url, new_url.source); - break; - } - case SEE_NULL: - break; - - default: - alert_see_error(ses, "follow_url_hook must return a string or null"); - } - - return EVENT_HOOK_STATUS_NEXT; -} - -static enum evhook_status -script_hook_pre_format_html(va_list ap, void *data) -{ - struct session *ses = va_arg(ap, struct session *); - struct cache_entry *cached = va_arg(ap, struct cache_entry *); - struct fragment *fragment = get_cache_fragment(cached); - unsigned char *url = struri(cached->uri); - struct SEE_interpreter *see = &see_interpreter; - struct SEE_value args_[2], *args[2] = { &args_[0], &args_[1] }; - struct SEE_value result; - - evhook_use_params(ses && cached); - - if (!cached->length || !*fragment->data) - return EVENT_HOOK_STATUS_NEXT; - - SEE_SET_STRING(args[0], SEE_string_sprintf(see, "%s", url)); - SEE_SET_STRING(args[1], SEE_string_sprintf(see, "%.*s", fragment->length, - fragment->data)); - - if (!call_see_hook(see, ses, "pre_format_html", args, sizeof_array(args), &result)) - return EVENT_HOOK_STATUS_NEXT; - - switch (SEE_VALUE_GET_TYPE(&result)) { - case SEE_STRING: - { - struct string new_html; - - if (convert_see_string(&new_html, result.u.string)) { - add_fragment(cached, 0, new_html.source, new_html.length); - normalize_cache_entry(cached, new_html.length); - } - break; - } - case SEE_NULL: - break; - - default: - alert_see_error(ses, "pre_format_hook must return a string or null"); - } - - return EVENT_HOOK_STATUS_NEXT; -} - -/* The function can return: - * - "PROXY:PORT" to use the specified proxy - * - "" to not use any proxy - * - nil to use the default proxies */ -static enum evhook_status -script_hook_get_proxy(va_list ap, void *data) -{ - unsigned char **new_proxy_url = va_arg(ap, unsigned char **); - unsigned char *url = va_arg(ap, unsigned char *); - struct SEE_interpreter *see = &see_interpreter; - struct SEE_value result, args_[1], *args[1] = { &args_[0] }; - - if (!new_proxy_url || !url) - return EVENT_HOOK_STATUS_NEXT; - - SEE_SET_STRING(args[0], SEE_string_sprintf(see, "%s", url)); - - if (!call_see_hook(see, NULL, "get_proxy", args, sizeof_array(args), &result)) - return EVENT_HOOK_STATUS_NEXT; - - switch (SEE_VALUE_GET_TYPE(&result)) { - case SEE_STRING: - { - struct string proxy; - - if (convert_see_string(&proxy, result.u.string)) - mem_free_set(new_proxy_url, proxy.source); - break; - } - case SEE_NULL: - break; - - default: - alert_see_error(NULL, "proxy_hook must return a string, undefined or null"); - } - - return EVENT_HOOK_STATUS_NEXT; -} - -static enum evhook_status -script_hook_quit(va_list ap, void *data) -{ - struct SEE_interpreter *see = &see_interpreter; - struct SEE_value result; - - call_see_hook(see, NULL, "quit", NULL, 0, &result); - - return EVENT_HOOK_STATUS_NEXT; -} - -struct event_hook_info see_scripting_hooks[] = { - { "goto-url", 0, script_hook_goto_url, NULL }, - { "follow-url", 0, script_hook_follow_url, NULL }, - { "pre-format-html", 0, script_hook_pre_format_html, NULL }, - { "get-proxy", 0, script_hook_get_proxy, NULL }, - { "quit", 0, script_hook_quit, NULL }, - - NULL_EVENT_HOOK_INFO, -}; diff --git a/src/scripting/see/hooks.h b/src/scripting/see/hooks.h deleted file mode 100644 index 1e20daf8c..000000000 --- a/src/scripting/see/hooks.h +++ /dev/null @@ -1,9 +0,0 @@ - -#ifndef EL__SCRIPTING_SEE_HOOKS_H -#define EL__SCRIPTING_SEE_HOOKS_H - -struct event_hook_info; - -extern struct event_hook_info see_scripting_hooks[]; - -#endif diff --git a/src/scripting/see/interface.c b/src/scripting/see/interface.c deleted file mode 100644 index cf253b2f8..000000000 --- a/src/scripting/see/interface.c +++ /dev/null @@ -1,209 +0,0 @@ -/* The ELinks SEE interface: */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include - -#include "elinks.h" - -#include "bfu/dialog.h" -#include "config/conf.h" -#include "config/home.h" -#include "config/options.h" -#include "config/opttypes.h" -#include "intl/gettext/libintl.h" -#include "main/module.h" -#include "scripting/scripting.h" -#include "scripting/see/core.h" -#include "scripting/see/see.h" -#include "session/session.h" -#include "util/error.h" -#include "util/file.h" -#include "util/string.h" - - -static void -navigator_preference(struct SEE_interpreter *see, struct SEE_object *self, - struct SEE_object *thisobj, int argc, struct SEE_value **argv, - struct SEE_value *res) -{ - struct SEE_value v; - struct string opt_name; - struct option *opt; - - SEE_SET_UNDEFINED(res); - - if (argc != 1 && argc != 2) return; - - SEE_ToString(see, argv[0], &v); - if (!convert_see_string(&opt_name, v.u.string)) - return; - - opt = get_opt_rec(config_options, opt_name.source); - done_string(&opt_name); - /* FIXME: Alert? */ - if (!opt) return; - - /* Set option */ - switch (opt->type) { - case OPT_BOOL: - { - long value = opt->value.number; - - if (argc == 1) { - SEE_SET_BOOLEAN(res, value); - return; - } - - SEE_ToBoolean(see, argv[1], &v); - value = !!v.u.boolean; - opt->value.number = value; - break; - } - case OPT_INT: - case OPT_LONG: - { - long value; - - if (argc == 1) { - SEE_SET_NUMBER(res, opt->value.number); - return; - } - - SEE_ToInteger(see, argv[1], &v); - value = SEE_ToInt32(see, &v); - if (opt->min <= value && value <= opt->max) - option_types[opt->type].set(opt, (unsigned char *) (&value)); - break; - } - case OPT_STRING: - case OPT_CODEPAGE: - case OPT_LANGUAGE: - case OPT_COLOR: - { - struct string opt_value; - - if (argc == 1) { - SEE_SET_STRING(res, SEE_string_sprintf(see, opt->value.string)); - return; - } - - SEE_ToString(see, argv[1], &v); - if (!convert_see_string(&opt_value, v.u.string)) - return; - - option_types[opt->type].set(opt, opt_value.source); - done_string(&opt_value); - break; - } - default: - return; - } - - if (argc == 2) { - opt->flags |= OPT_TOUCHED; - call_change_hooks(see_ses, opt, opt); - } -} - -static void -navigator_save_preferences(struct SEE_interpreter *see, struct SEE_object *self, - struct SEE_object *thisobj, int argc, struct SEE_value **argv, - struct SEE_value *res) -{ - if (see_ses) - write_config(see_ses->tab->term); -} - -static void -navigator_alert(struct SEE_interpreter *see, struct SEE_object *self, - struct SEE_object *thisobj, int argc, struct SEE_value **argv, - struct SEE_value *res) -{ - struct SEE_value v; - struct string string; - struct terminal *term; - - SEE_SET_UNDEFINED(res); - - if (!argc) return; - - SEE_ToString(see, argv[0], &v); - if (!convert_see_string(&string, v.u.string)) - return; - - if (!see_ses && list_empty(terminals)) { - usrerror("[SEE] %s", string.source); - done_string(&string); - return; - } - - term = see_ses ? see_ses->tab->term : terminals.next; - - info_box(term, MSGBOX_NO_TEXT_INTL | MSGBOX_FREE_TEXT, - N_("SEE Message"), ALIGN_LEFT, string.source); -} - -#if DATADRIVEN -_IDEA -struct object_info browser_object[] = { - "ELinks", SEE_ATTR_READONLY, - { /* Properties: */ - { "version", SEE_STRING, VERSION, SEE_ATTR_READONLY }, - { "home", SEE_STRING, NULL, SEE_ATTR_READONLY }, - }, - { /* Methods: (as name, handler, args) */ - { "write", elinks_see_write, SEE_ATTR_READONLY }, - { NULL } - }, -}; - -struct object_info *see_ -#endif - -void -init_see_interface(struct SEE_interpreter *see) -{ - unsigned char *home; - struct SEE_object *obj, *navigator; - struct SEE_value value; - struct SEE_string *name; - - /* Create the navigator browser object. Add it to the global space */ - navigator = SEE_Object_new(see); - SEE_SET_OBJECT(&value, navigator); - name = SEE_string_sprintf(see, "navigator"); - SEE_OBJECT_PUT(see, see->Global, name, &value, SEE_ATTR_READONLY); - - /* Create a string and attach as 'ELinks.version' */ - SEE_SET_STRING(&value, SEE_string_sprintf(see, VERSION)); - name = SEE_string_sprintf(see, "appVersion"); - SEE_OBJECT_PUT(see, navigator, name, &value, SEE_ATTR_READONLY); - - /* Create a string and attach as 'ELinks.home' */ - home = elinks_home ? elinks_home : (unsigned char *) CONFDIR; - SEE_SET_STRING(&value, SEE_string_sprintf(see, home)); - name = SEE_string_sprintf(see, "appHome"); - SEE_OBJECT_PUT(see, navigator, name, &value, SEE_ATTR_READONLY); - - /* Create an 'alert' method and attach to the browser object. */ - /* FIXME: The browser object and the Global object should be identical. */ - name = SEE_string_sprintf(see, "alert"); - obj = SEE_cfunction_make(see, navigator_alert, name, 1); - SEE_SET_OBJECT(&value, obj); - SEE_OBJECT_PUT(see, navigator, name, &value, 0); - SEE_OBJECT_PUT(see, see->Global, name, &value, 0); - - name = SEE_string_sprintf(see, "preference"); - obj = SEE_cfunction_make(see, navigator_preference, name, 1); - SEE_SET_OBJECT(&value, obj); - SEE_OBJECT_PUT(see, navigator, name, &value, 0); - - name = SEE_string_sprintf(see, "savePreferences"); - obj = SEE_cfunction_make(see, navigator_save_preferences, name, 1); - SEE_SET_OBJECT(&value, obj); - SEE_OBJECT_PUT(see, navigator, name, &value, 0); -} - diff --git a/src/scripting/see/interface.h b/src/scripting/see/interface.h deleted file mode 100644 index ff6e5903c..000000000 --- a/src/scripting/see/interface.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef EL__SCRIPTING_SEE_INTERFACE_H -#define EL__SCRIPTING_SEE_INTERFACE_H - -struct SEE_interpreter; - -void init_see_interface(struct SEE_interpreter *see); - - -#endif diff --git a/src/scripting/see/see.c b/src/scripting/see/see.c deleted file mode 100644 index ea1391b27..000000000 --- a/src/scripting/see/see.c +++ /dev/null @@ -1,22 +0,0 @@ -/* Simple Ecmascript Engine (SEE) browser scripting module */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "elinks.h" - -#include "main/module.h" -#include "scripting/see/core.h" -#include "scripting/see/hooks.h" - - -struct module see_scripting_module = struct_module( - /* name: */ "Simple Ecmascript Engine", - /* options: */ NULL, - /* events: */ see_scripting_hooks, - /* submodules: */ NULL, - /* data: */ NULL, - /* init: */ init_see, - /* done: */ NULL -); diff --git a/src/scripting/see/see.h b/src/scripting/see/see.h deleted file mode 100644 index 426bc01f7..000000000 --- a/src/scripting/see/see.h +++ /dev/null @@ -1,9 +0,0 @@ - -#ifndef EL__SCRIPTING_SEE_SEE_H -#define EL__SCRIPTING_SEE_SEE_H - -struct module; - -extern struct module see_scripting_module; - -#endif diff --git a/src/scripting/smjs/cache_object.c b/src/scripting/smjs/cache_object.c index 76d00a569..15dd78200 100644 --- a/src/scripting/smjs/cache_object.c +++ b/src/scripting/smjs/cache_object.c @@ -8,6 +8,7 @@ #include "cache/cache.h" #include "ecmascript/spidermonkey/util.h" +#include "scripting/smjs/cache_object.h" #include "scripting/smjs/core.h" #include "util/error.h" #include "util/memory.h" @@ -31,7 +32,7 @@ cache_entry_get_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp) { struct cache_entry *cached = JS_GetPrivate(ctx, obj); - if (!cached) return JS_FALSE; + if (!cache_entry_is_valid(cached)) return JS_FALSE; undef_to_jsval(ctx, vp); @@ -77,7 +78,7 @@ cache_entry_set_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp) { struct cache_entry *cached = JS_GetPrivate(ctx, obj); - if (!cached) return JS_FALSE; + if (!cache_entry_is_valid(cached)) return JS_FALSE; if (!JSVAL_IS_INT(id)) return JS_FALSE; diff --git a/src/scripting/smjs/core.h b/src/scripting/smjs/core.h index 87786d31f..c3450ba35 100644 --- a/src/scripting/smjs/core.h +++ b/src/scripting/smjs/core.h @@ -8,8 +8,8 @@ struct session; struct string; extern JSContext *smjs_ctx; -JSObject *smjs_elinks_object; -struct session *smjs_ses; +extern JSObject *smjs_elinks_object; +extern struct session *smjs_ses; void alert_smjs_error(unsigned char *msg);