HTML bug 1114: Don't doubly decode entities in attributes
[elinks.git] / src / ecmascript / see / window.c
blobf3c34da2794affe3ddc326a1f523df54761bf8ea
1 /* The SEE window object implementation. */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <time.h>
12 #include "elinks.h"
14 #include <see/see.h>
16 #include "bfu/dialog.h"
17 #include "cache/cache.h"
18 #include "cookies/cookies.h"
19 #include "dialogs/menu.h"
20 #include "dialogs/status.h"
21 #include "document/html/frames.h"
22 #include "document/document.h"
23 #include "document/forms.h"
24 #include "document/view.h"
25 #include "ecmascript/ecmascript.h"
26 #include "ecmascript/see/checktype.h"
27 #include "ecmascript/see/input.h"
28 #include "ecmascript/see/strings.h"
29 #include "ecmascript/see/window.h"
30 #include "intl/gettext/libintl.h"
31 #include "main/select.h"
32 #include "osdep/newwin.h"
33 #include "osdep/sysname.h"
34 #include "protocol/http/http.h"
35 #include "protocol/uri.h"
36 #include "session/history.h"
37 #include "session/location.h"
38 #include "session/session.h"
39 #include "session/task.h"
40 #include "terminal/tab.h"
41 #include "terminal/terminal.h"
42 #include "util/conv.h"
43 #include "util/memory.h"
44 #include "util/string.h"
45 #include "viewer/text/draw.h"
46 #include "viewer/text/form.h"
47 #include "viewer/text/link.h"
48 #include "viewer/text/vs.h"
50 static struct js_window_object *js_get_global_object(void *);
51 static struct js_window_object *js_try_resolve_frame(struct document_view *, unsigned char *);
52 static void window_get(struct SEE_interpreter *, struct SEE_object *, struct SEE_string *, struct SEE_value *);
53 static void window_put(struct SEE_interpreter *, struct SEE_object *, struct SEE_string *, struct SEE_value *, int);
54 static int window_canput(struct SEE_interpreter *, struct SEE_object *, struct SEE_string *);
55 static int window_hasproperty(struct SEE_interpreter *, struct SEE_object *, struct SEE_string *);
56 static void js_window_alert(struct SEE_interpreter *, struct SEE_object *, struct SEE_object *, int, struct SEE_value **, struct SEE_value *);
57 static void js_window_open(struct SEE_interpreter *, struct SEE_object *, struct SEE_object *, int, struct SEE_value **, struct SEE_value *);
58 static void js_setTimeout(struct SEE_interpreter *, struct SEE_object *, struct SEE_object *, int, struct SEE_value **, struct SEE_value *);
60 struct SEE_objectclass js_window_object_class = {
61 "window",
62 window_get,
63 window_put,
64 window_canput,
65 window_hasproperty,
66 SEE_no_delete,
67 SEE_no_defaultvalue,
68 SEE_no_enumerator,
69 NULL,
70 NULL,
71 NULL
74 static struct js_window_object *
75 js_get_global_object(void *data)
77 struct global_object *g = (struct global_object *)data;
78 return g->win;
81 static struct js_window_object *
82 js_try_resolve_frame(struct document_view *doc_view, unsigned char *id)
84 struct session *ses = doc_view->session;
85 struct frame *target;
87 assert(ses);
88 target = ses_find_frame(ses, id);
89 if (!target) return NULL;
90 if (target->vs.ecmascript_fragile)
91 ecmascript_reset_state(&target->vs);
92 if (!target->vs.ecmascript) return NULL;
93 return js_get_global_object(target->vs.ecmascript->backend_data);
96 static void
97 window_get(struct SEE_interpreter *interp, struct SEE_object *o,
98 struct SEE_string *p, struct SEE_value *res)
100 struct js_window_object *win = (struct js_window_object *)o;
101 struct view_state *vs = win->vs;
103 if (p == s_closed) {
104 SEE_SET_BOOLEAN(res, 0);
105 } else if (p == s_self || p == s_parent || p == s_top || p == s_status) {
106 SEE_SET_OBJECT(res, o);
107 #if 0
108 } else if (p == s_parent || p == s_top) {
109 struct document_view *doc_view = vs->doc_view;
110 struct document_view *top_view = doc_view->session->doc_view;
111 struct js_window_object *newjsframe;
113 assert(top_view && top_view->vs);
114 if (top_view->vs->ecmascript_fragile)
115 ecmascript_reset_state(top_view->vs);
116 if (!top_view->vs->ecmascript) {
117 SEE_SET_UNDEFINED(res);
118 return;
120 newjsframe = js_get_global_object(
121 top_view->vs->ecmascript->backend_data);
123 /* Keep this unrolled this way. Will have to check document.domain
124 * JS property. */
125 /* Note that this check is perhaps overparanoid. If top windows
126 * is alien but some other child window is not, we should still
127 * let the script walk thru. That'd mean moving the check to
128 * other individual properties in this switch. */
129 if (compare_uri(vs->uri, top_view->vs->uri, URI_HOST)) {
130 SEE_SET_OBJECT(res, (struct SEE_object *)newjsframe);
132 #endif
133 } else if (p == s_alert) {
134 SEE_SET_OBJECT(res, win->alert);
135 } else if (p == s_open) {
136 SEE_SET_OBJECT(res, win->open);
137 } else if (p == s_setTimeout) {
138 SEE_SET_OBJECT(res, win->setTimeout);
139 } else if (p == s_location) {
140 SEE_OBJECT_GET(interp, interp->Global, s_location, res);
141 } else if (p == s_navigator) {
142 SEE_OBJECT_GET(interp, interp->Global, s_navigator, res);
143 } else {
144 unsigned char *frame = see_string_to_unsigned_char(p);
145 struct document_view *doc_view = vs->doc_view;
146 struct js_window_object *obj;
148 if (frame && (obj = js_try_resolve_frame(doc_view, frame))) {
149 SEE_SET_OBJECT(res, (struct SEE_object *)obj);
150 } else {
151 SEE_SET_UNDEFINED(res);
153 mem_free_if(frame);
157 static void
158 window_put(struct SEE_interpreter *interp, struct SEE_object *o,
159 struct SEE_string *p, struct SEE_value *val, int attr)
161 if (p == s_location) {
162 struct js_window_object *win = (struct js_window_object *)o;
163 struct view_state *vs = win->vs;
164 struct document_view *doc_view = vs->doc_view;
165 unsigned char *str = see_value_to_unsigned_char(interp, val);
167 if (str) {
168 location_goto(doc_view, str);
169 mem_free(str);
171 } else if (p == s_status) {
172 struct global_object *g = (struct global_object *)interp;
173 struct js_window_object *win = g->win;
174 struct view_state *vs = win->vs;
175 struct document_view *doc_view = vs->doc_view;
176 struct session *ses = doc_view->session;
177 unsigned char *stat = see_value_to_unsigned_char(interp, val);
179 mem_free_set(&ses->status.window_status, stat);
180 print_screen_status(ses);
184 static int
185 window_canput(struct SEE_interpreter *interp, struct SEE_object *o,
186 struct SEE_string *p)
188 if (p == s_location || p == s_status)
189 return 1;
190 return 0;
193 static int
194 window_hasproperty(struct SEE_interpreter *interp, struct SEE_object *o,
195 struct SEE_string *p)
197 /* all unknown properties return UNDEFINED value */
198 return 1;
202 static void
203 js_window_alert(struct SEE_interpreter *interp, struct SEE_object *self,
204 struct SEE_object *thisobj, int argc, struct SEE_value **argv,
205 struct SEE_value *res)
207 struct global_object *g = (struct global_object *)interp;
208 struct js_window_object *win = g->win;
209 struct view_state *vs = win->vs;
210 unsigned char *string;
212 /* Do not check thisobj->objectclass. ELinks sets this
213 * function as a property of both the window object and the
214 * global object, so thisobj may validly refer to either. */
216 SEE_SET_BOOLEAN(res, 1);
217 if (argc < 1)
218 return;
220 string = see_value_to_unsigned_char(interp, argv[0]);
221 if (!string || !*string) {
222 mem_free_if(string);
223 return;
226 info_box(vs->doc_view->session->tab->term, MSGBOX_FREE_TEXT,
227 N_("JavaScript Alert"), ALIGN_CENTER, string);
232 static void
233 js_window_open(struct SEE_interpreter *interp, struct SEE_object *self,
234 struct SEE_object *thisobj, int argc, struct SEE_value **argv,
235 struct SEE_value *res)
237 struct global_object *g = (struct global_object *)interp;
238 struct js_window_object *win = g->win;
239 struct view_state *vs = win->vs;
240 struct document_view *doc_view = vs->doc_view;
241 struct session *ses = doc_view->session;
242 unsigned char *frame = NULL;
243 unsigned char *url, *url2;
244 struct uri *uri;
245 struct SEE_value url_value;
246 static time_t ratelimit_start;
247 static int ratelimit_count;
249 /* Do not check thisobj->objectclass. ELinks sets this
250 * function as a property of both the window object and the
251 * global object, so thisobj may validly refer to either. */
253 SEE_SET_OBJECT(res, (struct SEE_object *)win);
254 if (get_opt_bool("ecmascript.block_window_opening")) {
255 #ifdef CONFIG_LEDS
256 set_led_value(ses->status.popup_led, 'P');
257 #endif
258 return;
261 if (argc < 1) return;
263 /* Ratelimit window opening. Recursive window.open() is very nice.
264 * We permit at most 20 tabs in 2 seconds. The ratelimiter is very
265 * rough but shall suffice against the usual cases. */
267 if (!ratelimit_start || time(NULL) - ratelimit_start > 2) {
268 ratelimit_start = time(NULL);
269 ratelimit_count = 0;
270 } else {
271 ratelimit_count++;
272 if (ratelimit_count > 20)
273 return;
276 SEE_ToString(interp, argv[0], &url_value);
277 url = see_string_to_unsigned_char(url_value.u.string);
278 if (!url) return;
279 trim_chars(url, ' ', 0);
280 if (argc > 1) {
281 struct SEE_value target_value;
283 SEE_ToString(interp, argv[1], &target_value);
284 frame = see_string_to_unsigned_char(target_value.u.string);
285 if (!frame) {
286 mem_free(url);
287 return;
290 /* TODO: Support for window naming and perhaps some window features? */
292 url2 = join_urls(doc_view->document->uri, url);
293 mem_free(url);
294 if (!url2) {
295 mem_free_if(frame);
296 return;
298 uri = get_uri(url2, 0);
299 mem_free(url2);
300 if (!uri) {
301 mem_free_if(frame);
302 return;
305 if (frame && *frame && c_strcasecmp(frame, "_blank")) {
306 struct delayed_open *deo = mem_calloc(1, sizeof(*deo));
308 if (deo) {
309 deo->ses = ses;
310 deo->uri = get_uri_reference(uri);
311 deo->target = stracpy(frame);
312 /* target will be freed in delayed_goto_uri_frame */
313 register_bottom_half(delayed_goto_uri_frame, deo);
314 goto end;
318 if (!get_cmd_opt_bool("no-connect")
319 && !get_cmd_opt_bool("no-home")
320 && !get_cmd_opt_bool("anonymous")
321 && can_open_in_new(ses->tab->term)) {
322 open_uri_in_new_window(ses, uri, NULL, ENV_ANY,
323 CACHE_MODE_NORMAL, TASK_NONE);
324 } else {
325 /* When opening a new tab, we might get rerendered, losing our
326 * context and triggerring a disaster, so postpone that. */
327 struct delayed_open *deo = mem_calloc(1, sizeof(*deo));
329 if (deo) {
330 deo->ses = ses;
331 deo->uri = get_uri_reference(uri);
332 register_bottom_half(delayed_open, deo);
336 end:
337 done_uri(uri);
338 mem_free_if(frame);
341 static void
342 js_setTimeout(struct SEE_interpreter *interp, struct SEE_object *self,
343 struct SEE_object *thisobj, int argc, struct SEE_value **argv,
344 struct SEE_value *res)
346 struct ecmascript_interpreter *ei;
347 unsigned char *code;
348 int timeout;
350 /* Do not check thisobj->objectclass. ELinks sets this
351 * function as a property of both the window object and the
352 * global object, so thisobj may validly refer to either. */
354 if (argc != 2) return;
355 ei = ((struct global_object *)interp)->interpreter;
356 code = see_value_to_unsigned_char(interp, argv[0]);
357 timeout = SEE_ToInt32(interp, argv[1]);
358 ecmascript_set_timeout(ei, code, timeout);
361 void
362 init_js_window_object(struct ecmascript_interpreter *interpreter)
364 struct global_object *g = interpreter->backend_data;
365 struct SEE_interpreter *interp = &g->interp;
366 struct SEE_value v;
368 g->win = SEE_NEW(interp, struct js_window_object);
370 g->win->object.objectclass = &js_window_object_class;
371 g->win->object.Prototype = NULL;
372 g->win->vs = interpreter->vs;
374 SEE_SET_OBJECT(&v, (struct SEE_object *)g->win);
375 SEE_OBJECT_PUT(interp, interp->Global, s_window, &v, 0);
377 g->win->alert = SEE_cfunction_make(interp, js_window_alert, s_alert, 1);
378 g->win->open = SEE_cfunction_make(interp, js_window_open, s_open, 3);
379 g->win->setTimeout = SEE_cfunction_make(interp, js_setTimeout, s_setTimeout, 2);
381 SEE_OBJECT_GET(interp, (struct SEE_object *)g->win, s_top, &v);
382 SEE_OBJECT_PUT(interp, interp->Global, s_top, &v, 0);
384 SEE_OBJECT_GET(interp, (struct SEE_object *)g->win, s_self, &v);
385 SEE_OBJECT_PUT(interp, interp->Global, s_self, &v, 0);
387 SEE_OBJECT_GET(interp, (struct SEE_object *)g->win, s_alert, &v);
388 SEE_OBJECT_PUT(interp, interp->Global, s_alert, &v, 0);
389 SEE_OBJECT_GET(interp, (struct SEE_object *)g->win, s_open, &v);
390 SEE_OBJECT_PUT(interp, interp->Global, s_open, &v, 0);
391 SEE_OBJECT_GET(interp, (struct SEE_object *)g->win, s_setTimeout, &v);
392 SEE_OBJECT_PUT(interp, interp->Global, s_setTimeout, &v, 0);
395 void
396 checktime(struct SEE_interpreter *interp)
398 struct global_object *g = (struct global_object *)interp;
400 if (time(NULL) - g->exec_start > g->max_exec_time) {
401 struct terminal *term = g->win->vs->doc_view->session->tab->term;
402 /* A killer script! Alert! */
403 ecmascript_timeout_dialog(term, g->max_exec_time);
404 SEE_error_throw_string(interp, interp->Error, s_timeout);