mirror of
https://github.com/rkd77/elinks.git
synced 2025-06-30 22:19:29 -04:00
Merge with git+ssh://pasky.or.cz/srv/git/elinks.git
This commit is contained in:
commit
1aba95dffc
@ -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
|
||||
])
|
@ -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 ===================================================================
|
||||
|
@ -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)
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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)]))
|
||||
|
||||
|
861
src/document/dom/select.c
Normal file
861
src/document/dom/select.c
Normal file
@ -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;
|
||||
}
|
357
src/document/dom/select.h
Normal file
357
src/document/dom/select.h
Normal file
@ -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:
|
||||
*
|
||||
* <step>n<index>
|
||||
*
|
||||
* 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
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
@ -1,139 +0,0 @@
|
||||
/* SEE interface (scripting engine) */
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <see/see.h>
|
||||
|
||||
#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);
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
|
||||
#ifndef EL__SCRIPTING_SEE_CORE_H
|
||||
#define EL__SCRIPTING_SEE_CORE_H
|
||||
|
||||
#include <see/see.h>
|
||||
|
||||
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
|
@ -1,259 +0,0 @@
|
||||
/* Ruby scripting hooks */
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <see/see.h>
|
||||
|
||||
#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,
|
||||
};
|
@ -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
|
@ -1,209 +0,0 @@
|
||||
/* The ELinks SEE interface: */
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <see/see.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
|
@ -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
|
@ -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
|
||||
);
|
@ -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
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user