setTimeout: Kill timer when it timed out, fixes random crashes
[elinks.git] / src / ecmascript / ecmascript.c
blobe1231ffc3de53ee4ddd48a990003243a29a83a10
1 /* Base ECMAScript file. Mostly a proxy for specific library backends. */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include <stdlib.h>
9 #include "elinks.h"
11 #include "config/options.h"
12 #include "document/document.h"
13 #include "document/view.h"
14 #include "ecmascript/ecmascript.h"
15 #include "ecmascript/see.h"
16 #include "ecmascript/spidermonkey.h"
17 #include "intl/gettext/libintl.h"
18 #include "main/module.h"
19 #include "main/timer.h"
20 #include "osdep/osdep.h"
21 #include "protocol/protocol.h"
22 #include "protocol/uri.h"
23 #include "session/session.h"
24 #include "session/task.h"
25 #include "terminal/terminal.h"
26 #include "terminal/window.h"
27 #include "util/conv.h"
28 #include "util/string.h"
29 #include "viewer/text/view.h" /* current_frame() */
30 #include "viewer/text/form.h" /* <-ecmascript_reset_state() */
31 #include "viewer/text/vs.h"
34 /* TODO: We should have some kind of ACL for the scripts - i.e. ability to
35 * disallow the scripts to open new windows (or so that the windows are always
36 * directed to tabs, this particular option would be a tristate), disallow
37 * messing with your menubar/statusbar visibility, disallow changing the
38 * statusbar content etc. --pasky */
40 static struct option_info ecmascript_options[] = {
41 INIT_OPT_TREE("", N_("ECMAScript"),
42 "ecmascript", 0,
43 N_("ECMAScript options.")),
45 INIT_OPT_BOOL("ecmascript", N_("Enable"),
46 "enable", 0, 1,
47 N_("Whether to run those scripts inside of documents.")),
49 INIT_OPT_BOOL("ecmascript", N_("Script error reporting"),
50 "error_reporting", 0, 0,
51 N_("Open a message box when a script reports an error.")),
53 INIT_OPT_BOOL("ecmascript", N_("Ignore <noscript> content"),
54 "ignore_noscript", 0, 0,
55 N_("Whether to ignore content enclosed by the <noscript> tag\n"
56 "when ECMAScript is enabled.")),
58 INIT_OPT_INT("ecmascript", N_("Maximum execution time"),
59 "max_exec_time", 0, 1, 3600, 5,
60 N_("Maximum execution time in seconds for a script.")),
62 INIT_OPT_BOOL("ecmascript", N_("Pop-up window blocking"),
63 "block_window_opening", 0, 0,
64 N_("Whether to disallow scripts to open new windows or tabs.")),
66 NULL_OPTION_INFO,
69 #define NUMBER_OF_URLS_TO_REMEMBER 8
70 static struct {
71 unsigned char *url;
72 unsigned char *frame;
73 } u[NUMBER_OF_URLS_TO_REMEMBER];
74 static int url_index = 0;
76 int
77 ecmascript_check_url(unsigned char *url, unsigned char *frame)
79 int i;
80 /* Because of gradual rendering window.open is called many
81 * times with the same arguments.
82 * This workaround remembers NUMBER_OF_URLS_TO_REMEMBER last
83 * opened URLs and do not let open them again.
86 for (i = 0; i < NUMBER_OF_URLS_TO_REMEMBER; i++) {
87 if (!u[i].url) break;
88 if (!strcmp(u[i].url, url) && !strcmp(u[i].frame, frame)) {
89 mem_free(url);
90 mem_free(frame);
91 return 0;
94 mem_free_if(u[url_index].url);
95 mem_free_if(u[url_index].frame);
96 u[url_index].url = url;
97 u[url_index].frame = frame;
98 url_index++;
99 if (url_index >= NUMBER_OF_URLS_TO_REMEMBER) url_index = 0;
100 return 1;
103 void
104 ecmascript_free_urls(struct module *module)
106 int i;
108 for (i = 0; i < NUMBER_OF_URLS_TO_REMEMBER; i++) {
109 mem_free_if(u[i].url);
110 mem_free_if(u[i].frame);
114 #undef NUMBER_OF_URLS_TO_REMEMBER
116 struct ecmascript_interpreter *
117 ecmascript_get_interpreter(struct view_state *vs)
119 struct ecmascript_interpreter *interpreter;
121 assert(vs);
123 interpreter = mem_calloc(1, sizeof(*interpreter));
124 if (!interpreter)
125 return NULL;
127 interpreter->vs = vs;
128 init_list(interpreter->onload_snippets);
129 #ifdef CONFIG_ECMASCRIPT_SEE
130 see_get_interpreter(interpreter);
131 #else
132 spidermonkey_get_interpreter(interpreter);
133 #endif
134 init_string(&interpreter->code);
135 return interpreter;
138 void
139 ecmascript_put_interpreter(struct ecmascript_interpreter *interpreter)
141 assert(interpreter);
142 #ifdef CONFIG_ECMASCRIPT_SEE
143 see_put_interpreter(interpreter);
144 #else
145 spidermonkey_put_interpreter(interpreter);
146 #endif
147 free_string_list(&interpreter->onload_snippets);
148 done_string(&interpreter->code);
149 /* Is it superfluous? */
150 if (interpreter->vs->doc_view)
151 kill_timer(&interpreter->vs->doc_view->document->timeout);
152 interpreter->vs->ecmascript = NULL;
153 mem_free(interpreter);
156 void
157 ecmascript_eval(struct ecmascript_interpreter *interpreter,
158 struct string *code, struct string *ret)
160 if (!get_ecmascript_enable())
161 return;
162 assert(interpreter);
163 #ifdef CONFIG_ECMASCRIPT_SEE
164 see_eval(interpreter, code, ret);
165 #else
166 spidermonkey_eval(interpreter, code, ret);
167 #endif
170 unsigned char *
171 ecmascript_eval_stringback(struct ecmascript_interpreter *interpreter,
172 struct string *code)
174 if (!get_ecmascript_enable())
175 return NULL;
176 assert(interpreter);
177 #ifdef CONFIG_ECMASCRIPT_SEE
178 return see_eval_stringback(interpreter, code);
179 #else
180 return spidermonkey_eval_stringback(interpreter, code);
181 #endif
185 ecmascript_eval_boolback(struct ecmascript_interpreter *interpreter,
186 struct string *code)
188 if (!get_ecmascript_enable())
189 return -1;
190 assert(interpreter);
191 #ifdef CONFIG_ECMASCRIPT_SEE
192 return see_eval_boolback(interpreter, code);
193 #else
194 return spidermonkey_eval_boolback(interpreter, code);
195 #endif
199 void
200 ecmascript_reset_state(struct view_state *vs)
202 struct form_view *fv;
203 int i;
205 vs->ecmascript_fragile = 0;
206 if (vs->ecmascript)
207 ecmascript_put_interpreter(vs->ecmascript);
209 foreach (fv, vs->forms)
210 fv->ecmascript_obj = NULL;
211 for (i = 0; i < vs->form_info_len; i++)
212 vs->form_info[i].ecmascript_obj = NULL;
214 vs->ecmascript = ecmascript_get_interpreter(vs);
215 if (!vs->ecmascript)
216 vs->ecmascript_fragile = 1;
219 void
220 ecmascript_protocol_handler(struct session *ses, struct uri *uri)
222 struct document_view *doc_view = current_frame(ses);
223 struct string current_url = INIT_STRING(struri(uri), strlen(struri(uri)));
224 unsigned char *redirect_url, *redirect_abs_url;
225 struct uri *redirect_uri;
227 if (!doc_view) /* Blank initial document. TODO: Start at about:blank? */
228 return;
229 assert(doc_view->vs);
230 if (doc_view->vs->ecmascript_fragile)
231 ecmascript_reset_state(doc_view->vs);
232 if (!doc_view->vs->ecmascript)
233 return;
235 redirect_url = ecmascript_eval_stringback(doc_view->vs->ecmascript,
236 &current_url);
237 if (!redirect_url)
238 return;
239 /* XXX: This code snippet is duplicated over here,
240 * location_set_property(), html_a() and who knows where else. */
241 redirect_abs_url = join_urls(doc_view->document->uri,
242 trim_chars(redirect_url, ' ', 0));
243 mem_free(redirect_url);
244 if (!redirect_abs_url)
245 return;
246 redirect_uri = get_uri(redirect_abs_url, 0);
247 mem_free(redirect_abs_url);
248 if (!redirect_uri)
249 return;
251 /* XXX: Is that safe to do at this point? --pasky */
252 goto_uri_frame(ses, redirect_uri, doc_view->name,
253 CACHE_MODE_NORMAL);
254 done_uri(redirect_uri);
258 void
259 ecmascript_timeout_dialog(struct terminal *term, int max_exec_time)
261 info_box(term, MSGBOX_FREE_TEXT,
262 N_("JavaScript Emergency"), ALIGN_LEFT,
263 msg_text(term,
264 N_("A script embedded in the current document was running\n"
265 "for more than %d seconds. This probably means there is\n"
266 "a bug in the script and it could have halted the whole\n"
267 "ELinks, so the script execution was interrupted."),
268 max_exec_time));
272 void
273 ecmascript_set_action(unsigned char **action, unsigned char *string)
275 struct uri *protocol;
277 trim_chars(string, ' ', NULL);
278 protocol = get_uri(string, URI_PROTOCOL);
280 if (protocol) { /* full uri with protocol */
281 done_uri(protocol);
282 mem_free_set(action, string);
283 } else {
284 if (dir_sep(string[0])) { /* absolute uri, TODO: disk drive under WIN32 */
285 struct uri *uri = get_uri(*action, URI_HTTP_REFERRER_HOST);
287 if (uri->protocol == PROTOCOL_FILE) {
288 mem_free_set(action, straconcat(struri(uri), string, NULL));
290 else
291 mem_free_set(action, straconcat(struri(uri), string + 1, NULL));
292 done_uri(uri);
293 mem_free(string);
294 } else { /* relative uri */
295 unsigned char *last_slash = strrchr(*action, '/');
296 unsigned char *new_action;
298 if (last_slash) *(last_slash + 1) = '\0';
299 new_action = straconcat(*action, string, NULL);
300 mem_free_set(action, new_action);
301 mem_free(string);
306 static void
307 ecmascript_timeout_handler(void *i)
309 struct ecmascript_interpreter *interpreter = i;
311 assert(interpreter->vs->doc_view->document);
312 kill_timer(&interpreter->vs->doc_view->document->timeout);
314 ecmascript_eval(interpreter, &interpreter->code, NULL);
317 void
318 ecmascript_set_timeout(struct ecmascript_interpreter *interpreter, unsigned char *code, int timeout)
320 assert(interpreter && interpreter->vs->doc_view->document);
321 if (!code) return;
322 done_string(&interpreter->code);
323 init_string(&interpreter->code);
324 add_to_string(&interpreter->code, code);
325 mem_free(code);
326 install_timer(&interpreter->vs->doc_view->document->timeout, timeout, ecmascript_timeout_handler, interpreter);
329 static struct module *ecmascript_modules[] = {
330 #ifdef CONFIG_ECMASCRIPT_SEE
331 &see_module,
332 #elif defined(CONFIG_ECMASCRIPT_SMJS)
333 &spidermonkey_module,
334 #endif
335 NULL,
339 struct module ecmascript_module = struct_module(
340 /* name: */ N_("ECMAScript"),
341 /* options: */ ecmascript_options,
342 /* events: */ NULL,
343 /* submodules: */ ecmascript_modules,
344 /* data: */ NULL,
345 /* init: */ NULL,
346 /* done: */ ecmascript_free_urls