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 "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"),
42 N_("ECMAScript options.")),
44 INIT_OPT_BOOL("ecmascript", N_("Enable"),
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\n"
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.")),
68 #define NUMBER_OF_URLS_TO_REMEMBER 8
72 } u
[NUMBER_OF_URLS_TO_REMEMBER
];
73 static int url_index
= 0;
76 ecmascript_check_url(unsigned char *url
, unsigned char *frame
)
79 /* Because of gradual rendering window.open is called many
80 * times with the same arguments.
81 * This workaround remembers NUMBER_OF_URLS_TO_REMEMBER last
82 * opened URLs and do not let open them again.
85 for (i
= 0; i
< NUMBER_OF_URLS_TO_REMEMBER
; i
++) {
87 if (!strcmp(u
[i
].url
, url
) && !strcmp(u
[i
].frame
, frame
)) {
93 mem_free_if(u
[url_index
].url
);
94 mem_free_if(u
[url_index
].frame
);
95 u
[url_index
].url
= url
;
96 u
[url_index
].frame
= frame
;
98 if (url_index
>= NUMBER_OF_URLS_TO_REMEMBER
) url_index
= 0;
103 ecmascript_free_urls(struct module
*module
)
107 for (i
= 0; i
< NUMBER_OF_URLS_TO_REMEMBER
; i
++) {
108 mem_free_if(u
[i
].url
);
109 mem_free_if(u
[i
].frame
);
113 #undef NUMBER_OF_URLS_TO_REMEMBER
115 struct ecmascript_interpreter
*
116 ecmascript_get_interpreter(struct view_state
*vs
)
118 struct ecmascript_interpreter
*interpreter
;
122 interpreter
= mem_calloc(1, sizeof(*interpreter
));
126 interpreter
->vs
= vs
;
127 init_list(interpreter
->onload_snippets
);
128 #ifdef CONFIG_ECMASCRIPT_SEE
129 see_get_interpreter(interpreter
);
131 spidermonkey_get_interpreter(interpreter
);
137 ecmascript_put_interpreter(struct ecmascript_interpreter
*interpreter
)
140 #ifdef CONFIG_ECMASCRIPT_SEE
141 see_put_interpreter(interpreter
);
143 spidermonkey_put_interpreter(interpreter
);
145 free_string_list(&interpreter
->onload_snippets
);
146 interpreter
->vs
->ecmascript
= NULL
;
147 mem_free(interpreter
);
151 ecmascript_eval(struct ecmascript_interpreter
*interpreter
,
152 struct string
*code
, struct string
*ret
)
154 if (!get_ecmascript_enable())
157 #ifdef CONFIG_ECMASCRIPT_SEE
158 see_eval(interpreter
, code
, ret
);
160 spidermonkey_eval(interpreter
, code
, ret
);
165 ecmascript_eval_stringback(struct ecmascript_interpreter
*interpreter
,
168 if (!get_ecmascript_enable())
171 #ifdef CONFIG_ECMASCRIPT_SEE
172 return see_eval_stringback(interpreter
, code
);
174 return spidermonkey_eval_stringback(interpreter
, code
);
179 ecmascript_eval_boolback(struct ecmascript_interpreter
*interpreter
,
182 if (!get_ecmascript_enable())
185 #ifdef CONFIG_ECMASCRIPT_SEE
186 return see_eval_boolback(interpreter
, code
);
188 return spidermonkey_eval_boolback(interpreter
, code
);
194 ecmascript_reset_state(struct view_state
*vs
)
196 struct form_view
*fv
;
199 vs
->ecmascript_fragile
= 0;
201 ecmascript_put_interpreter(vs
->ecmascript
);
203 foreach (fv
, vs
->forms
)
204 fv
->ecmascript_obj
= NULL
;
205 for (i
= 0; i
< vs
->form_info_len
; i
++)
206 vs
->form_info
[i
].ecmascript_obj
= NULL
;
208 vs
->ecmascript
= ecmascript_get_interpreter(vs
);
210 vs
->ecmascript_fragile
= 1;
214 ecmascript_protocol_handler(struct session
*ses
, struct uri
*uri
)
216 struct document_view
*doc_view
= current_frame(ses
);
217 struct string current_url
= INIT_STRING(struri(uri
), strlen(struri(uri
)));
218 unsigned char *redirect_url
, *redirect_abs_url
;
219 struct uri
*redirect_uri
;
221 if (!doc_view
) /* Blank initial document. TODO: Start at about:blank? */
223 assert(doc_view
->vs
);
224 if (doc_view
->vs
->ecmascript_fragile
)
225 ecmascript_reset_state(doc_view
->vs
);
226 if (!doc_view
->vs
->ecmascript
)
229 redirect_url
= ecmascript_eval_stringback(doc_view
->vs
->ecmascript
,
233 /* XXX: This code snippet is duplicated over here,
234 * location_set_property(), html_a() and who knows where else. */
235 redirect_abs_url
= join_urls(doc_view
->document
->uri
,
236 trim_chars(redirect_url
, ' ', 0));
237 mem_free(redirect_url
);
238 if (!redirect_abs_url
)
240 redirect_uri
= get_uri(redirect_abs_url
, 0);
241 mem_free(redirect_abs_url
);
245 /* XXX: Is that safe to do at this point? --pasky */
246 goto_uri_frame(ses
, redirect_uri
, doc_view
->name
,
248 done_uri(redirect_uri
);
253 ecmascript_timeout_dialog(struct terminal
*term
, int max_exec_time
)
255 info_box(term
, MSGBOX_FREE_TEXT
,
256 N_("JavaScript Emergency"), ALIGN_LEFT
,
258 N_("A script embedded in the current document was running\n"
259 "for more than %d seconds. This probably means there is\n"
260 "a bug in the script and it could have halted the whole\n"
261 "ELinks, so the script execution was interrupted."),
267 ecmascript_set_action(unsigned char **action
, unsigned char *string
)
269 struct uri
*protocol
;
271 trim_chars(string
, ' ', NULL
);
272 protocol
= get_uri(string
, URI_PROTOCOL
);
274 if (protocol
) { /* full uri with protocol */
276 mem_free_set(action
, string
);
278 if (dir_sep(string
[0])) { /* absolute uri, TODO: disk drive under WIN32 */
279 struct uri
*uri
= get_uri(*action
, URI_HTTP_REFERRER_HOST
);
281 if (uri
->protocol
== PROTOCOL_FILE
) {
282 mem_free_set(action
, straconcat(struri(uri
), string
, NULL
));
285 mem_free_set(action
, straconcat(struri(uri
), string
+ 1, NULL
));
288 } else { /* relative uri */
289 unsigned char *last_slash
= strrchr(*action
, '/');
290 unsigned char *new_action
;
292 if (last_slash
) *(last_slash
+ 1) = '\0';
293 new_action
= straconcat(*action
, string
, NULL
);
294 mem_free_set(action
, new_action
);
300 static struct module
*ecmascript_modules
[] = {
301 #ifdef CONFIG_ECMASCRIPT_SEE
303 #elif defined(CONFIG_ECMASCRIPT_SMJS)
304 &spidermonkey_module
,
310 struct module ecmascript_module
= struct_module(
311 /* name: */ N_("ECMAScript"),
312 /* options: */ ecmascript_options
,
314 /* submodules: */ ecmascript_modules
,
317 /* done: */ ecmascript_free_urls