Bug 1016: Avoid JSFunctionSpec.
[elinks.git] / src / ecmascript / spidermonkey / location.c
blobdd5b40b0709303347e1fbb8fb172d98c4dfd71c6
1 /* The SpiderMonkey location and history objects implementation. */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
11 #include "elinks.h"
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/location.h"
26 #include "ecmascript/spidermonkey/window.h"
27 #include "intl/gettext/libintl.h"
28 #include "main/select.h"
29 #include "osdep/newwin.h"
30 #include "osdep/sysname.h"
31 #include "protocol/http/http.h"
32 #include "protocol/uri.h"
33 #include "session/history.h"
34 #include "session/location.h"
35 #include "session/session.h"
36 #include "session/task.h"
37 #include "terminal/tab.h"
38 #include "terminal/terminal.h"
39 #include "util/conv.h"
40 #include "util/memory.h"
41 #include "util/string.h"
42 #include "viewer/text/draw.h"
43 #include "viewer/text/form.h"
44 #include "viewer/text/link.h"
45 #include "viewer/text/vs.h"
48 static JSBool history_back(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
49 static JSBool history_forward(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
50 static JSBool history_go(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
52 const JSClass history_class = {
53 "history",
54 JSCLASS_HAS_PRIVATE,
55 JS_PropertyStub, JS_PropertyStub,
56 JS_PropertyStub, JS_PropertyStub,
57 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
60 const spidermonkeyFunctionSpec history_funcs[] = {
61 { "back", history_back, 0 },
62 { "forward", history_forward, 0 },
63 { "go", history_go, 1 },
64 { NULL }
67 /* @history_funcs{"back"} */
68 static JSBool
69 history_back(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
71 struct ecmascript_interpreter *interpreter = JS_GetContextPrivate(ctx);
72 struct document_view *doc_view = interpreter->vs->doc_view;
73 struct session *ses = doc_view->session;
75 go_back(ses);
77 /* history_back() must return 0 for onClick to cause displaying previous page
78 * and return non zero for <a href="javascript:history.back()"> to prevent
79 * "calculating" new link. Returned value 2 is changed to 0 in function
80 * spidermonkey_eval_boolback */
81 return 2;
84 /* @history_funcs{"forward"} */
85 static JSBool
86 history_forward(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
88 struct ecmascript_interpreter *interpreter = JS_GetContextPrivate(ctx);
89 struct document_view *doc_view = interpreter->vs->doc_view;
90 struct session *ses = doc_view->session;
92 go_unback(ses);
94 return 2;
97 /* @history_funcs{"go"} */
98 static JSBool
99 history_go(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
101 struct ecmascript_interpreter *interpreter = JS_GetContextPrivate(ctx);
102 struct document_view *doc_view = interpreter->vs->doc_view;
103 struct session *ses = doc_view->session;
104 int index;
105 struct location *loc;
107 if (argc != 1)
108 return JS_TRUE;
110 index = atol(jsval_to_string(ctx, &argv[0]));
112 for (loc = cur_loc(ses);
113 loc != (struct location *) &ses->history.history;
114 loc = index > 0 ? loc->next : loc->prev) {
115 if (!index) {
116 go_history(ses, loc);
117 break;
120 index += index > 0 ? -1 : 1;
123 return 2;
127 static JSBool location_get_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp);
128 static JSBool location_set_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp);
130 /* Each @location_class object must have a @window_class parent. */
131 const JSClass location_class = {
132 "location",
133 JSCLASS_HAS_PRIVATE,
134 JS_PropertyStub, JS_PropertyStub,
135 location_get_property, location_set_property,
136 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
139 /* Tinyids of properties. Use negative values to distinguish these
140 * from array indexes (even though this object has no array elements).
141 * ECMAScript code should not use these directly as in location[-1];
142 * future versions of ELinks may change the numbers. */
143 enum location_prop {
144 JSP_LOC_HREF = -1,
146 const JSPropertySpec location_props[] = {
147 { "href", JSP_LOC_HREF, JSPROP_ENUMERATE },
148 { NULL }
151 /* @location_class.getProperty */
152 static JSBool
153 location_get_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp)
155 JSObject *parent_win; /* instance of @window_class */
156 struct view_state *vs;
158 /* This can be called if @obj if not itself an instance of the
159 * appropriate class but has one in its prototype chain. Fail
160 * such calls. */
161 if (!JS_InstanceOf(ctx, obj, (JSClass *) &location_class, NULL))
162 return JS_FALSE;
163 parent_win = JS_GetParent(ctx, obj);
164 assert(JS_InstanceOf(ctx, parent_win, (JSClass *) &window_class, NULL));
165 if_assert_failed return JS_FALSE;
167 vs = JS_GetInstancePrivate(ctx, parent_win,
168 (JSClass *) &window_class, NULL);
170 if (!JSVAL_IS_INT(id))
171 return JS_TRUE;
173 undef_to_jsval(ctx, vp);
175 switch (JSVAL_TO_INT(id)) {
176 case JSP_LOC_HREF:
177 astring_to_jsval(ctx, vp, get_uri_string(vs->uri, URI_ORIGINAL));
178 break;
179 default:
180 /* Unrecognized integer property ID; someone is using
181 * the object as an array. SMJS builtin classes (e.g.
182 * js_RegExpClass) just return JS_TRUE in this case
183 * and leave *@vp unchanged. Do the same here.
184 * (Actually not quite the same, as we already used
185 * @undef_to_jsval.) */
186 break;
189 return JS_TRUE;
192 /* @location_class.setProperty */
193 static JSBool
194 location_set_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp)
196 JSObject *parent_win; /* instance of @window_class */
197 struct view_state *vs;
198 struct document_view *doc_view;
200 /* This can be called if @obj if not itself an instance of the
201 * appropriate class but has one in its prototype chain. Fail
202 * such calls. */
203 if (!JS_InstanceOf(ctx, obj, (JSClass *) &location_class, NULL))
204 return JS_FALSE;
205 parent_win = JS_GetParent(ctx, obj);
206 assert(JS_InstanceOf(ctx, parent_win, (JSClass *) &window_class, NULL));
207 if_assert_failed return JS_FALSE;
209 vs = JS_GetInstancePrivate(ctx, parent_win,
210 (JSClass *) &window_class, NULL);
211 doc_view = vs->doc_view;
213 if (!JSVAL_IS_INT(id))
214 return JS_TRUE;
216 switch (JSVAL_TO_INT(id)) {
217 case JSP_LOC_HREF:
218 location_goto(doc_view, jsval_to_string(ctx, vp));
219 break;
222 return JS_TRUE;
225 static JSBool location_toString(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
227 const spidermonkeyFunctionSpec location_funcs[] = {
228 { "toString", location_toString, 0 },
229 { "toLocaleString", location_toString, 0 },
230 { NULL }
233 /* @location_funcs{"toString"}, @location_funcs{"toLocaleString"} */
234 static JSBool
235 location_toString(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
237 return JS_GetProperty(ctx, obj, "href", rval);
240 struct delayed_goto {
241 /* It might look more convenient to pass doc_view around but it could
242 * disappear during wild dances inside of frames or so. */
243 struct view_state *vs;
244 struct uri *uri;
247 static void
248 delayed_goto(void *data)
250 struct delayed_goto *deg = data;
252 assert(deg);
253 if (deg->vs->doc_view
254 && deg->vs->doc_view == deg->vs->doc_view->session->doc_view) {
255 goto_uri_frame(deg->vs->doc_view->session, deg->uri,
256 deg->vs->doc_view->name,
257 CACHE_MODE_NORMAL);
259 done_uri(deg->uri);
260 mem_free(deg);
263 void
264 location_goto(struct document_view *doc_view, unsigned char *url)
266 unsigned char *new_abs_url;
267 struct uri *new_uri;
268 struct delayed_goto *deg;
270 /* Workaround for bug 611. Does not crash, but may lead to infinite loop.*/
271 if (!doc_view) return;
272 new_abs_url = join_urls(doc_view->document->uri,
273 trim_chars(url, ' ', 0));
274 if (!new_abs_url)
275 return;
276 new_uri = get_uri(new_abs_url, 0);
277 mem_free(new_abs_url);
278 if (!new_uri)
279 return;
280 deg = mem_calloc(1, sizeof(*deg));
281 if (!deg) {
282 done_uri(new_uri);
283 return;
285 assert(doc_view->vs);
286 deg->vs = doc_view->vs;
287 deg->uri = new_uri;
288 /* It does not seem to be very safe inside of frames to
289 * call goto_uri() right away. */
290 register_bottom_half(delayed_goto, deg);