1038: Remove remembering last 8 URLs. It did not work.
[elinks.git] / src / ecmascript / ecmascript.c
blob690a2926a80278fea5e1a60c45d9ad33a83ed5cb
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, 0,
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 struct ecmascript_interpreter *
70 ecmascript_get_interpreter(struct view_state *vs)
72 struct ecmascript_interpreter *interpreter;
74 assert(vs);
76 interpreter = mem_calloc(1, sizeof(*interpreter));
77 if (!interpreter)
78 return NULL;
80 interpreter->vs = vs;
81 interpreter->vs->ecmascript_fragile = 0;
82 init_list(interpreter->onload_snippets);
83 #ifdef CONFIG_ECMASCRIPT_SEE
84 see_get_interpreter(interpreter);
85 #else
86 spidermonkey_get_interpreter(interpreter);
87 #endif
88 init_string(&interpreter->code);
89 return interpreter;
92 void
93 ecmascript_put_interpreter(struct ecmascript_interpreter *interpreter)
95 assert(interpreter);
96 assert(interpreter->backend_nesting == 0);
97 /* If the assertion fails, it is better to leak the
98 * interpreter than to corrupt memory. */
99 if_assert_failed return;
101 #ifdef CONFIG_ECMASCRIPT_SEE
102 see_put_interpreter(interpreter);
103 #else
104 spidermonkey_put_interpreter(interpreter);
105 #endif
106 free_string_list(&interpreter->onload_snippets);
107 done_string(&interpreter->code);
108 /* Is it superfluous? */
109 if (interpreter->vs->doc_view)
110 kill_timer(&interpreter->vs->doc_view->document->timeout);
111 interpreter->vs->ecmascript = NULL;
112 interpreter->vs->ecmascript_fragile = 1;
113 mem_free(interpreter);
116 void
117 ecmascript_eval(struct ecmascript_interpreter *interpreter,
118 struct string *code, struct string *ret)
120 if (!get_ecmascript_enable())
121 return;
122 assert(interpreter);
123 interpreter->backend_nesting++;
124 #ifdef CONFIG_ECMASCRIPT_SEE
125 see_eval(interpreter, code, ret);
126 #else
127 spidermonkey_eval(interpreter, code, ret);
128 #endif
129 interpreter->backend_nesting--;
132 unsigned char *
133 ecmascript_eval_stringback(struct ecmascript_interpreter *interpreter,
134 struct string *code)
136 unsigned char *result;
138 if (!get_ecmascript_enable())
139 return NULL;
140 assert(interpreter);
141 interpreter->backend_nesting++;
142 #ifdef CONFIG_ECMASCRIPT_SEE
143 result = see_eval_stringback(interpreter, code);
144 #else
145 result = spidermonkey_eval_stringback(interpreter, code);
146 #endif
147 interpreter->backend_nesting--;
148 return result;
152 ecmascript_eval_boolback(struct ecmascript_interpreter *interpreter,
153 struct string *code)
155 int result;
157 if (!get_ecmascript_enable())
158 return -1;
159 assert(interpreter);
160 interpreter->backend_nesting++;
161 #ifdef CONFIG_ECMASCRIPT_SEE
162 result = see_eval_boolback(interpreter, code);
163 #else
164 result = spidermonkey_eval_boolback(interpreter, code);
165 #endif
166 interpreter->backend_nesting--;
167 return result;
170 void
171 ecmascript_detach_form_view(struct form_view *fv)
173 #ifdef CONFIG_ECMASCRIPT_SEE
174 see_detach_form_view(fv);
175 #else
176 spidermonkey_detach_form_view(fv);
177 #endif
180 void ecmascript_detach_form_state(struct form_state *fs)
182 #ifdef CONFIG_ECMASCRIPT_SEE
183 see_detach_form_state(fs);
184 #else
185 spidermonkey_detach_form_state(fs);
186 #endif
189 void ecmascript_moved_form_state(struct form_state *fs)
191 #ifdef CONFIG_ECMASCRIPT_SEE
192 see_moved_form_state(fs);
193 #else
194 spidermonkey_moved_form_state(fs);
195 #endif
198 void
199 ecmascript_reset_state(struct view_state *vs)
201 struct form_view *fv;
202 int i;
204 /* Normally, if vs->ecmascript == NULL, the associated
205 * ecmascript_obj pointers are also NULL. However, they might
206 * be non-NULL if the ECMAScript objects have been lazily
207 * created because of scripts running in sibling HTML frames. */
208 foreach (fv, vs->forms)
209 ecmascript_detach_form_view(fv);
210 for (i = 0; i < vs->form_info_len; i++)
211 ecmascript_detach_form_state(&vs->form_info[i]);
213 vs->ecmascript_fragile = 0;
214 if (vs->ecmascript)
215 ecmascript_put_interpreter(vs->ecmascript);
217 vs->ecmascript = ecmascript_get_interpreter(vs);
218 if (!vs->ecmascript)
219 vs->ecmascript_fragile = 1;
222 void
223 ecmascript_protocol_handler(struct session *ses, struct uri *uri)
225 struct document_view *doc_view = current_frame(ses);
226 struct string current_url = INIT_STRING(struri(uri), strlen(struri(uri)));
227 unsigned char *redirect_url, *redirect_abs_url;
228 struct uri *redirect_uri;
230 if (!doc_view) /* Blank initial document. TODO: Start at about:blank? */
231 return;
232 assert(doc_view->vs);
233 if (doc_view->vs->ecmascript_fragile)
234 ecmascript_reset_state(doc_view->vs);
235 if (!doc_view->vs->ecmascript)
236 return;
238 redirect_url = ecmascript_eval_stringback(doc_view->vs->ecmascript,
239 &current_url);
240 if (!redirect_url)
241 return;
242 /* XXX: This code snippet is duplicated over here,
243 * location_set_property(), html_a() and who knows where else. */
244 redirect_abs_url = join_urls(doc_view->document->uri,
245 trim_chars(redirect_url, ' ', 0));
246 mem_free(redirect_url);
247 if (!redirect_abs_url)
248 return;
249 redirect_uri = get_uri(redirect_abs_url, 0);
250 mem_free(redirect_abs_url);
251 if (!redirect_uri)
252 return;
254 /* XXX: Is that safe to do at this point? --pasky */
255 goto_uri_frame(ses, redirect_uri, doc_view->name,
256 CACHE_MODE_NORMAL);
257 done_uri(redirect_uri);
261 void
262 ecmascript_timeout_dialog(struct terminal *term, int max_exec_time)
264 info_box(term, MSGBOX_FREE_TEXT,
265 N_("JavaScript Emergency"), ALIGN_LEFT,
266 msg_text(term,
267 N_("A script embedded in the current document was running\n"
268 "for more than %d seconds. This probably means there is\n"
269 "a bug in the script and it could have halted the whole\n"
270 "ELinks, so the script execution was interrupted."),
271 max_exec_time));
275 void
276 ecmascript_set_action(unsigned char **action, unsigned char *string)
278 struct uri *protocol;
280 trim_chars(string, ' ', NULL);
281 protocol = get_uri(string, URI_PROTOCOL);
283 if (protocol) { /* full uri with protocol */
284 done_uri(protocol);
285 mem_free_set(action, string);
286 } else {
287 if (dir_sep(string[0])) { /* absolute uri, TODO: disk drive under WIN32 */
288 struct uri *uri = get_uri(*action, URI_HTTP_REFERRER_HOST);
290 if (uri->protocol == PROTOCOL_FILE) {
291 mem_free_set(action, straconcat(struri(uri), string, (unsigned char *) NULL));
293 else
294 mem_free_set(action, straconcat(struri(uri), string + 1, (unsigned char *) NULL));
295 done_uri(uri);
296 mem_free(string);
297 } else { /* relative uri */
298 unsigned char *last_slash = strrchr(*action, '/');
299 unsigned char *new_action;
301 if (last_slash) *(last_slash + 1) = '\0';
302 new_action = straconcat(*action, string,
303 (unsigned char *) NULL);
304 mem_free_set(action, new_action);
305 mem_free(string);
310 /* Timer callback for @interpreter->vs->doc_view->document->timeout.
311 * As explained in @install_timer, this function must erase the
312 * expired timer ID from all variables. */
313 static void
314 ecmascript_timeout_handler(void *i)
316 struct ecmascript_interpreter *interpreter = i;
318 assertm(interpreter->vs->doc_view != NULL,
319 "setTimeout: vs with no document (e_f %d)",
320 interpreter->vs->ecmascript_fragile);
321 interpreter->vs->doc_view->document->timeout = TIMER_ID_UNDEF;
322 /* The expired timer ID has now been erased. */
324 ecmascript_eval(interpreter, &interpreter->code, NULL);
327 void
328 ecmascript_set_timeout(struct ecmascript_interpreter *interpreter, unsigned char *code, int timeout)
330 assert(interpreter && interpreter->vs->doc_view->document);
331 if (!code) return;
332 done_string(&interpreter->code);
333 init_string(&interpreter->code);
334 add_to_string(&interpreter->code, code);
335 mem_free(code);
336 kill_timer(&interpreter->vs->doc_view->document->timeout);
337 install_timer(&interpreter->vs->doc_view->document->timeout, timeout, ecmascript_timeout_handler, interpreter);
340 static struct module *ecmascript_modules[] = {
341 #ifdef CONFIG_ECMASCRIPT_SEE
342 &see_module,
343 #elif defined(CONFIG_ECMASCRIPT_SMJS)
344 &spidermonkey_module,
345 #endif
346 NULL,
350 struct module ecmascript_module = struct_module(
351 /* name: */ N_("ECMAScript"),
352 /* options: */ ecmascript_options,
353 /* events: */ NULL,
354 /* submodules: */ ecmascript_modules,
355 /* data: */ NULL,
356 /* init: */ NULL,
357 /* done: */ NULL