Check if hb is NULL.
[elinks.git] / src / ecmascript / ecmascript.c
blob3df8e981e3bf6b8c7b69b95ddfd824c985696142
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/spidermonkey.h"
16 #include "intl/gettext/libintl.h"
17 #include "main/module.h"
18 #include "main/timer.h"
19 #include "osdep/osdep.h"
20 #include "protocol/protocol.h"
21 #include "protocol/uri.h"
22 #include "session/session.h"
23 #include "session/task.h"
24 #include "terminal/terminal.h"
25 #include "terminal/window.h"
26 #include "util/conv.h"
27 #include "util/string.h"
28 #include "viewer/text/view.h" /* current_frame() */
29 #include "viewer/text/form.h" /* <-ecmascript_reset_state() */
30 #include "viewer/text/vs.h"
33 /* TODO: We should have some kind of ACL for the scripts - i.e. ability to
34 * disallow the scripts to open new windows (or so that the windows are always
35 * directed to tabs, this particular option would be a tristate), disallow
36 * messing with your menubar/statusbar visibility, disallow changing the
37 * statusbar content etc. --pasky */
39 static struct option_info ecmascript_options[] = {
40 INIT_OPT_TREE("", N_("ECMAScript"),
41 "ecmascript", 0,
42 N_("ECMAScript options.")),
44 INIT_OPT_BOOL("ecmascript", N_("Enable"),
45 "enable", 0, 0,
46 N_("Whether to run those scripts inside of documents.")),
48 INIT_OPT_BOOL("ecmascript", N_("Script error reporting"),
49 "error_reporting", 0, 0,
50 N_("Open a message box when a script reports an error.")),
52 INIT_OPT_BOOL("ecmascript", N_("Ignore <noscript> content"),
53 "ignore_noscript", 0, 0,
54 N_("Whether to ignore content enclosed by the <noscript> tag "
55 "when ECMAScript is enabled.")),
57 INIT_OPT_INT("ecmascript", N_("Maximum execution time"),
58 "max_exec_time", 0, 1, 3600, 5,
59 N_("Maximum execution time in seconds for a script.")),
61 INIT_OPT_BOOL("ecmascript", N_("Pop-up window blocking"),
62 "block_window_opening", 0, 0,
63 N_("Whether to disallow scripts to open new windows or tabs.")),
65 NULL_OPTION_INFO,
68 static int interpreter_count;
70 struct ecmascript_interpreter *
71 ecmascript_get_interpreter(struct view_state *vs)
73 struct ecmascript_interpreter *interpreter;
75 assert(vs);
77 interpreter = mem_calloc(1, sizeof(*interpreter));
78 if (!interpreter)
79 return NULL;
81 ++interpreter_count;
83 interpreter->vs = vs;
84 interpreter->vs->ecmascript_fragile = 0;
85 init_list(interpreter->onload_snippets);
86 /* The following backend call reads interpreter->vs. */
87 if (
88 !spidermonkey_get_interpreter(interpreter)
89 ) {
90 /* Undo what was done above. */
91 interpreter->vs->ecmascript_fragile = 1;
92 mem_free(interpreter);
93 --interpreter_count;
94 return NULL;
97 init_string(&interpreter->code);
98 return interpreter;
101 void
102 ecmascript_put_interpreter(struct ecmascript_interpreter *interpreter)
104 assert(interpreter);
105 assert(interpreter->backend_nesting == 0);
106 /* If the assertion fails, it is better to leak the
107 * interpreter than to corrupt memory. */
108 if_assert_failed return;
110 spidermonkey_put_interpreter(interpreter);
111 free_string_list(&interpreter->onload_snippets);
112 done_string(&interpreter->code);
113 /* Is it superfluous? */
114 if (interpreter->vs->doc_view)
115 kill_timer(&interpreter->vs->doc_view->document->timeout);
116 interpreter->vs->ecmascript = NULL;
117 interpreter->vs->ecmascript_fragile = 1;
118 mem_free(interpreter);
119 --interpreter_count;
123 ecmascript_get_interpreter_count(void)
125 return interpreter_count;
128 void
129 ecmascript_eval(struct ecmascript_interpreter *interpreter,
130 struct string *code, struct string *ret)
132 if (!get_ecmascript_enable())
133 return;
134 assert(interpreter);
135 interpreter->backend_nesting++;
136 spidermonkey_eval(interpreter, code, ret);
137 interpreter->backend_nesting--;
140 unsigned char *
141 ecmascript_eval_stringback(struct ecmascript_interpreter *interpreter,
142 struct string *code)
144 unsigned char *result;
146 if (!get_ecmascript_enable())
147 return NULL;
148 assert(interpreter);
149 interpreter->backend_nesting++;
150 result = spidermonkey_eval_stringback(interpreter, code);
151 interpreter->backend_nesting--;
152 return result;
156 ecmascript_eval_boolback(struct ecmascript_interpreter *interpreter,
157 struct string *code)
159 int result;
161 if (!get_ecmascript_enable())
162 return -1;
163 assert(interpreter);
164 interpreter->backend_nesting++;
165 result = spidermonkey_eval_boolback(interpreter, code);
166 interpreter->backend_nesting--;
167 return result;
170 void
171 ecmascript_detach_form_view(struct form_view *fv)
173 spidermonkey_detach_form_view(fv);
176 void ecmascript_detach_form_state(struct form_state *fs)
178 spidermonkey_detach_form_state(fs);
181 void ecmascript_moved_form_state(struct form_state *fs)
183 spidermonkey_moved_form_state(fs);
186 void
187 ecmascript_reset_state(struct view_state *vs)
189 struct form_view *fv;
190 int i;
192 /* Normally, if vs->ecmascript == NULL, the associated
193 * ecmascript_obj pointers are also NULL. However, they might
194 * be non-NULL if the ECMAScript objects have been lazily
195 * created because of scripts running in sibling HTML frames. */
196 foreach (fv, vs->forms)
197 ecmascript_detach_form_view(fv);
198 for (i = 0; i < vs->form_info_len; i++)
199 ecmascript_detach_form_state(&vs->form_info[i]);
201 vs->ecmascript_fragile = 0;
202 if (vs->ecmascript)
203 ecmascript_put_interpreter(vs->ecmascript);
205 vs->ecmascript = ecmascript_get_interpreter(vs);
206 if (!vs->ecmascript)
207 vs->ecmascript_fragile = 1;
210 void
211 ecmascript_protocol_handler(struct session *ses, struct uri *uri)
213 struct document_view *doc_view = current_frame(ses);
214 struct string current_url = INIT_STRING(struri(uri), strlen(struri(uri)));
215 unsigned char *redirect_url, *redirect_abs_url;
216 struct uri *redirect_uri;
218 if (!doc_view) /* Blank initial document. TODO: Start at about:blank? */
219 return;
220 assert(doc_view->vs);
221 if (doc_view->vs->ecmascript_fragile)
222 ecmascript_reset_state(doc_view->vs);
223 if (!doc_view->vs->ecmascript)
224 return;
226 redirect_url = ecmascript_eval_stringback(doc_view->vs->ecmascript,
227 &current_url);
228 if (!redirect_url)
229 return;
230 /* XXX: This code snippet is duplicated over here,
231 * location_set_property(), html_a() and who knows where else. */
232 redirect_abs_url = join_urls(doc_view->document->uri,
233 trim_chars(redirect_url, ' ', 0));
234 mem_free(redirect_url);
235 if (!redirect_abs_url)
236 return;
237 redirect_uri = get_uri(redirect_abs_url, 0);
238 mem_free(redirect_abs_url);
239 if (!redirect_uri)
240 return;
242 /* XXX: Is that safe to do at this point? --pasky */
243 goto_uri_frame(ses, redirect_uri, doc_view->name,
244 CACHE_MODE_NORMAL);
245 done_uri(redirect_uri);
249 void
250 ecmascript_timeout_dialog(struct terminal *term, int max_exec_time)
252 info_box(term, MSGBOX_FREE_TEXT,
253 N_("JavaScript Emergency"), ALIGN_LEFT,
254 msg_text(term,
255 N_("A script embedded in the current document was running\n"
256 "for more than %d seconds. This probably means there is\n"
257 "a bug in the script and it could have halted the whole\n"
258 "ELinks, so the script execution was interrupted."),
259 max_exec_time));
263 void
264 ecmascript_set_action(unsigned char **action, unsigned char *string)
266 struct uri *protocol;
268 trim_chars(string, ' ', NULL);
269 protocol = get_uri(string, URI_PROTOCOL);
271 if (protocol) { /* full uri with protocol */
272 done_uri(protocol);
273 mem_free_set(action, string);
274 } else {
275 if (dir_sep(string[0])) { /* absolute uri, TODO: disk drive under WIN32 */
276 struct uri *uri = get_uri(*action, URI_HTTP_REFERRER_HOST);
278 if (uri->protocol == PROTOCOL_FILE) {
279 mem_free_set(action, straconcat(struri(uri), string, (unsigned char *) NULL));
281 else
282 mem_free_set(action, straconcat(struri(uri), string + 1, (unsigned char *) NULL));
283 done_uri(uri);
284 mem_free(string);
285 } else { /* relative uri */
286 unsigned char *last_slash = strrchr(*action, '/');
287 unsigned char *new_action;
289 if (last_slash) *(last_slash + 1) = '\0';
290 new_action = straconcat(*action, string,
291 (unsigned char *) NULL);
292 mem_free_set(action, new_action);
293 mem_free(string);
298 /* Timer callback for @interpreter->vs->doc_view->document->timeout.
299 * As explained in @install_timer, this function must erase the
300 * expired timer ID from all variables. */
301 static void
302 ecmascript_timeout_handler(void *i)
304 struct ecmascript_interpreter *interpreter = i;
306 assertm(interpreter->vs->doc_view != NULL,
307 "setTimeout: vs with no document (e_f %d)",
308 interpreter->vs->ecmascript_fragile);
309 interpreter->vs->doc_view->document->timeout = TIMER_ID_UNDEF;
310 /* The expired timer ID has now been erased. */
312 ecmascript_eval(interpreter, &interpreter->code, NULL);
315 void
316 ecmascript_set_timeout(struct ecmascript_interpreter *interpreter, unsigned char *code, int timeout)
318 assert(interpreter && interpreter->vs->doc_view->document);
319 if (!code) return;
320 done_string(&interpreter->code);
321 init_string(&interpreter->code);
322 add_to_string(&interpreter->code, code);
323 mem_free(code);
324 kill_timer(&interpreter->vs->doc_view->document->timeout);
325 install_timer(&interpreter->vs->doc_view->document->timeout, timeout, ecmascript_timeout_handler, interpreter);
328 static struct module *ecmascript_modules[] = {
329 #ifdef CONFIG_ECMASCRIPT_SMJS
330 &spidermonkey_module,
331 #endif
332 NULL,
336 struct module ecmascript_module = struct_module(
337 /* name: */ N_("ECMAScript"),
338 /* options: */ ecmascript_options,
339 /* events: */ NULL,
340 /* submodules: */ ecmascript_modules,
341 /* data: */ NULL,
342 /* init: */ NULL,
343 /* done: */ NULL