1038: Remove remembering last 8 URLs. It did not work.
[elinks.git] / src / ecmascript / see / window.c
blob1f37944c6a1e962e6313b729b502f81bfe7bce16
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 void location_goto(struct document_view *, unsigned char *);
62 struct SEE_objectclass js_window_object_class = {
63 "window",
64 window_get,
65 window_put,
66 window_canput,
67 window_hasproperty,
68 SEE_no_delete,
69 SEE_no_defaultvalue,
70 SEE_no_enumerator,
71 NULL,
72 NULL,
73 NULL
76 static struct js_window_object *
77 js_get_global_object(void *data)
79 struct global_object *g = (struct global_object *)data;
80 return g->win;
83 static struct js_window_object *
84 js_try_resolve_frame(struct document_view *doc_view, unsigned char *id)
86 struct session *ses = doc_view->session;
87 struct frame *target;
89 assert(ses);
90 target = ses_find_frame(ses, id);
91 if (!target) return NULL;
92 if (target->vs.ecmascript_fragile)
93 ecmascript_reset_state(&target->vs);
94 if (!target->vs.ecmascript) return NULL;
95 return js_get_global_object(target->vs.ecmascript->backend_data);
98 static void
99 window_get(struct SEE_interpreter *interp, struct SEE_object *o,
100 struct SEE_string *p, struct SEE_value *res)
102 struct js_window_object *win = (struct js_window_object *)o;
103 struct view_state *vs = win->vs;
105 if (p == s_closed) {
106 SEE_SET_BOOLEAN(res, 0);
107 } else if (p == s_self || p == s_parent || p == s_top || p == s_status) {
108 SEE_SET_OBJECT(res, o);
109 #if 0
110 } else if (p == s_parent || p == s_top) {
111 struct document_view *doc_view = vs->doc_view;
112 struct document_view *top_view = doc_view->session->doc_view;
113 struct js_window_object *newjsframe;
115 assert(top_view && top_view->vs);
116 if (top_view->vs->ecmascript_fragile)
117 ecmascript_reset_state(top_view->vs);
118 if (!top_view->vs->ecmascript) {
119 SEE_SET_UNDEFINED(res);
120 return;
122 newjsframe = js_get_global_object(
123 top_view->vs->ecmascript->backend_data);
125 /* Keep this unrolled this way. Will have to check document.domain
126 * JS property. */
127 /* Note that this check is perhaps overparanoid. If top windows
128 * is alien but some other child window is not, we should still
129 * let the script walk thru. That'd mean moving the check to
130 * other individual properties in this switch. */
131 if (compare_uri(vs->uri, top_view->vs->uri, URI_HOST)) {
132 SEE_SET_OBJECT(res, (struct SEE_object *)newjsframe);
134 #endif
135 } else if (p == s_alert) {
136 SEE_SET_OBJECT(res, win->alert);
137 } else if (p == s_open) {
138 SEE_SET_OBJECT(res, win->open);
139 } else if (p == s_setTimeout) {
140 SEE_SET_OBJECT(res, win->setTimeout);
141 } else if (p == s_location) {
142 SEE_OBJECT_GET(interp, interp->Global, s_location, res);
143 } else if (p == s_navigator) {
144 SEE_OBJECT_GET(interp, interp->Global, s_navigator, res);
145 } else {
146 unsigned char *frame = see_string_to_unsigned_char(p);
147 struct document_view *doc_view = vs->doc_view;
148 struct js_window_object *obj;
150 if (frame && (obj = js_try_resolve_frame(doc_view, frame))) {
151 SEE_SET_OBJECT(res, (struct SEE_object *)obj);
152 } else {
153 SEE_SET_UNDEFINED(res);
155 mem_free_if(frame);
159 static void
160 window_put(struct SEE_interpreter *interp, struct SEE_object *o,
161 struct SEE_string *p, struct SEE_value *val, int attr)
163 if (p == s_location) {
164 struct js_window_object *win = (struct js_window_object *)o;
165 struct view_state *vs = win->vs;
166 struct document_view *doc_view = vs->doc_view;
167 unsigned char *str = see_value_to_unsigned_char(interp, val);
169 if (str) {
170 location_goto(doc_view, str);
171 mem_free(str);
173 } else if (p == s_status) {
174 struct global_object *g = (struct global_object *)interp;
175 struct js_window_object *win = g->win;
176 struct view_state *vs = win->vs;
177 struct document_view *doc_view = vs->doc_view;
178 struct session *ses = doc_view->session;
179 unsigned char *stat = see_value_to_unsigned_char(interp, val);
181 mem_free_set(&ses->status.window_status, stat);
182 print_screen_status(ses);
186 static int
187 window_canput(struct SEE_interpreter *interp, struct SEE_object *o,
188 struct SEE_string *p)
190 if (p == s_location || p == s_status)
191 return 1;
192 return 0;
195 static int
196 window_hasproperty(struct SEE_interpreter *interp, struct SEE_object *o,
197 struct SEE_string *p)
199 /* all unknown properties return UNDEFINED value */
200 return 1;
204 static void
205 js_window_alert(struct SEE_interpreter *interp, struct SEE_object *self,
206 struct SEE_object *thisobj, int argc, struct SEE_value **argv,
207 struct SEE_value *res)
209 struct global_object *g = (struct global_object *)interp;
210 struct js_window_object *win = g->win;
211 struct view_state *vs = win->vs;
212 unsigned char *string;
214 /* Do not check thisobj->objectclass. ELinks sets this
215 * function as a property of both the window object and the
216 * global object, so thisobj may validly refer to either. */
218 SEE_SET_BOOLEAN(res, 1);
219 if (argc < 1)
220 return;
222 string = see_value_to_unsigned_char(interp, argv[0]);
223 if (!string || !*string) {
224 mem_free_if(string);
225 return;
228 info_box(vs->doc_view->session->tab->term, MSGBOX_FREE_TEXT,
229 N_("JavaScript Alert"), ALIGN_CENTER, string);
234 static void
235 js_window_open(struct SEE_interpreter *interp, struct SEE_object *self,
236 struct SEE_object *thisobj, int argc, struct SEE_value **argv,
237 struct SEE_value *res)
239 struct global_object *g = (struct global_object *)interp;
240 struct js_window_object *win = g->win;
241 struct view_state *vs = win->vs;
242 struct document_view *doc_view = vs->doc_view;
243 struct session *ses = doc_view->session;
244 unsigned char *frame = NULL;
245 unsigned char *url, *url2;
246 struct uri *uri;
247 struct SEE_value url_value;
248 static time_t ratelimit_start;
249 static int ratelimit_count;
251 /* Do not check thisobj->objectclass. ELinks sets this
252 * function as a property of both the window object and the
253 * global object, so thisobj may validly refer to either. */
255 SEE_SET_OBJECT(res, (struct SEE_object *)win);
256 if (get_opt_bool("ecmascript.block_window_opening")) {
257 #ifdef CONFIG_LEDS
258 set_led_value(ses->status.popup_led, 'P');
259 #endif
260 return;
263 if (argc < 1) return;
265 /* Ratelimit window opening. Recursive window.open() is very nice.
266 * We permit at most 20 tabs in 2 seconds. The ratelimiter is very
267 * rough but shall suffice against the usual cases. */
269 if (!ratelimit_start || time(NULL) - ratelimit_start > 2) {
270 ratelimit_start = time(NULL);
271 ratelimit_count = 0;
272 } else {
273 ratelimit_count++;
274 if (ratelimit_count > 20)
275 return;
278 SEE_ToString(interp, argv[0], &url_value);
279 url = see_string_to_unsigned_char(url_value.u.string);
280 if (!url) return;
281 trim_chars(url, ' ', 0);
282 if (argc > 1) {
283 struct SEE_value target_value;
285 SEE_ToString(interp, argv[1], &target_value);
286 frame = see_string_to_unsigned_char(target_value.u.string);
287 if (!frame) {
288 mem_free(url);
289 return;
292 /* TODO: Support for window naming and perhaps some window features? */
294 url2 = join_urls(doc_view->document->uri, url);
295 mem_free(url);
296 if (!url2) {
297 mem_free_if(frame);
298 return;
300 uri = get_uri(url2, 0);
301 mem_free(url2);
302 if (!uri) {
303 mem_free_if(frame);
304 return;
307 if (frame && *frame && strcasecmp(frame, "_blank")) {
308 struct delayed_open *deo = mem_calloc(1, sizeof(*deo));
310 if (deo) {
311 deo->ses = ses;
312 deo->uri = get_uri_reference(uri);
313 deo->target = stracpy(frame);
314 /* target will be freed in delayed_goto_uri_frame */
315 register_bottom_half(delayed_goto_uri_frame, deo);
316 goto end;
320 if (!get_cmd_opt_bool("no-connect")
321 && !get_cmd_opt_bool("no-home")
322 && !get_cmd_opt_bool("anonymous")
323 && can_open_in_new(ses->tab->term)) {
324 open_uri_in_new_window(ses, uri, NULL, ENV_ANY,
325 CACHE_MODE_NORMAL, TASK_NONE);
326 } else {
327 /* When opening a new tab, we might get rerendered, losing our
328 * context and triggerring a disaster, so postpone that. */
329 struct delayed_open *deo = mem_calloc(1, sizeof(*deo));
331 if (deo) {
332 deo->ses = ses;
333 deo->uri = get_uri_reference(uri);
334 register_bottom_half(delayed_open, deo);
338 end:
339 done_uri(uri);
340 mem_free_if(frame);
343 static void
344 js_setTimeout(struct SEE_interpreter *interp, struct SEE_object *self,
345 struct SEE_object *thisobj, int argc, struct SEE_value **argv,
346 struct SEE_value *res)
348 struct ecmascript_interpreter *ei;
349 unsigned char *code;
350 int timeout;
352 /* Do not check thisobj->objectclass. ELinks sets this
353 * function as a property of both the window object and the
354 * global object, so thisobj may validly refer to either. */
356 if (argc != 2) return;
357 ei = ((struct global_object *)interp)->interpreter;
358 code = see_value_to_unsigned_char(interp, argv[0]);
359 timeout = SEE_ToInt32(interp, argv[1]);
360 ecmascript_set_timeout(ei, code, timeout);
363 void
364 init_js_window_object(struct ecmascript_interpreter *interpreter)
366 struct global_object *g = interpreter->backend_data;
367 struct SEE_interpreter *interp = &g->interp;
368 struct SEE_value v;
370 g->win = SEE_NEW(interp, struct js_window_object);
372 g->win->object.objectclass = &js_window_object_class;
373 g->win->object.Prototype = NULL;
374 g->win->vs = interpreter->vs;
376 SEE_SET_OBJECT(&v, (struct SEE_object *)g->win);
377 SEE_OBJECT_PUT(interp, interp->Global, s_window, &v, 0);
379 g->win->alert = SEE_cfunction_make(interp, js_window_alert, s_alert, 1);
380 g->win->open = SEE_cfunction_make(interp, js_window_open, s_open, 3);
381 g->win->setTimeout = SEE_cfunction_make(interp, js_setTimeout, s_setTimeout, 2);
383 SEE_OBJECT_GET(interp, (struct SEE_object *)g->win, s_top, &v);
384 SEE_OBJECT_PUT(interp, interp->Global, s_top, &v, 0);
386 SEE_OBJECT_GET(interp, (struct SEE_object *)g->win, s_self, &v);
387 SEE_OBJECT_PUT(interp, interp->Global, s_self, &v, 0);
389 SEE_OBJECT_GET(interp, (struct SEE_object *)g->win, s_alert, &v);
390 SEE_OBJECT_PUT(interp, interp->Global, s_alert, &v, 0);
391 SEE_OBJECT_GET(interp, (struct SEE_object *)g->win, s_open, &v);
392 SEE_OBJECT_PUT(interp, interp->Global, s_open, &v, 0);
393 SEE_OBJECT_GET(interp, (struct SEE_object *)g->win, s_setTimeout, &v);
394 SEE_OBJECT_PUT(interp, interp->Global, s_setTimeout, &v, 0);
397 void
398 checktime(struct SEE_interpreter *interp)
400 struct global_object *g = (struct global_object *)interp;
402 if (time(NULL) - g->exec_start > g->max_exec_time) {
403 struct terminal *term = g->win->vs->doc_view->session->tab->term;
404 /* A killer script! Alert! */
405 ecmascript_timeout_dialog(term, g->max_exec_time);
406 SEE_error_throw_string(interp, interp->Error, s_timeout);