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/input.h"
27 #include "ecmascript/see/strings.h"
28 #include "ecmascript/see/window.h"
29 #include "intl/gettext/libintl.h"
30 #include "main/select.h"
31 #include "osdep/newwin.h"
32 #include "osdep/sysname.h"
33 #include "protocol/http/http.h"
34 #include "protocol/uri.h"
35 #include "session/history.h"
36 #include "session/location.h"
37 #include "session/session.h"
38 #include "session/task.h"
39 #include "terminal/tab.h"
40 #include "terminal/terminal.h"
41 #include "util/conv.h"
42 #include "util/memory.h"
43 #include "util/string.h"
44 #include "viewer/text/draw.h"
45 #include "viewer/text/form.h"
46 #include "viewer/text/link.h"
47 #include "viewer/text/vs.h"
49 static struct js_window_object
*js_get_global_object(void *);
50 static struct js_window_object
*js_try_resolve_frame(struct document_view
*, unsigned char *);
51 static void window_get(struct SEE_interpreter
*, struct SEE_object
*, struct SEE_string
*, struct SEE_value
*);
52 static void window_put(struct SEE_interpreter
*, struct SEE_object
*, struct SEE_string
*, struct SEE_value
*, int);
53 static int window_canput(struct SEE_interpreter
*, struct SEE_object
*, struct SEE_string
*);
54 static int window_hasproperty(struct SEE_interpreter
*, struct SEE_object
*, struct SEE_string
*);
55 static void js_window_alert(struct SEE_interpreter
*, struct SEE_object
*, struct SEE_object
*, int, struct SEE_value
**, struct SEE_value
*);
56 static void js_window_open(struct SEE_interpreter
*, struct SEE_object
*, struct SEE_object
*, int, struct SEE_value
**, struct SEE_value
*);
57 static void js_setTimeout(struct SEE_interpreter
*, struct SEE_object
*, struct SEE_object
*, int, struct SEE_value
**, struct SEE_value
*);
59 void location_goto(struct document_view
*, unsigned char *);
61 struct SEE_objectclass js_window_object_class
= {
75 static struct js_window_object
*
76 js_get_global_object(void *data
)
78 struct global_object
*g
= (struct global_object
*)data
;
82 static struct js_window_object
*
83 js_try_resolve_frame(struct document_view
*doc_view
, unsigned char *id
)
85 struct session
*ses
= doc_view
->session
;
89 target
= ses_find_frame(ses
, id
);
90 if (!target
) return NULL
;
91 if (target
->vs
.ecmascript_fragile
)
92 ecmascript_reset_state(&target
->vs
);
93 if (!target
->vs
.ecmascript
) return NULL
;
94 return js_get_global_object(target
->vs
.ecmascript
->backend_data
);
98 window_get(struct SEE_interpreter
*interp
, struct SEE_object
*o
,
99 struct SEE_string
*p
, struct SEE_value
*res
)
101 struct js_window_object
*win
= (struct js_window_object
*)o
;
102 struct view_state
*vs
= win
->vs
;
105 SEE_SET_BOOLEAN(res
, 0);
106 } else if (p
== s_self
|| p
== s_parent
|| p
== s_top
|| p
== s_status
) {
107 SEE_SET_OBJECT(res
, o
);
109 } else if (p
== s_parent
|| p
== s_top
) {
110 struct document_view
*doc_view
= vs
->doc_view
;
111 struct document_view
*top_view
= doc_view
->session
->doc_view
;
112 struct js_window_object
*newjsframe
;
114 assert(top_view
&& top_view
->vs
);
115 if (top_view
->vs
->ecmascript_fragile
)
116 ecmascript_reset_state(top_view
->vs
);
117 if (!top_view
->vs
->ecmascript
) {
118 SEE_SET_UNDEFINED(res
);
121 newjsframe
= js_get_global_object(
122 top_view
->vs
->ecmascript
->backend_data
);
124 /* Keep this unrolled this way. Will have to check document.domain
126 /* Note that this check is perhaps overparanoid. If top windows
127 * is alien but some other child window is not, we should still
128 * let the script walk thru. That'd mean moving the check to
129 * other individual properties in this switch. */
130 if (compare_uri(vs
->uri
, top_view
->vs
->uri
, URI_HOST
)) {
131 SEE_SET_OBJECT(res
, (struct SEE_object
*)newjsframe
);
134 } else if (p
== s_alert
) {
135 SEE_SET_OBJECT(res
, win
->alert
);
136 } else if (p
== s_open
) {
137 SEE_SET_OBJECT(res
, win
->open
);
138 } else if (p
== s_setTimeout
) {
139 SEE_SET_OBJECT(res
, win
->setTimeout
);
140 } else if (p
== s_location
) {
141 SEE_OBJECT_GET(interp
, interp
->Global
, s_location
, res
);
142 } else if (p
== s_navigator
) {
143 SEE_OBJECT_GET(interp
, interp
->Global
, s_navigator
, res
);
145 unsigned char *frame
= SEE_string_to_unsigned_char(p
);
146 struct document_view
*doc_view
= vs
->doc_view
;
147 struct js_window_object
*obj
;
149 if (frame
&& (obj
= js_try_resolve_frame(doc_view
, frame
))) {
150 SEE_SET_OBJECT(res
, (struct SEE_object
*)obj
);
152 SEE_SET_UNDEFINED(res
);
159 window_put(struct SEE_interpreter
*interp
, struct SEE_object
*o
,
160 struct SEE_string
*p
, struct SEE_value
*val
, int attr
)
162 if (p
== s_location
) {
163 struct js_window_object
*win
= (struct js_window_object
*)o
;
164 struct view_state
*vs
= win
->vs
;
165 struct document_view
*doc_view
= vs
->doc_view
;
166 unsigned char *str
= SEE_value_to_unsigned_char(interp
, val
);
169 location_goto(doc_view
, str
);
172 } else if (p
== s_status
) {
173 struct global_object
*g
= (struct global_object
*)interp
;
174 struct js_window_object
*win
= g
->win
;
175 struct view_state
*vs
= win
->vs
;
176 struct document_view
*doc_view
= vs
->doc_view
;
177 struct session
*ses
= doc_view
->session
;
178 unsigned char *stat
= SEE_value_to_unsigned_char(interp
, val
);
180 mem_free_set(&ses
->status
.window_status
, stat
);
181 print_screen_status(ses
);
186 window_canput(struct SEE_interpreter
*interp
, struct SEE_object
*o
,
187 struct SEE_string
*p
)
189 if (p
== s_location
|| p
== s_status
)
195 window_hasproperty(struct SEE_interpreter
*interp
, struct SEE_object
*o
,
196 struct SEE_string
*p
)
198 /* all unknown properties return UNDEFINED value */
204 js_window_alert(struct SEE_interpreter
*interp
, struct SEE_object
*self
,
205 struct SEE_object
*thisobj
, int argc
, struct SEE_value
**argv
,
206 struct SEE_value
*res
)
208 struct global_object
*g
= (struct global_object
*)interp
;
209 struct js_window_object
*win
= g
->win
;
210 struct view_state
*vs
= win
->vs
;
211 unsigned char *string
;
213 SEE_SET_BOOLEAN(res
, 1);
217 string
= SEE_value_to_unsigned_char(interp
, argv
[0]);
218 if (!string
|| !*string
) {
223 info_box(vs
->doc_view
->session
->tab
->term
, MSGBOX_FREE_TEXT
,
224 N_("JavaScript Alert"), ALIGN_CENTER
, string
);
230 js_window_open(struct SEE_interpreter
*interp
, struct SEE_object
*self
,
231 struct SEE_object
*thisobj
, int argc
, struct SEE_value
**argv
,
232 struct SEE_value
*res
)
234 struct global_object
*g
= (struct global_object
*)interp
;
235 struct js_window_object
*win
= g
->win
;
236 struct view_state
*vs
= win
->vs
;
237 struct document_view
*doc_view
= vs
->doc_view
;
238 struct session
*ses
= doc_view
->session
;
239 unsigned char *frame
= "";
242 struct SEE_value url_value
;
244 static time_t ratelimit_start
;
245 static int ratelimit_count
;
247 SEE_SET_OBJECT(res
, (struct SEE_object
*)win
);
248 if (get_opt_bool("ecmascript.block_window_opening")) {
250 set_led_value(ses
->status
.popup_led
, 'P');
255 if (argc
< 1) return;
257 /* Ratelimit window opening. Recursive window.open() is very nice.
258 * We permit at most 20 tabs in 2 seconds. The ratelimiter is very
259 * rough but shall suffice against the usual cases. */
261 if (!ratelimit_start
|| time(NULL
) - ratelimit_start
> 2) {
262 ratelimit_start
= time(NULL
);
266 if (ratelimit_count
> 20)
270 SEE_ToString(interp
, argv
[0], &url_value
);
271 url
= SEE_string_to_unsigned_char(url_value
.u
.string
);
273 trim_chars(url
, ' ', 0);
275 struct SEE_value target_value
;
277 SEE_ToString(interp
, argv
[1], &target_value
);
278 frame
= SEE_string_to_unsigned_char(target_value
.u
.string
);
283 /* url and frame will be freed by ecmascript_check_url */
284 if (!ecmascript_check_url(url
, frame
)) return;
286 /* TODO: Support for window naming and perhaps some window features? */
288 url
= join_urls(doc_view
->document
->uri
, url
);
290 uri
= get_uri(url
, 0);
294 if (*frame
&& strcasecmp(frame
, "_blank")) {
295 struct delayed_open
*deo
= mem_calloc(1, sizeof(*deo
));
299 deo
->uri
= get_uri_reference(uri
);
300 deo
->target
= stracpy(frame
);
301 /* target will be freed in delayed_goto_uri_frame */
302 register_bottom_half(delayed_goto_uri_frame
, deo
);
307 if (!get_cmd_opt_bool("no-connect")
308 && !get_cmd_opt_bool("no-home")
309 && !get_cmd_opt_bool("anonymous")
310 && can_open_in_new(ses
->tab
->term
)) {
311 open_uri_in_new_window(ses
, uri
, NULL
, ENV_ANY
,
312 CACHE_MODE_NORMAL
, TASK_NONE
);
314 /* When opening a new tab, we might get rerendered, losing our
315 * context and triggerring a disaster, so postpone that. */
316 struct delayed_open
*deo
= mem_calloc(1, sizeof(*deo
));
320 deo
->uri
= get_uri_reference(uri
);
321 register_bottom_half(delayed_open
, deo
);
330 js_setTimeout(struct SEE_interpreter
*interp
, struct SEE_object
*self
,
331 struct SEE_object
*thisobj
, int argc
, struct SEE_value
**argv
,
332 struct SEE_value
*res
)
334 struct ecmascript_interpreter
*ei
;
338 if (argc
!= 2) return;
339 ei
= ((struct global_object
*)interp
)->interpreter
;
340 code
= SEE_value_to_unsigned_char(interp
, argv
[0]);
341 timeout
= SEE_ToInt32(interp
, argv
[1]);
342 ecmascript_set_timeout(ei
, code
, timeout
);
346 init_js_window_object(struct ecmascript_interpreter
*interpreter
)
348 struct global_object
*g
= interpreter
->backend_data
;
349 struct SEE_interpreter
*interp
= &g
->interp
;
352 g
->win
= SEE_NEW(interp
, struct js_window_object
);
354 g
->win
->object
.objectclass
= &js_window_object_class
;
355 g
->win
->object
.Prototype
= NULL
;
356 g
->win
->vs
= interpreter
->vs
;
358 SEE_SET_OBJECT(&v
, (struct SEE_object
*)g
->win
);
359 SEE_OBJECT_PUT(interp
, interp
->Global
, s_window
, &v
, 0);
361 g
->win
->alert
= SEE_cfunction_make(interp
, js_window_alert
, s_alert
, 1);
362 g
->win
->open
= SEE_cfunction_make(interp
, js_window_open
, s_open
, 3);
363 g
->win
->setTimeout
= SEE_cfunction_make(interp
, js_setTimeout
, s_setTimeout
, 2);
365 SEE_OBJECT_GET(interp
, (struct SEE_object
*)g
->win
, s_top
, &v
);
366 SEE_OBJECT_PUT(interp
, interp
->Global
, s_top
, &v
, 0);
368 SEE_OBJECT_GET(interp
, (struct SEE_object
*)g
->win
, s_self
, &v
);
369 SEE_OBJECT_PUT(interp
, interp
->Global
, s_self
, &v
, 0);
371 SEE_OBJECT_GET(interp
, (struct SEE_object
*)g
->win
, s_alert
, &v
);
372 SEE_OBJECT_PUT(interp
, interp
->Global
, s_alert
, &v
, 0);
373 SEE_OBJECT_GET(interp
, (struct SEE_object
*)g
->win
, s_open
, &v
);
374 SEE_OBJECT_PUT(interp
, interp
->Global
, s_open
, &v
, 0);
375 SEE_OBJECT_GET(interp
, (struct SEE_object
*)g
->win
, s_setTimeout
, &v
);
376 SEE_OBJECT_PUT(interp
, interp
->Global
, s_setTimeout
, &v
, 0);
380 checktime(struct SEE_interpreter
*interp
)
382 struct global_object
*g
= (struct global_object
*)interp
;
384 if (time(NULL
) - g
->exec_start
> g
->max_exec_time
) {
385 struct terminal
*term
= g
->win
->vs
->doc_view
->session
->tab
->term
;
386 /* A killer script! Alert! */
387 ecmascript_timeout_dialog(term
, g
->max_exec_time
);
388 SEE_error_throw_string(interp
, interp
->Error
, s_timeout
);