1 /* The SEE window object implementation. */
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
= {
76 static struct js_window_object
*
77 js_get_global_object(void *data
)
79 struct global_object
*g
= (struct global_object
*)data
;
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
;
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
);
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
;
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
);
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
);
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
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
);
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
);
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
);
153 SEE_SET_UNDEFINED(res
);
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
);
170 location_goto(doc_view
, 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
);
187 window_canput(struct SEE_interpreter
*interp
, struct SEE_object
*o
,
188 struct SEE_string
*p
)
190 if (p
== s_location
|| p
== s_status
)
196 window_hasproperty(struct SEE_interpreter
*interp
, struct SEE_object
*o
,
197 struct SEE_string
*p
)
199 /* all unknown properties return UNDEFINED value */
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);
222 string
= see_value_to_unsigned_char(interp
, argv
[0]);
223 if (!string
|| !*string
) {
228 info_box(vs
->doc_view
->session
->tab
->term
, MSGBOX_FREE_TEXT
,
229 N_("JavaScript Alert"), ALIGN_CENTER
, string
);
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
;
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")) {
258 set_led_value(ses
->status
.popup_led
, 'P');
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
);
274 if (ratelimit_count
> 20)
278 SEE_ToString(interp
, argv
[0], &url_value
);
279 url
= see_string_to_unsigned_char(url_value
.u
.string
);
281 trim_chars(url
, ' ', 0);
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
);
292 /* TODO: Support for window naming and perhaps some window features? */
294 url2
= join_urls(doc_view
->document
->uri
, url
);
300 uri
= get_uri(url2
, 0);
307 if (frame
&& *frame
&& strcasecmp(frame
, "_blank")) {
308 struct delayed_open
*deo
= mem_calloc(1, sizeof(*deo
));
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
);
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
);
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
));
333 deo
->uri
= get_uri_reference(uri
);
334 register_bottom_half(delayed_open
, deo
);
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
;
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
);
364 init_js_window_object(struct ecmascript_interpreter
*interpreter
)
366 struct global_object
*g
= interpreter
->backend_data
;
367 struct SEE_interpreter
*interp
= &g
->interp
;
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);
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
);