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 struct 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\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.")),
69 #define NUMBER_OF_URLS_TO_REMEMBER 8
73 } u
[NUMBER_OF_URLS_TO_REMEMBER
];
74 static int url_index
= 0;
77 ecmascript_check_url(unsigned char *url
, unsigned char *frame
)
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
++) {
88 if (!strcmp(u
[i
].url
, url
) && !strcmp(u
[i
].frame
, frame
)) {
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
;
99 if (url_index
>= NUMBER_OF_URLS_TO_REMEMBER
) url_index
= 0;
104 ecmascript_free_urls(struct module
*module
)
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
;
123 interpreter
= mem_calloc(1, sizeof(*interpreter
));
127 interpreter
->vs
= vs
;
128 init_list(interpreter
->onload_snippets
);
129 #ifdef CONFIG_ECMASCRIPT_SEE
130 see_get_interpreter(interpreter
);
132 spidermonkey_get_interpreter(interpreter
);
134 init_string(&interpreter
->code
);
139 ecmascript_put_interpreter(struct ecmascript_interpreter
*interpreter
)
142 #ifdef CONFIG_ECMASCRIPT_SEE
143 see_put_interpreter(interpreter
);
145 spidermonkey_put_interpreter(interpreter
);
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
);
157 ecmascript_eval(struct ecmascript_interpreter
*interpreter
,
158 struct string
*code
, struct string
*ret
)
160 if (!get_ecmascript_enable())
163 #ifdef CONFIG_ECMASCRIPT_SEE
164 see_eval(interpreter
, code
, ret
);
166 spidermonkey_eval(interpreter
, code
, ret
);
171 ecmascript_eval_stringback(struct ecmascript_interpreter
*interpreter
,
174 if (!get_ecmascript_enable())
177 #ifdef CONFIG_ECMASCRIPT_SEE
178 return see_eval_stringback(interpreter
, code
);
180 return spidermonkey_eval_stringback(interpreter
, code
);
185 ecmascript_eval_boolback(struct ecmascript_interpreter
*interpreter
,
188 if (!get_ecmascript_enable())
191 #ifdef CONFIG_ECMASCRIPT_SEE
192 return see_eval_boolback(interpreter
, code
);
194 return spidermonkey_eval_boolback(interpreter
, code
);
200 ecmascript_reset_state(struct view_state
*vs
)
202 struct form_view
*fv
;
205 vs
->ecmascript_fragile
= 0;
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
);
216 vs
->ecmascript_fragile
= 1;
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? */
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
)
235 redirect_url
= ecmascript_eval_stringback(doc_view
->vs
->ecmascript
,
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
)
246 redirect_uri
= get_uri(redirect_abs_url
, 0);
247 mem_free(redirect_abs_url
);
251 /* XXX: Is that safe to do at this point? --pasky */
252 goto_uri_frame(ses
, redirect_uri
, doc_view
->name
,
254 done_uri(redirect_uri
);
259 ecmascript_timeout_dialog(struct terminal
*term
, int max_exec_time
)
261 info_box(term
, MSGBOX_FREE_TEXT
,
262 N_("JavaScript Emergency"), ALIGN_LEFT
,
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."),
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 */
282 mem_free_set(action
, string
);
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
));
291 mem_free_set(action
, straconcat(struri(uri
), string
+ 1, NULL
));
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
);
307 ecmascript_timeout_handler(void *i
)
309 struct ecmascript_interpreter
*interpreter
= i
;
311 assertm(interpreter
->vs
->doc_view
, "setTimeout: vs with no document (e_f %d)", interpreter
->vs
->doc_view
);
312 interpreter
->vs
->doc_view
->document
->timeout
= TIMER_ID_UNDEF
;
314 ecmascript_eval(interpreter
, &interpreter
->code
, NULL
);
318 ecmascript_set_timeout(struct ecmascript_interpreter
*interpreter
, unsigned char *code
, int timeout
)
320 assert(interpreter
&& interpreter
->vs
->doc_view
->document
);
322 done_string(&interpreter
->code
);
323 init_string(&interpreter
->code
);
324 add_to_string(&interpreter
->code
, 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
332 #elif defined(CONFIG_ECMASCRIPT_SMJS)
333 &spidermonkey_module
,
339 struct module ecmascript_module
= struct_module(
340 /* name: */ N_("ECMAScript"),
341 /* options: */ ecmascript_options
,
343 /* submodules: */ ecmascript_modules
,
346 /* done: */ ecmascript_free_urls