Use update_bookmark() in SMJS bookmark object.
[elinks.git] / src / scripting / smjs / bookmarks.c
blobf12f84bf257a52f069761bed290dac52fc01caf8
1 /* "elinks.bookmarks" */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include "elinks.h"
9 #include "bookmarks/bookmarks.h"
10 #include "ecmascript/spidermonkey-shared.h"
11 #include "intl/charsets.h"
12 #include "main/event.h"
13 #include "scripting/smjs/core.h"
14 #include "scripting/smjs/elinks_object.h"
15 #include "util/memory.h"
18 static const JSClass bookmark_class, bookmark_folder_class; /* defined below */
21 /*** common code ***/
23 static JSObject *
24 smjs_get_bookmark_generic_object(struct bookmark *bookmark, JSClass *clasp)
26 JSObject *jsobj;
28 assert(clasp == &bookmark_class || clasp == &bookmark_folder_class);
29 if_assert_failed return NULL;
31 jsobj = JS_NewObject(smjs_ctx, clasp, NULL, NULL);
32 if (!jsobj) return NULL;
34 if (!bookmark) return jsobj;
36 if (JS_TRUE == JS_SetPrivate(smjs_ctx, jsobj, bookmark)) { /* to @bookmark_class or @bookmark_folder_class */
37 object_lock(bookmark);
39 return jsobj;
42 return NULL;
45 /* @bookmark_class.finalize, @bookmark_folder_class.finalize */
46 static void
47 bookmark_finalize(JSContext *ctx, JSObject *obj)
49 struct bookmark *bookmark;
51 assert(JS_InstanceOf(ctx, obj, (JSClass *) &bookmark_class, NULL)
52 || JS_InstanceOf(ctx, obj, (JSClass *) &bookmark_folder_class, NULL));
53 if_assert_failed return;
55 bookmark = JS_GetPrivate(ctx, obj); /* from @bookmark_class or @bookmark_folder_class */
57 if (bookmark) object_unlock(bookmark);
61 /*** bookmark object ***/
63 /* Tinyids of properties. Use negative values to distinguish these
64 * from array indexes (even though this object has no array elements).
65 * ECMAScript code should not use these directly as in bookmark[-1];
66 * future versions of ELinks may change the numbers. */
67 enum bookmark_prop {
68 BOOKMARK_TITLE = -1,
69 BOOKMARK_URL = -2,
70 BOOKMARK_CHILDREN = -3,
73 static const JSPropertySpec bookmark_props[] = {
74 { "title", BOOKMARK_TITLE, JSPROP_ENUMERATE },
75 { "url", BOOKMARK_URL, JSPROP_ENUMERATE },
76 { "children", BOOKMARK_CHILDREN, JSPROP_ENUMERATE | JSPROP_READONLY },
77 { NULL }
80 static JSObject *smjs_get_bookmark_folder_object(struct bookmark *bookmark);
82 /** Convert a string retrieved from struct bookmark to a jsval.
84 * @return JS_TRUE if successful. On error, report the error and
85 * return JS_FALSE. */
86 static JSBool
87 bookmark_string_to_jsval(JSContext *ctx, const unsigned char *str, jsval *vp)
89 JSString *jsstr = utf8_to_jsstring(ctx, str, -1);
91 if (jsstr == NULL)
92 return JS_FALSE;
93 *vp = STRING_TO_JSVAL(jsstr);
94 return JS_TRUE;
97 /** Convert a jsval to a string and store it in struct bookmark.
99 * @param ctx
100 * Context for memory allocations and error reports.
101 * @param val
102 * The @c jsval that should be converted.
103 * @param[in,out] result
104 * A string allocated with mem_alloc().
105 * On success, this function frees the original string, if any.
107 * @return JS_TRUE if successful. On error, report the error to
108 * SpiderMonkey and return JS_FALSE. */
109 static JSBool
110 jsval_to_bookmark_string(JSContext *ctx, jsval val, unsigned char **result)
112 JSString *jsstr = NULL;
113 unsigned char *str;
115 /* jsstring_to_utf8() might GC; protect the string to come. */
116 if (!JS_AddNamedRoot(ctx, &jsstr, "jsval_to_bookmark_string"))
117 return JS_FALSE;
119 jsstr = JS_ValueToString(ctx, val);
120 if (jsstr == NULL) {
121 JS_RemoveRoot(ctx, &jsstr);
122 return JS_FALSE;
125 str = jsstring_to_utf8(ctx, jsstr, NULL);
126 if (str == NULL) {
127 JS_RemoveRoot(ctx, &jsstr);
128 return JS_FALSE;
131 JS_RemoveRoot(ctx, &jsstr);
132 mem_free_set(result, str);
133 return JS_TRUE;
136 /* @bookmark_class.getProperty */
137 static JSBool
138 bookmark_get_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp)
140 struct bookmark *bookmark;
142 /* This can be called if @obj if not itself an instance of the
143 * appropriate class but has one in its prototype chain. Fail
144 * such calls. */
145 if (!JS_InstanceOf(ctx, obj, (JSClass *) &bookmark_class, NULL))
146 return JS_FALSE;
148 bookmark = JS_GetInstancePrivate(ctx, obj,
149 (JSClass *) &bookmark_class, NULL);
151 if (!bookmark) return JS_FALSE;
153 undef_to_jsval(ctx, vp);
155 if (!JSVAL_IS_INT(id))
156 return JS_FALSE;
158 switch (JSVAL_TO_INT(id)) {
159 case BOOKMARK_TITLE:
160 return bookmark_string_to_jsval(ctx, bookmark->title, vp);
161 case BOOKMARK_URL:
162 return bookmark_string_to_jsval(ctx, bookmark->url, vp);
163 case BOOKMARK_CHILDREN:
164 *vp = OBJECT_TO_JSVAL(smjs_get_bookmark_folder_object(bookmark));
166 return JS_TRUE;
167 default:
168 /* Unrecognized integer property ID; someone is using
169 * the object as an array. SMJS builtin classes (e.g.
170 * js_RegExpClass) just return JS_TRUE in this case
171 * and leave *@vp unchanged. Do the same here.
172 * (Actually not quite the same, as we already used
173 * @undef_to_jsval.) */
174 return JS_TRUE;
178 /* @bookmark_class.setProperty */
179 static JSBool
180 bookmark_set_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp)
182 struct bookmark *bookmark;
183 unsigned char *title = NULL;
184 unsigned char *url = NULL;
185 int ok;
187 /* This can be called if @obj if not itself an instance of the
188 * appropriate class but has one in its prototype chain. Fail
189 * such calls. */
190 if (!JS_InstanceOf(ctx, obj, (JSClass *) &bookmark_class, NULL))
191 return JS_FALSE;
193 bookmark = JS_GetInstancePrivate(ctx, obj,
194 (JSClass *) &bookmark_class, NULL);
196 if (!bookmark) return JS_FALSE;
198 if (!JSVAL_IS_INT(id))
199 return JS_FALSE;
201 switch (JSVAL_TO_INT(id)) {
202 case BOOKMARK_TITLE:
203 if (!jsval_to_bookmark_string(ctx, *vp, &title))
204 return JS_FALSE;
205 break;
206 case BOOKMARK_URL:
207 if (!jsval_to_bookmark_string(ctx, *vp, &url))
208 return JS_FALSE;
209 break;
210 default:
211 /* Unrecognized integer property ID; someone is using
212 * the object as an array. SMJS builtin classes (e.g.
213 * js_RegExpClass) just return JS_TRUE in this case.
214 * Do the same here. */
215 return JS_TRUE;
218 ok = update_bookmark(bookmark, get_cp_index("UTF-8"), title, url);
219 mem_free_if(title);
220 mem_free_if(url);
221 return ok ? JS_TRUE : JS_FALSE;
224 static const JSClass bookmark_class = {
225 "bookmark",
226 JSCLASS_HAS_PRIVATE, /* struct bookmark * */
227 JS_PropertyStub, JS_PropertyStub,
228 bookmark_get_property, bookmark_set_property,
229 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, bookmark_finalize,
232 static JSObject *
233 smjs_get_bookmark_object(struct bookmark *bookmark)
235 JSObject *jsobj;
237 jsobj = smjs_get_bookmark_generic_object(bookmark,
238 (JSClass *) &bookmark_class);
240 if (jsobj
241 && JS_TRUE == JS_DefineProperties(smjs_ctx, jsobj,
242 (JSPropertySpec *) bookmark_props))
243 return jsobj;
245 return NULL;
249 /*** bookmark folder object ***/
251 /* @bookmark_folder_class.getProperty */
252 static JSBool
253 bookmark_folder_get_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp)
255 struct bookmark *bookmark;
256 struct bookmark *folder;
257 unsigned char *title = NULL;
259 /* This can be called if @obj if not itself an instance of the
260 * appropriate class but has one in its prototype chain. Fail
261 * such calls. */
262 if (!JS_InstanceOf(ctx, obj, (JSClass *) &bookmark_folder_class, NULL))
263 return JS_FALSE;
265 folder = JS_GetInstancePrivate(ctx, obj,
266 (JSClass *) &bookmark_folder_class, NULL);
268 *vp = JSVAL_NULL;
270 if (!jsval_to_bookmark_string(ctx, id, &title))
271 return JS_FALSE;
273 bookmark = get_bookmark_by_name(folder, title);
274 if (bookmark) {
275 *vp = OBJECT_TO_JSVAL(smjs_get_bookmark_object(bookmark));
278 mem_free(title);
279 return JS_TRUE;
282 static const JSClass bookmark_folder_class = {
283 "bookmark_folder",
284 JSCLASS_HAS_PRIVATE, /* struct bookmark * */
285 JS_PropertyStub, JS_PropertyStub,
286 bookmark_folder_get_property, JS_PropertyStub,
287 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, bookmark_finalize,
290 static JSObject *
291 smjs_get_bookmark_folder_object(struct bookmark *bookmark)
293 return smjs_get_bookmark_generic_object(bookmark,
294 (JSClass *) &bookmark_folder_class);
297 void
298 smjs_init_bookmarks_interface(void)
300 jsval val;
301 struct JSObject *bookmarks_object;
303 if (!smjs_ctx || !smjs_elinks_object)
304 return;
306 bookmarks_object = smjs_get_bookmark_folder_object(NULL);
307 if (!bookmarks_object) return;
309 val = OBJECT_TO_JSVAL(bookmarks_object);
311 JS_SetProperty(smjs_ctx, smjs_elinks_object, "bookmarks", &val);