1 /* "elinks.bookmarks" */
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 */
24 smjs_get_bookmark_generic_object(struct bookmark
*bookmark
, JSClass
*clasp
)
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
);
45 /* @bookmark_class.finalize, @bookmark_folder_class.finalize */
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. */
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
},
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
87 bookmark_string_to_jsval(JSContext
*ctx
, const unsigned char *str
, jsval
*vp
)
89 JSString
*jsstr
= utf8_to_jsstring(ctx
, str
, -1);
93 *vp
= STRING_TO_JSVAL(jsstr
);
97 /** Convert a jsval to a string and store it in struct bookmark.
100 * Context for memory allocations and error reports.
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. */
110 jsval_to_bookmark_string(JSContext
*ctx
, jsval val
, unsigned char **result
)
112 JSString
*jsstr
= NULL
;
115 /* JS_ValueToString constructs a new string if val is not
116 * already a string. Protect the new string from the garbage
117 * collector, which jsstring_to_utf8() may trigger.
119 * Actually, SpiderMonkey 1.8.5 does not require this
120 * JS_AddNamedStringRoot call because it conservatively scans
121 * the C stack for GC roots. Do the call anyway, because:
122 * 1. Omitting the call would require somehow ensuring that the
123 * C compiler won't reuse the stack location too early.
124 * (See template class js::Anchor in <jsapi.h>.)
125 * 2. Later versions of SpiderMonkey are switching back to
126 * precise GC rooting, with a C++-only API.
127 * 3. jsval_to_bookmark_string() does not seem speed-critical. */
128 if (!JS_AddNamedStringRoot(ctx
, &jsstr
, "jsval_to_bookmark_string"))
131 jsstr
= JS_ValueToString(ctx
, val
);
133 JS_RemoveStringRoot(ctx
, &jsstr
);
137 str
= jsstring_to_utf8(ctx
, jsstr
, NULL
);
139 JS_RemoveStringRoot(ctx
, &jsstr
);
143 JS_RemoveStringRoot(ctx
, &jsstr
);
144 mem_free_set(result
, str
);
148 /* @bookmark_class.getProperty */
150 bookmark_get_property(JSContext
*ctx
, JSObject
*obj
, jsid id
, jsval
*vp
)
152 struct bookmark
*bookmark
;
154 /* This can be called if @obj if not itself an instance of the
155 * appropriate class but has one in its prototype chain. Fail
157 if (!JS_InstanceOf(ctx
, obj
, (JSClass
*) &bookmark_class
, NULL
))
160 bookmark
= JS_GetInstancePrivate(ctx
, obj
,
161 (JSClass
*) &bookmark_class
, NULL
);
163 if (!bookmark
) return JS_FALSE
;
165 undef_to_jsval(ctx
, vp
);
167 if (!JSID_IS_INT(id
))
170 switch (JSID_TO_INT(id
)) {
172 return bookmark_string_to_jsval(ctx
, bookmark
->title
, vp
);
174 return bookmark_string_to_jsval(ctx
, bookmark
->url
, vp
);
175 case BOOKMARK_CHILDREN
:
176 *vp
= OBJECT_TO_JSVAL(smjs_get_bookmark_folder_object(bookmark
));
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.) */
190 /* @bookmark_class.setProperty */
192 bookmark_set_property(JSContext
*ctx
, JSObject
*obj
, jsid id
, JSBool strict
, jsval
*vp
)
194 struct bookmark
*bookmark
;
195 unsigned char *title
= NULL
;
196 unsigned char *url
= NULL
;
199 /* This can be called if @obj if not itself an instance of the
200 * appropriate class but has one in its prototype chain. Fail
202 if (!JS_InstanceOf(ctx
, obj
, (JSClass
*) &bookmark_class
, NULL
))
205 bookmark
= JS_GetInstancePrivate(ctx
, obj
,
206 (JSClass
*) &bookmark_class
, NULL
);
208 if (!bookmark
) return JS_FALSE
;
210 if (!JSID_IS_INT(id
))
213 switch (JSID_TO_INT(id
)) {
215 if (!jsval_to_bookmark_string(ctx
, *vp
, &title
))
219 if (!jsval_to_bookmark_string(ctx
, *vp
, &url
))
223 /* Unrecognized integer property ID; someone is using
224 * the object as an array. SMJS builtin classes (e.g.
225 * js_RegExpClass) just return JS_TRUE in this case.
226 * Do the same here. */
230 ok
= update_bookmark(bookmark
, get_cp_index("UTF-8"), title
, url
);
233 return ok
? JS_TRUE
: JS_FALSE
;
236 static const JSClass bookmark_class
= {
238 JSCLASS_HAS_PRIVATE
, /* struct bookmark * */
239 JS_PropertyStub
, JS_PropertyStub
,
240 bookmark_get_property
, bookmark_set_property
,
241 JS_EnumerateStub
, JS_ResolveStub
, JS_ConvertStub
, bookmark_finalize
,
245 smjs_get_bookmark_object(struct bookmark
*bookmark
)
249 jsobj
= smjs_get_bookmark_generic_object(bookmark
,
250 (JSClass
*) &bookmark_class
);
253 && JS_TRUE
== JS_DefineProperties(smjs_ctx
, jsobj
,
254 (JSPropertySpec
*) bookmark_props
))
261 /*** bookmark folder object ***/
263 /* @bookmark_folder_class.getProperty */
265 bookmark_folder_get_property(JSContext
*ctx
, JSObject
*obj
, jsid id
, jsval
*vp
)
267 struct bookmark
*bookmark
;
268 struct bookmark
*folder
;
269 jsval title_jsval
= JSVAL_VOID
;
270 unsigned char *title
= NULL
;
272 /* This can be called if @obj if not itself an instance of the
273 * appropriate class but has one in its prototype chain. Fail
275 if (!JS_InstanceOf(ctx
, obj
, (JSClass
*) &bookmark_folder_class
, NULL
))
278 folder
= JS_GetInstancePrivate(ctx
, obj
,
279 (JSClass
*) &bookmark_folder_class
, NULL
);
283 if (!JS_IdToValue(ctx
, id
, &title_jsval
))
286 if (!jsval_to_bookmark_string(ctx
, title_jsval
, &title
))
289 bookmark
= get_bookmark_by_name(folder
, title
);
291 *vp
= OBJECT_TO_JSVAL(smjs_get_bookmark_object(bookmark
));
298 static const JSClass bookmark_folder_class
= {
300 JSCLASS_HAS_PRIVATE
, /* struct bookmark * */
301 JS_PropertyStub
, JS_PropertyStub
,
302 bookmark_folder_get_property
, JS_StrictPropertyStub
,
303 JS_EnumerateStub
, JS_ResolveStub
, JS_ConvertStub
, bookmark_finalize
,
307 smjs_get_bookmark_folder_object(struct bookmark
*bookmark
)
309 return smjs_get_bookmark_generic_object(bookmark
,
310 (JSClass
*) &bookmark_folder_class
);
314 smjs_init_bookmarks_interface(void)
317 struct JSObject
*bookmarks_object
;
319 if (!smjs_ctx
|| !smjs_elinks_object
)
322 bookmarks_object
= smjs_get_bookmark_folder_object(NULL
);
323 if (!bookmarks_object
) return;
325 val
= OBJECT_TO_JSVAL(bookmarks_object
);
327 JS_SetProperty(smjs_ctx
, smjs_elinks_object
, "bookmarks", &val
);