Bug 1026: Protect callback of elinks.load_uri from GC
[elinks/kon.git] / src / scripting / smjs / load_uri.c
blobb8adcc2caad257d6a32c75f7e4bee3ef7e04bf7e
1 /* ECMAScript browser scripting module */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include "elinks.h"
9 #include "ecmascript/spidermonkey/util.h"
10 #include "network/connection.h"
11 #include "protocol/uri.h"
12 #include "scripting/smjs/core.h"
13 #include "scripting/smjs/cache_object.h"
14 #include "scripting/smjs/elinks_object.h"
15 #include "session/download.h"
18 struct smjs_load_uri_hop {
19 struct session *ses;
21 /* SpiderMonkey versions earlier than 1.8 cannot properly call
22 * a closure if given just a JSFunction pointer. They need a
23 * jsval that points to the corresponding JSObject. Besides,
24 * JS_AddNamedRoot is not documented to support JSFunction
25 * pointers. */
26 jsval callback;
29 static void
30 smjs_loading_callback(struct download *download, void *data)
32 struct session *saved_smjs_ses = smjs_ses;
33 struct smjs_load_uri_hop *hop = data;
34 jsval args[1], rval;
35 JSObject *cache_entry_object;
37 if (is_in_progress_state(download->state)) return;
39 if (!download->cached) goto end;
41 /* download->cached->object.refcount is typically 0 here
42 * because no struct document uses the cache entry. Because
43 * the connection is no longer using the cache entry either,
44 * it can be garbage collected. Don't let that happen while
45 * the script is using it. */
46 object_lock(download->cached);
48 smjs_ses = hop->ses;
50 cache_entry_object = smjs_get_cache_entry_object(download->cached);
51 if (!cache_entry_object) goto end;
53 args[0] = OBJECT_TO_JSVAL(cache_entry_object);
54 JS_CallFunctionValue(smjs_ctx, NULL, hop->callback, 1, args, &rval);
56 end:
57 if (download->cached)
58 object_unlock(download->cached);
59 JS_RemoveRoot(smjs_ctx, &hop->callback);
60 mem_free(download->data);
61 mem_free(download);
63 smjs_ses = saved_smjs_ses;
66 static JSBool
67 smjs_load_uri(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv,
68 jsval *rval)
70 struct smjs_load_uri_hop *hop;
71 struct download *download;
72 JSString *jsstr;
73 unsigned char *uri_string;
74 struct uri *uri;
76 if (argc < 2) return JS_FALSE;
78 jsstr = JS_ValueToString(smjs_ctx, argv[0]);
79 uri_string = JS_GetStringBytes(jsstr);
81 uri = get_uri(uri_string, 0);
82 if (!uri) return JS_FALSE;
84 download = mem_alloc(sizeof(*download));
85 if (!download) {
86 done_uri(uri);
87 return JS_FALSE;
90 hop = mem_alloc(sizeof(*hop));
91 if (!hop) {
92 mem_free(download);
93 done_uri(uri);
94 return JS_FALSE;
97 hop->callback = argv[1];
98 hop->ses = smjs_ses;
99 if (!JS_AddNamedRoot(smjs_ctx, &hop->callback,
100 "smjs_load_uri_hop.callback")) {
101 mem_free(hop);
102 mem_free(download);
103 done_uri(uri);
104 return JS_FALSE;
107 download->data = hop;
108 download->callback = (download_callback_T *) smjs_loading_callback;
110 load_uri(uri, NULL, download, PRI_MAIN, CACHE_MODE_NORMAL, -1);
112 done_uri(uri);
114 return JS_TRUE;
117 void
118 smjs_init_load_uri_interface(void)
120 if (!smjs_ctx || !smjs_elinks_object)
121 return;
123 JS_DefineFunction(smjs_ctx, smjs_elinks_object, "load_uri",
124 &smjs_load_uri, 2, 0);