1 /* Base ECMAScript file. Mostly a proxy for specific library backends. */
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 union option_info ecmascript_options
[] = {
41 INIT_OPT_TREE("", N_("ECMAScript"),
43 N_("ECMAScript options.")),
45 INIT_OPT_BOOL("ecmascript", N_("Enable"),
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 "
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.")),
69 struct ecmascript_interpreter
*
70 ecmascript_get_interpreter(struct view_state
*vs
)
72 struct ecmascript_interpreter
*interpreter
;
76 interpreter
= mem_calloc(1, sizeof(*interpreter
));
81 interpreter
->vs
->ecmascript_fragile
= 0;
82 init_list(interpreter
->onload_snippets
);
83 /* The following backend call reads interpreter->vs. */
85 #ifdef CONFIG_ECMASCRIPT_SEE
86 !see_get_interpreter(interpreter
)
88 !spidermonkey_get_interpreter(interpreter
)
91 /* Undo what was done above. */
92 interpreter
->vs
->ecmascript_fragile
= 1;
93 mem_free(interpreter
);
97 init_string(&interpreter
->code
);
102 ecmascript_put_interpreter(struct ecmascript_interpreter
*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 #ifdef CONFIG_ECMASCRIPT_SEE
111 see_put_interpreter(interpreter
);
113 spidermonkey_put_interpreter(interpreter
);
115 free_string_list(&interpreter
->onload_snippets
);
116 done_string(&interpreter
->code
);
117 /* Is it superfluous? */
118 if (interpreter
->vs
->doc_view
)
119 kill_timer(&interpreter
->vs
->doc_view
->document
->timeout
);
120 interpreter
->vs
->ecmascript
= NULL
;
121 interpreter
->vs
->ecmascript_fragile
= 1;
122 mem_free(interpreter
);
126 ecmascript_eval(struct ecmascript_interpreter
*interpreter
,
127 struct string
*code
, struct string
*ret
)
129 if (!get_ecmascript_enable())
132 interpreter
->backend_nesting
++;
133 #ifdef CONFIG_ECMASCRIPT_SEE
134 see_eval(interpreter
, code
, ret
);
136 spidermonkey_eval(interpreter
, code
, ret
);
138 interpreter
->backend_nesting
--;
142 ecmascript_eval_stringback(struct ecmascript_interpreter
*interpreter
,
145 unsigned char *result
;
147 if (!get_ecmascript_enable())
150 interpreter
->backend_nesting
++;
151 #ifdef CONFIG_ECMASCRIPT_SEE
152 result
= see_eval_stringback(interpreter
, code
);
154 result
= spidermonkey_eval_stringback(interpreter
, code
);
156 interpreter
->backend_nesting
--;
161 ecmascript_eval_boolback(struct ecmascript_interpreter
*interpreter
,
166 if (!get_ecmascript_enable())
169 interpreter
->backend_nesting
++;
170 #ifdef CONFIG_ECMASCRIPT_SEE
171 result
= see_eval_boolback(interpreter
, code
);
173 result
= spidermonkey_eval_boolback(interpreter
, code
);
175 interpreter
->backend_nesting
--;
180 ecmascript_detach_form_view(struct form_view
*fv
)
182 #ifdef CONFIG_ECMASCRIPT_SEE
183 see_detach_form_view(fv
);
185 spidermonkey_detach_form_view(fv
);
189 void ecmascript_detach_form_state(struct form_state
*fs
)
191 #ifdef CONFIG_ECMASCRIPT_SEE
192 see_detach_form_state(fs
);
194 spidermonkey_detach_form_state(fs
);
198 void ecmascript_moved_form_state(struct form_state
*fs
)
200 #ifdef CONFIG_ECMASCRIPT_SEE
201 see_moved_form_state(fs
);
203 spidermonkey_moved_form_state(fs
);
208 ecmascript_reset_state(struct view_state
*vs
)
210 struct form_view
*fv
;
213 /* Normally, if vs->ecmascript == NULL, the associated
214 * ecmascript_obj pointers are also NULL. However, they might
215 * be non-NULL if the ECMAScript objects have been lazily
216 * created because of scripts running in sibling HTML frames. */
217 foreach (fv
, vs
->forms
)
218 ecmascript_detach_form_view(fv
);
219 for (i
= 0; i
< vs
->form_info_len
; i
++)
220 ecmascript_detach_form_state(&vs
->form_info
[i
]);
222 vs
->ecmascript_fragile
= 0;
224 ecmascript_put_interpreter(vs
->ecmascript
);
226 vs
->ecmascript
= ecmascript_get_interpreter(vs
);
228 vs
->ecmascript_fragile
= 1;
232 ecmascript_protocol_handler(struct session
*ses
, struct uri
*uri
)
234 struct document_view
*doc_view
= current_frame(ses
);
235 struct string current_url
= INIT_STRING(struri(uri
), strlen(struri(uri
)));
236 unsigned char *redirect_url
, *redirect_abs_url
;
237 struct uri
*redirect_uri
;
239 if (!doc_view
) /* Blank initial document. TODO: Start at about:blank? */
241 assert(doc_view
->vs
);
242 if (doc_view
->vs
->ecmascript_fragile
)
243 ecmascript_reset_state(doc_view
->vs
);
244 if (!doc_view
->vs
->ecmascript
)
247 redirect_url
= ecmascript_eval_stringback(doc_view
->vs
->ecmascript
,
251 /* XXX: This code snippet is duplicated over here,
252 * location_set_property(), html_a() and who knows where else. */
253 redirect_abs_url
= join_urls(doc_view
->document
->uri
,
254 trim_chars(redirect_url
, ' ', 0));
255 mem_free(redirect_url
);
256 if (!redirect_abs_url
)
258 redirect_uri
= get_uri(redirect_abs_url
, 0);
259 mem_free(redirect_abs_url
);
263 /* XXX: Is that safe to do at this point? --pasky */
264 goto_uri_frame(ses
, redirect_uri
, doc_view
->name
,
266 done_uri(redirect_uri
);
271 ecmascript_timeout_dialog(struct terminal
*term
, int max_exec_time
)
273 info_box(term
, MSGBOX_FREE_TEXT
,
274 N_("JavaScript Emergency"), ALIGN_LEFT
,
276 N_("A script embedded in the current document was running\n"
277 "for more than %d seconds. This probably means there is\n"
278 "a bug in the script and it could have halted the whole\n"
279 "ELinks, so the script execution was interrupted."),
285 ecmascript_set_action(unsigned char **action
, unsigned char *string
)
287 struct uri
*protocol
;
289 trim_chars(string
, ' ', NULL
);
290 protocol
= get_uri(string
, URI_PROTOCOL
);
292 if (protocol
) { /* full uri with protocol */
294 mem_free_set(action
, string
);
296 if (dir_sep(string
[0])) { /* absolute uri, TODO: disk drive under WIN32 */
297 struct uri
*uri
= get_uri(*action
, URI_HTTP_REFERRER_HOST
);
299 if (uri
->protocol
== PROTOCOL_FILE
) {
300 mem_free_set(action
, straconcat(struri(uri
), string
, (unsigned char *) NULL
));
303 mem_free_set(action
, straconcat(struri(uri
), string
+ 1, (unsigned char *) NULL
));
306 } else { /* relative uri */
307 unsigned char *last_slash
= strrchr(*action
, '/');
308 unsigned char *new_action
;
310 if (last_slash
) *(last_slash
+ 1) = '\0';
311 new_action
= straconcat(*action
, string
,
312 (unsigned char *) NULL
);
313 mem_free_set(action
, new_action
);
319 /* Timer callback for @interpreter->vs->doc_view->document->timeout.
320 * As explained in @install_timer, this function must erase the
321 * expired timer ID from all variables. */
323 ecmascript_timeout_handler(void *i
)
325 struct ecmascript_interpreter
*interpreter
= i
;
327 assertm(interpreter
->vs
->doc_view
!= NULL
,
328 "setTimeout: vs with no document (e_f %d)",
329 interpreter
->vs
->ecmascript_fragile
);
330 interpreter
->vs
->doc_view
->document
->timeout
= TIMER_ID_UNDEF
;
331 /* The expired timer ID has now been erased. */
333 ecmascript_eval(interpreter
, &interpreter
->code
, NULL
);
337 ecmascript_set_timeout(struct ecmascript_interpreter
*interpreter
, unsigned char *code
, int timeout
)
339 assert(interpreter
&& interpreter
->vs
->doc_view
->document
);
341 done_string(&interpreter
->code
);
342 init_string(&interpreter
->code
);
343 add_to_string(&interpreter
->code
, code
);
345 kill_timer(&interpreter
->vs
->doc_view
->document
->timeout
);
346 install_timer(&interpreter
->vs
->doc_view
->document
->timeout
, timeout
, ecmascript_timeout_handler
, interpreter
);
349 static struct module
*ecmascript_modules
[] = {
350 #ifdef CONFIG_ECMASCRIPT_SEE
352 #elif defined(CONFIG_ECMASCRIPT_SMJS)
353 &spidermonkey_module
,
359 struct module ecmascript_module
= struct_module(
360 /* name: */ N_("ECMAScript"),
361 /* options: */ ecmascript_options
,
363 /* submodules: */ ecmascript_modules
,