SpiderMonkey: fix issue with javascript:history.back()
[elinks.git] / src / ecmascript / spidermonkey / location.c
blobd1fbdfb0081aa8f5e17125e94c577de180abdd36
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, uintN argc, jsval *rval);
49 static JSBool history_forward(JSContext *ctx, uintN argc, jsval *rval);
50 static JSBool history_go(JSContext *ctx, uintN argc, jsval *rval);
52 const JSClass history_class = {
53 "history",
54 JSCLASS_HAS_PRIVATE,
55 JS_PropertyStub, JS_PropertyStub,
56 JS_PropertyStub, JS_StrictPropertyStub,
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, uintN argc, 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 JS_SET_RVAL(ctx, rval, JSVAL_NULL);
82 return 2;
85 /* @history_funcs{"forward"} */
86 static JSBool
87 history_forward(JSContext *ctx, uintN argc, jsval *rval)
89 struct ecmascript_interpreter *interpreter = JS_GetContextPrivate(ctx);
90 struct document_view *doc_view = interpreter->vs->doc_view;
91 struct session *ses = doc_view->session;
93 go_unback(ses);
95 JS_SET_RVAL(ctx, rval, JSVAL_NULL);
96 return 2;
99 /* @history_funcs{"go"} */
100 static JSBool
101 history_go(JSContext *ctx, uintN argc, jsval *rval)
103 struct ecmascript_interpreter *interpreter = JS_GetContextPrivate(ctx);
104 struct document_view *doc_view = interpreter->vs->doc_view;
105 struct session *ses = doc_view->session;
106 jsval *argv = JS_ARGV(ctx, rval);
107 int index;
108 struct location *loc;
110 if (argc != 1)
111 return JS_TRUE;
113 index = atol(jsval_to_string(ctx, &argv[0]));
115 for (loc = cur_loc(ses);
116 loc != (struct location *) &ses->history.history;
117 loc = index > 0 ? loc->next : loc->prev) {
118 if (!index) {
119 go_history(ses, loc);
120 break;
123 index += index > 0 ? -1 : 1;
126 JS_SET_RVAL(ctx, rval, JSVAL_NULL);
127 return 2;
131 static JSBool location_get_property(JSContext *ctx, JSObject *obj, jsid id, jsval *vp);
132 static JSBool location_set_property(JSContext *ctx, JSObject *obj, jsid id, JSBool strict, jsval *vp);
134 /* Each @location_class object must have a @window_class parent. */
135 const JSClass location_class = {
136 "location",
137 JSCLASS_HAS_PRIVATE,
138 JS_PropertyStub, JS_PropertyStub,
139 location_get_property, location_set_property,
140 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
143 /* Tinyids of properties. Use negative values to distinguish these
144 * from array indexes (even though this object has no array elements).
145 * ECMAScript code should not use these directly as in location[-1];
146 * future versions of ELinks may change the numbers. */
147 enum location_prop {
148 JSP_LOC_HREF = -1,
150 const JSPropertySpec location_props[] = {
151 { "href", JSP_LOC_HREF, JSPROP_ENUMERATE },
152 { NULL }
155 /* @location_class.getProperty */
156 static JSBool
157 location_get_property(JSContext *ctx, JSObject *obj, jsid id, jsval *vp)
159 JSObject *parent_win; /* instance of @window_class */
160 struct view_state *vs;
162 /* This can be called if @obj if not itself an instance of the
163 * appropriate class but has one in its prototype chain. Fail
164 * such calls. */
165 if (!JS_InstanceOf(ctx, obj, (JSClass *) &location_class, NULL))
166 return JS_FALSE;
167 parent_win = JS_GetParent(ctx, obj);
168 assert(JS_InstanceOf(ctx, parent_win, (JSClass *) &window_class, NULL));
169 if_assert_failed return JS_FALSE;
171 vs = JS_GetInstancePrivate(ctx, parent_win,
172 (JSClass *) &window_class, NULL);
174 if (!JSID_IS_INT(id))
175 return JS_TRUE;
177 undef_to_jsval(ctx, vp);
179 switch (JSID_TO_INT(id)) {
180 case JSP_LOC_HREF:
181 astring_to_jsval(ctx, vp, get_uri_string(vs->uri, URI_ORIGINAL));
182 break;
183 default:
184 /* Unrecognized integer property ID; someone is using
185 * the object as an array. SMJS builtin classes (e.g.
186 * js_RegExpClass) just return JS_TRUE in this case
187 * and leave *@vp unchanged. Do the same here.
188 * (Actually not quite the same, as we already used
189 * @undef_to_jsval.) */
190 break;
193 return JS_TRUE;
196 /* @location_class.setProperty */
197 static JSBool
198 location_set_property(JSContext *ctx, JSObject *obj, jsid id, JSBool strict, jsval *vp)
200 JSObject *parent_win; /* instance of @window_class */
201 struct view_state *vs;
202 struct document_view *doc_view;
204 /* This can be called if @obj if not itself an instance of the
205 * appropriate class but has one in its prototype chain. Fail
206 * such calls. */
207 if (!JS_InstanceOf(ctx, obj, (JSClass *) &location_class, NULL))
208 return JS_FALSE;
209 parent_win = JS_GetParent(ctx, obj);
210 assert(JS_InstanceOf(ctx, parent_win, (JSClass *) &window_class, NULL));
211 if_assert_failed return JS_FALSE;
213 vs = JS_GetInstancePrivate(ctx, parent_win,
214 (JSClass *) &window_class, NULL);
215 doc_view = vs->doc_view;
217 if (!JSID_IS_INT(id))
218 return JS_TRUE;
220 switch (JSID_TO_INT(id)) {
221 case JSP_LOC_HREF:
222 location_goto(doc_view, jsval_to_string(ctx, vp));
223 break;
226 return JS_TRUE;
229 static JSBool location_toString(JSContext *ctx, uintN argc, jsval *rval);
231 const spidermonkeyFunctionSpec location_funcs[] = {
232 { "toString", location_toString, 0 },
233 { "toLocaleString", location_toString, 0 },
234 { NULL }
237 /* @location_funcs{"toString"}, @location_funcs{"toLocaleString"} */
238 static JSBool
239 location_toString(JSContext *ctx, uintN argc, jsval *rval)
241 jsval val;
242 JSObject *obj = JS_THIS_OBJECT(ctx, rval);
243 JSBool ret = JS_GetProperty(ctx, obj, "href", &val);
245 JS_SET_RVAL(ctx, rval, val);
246 return ret;
249 struct delayed_goto {
250 /* It might look more convenient to pass doc_view around but it could
251 * disappear during wild dances inside of frames or so. */
252 struct view_state *vs;
253 struct uri *uri;
256 static void
257 delayed_goto(void *data)
259 struct delayed_goto *deg = data;
261 assert(deg);
262 if (deg->vs->doc_view
263 && deg->vs->doc_view == deg->vs->doc_view->session->doc_view) {
264 goto_uri_frame(deg->vs->doc_view->session, deg->uri,
265 deg->vs->doc_view->name,
266 CACHE_MODE_NORMAL);
268 done_uri(deg->uri);
269 mem_free(deg);
272 void
273 location_goto(struct document_view *doc_view, unsigned char *url)
275 unsigned char *new_abs_url;
276 struct uri *new_uri;
277 struct delayed_goto *deg;
279 /* Workaround for bug 611. Does not crash, but may lead to infinite loop.*/
280 if (!doc_view) return;
281 new_abs_url = join_urls(doc_view->document->uri,
282 trim_chars(url, ' ', 0));
283 if (!new_abs_url)
284 return;
285 new_uri = get_uri(new_abs_url, 0);
286 mem_free(new_abs_url);
287 if (!new_uri)
288 return;
289 deg = mem_calloc(1, sizeof(*deg));
290 if (!deg) {
291 done_uri(new_uri);
292 return;
294 assert(doc_view->vs);
295 deg->vs = doc_view->vs;
296 deg->uri = new_uri;
297 /* It does not seem to be very safe inside of frames to
298 * call goto_uri() right away. */
299 register_bottom_half(delayed_goto, deg);