1 /* The SpiderMonkey window object implementation. */
13 #include "ecmascript/spidermonkey/util.h"
15 #include "bfu/dialog.h"
16 #include "cache/cache.h"
17 #include "cookies/cookies.h"
18 #include "dialogs/menu.h"
19 #include "dialogs/status.h"
20 #include "document/html/frames.h"
21 #include "document/document.h"
22 #include "document/forms.h"
23 #include "document/view.h"
24 #include "ecmascript/ecmascript.h"
25 #include "ecmascript/spidermonkey/window.h"
26 #include "intl/gettext/libintl.h"
27 #include "main/select.h"
28 #include "osdep/newwin.h"
29 #include "osdep/sysname.h"
30 #include "protocol/http/http.h"
31 #include "protocol/uri.h"
32 #include "session/history.h"
33 #include "session/location.h"
34 #include "session/session.h"
35 #include "session/task.h"
36 #include "terminal/tab.h"
37 #include "terminal/terminal.h"
38 #include "util/conv.h"
39 #include "util/memory.h"
40 #include "util/string.h"
41 #include "viewer/text/draw.h"
42 #include "viewer/text/form.h"
43 #include "viewer/text/link.h"
44 #include "viewer/text/vs.h"
47 static JSBool
window_get_property(JSContext
*ctx
, JSObject
*obj
, jsval id
, jsval
*vp
);
48 static JSBool
window_set_property(JSContext
*ctx
, JSObject
*obj
, jsval id
, jsval
*vp
);
50 const JSClass window_class
= {
53 JS_PropertyStub
, JS_PropertyStub
,
54 window_get_property
, window_set_property
,
55 JS_EnumerateStub
, JS_ResolveStub
, JS_ConvertStub
, JS_FinalizeStub
65 /* "location" is special because we need to simulate "location.href"
66 * when the code is asking directly for "location". We do not register
67 * it as a "known" property since that was yielding strange bugs
68 * (SpiderMonkey was still asking us about the "location" string after
69 * assigning to it once), instead we do just a little string
71 const JSPropertySpec window_props
[] = {
72 { "closed", JSP_WIN_CLOSED
, JSPROP_ENUMERATE
| JSPROP_READONLY
},
73 { "parent", JSP_WIN_PARENT
, JSPROP_ENUMERATE
| JSPROP_READONLY
},
74 { "self", JSP_WIN_SELF
, JSPROP_ENUMERATE
| JSPROP_READONLY
},
75 { "top", JSP_WIN_TOP
, JSPROP_ENUMERATE
| JSPROP_READONLY
},
76 { "window", JSP_WIN_SELF
, JSPROP_ENUMERATE
| JSPROP_READONLY
},
82 try_resolve_frame(struct document_view
*doc_view
, unsigned char *id
)
84 struct session
*ses
= doc_view
->session
;
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_GetGlobalObject(target
->vs
.ecmascript
->backend_data
);
97 static struct frame_desc
*
98 find_child_frame(struct document_view
*doc_view
, struct frame_desc
*tframe
)
100 struct frameset_desc
*frameset
= doc_view
->document
->frame_desc
;
106 for (i
= 0; i
< frameset
->n
; i
++) {
107 struct frame_desc
*frame
= &frameset
->frame_desc
[i
];
118 window_get_property(JSContext
*ctx
, JSObject
*obj
, jsval id
, jsval
*vp
)
120 struct view_state
*vs
= JS_GetPrivate(ctx
, obj
);
122 /* No need for special window.location measurements - when
123 * location is then evaluated in string context, toString()
124 * is called which we overrode for that class below, so
125 * everything's fine. */
126 if (JSVAL_IS_STRING(id
)) {
127 struct document_view
*doc_view
= vs
->doc_view
;
130 obj
= try_resolve_frame(doc_view
, jsval_to_string(ctx
, &id
));
131 /* TODO: Try other lookups (mainly element lookup) until
132 * something yields data. */
134 object_to_jsval(ctx
, vp
, obj
);
139 if (!JSVAL_IS_INT(id
))
142 undef_to_jsval(ctx
, vp
);
144 switch (JSVAL_TO_INT(id
)) {
146 /* TODO: It will be a major PITA to implement this properly.
147 * Well, perhaps not so much if we introduce reference tracking
148 * for (struct session)? Still... --pasky */
149 boolean_to_jsval(ctx
, vp
, 0);
152 object_to_jsval(ctx
, vp
, obj
);
155 /* XXX: It would be nice if the following worked, yes.
156 * The problem is that we get called at the point where
157 * document.frame properties are going to be mostly NULL.
158 * But the problem is deeper because at that time we are
159 * yet building scrn_frames so our parent might not be there
160 * yet (XXX: is this true?). The true solution will be to just
161 * have struct document_view *(document_view.parent). --pasky */
162 /* FIXME: So now we alias window.parent to window.top, which is
163 * INCORRECT but works for the most common cases of just two
164 * frames. Better something than nothing. */
167 /* This is horrible. */
168 struct document_view
*doc_view
= vs
->doc_view
;
169 struct session
*ses
= doc_view
->session
;
170 struct frame_desc
*frame
= doc_view
->document
->frame
;
172 if (!ses
->doc_view
->document
->frame_desc
) {
173 INTERNAL("Looking for parent but there're no frames.");
177 doc_view
= ses
->doc_view
;
178 if (find_child_frame(doc_view
, frame
))
180 foreach (doc_view
, ses
->scrn_frames
) {
181 if (find_child_frame(doc_view
, frame
))
184 INTERNAL("Cannot find frame %s parent.",doc_view
->name
);
188 some_domain_security_check();
189 if (doc_view
->vs
.ecmascript_fragile
)
190 ecmascript_reset_state(&doc_view
->vs
);
191 assert(doc_view
->ecmascript
);
192 object_to_jsval(ctx
, vp
, JS_GetGlobalObject(doc_view
->ecmascript
->backend_data
));
198 struct document_view
*doc_view
= vs
->doc_view
;
199 struct document_view
*top_view
= doc_view
->session
->doc_view
;
200 JSObject
*newjsframe
;
202 assert(top_view
&& top_view
->vs
);
203 if (top_view
->vs
->ecmascript_fragile
)
204 ecmascript_reset_state(top_view
->vs
);
205 if (!top_view
->vs
->ecmascript
)
207 newjsframe
= JS_GetGlobalObject(top_view
->vs
->ecmascript
->backend_data
);
209 /* Keep this unrolled this way. Will have to check document.domain
211 /* Note that this check is perhaps overparanoid. If top windows
212 * is alien but some other child window is not, we should still
213 * let the script walk thru. That'd mean moving the check to
214 * other individual properties in this switch. */
215 if (compare_uri(vs
->uri
, top_view
->vs
->uri
, URI_HOST
))
216 object_to_jsval(ctx
, vp
, newjsframe
);
218 /****X*X*X*** SECURITY VIOLATION! RED ALERT, SHIELDS UP! ***X*X*X****\
219 |* (Pasky was apparently looking at the Links2 JS code . ___ ^.^ *|
220 \* for too long.) `.(,_,)\o/ */
224 INTERNAL("Invalid ID %d in window_get_property().", JSVAL_TO_INT(id
));
231 void location_goto(struct document_view
*doc_view
, unsigned char *url
);
234 window_set_property(JSContext
*ctx
, JSObject
*obj
, jsval id
, jsval
*vp
)
236 struct view_state
*vs
= JS_GetPrivate(ctx
, obj
);
238 if (JSVAL_IS_STRING(id
)) {
239 if (!strcmp(jsval_to_string(ctx
, &id
), "location")) {
240 struct document_view
*doc_view
= vs
->doc_view
;
242 location_goto(doc_view
, jsval_to_string(ctx
, vp
));
243 /* Do NOT touch our .location property, evil
250 if (!JSVAL_IS_INT(id
))
253 switch (JSVAL_TO_INT(id
)) {
255 INTERNAL("Invalid ID %d in window_set_property().", JSVAL_TO_INT(id
));
263 static JSBool
window_alert(JSContext
*ctx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
);
264 static JSBool
window_open(JSContext
*ctx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
);
266 const JSFunctionSpec window_funcs
[] = {
267 { "alert", window_alert
, 1 },
268 { "open", window_open
, 3 },
273 window_alert(JSContext
*ctx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
275 struct view_state
*vs
= JS_GetPrivate(ctx
, obj
);
276 unsigned char *string
;
281 string
= jsval_to_string(ctx
, &argv
[0]);
285 info_box(vs
->doc_view
->session
->tab
->term
, MSGBOX_FREE_TEXT
,
286 N_("JavaScript Alert"), ALIGN_CENTER
, stracpy(string
));
288 undef_to_jsval(ctx
, rval
);
293 window_open(JSContext
*ctx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
295 struct view_state
*vs
= JS_GetPrivate(ctx
, obj
);
296 struct document_view
*doc_view
= vs
->doc_view
;
297 struct session
*ses
= doc_view
->session
;
298 unsigned char *target
= "";
301 static time_t ratelimit_start
;
302 static int ratelimit_count
;
304 if (get_opt_bool("ecmascript.block_window_opening")) {
306 set_led_value(ses
->status
.popup_led
, 'P');
311 if (argc
< 1) return JS_TRUE
;
313 url
= jsval_to_string(ctx
, &argv
[0]);
315 JSString
*url_string
= JS_ValueToString(ctx
, argv
[0]);
316 JSString
*target_string
= JS_ValueToString(ctx
, argv
[1]);
318 #define NUMBER_OF_URLS_TO_REMEMBER 8
322 } strings
[NUMBER_OF_URLS_TO_REMEMBER
];
325 target
= jsval_to_string(ctx
, &argv
[1]);
326 for (i
= 0; i
< NUMBER_OF_URLS_TO_REMEMBER
; i
++) {
327 if (!(strings
[i
].url
&& strings
[i
].frame
))
329 if (!JS_CompareStrings(url_string
, strings
[i
].url
)
330 && !JS_CompareStrings(target_string
, strings
[i
].frame
))
333 strings
[indeks
].url
= JS_InternString(ctx
, url
);
334 strings
[indeks
].frame
= JS_InternString(ctx
, target
);
336 if (indeks
>= NUMBER_OF_URLS_TO_REMEMBER
) indeks
= 0;
337 #undef NUMBER_OF_URLS_TO_REMEMBER
340 /* Ratelimit window opening. Recursive window.open() is very nice.
341 * We permit at most 20 tabs in 2 seconds. The ratelimiter is very
342 * rough but shall suffice against the usual cases. */
343 if (!ratelimit_start
|| time(NULL
) - ratelimit_start
> 2) {
344 ratelimit_start
= time(NULL
);
348 if (ratelimit_count
> 20)
352 /* TODO: Support for window naming and perhaps some window features? */
354 url
= join_urls(doc_view
->document
->uri
,
355 trim_chars(url
, ' ', 0));
356 if (!url
) return JS_TRUE
;
357 uri
= get_uri(url
, 0);
359 if (!uri
) return JS_TRUE
;
362 if (*target
&& strcasecmp(target
, "_blank")) {
363 struct delayed_open
*deo
= mem_calloc(1, sizeof(*deo
));
367 deo
->uri
= get_uri_reference(uri
);
368 deo
->target
= stracpy(target
);
369 register_bottom_half(delayed_goto_uri_frame
, deo
);
370 boolean_to_jsval(ctx
, rval
, 1);
375 if (!get_cmd_opt_bool("no-connect")
376 && !get_cmd_opt_bool("no-home")
377 && !get_cmd_opt_bool("anonymous")
378 && can_open_in_new(ses
->tab
->term
)) {
379 open_uri_in_new_window(ses
, uri
, NULL
, ENV_ANY
,
380 CACHE_MODE_NORMAL
, TASK_NONE
);
381 boolean_to_jsval(ctx
, rval
, 1);
383 /* When opening a new tab, we might get rerendered, losing our
384 * context and triggerring a disaster, so postpone that. */
385 struct delayed_open
*deo
= mem_calloc(1, sizeof(*deo
));
389 deo
->uri
= get_uri_reference(uri
);
390 register_bottom_half(delayed_open
, deo
);
391 boolean_to_jsval(ctx
, rval
, 1);
393 undef_to_jsval(ctx
, rval
);