Line data Source code
1 : /*
2 : * GPAC - Multimedia Framework C SDK
3 : *
4 : * Authors: Jean Le Feuvre
5 : * Copyright (c) Telecom ParisTech 2000-2019
6 : * All rights reserved
7 : *
8 : * This file is part of GPAC / Scene Graph sub-project
9 : *
10 : * GPAC is free software; you can redistribute it and/or modify
11 : * it under the terms of the GNU Lesser General Public License as published by
12 : * the Free Software Foundation; either version 2, or (at your option)
13 : * any later version.
14 : *
15 : * GPAC is distributed in the hope that it will be useful,
16 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 : * GNU Lesser General Public License for more details.
19 : *
20 : * You should have received a copy of the GNU Lesser General Public
21 : * License along with this library; see the file COPYING. If not, write to
22 : * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23 : *
24 : */
25 :
26 : #include <gpac/internal/scenegraph_dev.h>
27 :
28 :
29 : #ifdef GPAC_HAS_QJS
30 :
31 : #include <gpac/internal/compositor_dev.h>
32 : #include <gpac/modules/compositor_ext.h>
33 :
34 : #include "qjs_common.h"
35 :
36 : typedef struct
37 : {
38 :
39 : u8 is_setup;
40 : /*set to 1 for proto IS fields*/
41 : u8 IS_route;
42 : /*set to 1 for JS route to fun*/
43 : u8 script_route;
44 :
45 : u32 ID;
46 : char *name;
47 :
48 : /*scope of this route*/
49 : GF_SceneGraph *graph;
50 : u32 lastActivateTime;
51 :
52 : GF_Node *FromNode;
53 : GF_FieldInfo FromField;
54 :
55 : GF_Node *ToNode;
56 : GF_FieldInfo ToField;
57 :
58 : JSValue obj;
59 : JSValue fun;
60 : } GF_RouteToScript;
61 :
62 : typedef struct __gf_js_field
63 : {
64 : GF_FieldInfo field;
65 : GF_Node *owner;
66 : JSValue obj;
67 :
68 : /*list of JSValue * for MFFields (except MFNode) or NULL
69 : we need this to keep the link between an MF slot and the parent node since everything is passed by reference*/
70 : JSValue *mfvals;
71 : u32 mfvals_count;
72 :
73 : /*pointer to the SFNode if this is an SFNode or MFNode[i] field */
74 : GF_Node *node;
75 : /*when creating MFnode from inside the script, the node list is stored here until attached to an object*/
76 : GF_ChildNodeItem *temp_list;
77 : /*only set when not owned by a node, in which case field.far_ptr is also set to this value*/
78 : void *field_ptr;
79 :
80 : /*context in which the field was created*/
81 : struct JSContext *js_ctx;
82 : } GF_JSField;
83 :
84 0 : JSValue js_throw_err_msg(JSContext *ctx, s32 err, const char *fmt, ...)
85 : {
86 0 : JSValue obj = JS_NewError(ctx);
87 0 : if (JS_IsException(obj)) {
88 0 : obj = JS_NULL;
89 : } else {
90 0 : JS_DefinePropertyValueStr(ctx, obj, "code", JS_NewInt32(ctx, err), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
91 0 : if (fmt) {
92 : char szMsg[2050];
93 : va_list vl;
94 0 : va_start(vl, fmt);
95 : vsnprintf(szMsg, 2048, fmt, vl);
96 0 : va_end(vl);
97 :
98 0 : JS_DefinePropertyValueStr(ctx, obj, "message", JS_NewString(ctx, szMsg), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
99 : }
100 : }
101 0 : return JS_Throw(ctx, obj);
102 : }
103 :
104 2 : JSValue js_throw_err(JSContext *ctx, s32 err)
105 : {
106 2 : JSValue obj = JS_NewError(ctx);
107 2 : if (JS_IsException(obj)) {
108 0 : obj = JS_NULL;
109 : } else {
110 2 : JS_DefinePropertyValueStr(ctx, obj, "code", JS_NewInt32(ctx, err), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
111 : }
112 2 : return JS_Throw(ctx, obj);
113 : }
114 :
115 :
116 : #define _ScriptMessage(_c, _msg) { \
117 : GF_Node *_n = (GF_Node *) JS_GetContextOpaque(_c); \
118 : if (_n->sgprivate->scenegraph->script_action) {\
119 : GF_JSAPIParam par; \
120 : par.info.e = GF_SCRIPT_INFO; \
121 : par.info.msg = (_msg); \
122 : _n->sgprivate->scenegraph->script_action(_n->sgprivate->scenegraph->script_action_cbck, GF_JSAPI_OP_MESSAGE, NULL, &par);\
123 : } \
124 : }
125 :
126 : #ifndef GPAC_DISABLE_PLAYER
127 72 : static Bool ScriptAction(JSContext *c, GF_SceneGraph *scene, u32 type, GF_Node *node, GF_JSAPIParam *param)
128 : {
129 72 : if (!scene) {
130 0 : GF_Node *n = (GF_Node *) JS_GetContextOpaque(c);
131 0 : scene = n->sgprivate->scenegraph;
132 : }
133 72 : if (scene->script_action)
134 72 : return scene->script_action(scene->script_action_cbck, type, node, param);
135 : return 0;
136 : }
137 : #endif// GPAC_DISABLE_PLAYER
138 :
139 : GF_JSClass SFNodeClass;
140 : #ifndef GPAC_DISABLE_VRML
141 : GF_JSClass globalClass;
142 : GF_JSClass browserClass;
143 : GF_JSClass SFVec2fClass;
144 : GF_JSClass SFVec3fClass;
145 : GF_JSClass SFRotationClass;
146 : GF_JSClass SFColorClass;
147 : GF_JSClass SFImageClass;
148 : GF_JSClass MFInt32Class;
149 : GF_JSClass MFBoolClass;
150 : GF_JSClass MFFloatClass;
151 : GF_JSClass MFTimeClass;
152 : GF_JSClass MFVec2fClass;
153 : GF_JSClass MFVec3fClass;
154 : GF_JSClass MFRotationClass;
155 : GF_JSClass MFColorClass;
156 : GF_JSClass MFStringClass;
157 : GF_JSClass MFUrlClass;
158 : GF_JSClass MFNodeClass;
159 : GF_JSClass AnyClass;
160 : #endif
161 :
162 : typedef struct
163 : {
164 : JSRuntime *js_runtime;
165 : u32 nb_inst;
166 : JSContext *ctx;
167 :
168 : GF_Mutex *mx;
169 : GF_List *allocated_contexts;
170 : } GF_JSRuntime;
171 :
172 : static GF_JSRuntime *js_rt = NULL;
173 :
174 0 : int qjs_module_set_import_meta(JSContext *ctx, JSValueConst func_val, Bool use_realpath, Bool is_main)
175 : {
176 : JSModuleDef *m;
177 : JSValue meta_obj;
178 : JSAtom module_name_atom;
179 : const char *module_name, *src_file;
180 :
181 : assert(JS_VALUE_GET_TAG(func_val) == JS_TAG_MODULE);
182 0 : m = JS_VALUE_GET_PTR(func_val);
183 :
184 0 : module_name_atom = JS_GetModuleName(ctx, m);
185 0 : module_name = JS_AtomToCString(ctx, module_name_atom);
186 0 : JS_FreeAtom(ctx, module_name_atom);
187 0 : if (!module_name)
188 : return -1;
189 : src_file = module_name;
190 0 : if (!strchr(module_name, ':')) {
191 : char buf[GF_MAX_PATH + 16];
192 : strcpy(buf, "file://");
193 : #if !defined(_WIN32)
194 : /* realpath() cannot be used with modules compiled with qjsc
195 : because the corresponding module source code is not
196 : necessarily present */
197 0 : if (use_realpath) {
198 0 : char *res = realpath(module_name, buf + strlen(buf));
199 0 : if (!res) {
200 0 : JS_ThrowTypeError(ctx, "realpath failure");
201 0 : JS_FreeCString(ctx, module_name);
202 0 : return -1;
203 : }
204 : src_file = res;
205 : }
206 : #endif
207 : }
208 :
209 0 : meta_obj = JS_GetImportMeta(ctx, m);
210 0 : if (JS_IsException(meta_obj)) {
211 0 : JS_FreeCString(ctx, module_name);
212 0 : return -1;
213 : }
214 0 : JS_DefinePropertyValueStr(ctx, meta_obj, "url", JS_NewString(ctx, src_file), JS_PROP_C_W_E);
215 0 : JS_DefinePropertyValueStr(ctx, meta_obj, "main", JS_NewBool(ctx, is_main), JS_PROP_C_W_E);
216 0 : JS_FreeCString(ctx, module_name);
217 : JS_FreeValue(ctx, meta_obj);
218 : return 0;
219 : }
220 :
221 :
222 : #ifndef GPAC_STATIC_BUILD
223 :
224 : #if defined(WIN32) || defined(_WIN32_WCE)
225 : #include <windows.h>
226 : #else
227 : #include <dlfcn.h>
228 : #endif
229 :
230 : typedef JSModuleDef *(JSInitModuleFunc)(JSContext *ctx, const char *module_name);
231 :
232 0 : static JSModuleDef *qjs_module_loader_dyn_lib(JSContext *ctx,
233 : const char *module_name)
234 : {
235 : JSModuleDef *m=NULL;
236 : void *hd;
237 : JSInitModuleFunc *init;
238 : char *filename;
239 :
240 0 : if (!strchr(module_name, '/') || !strchr(module_name, '\\')) {
241 : /* must add a '/' so that the DLL is not searched in the system library paths */
242 0 : filename = gf_malloc(strlen(module_name) + 2 + 1);
243 0 : if (!filename) return NULL;
244 : strcpy(filename, "./");
245 0 : strcpy(filename + 2, module_name);
246 : } else {
247 : filename = (char *)module_name;
248 : }
249 :
250 : /* load dynamic lib */
251 : #ifdef WIN32
252 : hd = LoadLibrary(filename);
253 : #else
254 0 : hd = dlopen(filename, RTLD_NOW | RTLD_LOCAL);
255 : #endif
256 :
257 0 : if (filename != module_name)
258 0 : gf_free(filename);
259 :
260 0 : if (!hd) {
261 0 : JS_ThrowReferenceError(ctx, "could not load module filename '%s' as shared library", module_name);
262 0 : return NULL;
263 : }
264 :
265 : #ifdef WIN32
266 : init = (JSInitModuleFunc *) GetProcAddress(hd, "js_init_module");
267 : #else
268 0 : init = (JSInitModuleFunc *) dlsym(hd, "js_init_module");
269 : #endif
270 :
271 0 : if (!init) {
272 0 : JS_ThrowReferenceError(ctx, "could not load module filename '%s': js_init_module not found", module_name);
273 : } else {
274 0 : m = init(ctx, module_name);
275 0 : if (!m) {
276 0 : JS_ThrowReferenceError(ctx, "could not load module filename '%s': initialization error", module_name);
277 : }
278 : }
279 : #ifdef WIN32
280 : FreeLibrary(hd);
281 : #else
282 0 : dlclose(hd);
283 : #endif
284 :
285 0 : return m;
286 : }
287 :
288 : #endif // GPAC_STATIC_BUILD
289 :
290 0 : JSModuleDef *qjs_module_loader(JSContext *ctx, const char *module_name, void *opaque)
291 : {
292 : JSModuleDef *m;
293 0 : const char *fext = gf_file_ext_start(module_name);
294 :
295 0 : if (fext && (!strcmp(fext, ".so") || !strcmp(fext, ".dll") || !strcmp(fext, ".dylib")) ) {
296 : #ifndef GPAC_STATIC_BUILD
297 0 : m = qjs_module_loader_dyn_lib(ctx, module_name);
298 : #else
299 : JS_ThrowReferenceError(ctx, "could not load module filename '%s', dynamic library loading disabled in build", module_name);
300 : m = NULL;
301 : #endif
302 : } else {
303 : u32 buf_len;
304 : u8 *buf;
305 : JSValue func_val;
306 0 : GF_Err e = gf_file_load_data(module_name, &buf, &buf_len);
307 :
308 0 : if (e != GF_OK) {
309 0 : JS_ThrowReferenceError(ctx, "could not load module filename '%s': %s", module_name, gf_error_to_string(e) );
310 0 : return NULL;
311 : }
312 : /* compile the module */
313 0 : func_val = JS_Eval(ctx, (char *)buf, buf_len, module_name, JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY);
314 0 : js_free(ctx, buf);
315 0 : if (JS_IsException(func_val))
316 : return NULL;
317 : /* XXX: could propagate the exception */
318 0 : qjs_module_set_import_meta(ctx, func_val, GF_TRUE, GF_FALSE);
319 : /* the module is already referenced, so we must free it */
320 : m = JS_VALUE_GET_PTR(func_val);
321 : JS_FreeValue(ctx, func_val);
322 : }
323 : return m;
324 : }
325 :
326 480 : JSContext *gf_js_create_context()
327 : {
328 : JSContext *ctx;
329 480 : if (!js_rt) {
330 117 : JSRuntime *js_runtime = JS_NewRuntime();
331 117 : if (!js_runtime) {
332 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[ECMAScript] Cannot allocate ECMAScript runtime\n"));
333 : return NULL;
334 : }
335 117 : GF_SAFEALLOC(js_rt, GF_JSRuntime);
336 117 : if (!js_rt) {
337 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[JS] Failed to create script runtime\n"));
338 : return NULL;
339 : }
340 117 : js_rt->js_runtime = js_runtime;
341 117 : js_rt->allocated_contexts = gf_list_new();
342 117 : js_rt->mx = gf_mx_new("JavaScript");
343 117 : GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[ECMAScript] ECMAScript runtime allocated %p\n", js_runtime));
344 :
345 117 : JS_SetModuleLoaderFunc(js_rt->js_runtime, NULL, qjs_module_loader, NULL);
346 : }
347 480 : js_rt->nb_inst++;
348 :
349 480 : gf_mx_p(js_rt->mx);
350 :
351 480 : ctx = JS_NewContext(js_rt->js_runtime);
352 :
353 480 : gf_list_add(js_rt->allocated_contexts, ctx);
354 480 : gf_mx_v(js_rt->mx);
355 :
356 480 : return ctx;
357 : }
358 :
359 480 : void gf_js_delete_context(JSContext *ctx)
360 : {
361 480 : if (!js_rt) return;
362 :
363 480 : gf_js_call_gc(ctx);
364 :
365 480 : gf_mx_p(js_rt->mx);
366 480 : gf_list_del_item(js_rt->allocated_contexts, ctx);
367 480 : JS_FreeContext(ctx);
368 480 : gf_mx_v(js_rt->mx);
369 :
370 480 : js_rt->nb_inst --;
371 480 : if (js_rt->nb_inst == 0) {
372 : //persistent context, do not delete runtime but perform GC
373 117 : if (gf_opts_get_bool("temp", "peristent-jsrt")) {
374 0 : JS_RunGC(js_rt->js_runtime);
375 0 : return;
376 : }
377 :
378 117 : JS_FreeRuntime(js_rt->js_runtime);
379 117 : gf_list_del(js_rt->allocated_contexts);
380 117 : gf_mx_del(js_rt->mx);
381 117 : gf_free(js_rt);
382 117 : js_rt = NULL;
383 : }
384 : }
385 : GF_EXPORT
386 1 : void gf_js_delete_runtime()
387 : {
388 1 : if (js_rt) {
389 0 : JS_FreeRuntime(js_rt->js_runtime);
390 0 : gf_list_del(js_rt->allocated_contexts);
391 0 : gf_mx_del(js_rt->mx);
392 0 : gf_free(js_rt);
393 0 : js_rt = NULL;
394 : }
395 1 : }
396 :
397 :
398 : #ifndef GPAC_DISABLE_SVG
399 : /*SVG tags for script handling*/
400 : #include <gpac/nodes_svg.h>
401 :
402 : GF_Node *dom_get_element(JSContext *c, JSValue obj);
403 : #endif
404 :
405 :
406 : void gf_sg_script_to_node_field(struct JSContext *c, JSValue v, GF_FieldInfo *field, GF_Node *owner, GF_JSField *parent);
407 : JSValue gf_sg_script_to_qjs_field(GF_ScriptPriv *priv, GF_FieldInfo *field, GF_Node *parent, Bool force_evaluate);
408 :
409 : #ifndef GPAC_DISABLE_PLAYER
410 : static void JSScript_NodeModified(GF_SceneGraph *sg, GF_Node *node, GF_FieldInfo *info, GF_Node *script);
411 : #endif
412 :
413 :
414 : Bool JSScriptFromFile(GF_Node *node, const char *opt_file, Bool no_complain, JSValue *rval);
415 :
416 : #ifndef GPAC_DISABLE_SVG
417 : static JSValue vrml_event_add_listener(JSContext *c, JSValueConst this_val, int argc, JSValueConst *argv);
418 : static JSValue vrml_event_remove_listener(JSContext *c, JSValueConst this_val, int argc, JSValueConst *argv);
419 : #endif // GPAC_DISABLE_SVG
420 :
421 510 : void gf_js_call_gc(JSContext *c)
422 : {
423 510 : gf_js_lock(c, 1);
424 510 : JS_RunGC(js_rt->js_runtime);
425 510 : gf_js_lock(c, 0);
426 510 : }
427 :
428 0 : void do_js_gc(JSContext *c, GF_Node *node)
429 : {
430 : #ifdef FORCE_GC
431 : node->sgprivate->scenegraph->trigger_gc = GF_TRUE;
432 : #endif
433 :
434 2255 : if (node->sgprivate->scenegraph->trigger_gc) {
435 2 : node->sgprivate->scenegraph->trigger_gc = GF_FALSE;
436 2 : gf_js_call_gc(c);
437 : }
438 0 : }
439 :
440 :
441 1817 : static JSValue js_print_ex(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, u32 ltool, u32 error_type)
442 : {
443 : int i=0;
444 : Bool first=GF_TRUE;
445 1817 : s32 logl = GF_LOG_INFO;
446 : JSValue v, g;
447 : const char *c_logname=NULL;
448 : const char *log_name = "JS";
449 :
450 2468 : if ((argc>1) && JS_IsNumber(argv[0])) {
451 651 : JS_ToInt32(ctx, &logl, argv[0]);
452 : i=1;
453 : }
454 1817 : if (error_type)
455 81 : logl = GF_LOG_ERROR;
456 1817 : g = JS_GetGlobalObject(ctx);
457 1817 : v = JS_GetPropertyStr(ctx, g, "_gpac_log_name");
458 1817 : if (!JS_IsUndefined(v) && !JS_IsNull(v)) {
459 : c_logname = JS_ToCString(ctx, v);
460 : JS_FreeValue(ctx, v);
461 1651 : if (c_logname) {
462 : log_name = c_logname;
463 1651 : if (!strlen(log_name))
464 : log_name = NULL;
465 : }
466 : }
467 : JS_FreeValue(ctx, g);
468 :
469 1817 : if (log_name) {
470 : #ifndef GPAC_DISABLE_LOG
471 1600 : GF_LOG(logl, ltool, ("[%s] ", log_name));
472 : #else
473 : fprintf(stderr, "[%s] ", log_name);
474 : #endif
475 : }
476 1817 : if (error_type==2) {
477 : #ifndef GPAC_DISABLE_LOG
478 0 : GF_LOG(logl, ltool, ("Throw "));
479 : #else
480 : fprintf(stderr, "Throw ");
481 : #endif
482 : }
483 :
484 1817 : for (; i < argc; i++) {
485 1817 : const char *str = JS_ToCString(ctx, argv[i]);
486 1817 : if (!str) return JS_EXCEPTION;
487 :
488 1817 : if (logl==-1) {
489 0 : gf_sys_format_help(stderr, GF_PRINTARG_HIGHLIGHT_FIRST, "%s\n", str);
490 1817 : } else if (logl==-2) {
491 0 : gf_sys_format_help(stderr, 0, "%s\n", str);
492 1817 : } else if (logl<0) {
493 0 : fprintf(stderr, "%s%s", (first) ? "" : " ", str);
494 : } else {
495 : #ifndef GPAC_DISABLE_LOG
496 1817 : GF_LOG(logl, ltool, ("%s%s", (first) ? "" : " ", str));
497 : #else
498 : fprintf(stderr, "%s%s", (first) ? "" : " ", str);
499 : #endif
500 : }
501 1817 : JS_FreeCString(ctx, str);
502 : first=GF_FALSE;
503 : }
504 : #ifndef GPAC_DISABLE_LOG
505 1817 : GF_LOG(logl, ltool, ("\n"));
506 : #else
507 : fprintf(stderr, "\n");
508 : #endif
509 1817 : if (c_logname) JS_FreeCString(ctx, c_logname);
510 1817 : return JS_UNDEFINED;
511 : }
512 1736 : JSValue js_print(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
513 : {
514 1736 : return js_print_ex(ctx, this_val, argc, argv, GF_LOG_CONSOLE, GF_FALSE);
515 :
516 : }
517 :
518 81 : void js_dump_error(JSContext *ctx)
519 : {
520 : JSValue exception_val;
521 : Bool is_error;
522 : u32 err_type = 1;
523 81 : exception_val = JS_GetException(ctx);
524 81 : is_error = JS_IsError(ctx, exception_val);
525 81 : if (!is_error) err_type = 2;
526 :
527 :
528 81 : js_print_ex(ctx, JS_NULL, 1, (JSValueConst *)&exception_val, GF_LOG_SCRIPT, err_type);
529 :
530 81 : if (is_error) {
531 81 : JSValue val = JS_GetPropertyStr(ctx, exception_val, "stack");
532 81 : if (!JS_IsUndefined(val)) {
533 : const char *stack = JS_ToCString(ctx, val);
534 : #ifndef GPAC_DISABLE_LOG
535 81 : GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("%s\n", stack) );
536 : #else
537 : fprintf(stderr, "%s\n", stack);
538 : #endif
539 81 : JS_FreeCString(ctx, stack);
540 : }
541 : JS_FreeValue(ctx, val);
542 : }
543 : JS_FreeValue(ctx, exception_val);
544 81 : }
545 :
546 5335 : void js_do_loop(JSContext *ctx)
547 : {
548 0 : while (1) {
549 : JSContext *ctx1;
550 5335 : int err = JS_ExecutePendingJob(JS_GetRuntime(ctx), &ctx1);
551 5335 : if (err <= 0) {
552 5335 : if (err < 0) {
553 0 : js_dump_error(ctx1);
554 : }
555 5335 : break;
556 : }
557 : }
558 5335 : }
559 :
560 : #ifndef GPAC_DISABLE_VRML
561 :
562 : /*MPEG4 & X3D tags (for node tables & script handling)*/
563 : #include <gpac/nodes_mpeg4.h>
564 : #include <gpac/nodes_x3d.h>
565 :
566 0 : void SFColor_fromHSV(SFColor *col)
567 : {
568 : Fixed f, q, t, p, hue, sat, val;
569 : u32 i;
570 0 : hue = col->red;
571 0 : sat = col->green;
572 0 : val = col->blue;
573 0 : if (sat==0) {
574 0 : col->red = col->green = col->blue = val;
575 0 : return;
576 : }
577 0 : if (hue == FIX_ONE) hue = 0;
578 0 : else hue *= 6;
579 0 : i = FIX2INT( gf_floor(hue) );
580 0 : f = hue-i;
581 0 : p = gf_mulfix(val, FIX_ONE - sat);
582 0 : q = gf_mulfix(val, FIX_ONE - gf_mulfix(sat,f));
583 0 : t = gf_mulfix(val, FIX_ONE - gf_mulfix(sat, FIX_ONE - f));
584 0 : switch (i) {
585 0 : case 0:
586 0 : col->red = val;
587 0 : col->green = t;
588 0 : col->blue = p;
589 0 : break;
590 0 : case 1:
591 0 : col->red = q;
592 0 : col->green = val;
593 0 : col->blue = p;
594 0 : break;
595 0 : case 2:
596 0 : col->red = p;
597 0 : col->green = val;
598 0 : col->blue = t;
599 0 : break;
600 0 : case 3:
601 0 : col->red = p;
602 0 : col->green = q;
603 : col->blue = val;
604 0 : break;
605 0 : case 4:
606 0 : col->red = t;
607 0 : col->green = p;
608 : col->blue = val;
609 0 : break;
610 0 : case 5:
611 0 : col->red = val;
612 0 : col->green = p;
613 0 : col->blue = q;
614 0 : break;
615 : }
616 : }
617 :
618 0 : void SFColor_toHSV(SFColor *col)
619 : {
620 : Fixed h, s;
621 0 : Fixed _max = MAX(col->red, MAX(col->green, col->blue));
622 0 : Fixed _min = MIN(col->red, MAX(col->green, col->blue));
623 :
624 0 : s = (_max == 0) ? 0 : gf_divfix(_max - _min, _max);
625 0 : if (s != 0) {
626 0 : Fixed rl = gf_divfix(_max - col->red, _max - _min);
627 0 : Fixed gl = gf_divfix(_max - col->green, _max - _min);
628 0 : Fixed bl = gf_divfix(_max - col->blue, _max - _min);
629 0 : if (_max == col->red) {
630 0 : if (_min == col->green) h = 60*(5+bl);
631 0 : else h = 60*(1-gl);
632 0 : } else if (_max == col->green) {
633 0 : if (_min == col->blue) h = 60*(1+rl);
634 0 : else h = 60*(3-bl);
635 : } else {
636 0 : if (_min == col->red) h = 60*(3+gl);
637 0 : else h = 60*(5-rl);
638 : }
639 : } else {
640 : h = 0;
641 : }
642 0 : col->red = h;
643 0 : col->green = s;
644 0 : col->blue = _max;
645 0 : }
646 :
647 9371 : static GFINLINE GF_JSField *NewJSField(JSContext *c)
648 : {
649 : GF_JSField *ptr;
650 9371 : GF_SAFEALLOC(ptr, GF_JSField);
651 9371 : if (!ptr) return NULL;
652 9371 : ptr->js_ctx = c;
653 9371 : ptr->obj = JS_UNDEFINED;
654 9371 : return ptr;
655 : }
656 :
657 : static GFINLINE M_Script *JS_GetScript(JSContext *c)
658 : {
659 20468 : return (M_Script *) JS_GetContextOpaque(c);
660 : }
661 : static GFINLINE GF_ScriptPriv *JS_GetScriptStack(JSContext *c)
662 : {
663 63755 : M_Script *script = (M_Script *) JS_GetContextOpaque(c);
664 63755 : return script->sgprivate->UserPrivate;
665 : }
666 :
667 :
668 0 : static JSValue getName(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
669 : {
670 0 : return JS_NewString(ctx, "GPAC RichMediaEngine");
671 : }
672 0 : static JSValue getVersion(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
673 : {
674 0 : return JS_NewString(ctx, gf_gpac_version());
675 : }
676 0 : static JSValue getCurrentSpeed(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
677 : {
678 : GF_JSAPIParam par;
679 0 : GF_Node *node = JS_GetContextOpaque(ctx);
680 0 : par.time = 0;
681 0 : ScriptAction(ctx, NULL, GF_JSAPI_OP_GET_SPEED, node->sgprivate->scenegraph->RootNode, &par);
682 0 : return JS_NewFloat64(ctx, par.time);
683 : }
684 0 : static JSValue getCurrentFrameRate(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
685 : {
686 : GF_JSAPIParam par;
687 0 : GF_Node *node = JS_GetContextOpaque(ctx);
688 0 : par.time = 0;
689 0 : ScriptAction(ctx, NULL, GF_JSAPI_OP_GET_FPS, node->sgprivate->scenegraph->RootNode, &par);
690 0 : return JS_NewFloat64(ctx, par.time);
691 : }
692 0 : static JSValue getWorldURL(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
693 : {
694 : GF_JSAPIParam par;
695 0 : GF_Node *node = JS_GetContextOpaque(ctx);
696 0 : par.uri.url = NULL;
697 0 : par.uri.nb_params = 0;
698 0 : if (ScriptAction(ctx, NULL, GF_JSAPI_OP_RESOLVE_URI, node->sgprivate->scenegraph->RootNode, &par)) {
699 0 : JSValue ret = JS_NewString(ctx, par.uri.url);
700 0 : gf_free(par.uri.url);
701 0 : return ret;
702 : }
703 0 : return JS_UNDEFINED;
704 : }
705 :
706 27763 : static JSValue node_get_binding(GF_ScriptPriv *priv, GF_Node *node)
707 : {
708 : GF_JSField *field;
709 :
710 27763 : if (!node) return JS_NULL;
711 :
712 27763 : if (node->sgprivate->interact && node->sgprivate->interact->js_binding && node->sgprivate->interact->js_binding->pf) {
713 : field = node->sgprivate->interact->js_binding->pf;
714 : assert(JS_IsObject(field->obj));
715 : assert(JS_GetOpaque(field->obj, SFNodeClass.class_id)!=NULL);
716 24492 : return field->obj;
717 : }
718 :
719 3271 : field = NewJSField(priv->js_ctx);
720 3271 : field->field.fieldType = GF_SG_VRML_SFNODE;
721 3271 : field->node = node;
722 3271 : field->field.far_ptr = &field->node;
723 :
724 :
725 3271 : node->sgprivate->flags |= GF_NODE_HAS_BINDING;
726 3271 : gf_node_register(node, NULL);
727 :
728 3271 : field->obj = JS_NewObjectClass(priv->js_ctx, SFNodeClass.class_id);
729 3271 : JS_SetOpaque(field->obj, field);
730 3271 : gf_list_add(priv->jsf_cache, field);
731 :
732 : /*remember the object*/
733 3271 : if (!node->sgprivate->interact) {
734 2206 : GF_SAFEALLOC(node->sgprivate->interact, struct _node_interactive_ext);
735 2206 : if (!node->sgprivate->interact) {
736 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[VRMLJS] Failed to create interact storage\n"));
737 0 : return JS_EXCEPTION;
738 : }
739 : }
740 3271 : if (!node->sgprivate->interact->js_binding) {
741 2961 : GF_SAFEALLOC(node->sgprivate->interact->js_binding, struct _node_js_binding);
742 2961 : if (!node->sgprivate->interact->js_binding) {
743 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[VRMLJS] Failed to create JS bindings storage\n"));
744 0 : return JS_EXCEPTION;
745 : }
746 2961 : node->sgprivate->interact->js_binding->fields = gf_list_new();
747 : }
748 3271 : node->sgprivate->flags |= GF_NODE_HAS_BINDING;
749 3271 : node->sgprivate->interact->js_binding->pf = field;
750 3271 : return field->obj;
751 : }
752 :
753 0 : static JSValue getScript(JSContext *c, JSValueConst this_val, int argc, JSValueConst *argv)
754 : {
755 : GF_ScriptPriv *priv = JS_GetScriptStack(c);
756 0 : GF_Node *node = JS_GetContextOpaque(c);
757 0 : return JS_DupValue(c, node_get_binding(priv, node) );
758 : }
759 :
760 33 : static JSValue loadScript(JSContext *c, JSValueConst this_val, int argc, JSValueConst *argv)
761 : {
762 : Bool no_complain = 0;
763 : const char *url;
764 33 : GF_Node *node = JS_GetContextOpaque(c);
765 33 : JSValue aval = JS_UNDEFINED;
766 66 : if (!node || !argc || !JS_IsString(argv[0])) return JS_EXCEPTION;
767 :
768 54 : if ((argc>1) && JS_IsBool(argv[1])) no_complain = (JS_ToBool(c,argv[1])) ? 1 : 0;
769 :
770 : url = JS_ToCString(c, argv[0]);
771 33 : if (url) {
772 33 : JSScriptFromFile(node, url, no_complain, &aval);
773 : }
774 33 : JS_FreeCString(c, url);
775 33 : return aval;
776 : }
777 :
778 0 : static JSValue getProto(JSContext *c, JSValueConst this_val, int argc, JSValueConst *argv)
779 : {
780 : GF_ScriptPriv *priv = JS_GetScriptStack(c);
781 0 : GF_Node *node = JS_GetContextOpaque(c);
782 :
783 0 : if (!node->sgprivate->scenegraph->pOwningProto) {
784 0 : return JS_NULL;
785 : }
786 : node = (GF_Node *) node->sgprivate->scenegraph->pOwningProto;
787 0 : return JS_DupValue(c, node_get_binding(priv, node));
788 : }
789 :
790 : #ifndef GPAC_DISABLE_SVG
791 : GF_Node *gf_sm_load_svg_from_string(GF_SceneGraph *sg, char *svg_str);
792 : #endif
793 :
794 0 : static JSValue vrml_parse_xml(JSContext *c, JSValueConst this_val, int argc, JSValueConst *argv)
795 : {
796 : #ifndef GPAC_DISABLE_SVG
797 : GF_SceneGraph *sg;
798 : GF_Node *node;
799 : const char *str;
800 :
801 : str = JS_ToCString(c, argv[0]);
802 0 : if (!str) return JS_TRUE;
803 :
804 0 : node = JS_GetContextOpaque(c);
805 0 : sg = node->sgprivate->scenegraph;
806 :
807 0 : node = gf_sm_load_svg_from_string(sg, (char *) str);
808 0 : JS_FreeCString(c, str);
809 0 : return dom_element_construct(c, node);
810 : #else
811 : return JS_EXCEPTION;
812 : #endif
813 : }
814 :
815 :
816 0 : static JSValue getElementById(JSContext *c, JSValueConst this_val, int argc, JSValueConst *argv)
817 : {
818 : GF_Node *elt;
819 : const char *name = NULL;
820 0 : u32 ID = 0;
821 : GF_ScriptPriv *priv = JS_GetScriptStack(c);
822 0 : GF_Node *sc = JS_GetContextOpaque(c);
823 0 : if (JS_IsString(argv[0])) name = JS_ToCString(c, argv[0]);
824 0 : else if (JS_IsInteger(argv[0])) {
825 0 : if (JS_ToInt32(c, &ID, argv[0]))
826 0 : return JS_EXCEPTION;
827 : }
828 0 : if (!ID && !name) return JS_EXCEPTION;
829 :
830 : elt = NULL;
831 0 : if (ID) elt = gf_sg_find_node(sc->sgprivate->scenegraph, ID);
832 0 : else elt = gf_sg_find_node_by_name(sc->sgprivate->scenegraph, (char *) name);
833 :
834 0 : JS_FreeCString(c, name);
835 0 : return JS_DupValue(c, node_get_binding(priv, elt));
836 : }
837 :
838 0 : static JSValue replaceWorld(JSContext *c, JSValueConst this_val, int argc, JSValueConst *argv)
839 : {
840 0 : return JS_TRUE;
841 : }
842 :
843 477 : static void on_route_to_object(GF_Node *node, GF_Route *_r)
844 : {
845 : JSValue argv[2], rval;
846 : Double time;
847 : GF_FieldInfo t_info;
848 : GF_ScriptPriv *priv;
849 : JSValue obj;
850 : GF_RouteToScript *r = (GF_RouteToScript *)_r;
851 477 : if (!node)
852 236 : return;
853 477 : priv = gf_node_get_private(node);
854 477 : if (!priv)
855 : return;
856 :
857 477 : if (!r->FromNode) {
858 236 : JS_FreeValue(priv->js_ctx, r->fun);
859 236 : r->fun = JS_UNDEFINED;
860 236 : JS_FreeValue(priv->js_ctx, r->obj);
861 236 : r->obj = JS_UNDEFINED;
862 236 : return;
863 : }
864 :
865 241 : obj = r->obj;
866 241 : if (JS_IsUndefined(obj) || JS_IsNull(obj))
867 0 : obj = priv->js_obj;
868 :
869 : memset(&t_info, 0, sizeof(GF_FieldInfo));
870 241 : time = gf_node_get_scene_time(node);
871 241 : t_info.far_ptr = &time;
872 241 : t_info.fieldType = GF_SG_VRML_SFTIME;
873 241 : t_info.fieldIndex = -1;
874 241 : t_info.name = "timestamp";
875 :
876 241 : gf_js_lock(priv->js_ctx, 1);
877 :
878 241 : argv[1] = gf_sg_script_to_qjs_field(priv, &t_info, node, 1);
879 241 : argv[0] = gf_sg_script_to_qjs_field(priv, &r->FromField, r->FromNode, 1);
880 :
881 241 : rval = JS_Call(priv->js_ctx, r->fun, obj, 2, argv);
882 241 : if (JS_IsException(rval))
883 0 : js_dump_error(priv->js_ctx);
884 :
885 241 : JS_FreeValue(priv->js_ctx, argv[0]);
886 241 : JS_FreeValue(priv->js_ctx, argv[1]);
887 241 : JS_FreeValue(priv->js_ctx, rval);
888 :
889 241 : js_do_loop(priv->js_ctx);
890 241 : gf_js_lock(priv->js_ctx, 0);
891 :
892 241 : do_js_gc(priv->js_ctx, node);
893 : }
894 :
895 1016 : static JSValue addRoute(JSContext *c, JSValueConst this_val, int argc, JSValueConst *argv)
896 : {
897 : GF_JSField *ptr;
898 : GF_Node *n1, *n2;
899 : const char *f1;
900 : GF_FieldInfo info;
901 : u32 f_id1, f_id2;
902 : GF_Err e;
903 1016 : if (argc!=4) return JS_EXCEPTION;
904 1016 : ptr = (GF_JSField *) JS_GetOpaque(argv[0], SFNodeClass.class_id);
905 1016 : if (!ptr) return JS_EXCEPTION;
906 :
907 : assert(ptr->field.fieldType==GF_SG_VRML_SFNODE);
908 1016 : n1 = * ((GF_Node **)ptr->field.far_ptr);
909 1016 : if (!n1) return JS_EXCEPTION;
910 : n2 = NULL;
911 :
912 2032 : if (!JS_IsString(argv[1])) return JS_EXCEPTION;
913 : f1 = JS_ToCString(c, argv[1]);
914 1016 : if (!f1) return JS_FALSE;
915 1016 : if (!strnicmp(f1, "_field", 6)) {
916 0 : f_id1 = atoi(f1+6);
917 0 : e = gf_node_get_field(n1, f_id1, &info);
918 : } else {
919 1016 : e = gf_node_get_field_by_name(n1, (char *) f1, &info);
920 1016 : f_id1 = info.fieldIndex;
921 : }
922 1016 : JS_FreeCString(c, f1);
923 1016 : if (e != GF_OK) return JS_EXCEPTION;
924 :
925 :
926 2032 : if (!JS_IsObject(argv[2])) return JS_EXCEPTION;
927 :
928 1016 : ptr = (GF_JSField *) JS_GetOpaque(argv[2], SFNodeClass.class_id);
929 :
930 : /*regular route*/
931 2023 : if (ptr && JS_IsString(argv[3]) ) {
932 : const char *f2;
933 : GF_Route *r;
934 : assert(ptr->field.fieldType==GF_SG_VRML_SFNODE);
935 780 : n2 = * ((GF_Node **)ptr->field.far_ptr);
936 780 : if (!n2) return JS_EXCEPTION;
937 :
938 : f2 = JS_ToCString(c, argv[3]);
939 780 : if (!f2) return JS_EXCEPTION;
940 :
941 780 : if (!strnicmp(f2, "_field", 6)) {
942 0 : f_id2 = atoi(f2+6);
943 0 : e = gf_node_get_field(n2, f_id2, &info);
944 : } else {
945 1560 : if ((n2->sgprivate->tag==TAG_MPEG4_Script)
946 : #ifndef GPAC_DISABLE_X3D
947 780 : || (n2->sgprivate->tag==TAG_X3D_Script)
948 : #endif
949 : ) {
950 0 : GF_FieldInfo src = info;
951 0 : if (gf_node_get_field_by_name(n2, (char *) f2, &info) != GF_OK) {
952 0 : gf_sg_script_field_new(n2, GF_SG_SCRIPT_TYPE_EVENT_IN, src.fieldType, f2);
953 : }
954 : }
955 780 : e = gf_node_get_field_by_name(n2, (char *) f2, &info);
956 780 : f_id2 = info.fieldIndex;
957 : }
958 780 : JS_FreeCString(c, f2);
959 780 : if (e != GF_OK) return JS_EXCEPTION;
960 :
961 780 : r = gf_sg_route_new(n1->sgprivate->scenegraph, n1, f_id1, n2, f_id2);
962 780 : if (!r) return JS_EXCEPTION;
963 : }
964 : /*route to object*/
965 : else {
966 236 : u32 i = 0;
967 : const char *fun_name;
968 : JSAtom atom;
969 : GF_RouteToScript *r = NULL;
970 236 : if (!JS_IsFunction(c, argv[3]) ) return JS_EXCEPTION;
971 :
972 236 : atom = JS_ValueToAtom(c, argv[3]);
973 236 : fun_name = JS_AtomToCString(c, atom);
974 236 : if (fun_name && n1->sgprivate->interact && n1->sgprivate->interact->routes ) {
975 272 : while ( (r = (GF_RouteToScript*)gf_list_enum(n1->sgprivate->interact->routes, &i) )) {
976 145 : if ( (r->FromNode == n1)
977 145 : && (r->FromField.fieldIndex == f_id1)
978 0 : && (r->ToNode == (GF_Node*)JS_GetScript(c))
979 0 : && !stricmp(r->ToField.name, fun_name)
980 : )
981 : break;
982 : }
983 : }
984 236 : JS_FreeCString(c, fun_name);
985 236 : JS_FreeAtom(c, atom);
986 :
987 236 : if ( !r ) {
988 236 : GF_SAFEALLOC(r, GF_RouteToScript)
989 236 : if (!r) return JS_FALSE;
990 236 : r->script_route = 1;
991 236 : r->FromNode = n1;
992 236 : r->FromField.fieldIndex = f_id1;
993 236 : gf_node_get_field(r->FromNode, f_id1, &r->FromField);
994 :
995 236 : r->ToNode = (GF_Node*)JS_GetScript(c);
996 236 : r->ToField.fieldType = GF_SG_VRML_SCRIPT_FUNCTION;
997 236 : r->ToField.on_event_in = on_route_to_object;
998 236 : r->ToField.eventType = GF_SG_EVENT_IN;
999 236 : r->ToField.far_ptr = NULL;
1000 236 : r->ToField.name = fun_name;
1001 :
1002 236 : r->obj = JS_DupValue(c, argv[2]);
1003 236 : r->fun = JS_DupValue(c, argv[3]);
1004 :
1005 236 : r->is_setup = 1;
1006 236 : r->graph = n1->sgprivate->scenegraph;
1007 :
1008 236 : if (!n1->sgprivate->interact) {
1009 0 : GF_SAFEALLOC(n1->sgprivate->interact, struct _node_interactive_ext);
1010 0 : if (!n1->sgprivate->interact) {
1011 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[VRMLJS] Failed to create interact storage\n"));
1012 0 : return JS_EXCEPTION;
1013 : }
1014 : }
1015 236 : if (!n1->sgprivate->interact->routes) n1->sgprivate->interact->routes = gf_list_new();
1016 236 : gf_list_add(n1->sgprivate->interact->routes, r);
1017 236 : gf_list_add(n1->sgprivate->scenegraph->Routes, r);
1018 : }
1019 : }
1020 :
1021 1016 : return JS_UNDEFINED;
1022 : }
1023 :
1024 4 : static JSValue deleteRoute(JSContext *c, JSValueConst this_val, int argc, JSValueConst *argv)
1025 : {
1026 : GF_JSField *ptr;
1027 : GF_Node *n1, *n2;
1028 : const char *f1, *f2;
1029 : GF_FieldInfo info;
1030 : GF_RouteToScript *rts;
1031 : GF_Err e;
1032 : u32 f_id1, f_id2, i;
1033 4 : if (argc!=4) return JS_FALSE;
1034 :
1035 8 : if (!JS_IsObject(argv[0]) || JS_IsNull(argv[0]))
1036 0 : return JS_EXCEPTION;
1037 :
1038 4 : ptr = JS_GetOpaque(argv[0], SFNodeClass.class_id);
1039 4 : if (!ptr) return JS_EXCEPTION;
1040 : assert(ptr->field.fieldType==GF_SG_VRML_SFNODE);
1041 :
1042 16 : if (JS_IsString(argv[1]) && JS_IsNull(argv[2]) && JS_IsNull(argv[3])) {
1043 4 : n1 = * ((GF_Node **)ptr->field.far_ptr);
1044 : f1 = JS_ToCString(c, argv[1]);
1045 4 : if (!strcmp(f1, "ALL")) {
1046 4 : while (n1->sgprivate->interact && n1->sgprivate->interact->routes && gf_list_count(n1->sgprivate->interact->routes) ) {
1047 0 : GF_Route *r = gf_list_get(n1->sgprivate->interact->routes, 0);
1048 0 : gf_sg_route_del(r);
1049 : }
1050 : }
1051 4 : JS_FreeCString(c, f1);
1052 4 : return JS_UNDEFINED;
1053 : }
1054 0 : if (!JS_IsString(argv[1]) || !JS_IsString(argv[3])) return JS_EXCEPTION;
1055 :
1056 0 : n1 = * ((GF_Node **)ptr->field.far_ptr);
1057 :
1058 0 : ptr = JS_GetOpaque(argv[2], SFNodeClass.class_id);
1059 0 : if (!ptr) return JS_EXCEPTION;
1060 : assert(ptr->field.fieldType==GF_SG_VRML_SFNODE);
1061 0 : n2 = * ((GF_Node **)ptr->field.far_ptr);
1062 :
1063 0 : if (!n1 || !n2) return JS_EXCEPTION;
1064 0 : if (!n1->sgprivate->interact) return JS_UNDEFINED;
1065 :
1066 : f1 = JS_ToCString(c, argv[1]);
1067 : f2 = JS_ToCString(c, argv[3]);
1068 0 : if (!f1 || !f2) {
1069 0 : JS_FreeCString(c, f1);
1070 0 : JS_FreeCString(c, f2);
1071 0 : return JS_EXCEPTION;
1072 : }
1073 :
1074 0 : if (!strnicmp(f1, "_field", 6)) {
1075 0 : f_id1 = atoi(f1+6);
1076 0 : e = gf_node_get_field(n1, f_id1, &info);
1077 : } else {
1078 0 : e = gf_node_get_field_by_name(n1, (char *)f1, &info);
1079 0 : f_id1 = info.fieldIndex;
1080 : }
1081 0 : JS_FreeCString(c, f1);
1082 0 : if (e != GF_OK) return JS_EXCEPTION;
1083 :
1084 0 : if (!strnicmp(f2, "_field", 6)) {
1085 0 : f_id2 = atoi(f2+6);
1086 0 : e = gf_node_get_field(n2, f_id2, &info);
1087 : } else {
1088 0 : e = gf_node_get_field_by_name(n2, (char *)f2, &info);
1089 0 : f_id2 = info.fieldIndex;
1090 : }
1091 0 : JS_FreeCString(c, f2);
1092 0 : if (e != GF_OK) return JS_EXCEPTION;
1093 :
1094 0 : i=0;
1095 0 : while ((rts = gf_list_enum(n1->sgprivate->interact->routes, &i))) {
1096 0 : if (rts->FromField.fieldIndex != f_id1) continue;
1097 0 : if (rts->ToNode != n2) continue;
1098 0 : if (rts->ToField.fieldIndex != f_id2) continue;
1099 : JS_FreeValue(c, rts->fun);
1100 : JS_FreeValue(c, rts->obj);
1101 0 : gf_sg_route_del((GF_Route *) rts);
1102 0 : return JS_UNDEFINED;
1103 : }
1104 0 : return JS_UNDEFINED;
1105 : }
1106 :
1107 0 : static JSValue loadURL(JSContext *c, JSValueConst this_val, int argc, JSValueConst *argv)
1108 : {
1109 : u32 i;
1110 : GF_JSAPIParam par;
1111 : GF_JSField *f;
1112 0 : M_Script *script = (M_Script *) JS_GetContextOpaque(c);
1113 :
1114 0 : if (argc < 1) return JS_EXCEPTION;
1115 :
1116 0 : if (JS_IsString(argv[0])) {
1117 : Bool res;
1118 0 : par.uri.url = (char *) JS_ToCString(c, argv[0]);
1119 : /*TODO add support for params*/
1120 0 : par.uri.nb_params = 0;
1121 0 : res = ScriptAction(c, NULL, GF_JSAPI_OP_LOAD_URL, (GF_Node *)script, &par);
1122 0 : JS_FreeCString(c, par.uri.url);
1123 0 : return res ? JS_TRUE : JS_FALSE;
1124 : }
1125 0 : if (!JS_IsObject(argv[0])) return JS_EXCEPTION;
1126 :
1127 0 : f = (GF_JSField *) JS_GetOpaque_Nocheck(argv[0]);
1128 0 : if (!f || !f->mfvals) return JS_EXCEPTION;
1129 :
1130 0 : for (i=0; i<f->mfvals_count; i++) {
1131 : Bool res=GF_FALSE;
1132 0 : JSValue item = f->mfvals[i];
1133 :
1134 0 : if (JS_IsString(item)) {
1135 0 : par.uri.url = (char *) JS_ToCString(c, item);
1136 : /*TODO add support for params*/
1137 0 : par.uri.nb_params = 0;
1138 0 : res = ScriptAction(c, NULL, GF_JSAPI_OP_LOAD_URL, (GF_Node*)script, &par);
1139 0 : JS_FreeCString(c, par.uri.url);
1140 : }
1141 0 : if (res) return JS_TRUE;
1142 : }
1143 0 : return JS_FALSE;
1144 : }
1145 :
1146 0 : static JSValue setDescription(JSContext *c, JSValueConst this_val, int argc, JSValueConst *argv)
1147 : {
1148 : GF_JSAPIParam par;
1149 0 : GF_Node *node = JS_GetContextOpaque(c);
1150 0 : if (!argc || !JS_IsString(argv[0])) return JS_EXCEPTION;
1151 0 : par.uri.url = (char *) JS_ToCString(c, argv[0]);
1152 0 : ScriptAction(c, NULL, GF_JSAPI_OP_SET_TITLE, node->sgprivate->scenegraph->RootNode, &par);
1153 0 : JS_FreeCString(c, par.uri.url);
1154 0 : return JS_TRUE;
1155 : }
1156 :
1157 0 : static JSValue createVrmlFromString(JSContext *c, JSValueConst this_val, int argc, JSValueConst *argv)
1158 : {
1159 : #ifndef GPAC_DISABLE_LOADER_BT
1160 : GF_ScriptPriv *priv;
1161 : GF_FieldInfo field;
1162 : JSValue res;
1163 : /*BT/VRML from string*/
1164 : GF_List *gf_sm_load_bt_from_string(GF_SceneGraph *in_scene, char *node_str, Bool force_wrl);
1165 : const char *str;
1166 : GF_List *nlist;
1167 0 : GF_Node *sc_node = JS_GetContextOpaque(c);
1168 0 : if (!sc_node || (argc < 1)) return JS_EXCEPTION;
1169 :
1170 0 : if (!JS_IsString(argv[0])) return JS_EXCEPTION;
1171 : str = JS_ToCString(c, argv[0]);
1172 0 : nlist = gf_sm_load_bt_from_string(sc_node->sgprivate->scenegraph, (char *)str, 1);
1173 0 : JS_FreeCString(c, str);
1174 0 : if (!nlist) return JS_EXCEPTION;
1175 :
1176 : priv = JS_GetScriptStack(c);
1177 : memset(&field, 0, sizeof(GF_FieldInfo));
1178 0 : field.fieldType = GF_SG_VRML_MFNODE;
1179 0 : field.far_ptr = &nlist;
1180 0 : res = gf_sg_script_to_qjs_field(priv, &field, NULL, 0);
1181 :
1182 : /*don't forget to unregister all this stuff*/
1183 0 : while (gf_list_count(nlist)) {
1184 0 : GF_Node *n = gf_list_get(nlist, 0);
1185 0 : gf_list_rem(nlist, 0);
1186 0 : gf_node_unregister(n, NULL);
1187 : }
1188 0 : gf_list_del(nlist);
1189 0 : return res;
1190 : #else
1191 : return JS_EXCEPTION;
1192 : #endif
1193 : }
1194 :
1195 : void gf_node_event_out_proto(GF_Node *node, u32 FieldIndex);
1196 :
1197 64535 : void Script_FieldChanged(JSContext *c, GF_Node *parent, GF_JSField *parent_owner, GF_FieldInfo *field)
1198 : {
1199 : u32 script_field;
1200 : u32 i;
1201 :
1202 64535 : if (!parent && parent_owner) {
1203 44334 : parent = parent_owner->owner;
1204 44334 : field = &parent_owner->field;
1205 : }
1206 128017 : if (!parent) return;
1207 :
1208 : script_field = 0;
1209 101582 : if ((parent->sgprivate->tag == TAG_MPEG4_Script)
1210 : #ifndef GPAC_DISABLE_X3D
1211 50791 : || (parent->sgprivate->tag == TAG_X3D_Script)
1212 : #endif
1213 : ) {
1214 : script_field = 1;
1215 1053 : if ( (GF_Node *) JS_GetContextOpaque(c) == parent) script_field = 2;
1216 : }
1217 :
1218 : if (script_field!=2) {
1219 49738 : if (field->on_event_in) field->on_event_in(parent, NULL);
1220 49733 : else if (script_field && (field->eventType==GF_SG_EVENT_IN) ) {
1221 0 : gf_sg_script_event_in(parent, field);
1222 0 : gf_node_changed_internal(parent, field, 0);
1223 0 : return;
1224 : }
1225 : /*field has changed, set routes...*/
1226 49738 : if (parent->sgprivate->tag == TAG_ProtoNode) {
1227 : GF_ProtoInstance *inst = (GF_ProtoInstance *)parent;
1228 17271 : gf_sg_proto_propagate_event(parent, field->fieldIndex, (GF_Node*)JS_GetScript(c));
1229 : /* Node exposedField can also be routed to another field */
1230 17271 : gf_node_event_out_proto(parent, field->fieldIndex);
1231 :
1232 : //hardcoded protos be implemented in ways not inspecting the node_dirty propagation scheme (eg defining an SFNode in their interface, not linked with the graph).
1233 : //in this case handle the node as a regular one
1234 17271 : if (inst->flags & GF_SG_PROTO_HARDCODED) {
1235 149 : gf_node_changed_internal(parent, field, 0);
1236 : }
1237 : } else {
1238 32467 : gf_node_event_out(parent, field->fieldIndex);
1239 32467 : gf_node_changed_internal(parent, field, 0);
1240 : }
1241 : return;
1242 : }
1243 : /*otherwise mark field if eventOut*/
1244 : if (parent_owner || parent) {
1245 1053 : GF_ScriptPriv *priv = parent ? parent->sgprivate->UserPrivate : parent_owner->owner->sgprivate->UserPrivate;
1246 : GF_ScriptField *sf;
1247 1053 : i=0;
1248 16367 : while ((sf = gf_list_enum(priv->fields, &i))) {
1249 14261 : if (sf->ALL_index == field->fieldIndex) {
1250 : /*queue eventOut*/
1251 1053 : if (sf->eventType == GF_SG_EVENT_OUT) {
1252 1012 : sf->activate_event_out = 1;
1253 : }
1254 : }
1255 : }
1256 : }
1257 : }
1258 :
1259 1012 : static JSValue gf_sg_script_eventout_set_prop(JSContext *c, JSValueConst this_val, JSValueConst val, int magic)
1260 : {
1261 : u32 i;
1262 : GF_ScriptPriv *script;
1263 : GF_Node *n;
1264 : GF_ScriptField *sf;
1265 :
1266 : script = JS_GetScriptStack(c);
1267 1012 : if (!script) return JS_EXCEPTION;
1268 : n = (GF_Node *) JS_GetScript(c);
1269 1012 : if (!n) return JS_EXCEPTION;
1270 :
1271 1012 : i=0;
1272 14296 : while ((sf = gf_list_enum(script->fields, &i))) {
1273 13284 : if (sf->magic==magic) {
1274 : GF_FieldInfo info;
1275 1012 : gf_node_get_field(n, sf->ALL_index, &info);
1276 1012 : gf_sg_script_to_node_field(c, val, &info, n, NULL);
1277 1012 : sf->activate_event_out = 1;
1278 1012 : return JS_UNDEFINED;
1279 : }
1280 : }
1281 0 : return JS_EXCEPTION;
1282 : }
1283 :
1284 :
1285 : /*generic ToString method*/
1286 0 : static GFINLINE void sffield_toString(char **str, void *f_ptr, u32 fieldType)
1287 : {
1288 : char temp[1000];
1289 :
1290 0 : switch (fieldType) {
1291 0 : case GF_SG_VRML_SFVEC2F:
1292 : {
1293 0 : SFVec2f val = * ((SFVec2f *) f_ptr);
1294 0 : sprintf(temp, "%f %f", FIX2FLT(val.x), FIX2FLT(val.y));
1295 0 : gf_dynstrcat(str, temp, NULL);
1296 : break;
1297 : }
1298 0 : case GF_SG_VRML_SFVEC3F:
1299 : {
1300 0 : SFVec3f val = * ((SFVec3f *) f_ptr);
1301 0 : sprintf(temp, "%f %f %f", FIX2FLT(val.x), FIX2FLT(val.y), FIX2FLT(val.z));
1302 0 : gf_dynstrcat(str, temp, NULL);
1303 : break;
1304 : }
1305 0 : case GF_SG_VRML_SFVEC4F:
1306 : {
1307 0 : SFVec4f val = * ((SFVec4f *) f_ptr);
1308 0 : sprintf(temp, "%f %f %f %f", FIX2FLT(val.x), FIX2FLT(val.y), FIX2FLT(val.z), FIX2FLT(val.q));
1309 0 : gf_dynstrcat(str, temp, NULL);
1310 : break;
1311 : }
1312 0 : case GF_SG_VRML_SFROTATION:
1313 : {
1314 0 : SFRotation val = * ((SFRotation *) f_ptr);
1315 0 : sprintf(temp, "%f %f %f %f", FIX2FLT(val.x), FIX2FLT(val.y), FIX2FLT(val.z), FIX2FLT(val.q));
1316 0 : gf_dynstrcat(str, temp, NULL);
1317 : break;
1318 : }
1319 0 : case GF_SG_VRML_SFCOLOR:
1320 : {
1321 0 : SFColor val = * ((SFColor *) f_ptr);
1322 0 : sprintf(temp, "%f %f %f", FIX2FLT(val.red), FIX2FLT(val.green), FIX2FLT(val.blue));
1323 0 : gf_dynstrcat(str, temp, NULL);
1324 : break;
1325 : }
1326 0 : case GF_SG_VRML_SFIMAGE:
1327 : {
1328 : SFImage *val = ((SFImage *)f_ptr);
1329 0 : sprintf(temp, "%dx%dx%d", val->width, val->height, val->numComponents);
1330 0 : gf_dynstrcat(str, temp, NULL);
1331 0 : break;
1332 : }
1333 :
1334 : }
1335 0 : }
1336 :
1337 19196 : static void JS_ObjectDestroyed(JSRuntime *rt, JSValue obj, GF_JSField *ptr, Bool is_js_call)
1338 : {
1339 19196 : JS_SetOpaque(obj, NULL);
1340 19196 : if (!ptr) return;
1341 :
1342 : /*if ptr is a node, remove node binding*/
1343 9371 : if (ptr->node
1344 3271 : && ptr->node->sgprivate->interact
1345 3271 : && ptr->node->sgprivate->interact->js_binding
1346 3271 : && (ptr->node->sgprivate->interact->js_binding->pf == ptr)
1347 : ) {
1348 3271 : ptr->node->sgprivate->interact->js_binding->pf = NULL;
1349 : }
1350 :
1351 : /*if ptr is a field, remove field binding from parent*/
1352 9371 : if (ptr->owner && ptr->owner->sgprivate->interact && ptr->owner->sgprivate->interact->js_binding) {
1353 6532 : gf_list_del_item(ptr->owner->sgprivate->interact->js_binding->fields, ptr);
1354 : }
1355 :
1356 : /*
1357 : If object is still registered, remove it from the js_cache
1358 : */
1359 18742 : if (!JS_IsUndefined(ptr->obj) && is_js_call) {
1360 2115 : if (ptr->js_ctx) {
1361 : GF_ScriptPriv *priv;
1362 2115 : if (gf_list_find(js_rt->allocated_contexts, ptr->js_ctx) < 0)
1363 : return;
1364 2115 : priv = JS_GetScriptStack(ptr->js_ctx);
1365 2115 : gf_list_del_item(priv->jsf_cache, ptr);
1366 : }
1367 2115 : ptr->obj = JS_UNDEFINED;
1368 : }
1369 : }
1370 :
1371 :
1372 0 : static JSValue field_toString(JSContext *c, JSValueConst this_val, int argc, JSValueConst *argv)
1373 : {
1374 : u32 i;
1375 : JSValue item;
1376 : Double d;
1377 0 : char *str = NULL;
1378 0 : GF_JSField *f = JS_GetOpaque_Nocheck(this_val);
1379 0 : if (!f) return JS_FALSE;
1380 :
1381 0 : if (gf_sg_vrml_is_sf_field(f->field.fieldType)) {
1382 0 : sffield_toString(&str, f->field.far_ptr, f->field.fieldType);
1383 : } else {
1384 0 : if (f->field.fieldType == GF_SG_VRML_MFNODE) return JS_TRUE;
1385 :
1386 0 : gf_dynstrcat(&str, "[", NULL);
1387 :
1388 0 : for (i=0; i<f->mfvals_count; i++) {
1389 : char temp[1000];
1390 : s32 ival;
1391 0 : item = f->mfvals[i];
1392 0 : switch (f->field.fieldType) {
1393 0 : case GF_SG_VRML_MFBOOL:
1394 0 : sprintf(temp, "%s", JS_ToBool(c, item) ? "TRUE" : "FALSE");
1395 0 : gf_dynstrcat(&str, temp, NULL);
1396 0 : break;
1397 0 : case GF_SG_VRML_MFINT32:
1398 0 : JS_ToInt32(c, &ival, item);
1399 0 : sprintf(temp, "%d", ival);
1400 0 : gf_dynstrcat(&str, temp, NULL);
1401 0 : break;
1402 0 : case GF_SG_VRML_MFFLOAT:
1403 : case GF_SG_VRML_MFTIME:
1404 0 : JS_ToFloat64(c, &d, item);
1405 0 : sprintf(temp, "%g", d);
1406 0 : gf_dynstrcat(&str, temp, NULL);
1407 0 : break;
1408 0 : case GF_SG_VRML_MFSTRING:
1409 : case GF_SG_VRML_MFURL:
1410 : {
1411 : char *str_val = (char *) JS_ToCString(c, item);
1412 0 : gf_dynstrcat(&str, str_val, NULL);
1413 0 : JS_FreeCString(c, str_val);
1414 : }
1415 0 : break;
1416 0 : default:
1417 0 : if (JS_IsObject(item)) {
1418 0 : GF_JSField *sf = (GF_JSField *) JS_GetOpaque_Nocheck(item);
1419 0 : sffield_toString(&str, sf->field.far_ptr, sf->field.fieldType);
1420 : }
1421 : break;
1422 : }
1423 0 : if (i < f->mfvals_count-1) gf_dynstrcat(&str, ", ", NULL);
1424 : }
1425 0 : gf_dynstrcat(&str, "]", NULL);
1426 : }
1427 0 : item = JS_NewString(c, str ? str : "");
1428 0 : if (str) gf_free(str);
1429 0 : return item;
1430 : }
1431 :
1432 1949 : static JSValue SFNodeConstructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
1433 : {
1434 : u32 tag, ID;
1435 : GF_Node *new_node;
1436 : GF_JSField *field;
1437 : GF_Proto *proto;
1438 : GF_SceneGraph *sg;
1439 : char *node_name;
1440 : GF_ScriptPriv *priv = JS_GetScriptStack(c);
1441 : M_Script *sc = JS_GetScript(c);
1442 :
1443 3898 : if (argc && !JS_IsString(argv[0]))
1444 0 : return JS_EXCEPTION;
1445 :
1446 : tag = 0;
1447 1949 : if (!argc) {
1448 0 : JSValue obj = JS_NewObjectClass(c, SFNodeClass.class_id);
1449 0 : if (JS_IsException(obj)) return obj;
1450 0 : field = NewJSField(c);
1451 0 : field->field.fieldType = GF_SG_VRML_SFNODE;
1452 0 : field->node = NULL;
1453 0 : field->field.far_ptr = &field->node;
1454 0 : JS_SetOpaque(obj, field);
1455 0 : return obj;
1456 : }
1457 :
1458 : ID = 0;
1459 : node_name = (char *) JS_ToCString(c, argv[0]);
1460 1949 : if (!node_name) return JS_EXCEPTION;
1461 :
1462 1949 : if (!strnicmp(node_name, "_proto", 6)) {
1463 780 : ID = atoi(node_name+6);
1464 390 : JS_FreeCString(c, node_name);
1465 : node_name = NULL;
1466 :
1467 536 : locate_proto:
1468 :
1469 : /*locate proto in current graph and all parents*/
1470 463 : sg = sc->sgprivate->scenegraph;
1471 : while (1) {
1472 463 : proto = gf_sg_find_proto(sg, ID, node_name);
1473 463 : if (proto) break;
1474 0 : if (!sg->parent_scene) break;
1475 : sg = sg->parent_scene;
1476 : }
1477 463 : if (!proto) {
1478 0 : JS_FreeCString(c, node_name);
1479 0 : return JS_FALSE;
1480 : }
1481 : /* create interface and load code in current graph*/
1482 463 : new_node = gf_sg_proto_create_instance(sc->sgprivate->scenegraph, proto);
1483 463 : if (!new_node) {
1484 0 : JS_FreeCString(c, node_name);
1485 0 : return JS_FALSE;
1486 : }
1487 : /*OK, instantiate proto code*/
1488 463 : if (gf_sg_proto_load_code(new_node) != GF_OK) {
1489 0 : gf_node_unregister(new_node, NULL);
1490 0 : JS_FreeCString(c, node_name);
1491 0 : return JS_FALSE;
1492 : }
1493 : } else {
1494 1559 : switch (sc->sgprivate->tag) {
1495 1559 : case TAG_MPEG4_Script:
1496 1559 : tag = gf_node_mpeg4_type_by_class_name(node_name);
1497 1559 : break;
1498 : #ifndef GPAC_DISABLE_X3D
1499 0 : case TAG_X3D_Script:
1500 0 : tag = gf_node_x3d_type_by_class_name(node_name);
1501 0 : break;
1502 : #endif
1503 : }
1504 1559 : if (!tag) goto locate_proto;
1505 1486 : new_node = gf_node_new(sc->sgprivate->scenegraph, tag);
1506 1486 : if (!new_node) {
1507 0 : JS_FreeCString(c, node_name);
1508 0 : return JS_FALSE;
1509 : }
1510 1486 : gf_node_init(new_node);
1511 : }
1512 :
1513 1949 : JS_FreeCString(c, node_name);
1514 :
1515 1949 : return JS_DupValue(c, node_get_binding(priv, new_node));
1516 : }
1517 6388 : static void node_finalize_ex(JSRuntime *rt, JSValue obj, Bool is_js_call)
1518 : {
1519 6388 : GF_JSField *ptr = (GF_JSField *) JS_GetOpaque(obj, SFNodeClass.class_id);
1520 6388 : JS_ObjectDestroyed(rt, obj, ptr, is_js_call);
1521 6388 : if (!ptr) return;
1522 :
1523 3265 : if (ptr->node
1524 : /*num_instances may be 0 if the node is the script being destroyed*/
1525 3265 : && ptr->node->sgprivate->num_instances
1526 : ) {
1527 :
1528 3265 : GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[VRML JS] unregistering node %s (%s)\n", gf_node_get_name(ptr->node), gf_node_get_class_name(ptr->node)));
1529 :
1530 3265 : gf_node_unregister(ptr->node, NULL);
1531 : }
1532 3265 : gf_free(ptr);
1533 : }
1534 :
1535 3675 : static void node_finalize(JSRuntime *rt, JSValue val)
1536 : {
1537 3675 : node_finalize_ex(rt, val, GF_TRUE);
1538 :
1539 3675 : }
1540 :
1541 0 : static JSValue node_toString(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1542 : {
1543 : char str[1000];
1544 : u32 id;
1545 : GF_Node *n;
1546 : GF_JSField *f;
1547 : const char *name;
1548 :
1549 0 : f = (GF_JSField *) JS_GetOpaque(obj, SFNodeClass.class_id);
1550 0 : if (!f) return JS_EXCEPTION;
1551 :
1552 0 : str[0] = 0;
1553 0 : n = * ((GF_Node **)f->field.far_ptr);
1554 0 : if (!n) return JS_EXCEPTION;
1555 :
1556 0 : name = gf_node_get_name_and_id(n, &id);
1557 0 : if (id) {
1558 0 : if (name) {
1559 : snprintf(str, 500, "DEF %s ", name);
1560 : } else {
1561 0 : snprintf(str, 500, "DEF %d ", id - 1);
1562 : }
1563 : }
1564 0 : strncat(str, gf_node_get_class_name(n), 500);
1565 0 : return JS_NewString(c, (const char *) str);
1566 : }
1567 :
1568 36 : static JSValue node_getTime(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1569 : {
1570 : GF_Node *n;
1571 : GF_JSField *f;
1572 36 : f = (GF_JSField *) JS_GetOpaque(obj, SFNodeClass.class_id);
1573 36 : if (!f) return JS_EXCEPTION;
1574 36 : n = * ((GF_Node **)f->field.far_ptr);
1575 36 : if (!n) return JS_EXCEPTION;
1576 36 : return JS_NewFloat64(c, gf_node_get_scene_time(n));
1577 : }
1578 :
1579 46463 : static JSValue node_getProperty(JSContext *c, JSValueConst obj, JSAtom atom, JSValueConst receiver)
1580 : {
1581 : GF_Node *n;
1582 : u32 index;
1583 : JSValue res;
1584 : const char *fieldName;
1585 : GF_FieldInfo info;
1586 : GF_JSField *ptr;
1587 : GF_ScriptPriv *priv;
1588 :
1589 46463 : ptr = (GF_JSField *) JS_GetOpaque(obj, SFNodeClass.class_id);
1590 46463 : if (!ptr) return JS_EXCEPTION;
1591 : assert(ptr->field.fieldType==GF_SG_VRML_SFNODE);
1592 46463 : n = * ((GF_Node **)ptr->field.far_ptr);
1593 46463 : if (!n) return JS_EXCEPTION;
1594 : priv = JS_GetScriptStack(c);
1595 :
1596 46463 : fieldName = JS_AtomToCString(c, atom);
1597 46463 : if (!fieldName) return JS_EXCEPTION;
1598 :
1599 46463 : if (!strcmp(fieldName, "toString")) {
1600 0 : JS_FreeCString(c, fieldName);
1601 : return JS_DupValue(priv->js_ctx, priv->node_toString_fun);
1602 : }
1603 46463 : if (!strcmp(fieldName, "getTime")) {
1604 36 : JS_FreeCString(c, fieldName);
1605 : return JS_DupValue(priv->js_ctx, priv->node_getTime_fun);
1606 : }
1607 46427 : if (!strcmp(fieldName, "addEventListener") || !strcmp(fieldName, "addEventListenerNS")) {
1608 37 : JS_FreeCString(c, fieldName);
1609 : return JS_DupValue(priv->js_ctx, priv->node_addEventListener_fun);
1610 : }
1611 46390 : if (!strcmp(fieldName, "removeEventListener") || !strcmp(fieldName, "removeEventListenerNS")) {
1612 0 : JS_FreeCString(c, fieldName);
1613 : return JS_DupValue(priv->js_ctx, priv->node_removeEventListener_fun);
1614 : }
1615 : /*fieldID indexing*/
1616 46390 : if (!strnicmp(fieldName, "_field", 6)) {
1617 0 : index = atoi(fieldName+6);
1618 0 : if ( gf_node_get_field(n, index, &info) == GF_OK) {
1619 0 : res = gf_sg_script_to_qjs_field(priv, &info, n, 0);
1620 0 : JS_FreeCString(c, fieldName);
1621 0 : return res;
1622 : }
1623 46390 : } else if ( gf_node_get_field_by_name(n, (char *) fieldName, &info) == GF_OK) {
1624 44507 : res = gf_sg_script_to_qjs_field(priv, &info, n, 0);
1625 44507 : JS_FreeCString(c, fieldName);
1626 44507 : return res;
1627 : }
1628 :
1629 1883 : if (!strcmp(fieldName, "_bounds")) {
1630 : GF_JSAPIParam par;
1631 0 : par.bbox.is_set = 0;
1632 0 : if (ScriptAction(c, n->sgprivate->scenegraph, GF_JSAPI_OP_GET_LOCAL_BBOX, (GF_Node *)n, &par) ) {
1633 0 : JSValue _obj = JS_NewObjectClass(priv->js_ctx, AnyClass.class_id);
1634 : Float x, y, w, h;
1635 : x = y = w = h = 0;
1636 0 : if (par.bbox.is_set) {
1637 0 : x = FIX2FLT(par.bbox.min_edge.x);
1638 0 : y = FIX2FLT(par.bbox.min_edge.y);
1639 0 : w = FIX2FLT(par.bbox.max_edge.x - par.bbox.min_edge.x);
1640 0 : h = FIX2FLT(par.bbox.max_edge.y - par.bbox.min_edge.y);
1641 : }
1642 0 : JS_SetPropertyStr(priv->js_ctx, _obj, "x", JS_NewFloat64(c, x));
1643 0 : JS_SetPropertyStr(priv->js_ctx, _obj, "y", JS_NewFloat64(c, y));
1644 0 : JS_SetPropertyStr(priv->js_ctx, _obj, "width", JS_NewFloat64(c, w));
1645 0 : JS_SetPropertyStr(priv->js_ctx, _obj, "height", JS_NewFloat64(c, h));
1646 0 : JS_FreeCString(c, fieldName);
1647 0 : return _obj;
1648 : }
1649 : }
1650 1883 : JS_FreeCString(c, fieldName);
1651 1883 : return JS_UNDEFINED;
1652 : }
1653 :
1654 27414 : static int node_setProperty(JSContext *c, JSValueConst obj, JSAtom atom, JSValueConst value, JSValueConst receiver, int flags)
1655 : {
1656 : GF_Node *n;
1657 : GF_FieldInfo info;
1658 : u32 index;
1659 : const char *fieldname;
1660 : Bool isOK = GF_FALSE;
1661 : GF_JSField *ptr;
1662 :
1663 27414 : ptr = (GF_JSField *) JS_GetOpaque(obj, SFNodeClass.class_id);
1664 27414 : if (! ptr) return -1;
1665 27414 : fieldname = JS_AtomToCString(c, atom);
1666 27414 : if (!fieldname) return -1;
1667 :
1668 : assert(ptr->field.fieldType==GF_SG_VRML_SFNODE);
1669 27414 : n = * ((GF_Node **)ptr->field.far_ptr);
1670 :
1671 : /*fieldID indexing*/
1672 27414 : if (!strnicmp(fieldname, "_field", 6)) {
1673 0 : index = atoi(fieldname+6);
1674 0 : JS_FreeCString(c, fieldname);
1675 0 : if ( gf_node_get_field(n, index, &info) == GF_OK) {
1676 : isOK = GF_TRUE;
1677 : }
1678 : } else {
1679 27414 : if (gf_node_get_field_by_name(n, (char *)fieldname, &info) == GF_OK) {
1680 : isOK = GF_TRUE;
1681 : } else {
1682 : /*VRML style*/
1683 8225 : if (!strnicmp(fieldname, "set_", 4)) {
1684 1782 : if (gf_node_get_field_by_name(n, (char *) fieldname + 4, &info) == GF_OK) {
1685 : isOK = GF_TRUE;
1686 : }
1687 : }
1688 : }
1689 : }
1690 : if (!isOK) {
1691 8225 : JS_FreeCString(c, fieldname);
1692 8225 : JS_DefinePropertyValue(c, obj, atom, JS_DupValue(c, value), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
1693 8225 : return 0;
1694 : }
1695 :
1696 19189 : JS_FreeCString(c, fieldname);
1697 :
1698 19189 : if (gf_node_get_tag(n)==TAG_ProtoNode)
1699 17195 : gf_sg_proto_mark_field_loaded(n, &info);
1700 :
1701 19189 : gf_sg_script_to_node_field(c, value, &info, n, ptr);
1702 19189 : return 1;
1703 : }
1704 :
1705 :
1706 : /* Generic field destructor */
1707 6307 : static void field_finalize(JSRuntime *rt, JSValue obj)
1708 : {
1709 6307 : GF_JSField *ptr = (GF_JSField *) JS_GetOpaque_Nocheck(obj);
1710 6307 : JS_ObjectDestroyed(rt, obj, ptr, 1);
1711 6307 : if (!ptr) return;
1712 :
1713 4287 : if (ptr->field_ptr) gf_sg_vrml_field_pointer_del(ptr->field_ptr, ptr->field.fieldType);
1714 4287 : gf_free(ptr);
1715 : }
1716 :
1717 :
1718 :
1719 : /*SFImage class functions */
1720 0 : static GFINLINE GF_JSField *SFImage_Create(JSContext *c, JSValue obj, u32 w, u32 h, u32 nbComp, MFInt32 *pixels)
1721 : {
1722 : u32 i, len;
1723 : GF_JSField *field;
1724 : SFImage *v;
1725 0 : field = NewJSField(c);
1726 0 : v = gf_sg_vrml_field_pointer_new(GF_SG_VRML_SFIMAGE);
1727 0 : field->field_ptr = field->field.far_ptr = v;
1728 0 : field->field.fieldType = GF_SG_VRML_SFIMAGE;
1729 0 : v->width = w;
1730 0 : v->height = h;
1731 0 : v->numComponents = nbComp;
1732 0 : v->pixels = (u8 *) gf_malloc(sizeof(u8) * nbComp * w * h);
1733 0 : len = MIN(nbComp * w * h, pixels->count);
1734 0 : for (i=0; i<len; i++) v->pixels[i] = (u8) pixels->vals[i];
1735 0 : JS_SetOpaque(obj, field);
1736 0 : return field;
1737 : }
1738 :
1739 0 : static JSValue SFImageConstructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
1740 : {
1741 : u32 w, h, nbComp;
1742 : MFInt32 *pixels;
1743 : JSValue obj;
1744 0 : if (argc<4) return JS_EXCEPTION;
1745 0 : if (!JS_IsInteger(argv[0]) || !JS_IsInteger(argv[1]) || !JS_IsInteger(argv[2]) || !JS_IsObject(argv[3]) )
1746 0 : return JS_EXCEPTION;
1747 :
1748 0 : pixels = JS_GetOpaque(argv[3], MFInt32Class.class_id);
1749 0 : if (!pixels) return JS_EXCEPTION;
1750 :
1751 0 : obj = JS_NewObjectClass(c, SFImageClass.class_id);
1752 0 : if (JS_IsException(obj)) return obj;
1753 0 : JS_ToInt32(c, &w, argv[0]);
1754 0 : JS_ToInt32(c, &h, argv[1]);
1755 0 : JS_ToInt32(c, &nbComp, argv[2]);
1756 0 : SFImage_Create(c, obj, w, h, nbComp, pixels);
1757 0 : return obj;
1758 : }
1759 :
1760 0 : static JSValue image_getProperty(JSContext *c, JSValueConst this_val, int magic)
1761 : {
1762 : SFImage *sfi;
1763 : GF_ScriptPriv *priv = JS_GetScriptStack(c);
1764 0 : GF_JSField *val = (GF_JSField *) JS_GetOpaque(this_val, SFImageClass.class_id);
1765 0 : if (!val) return JS_EXCEPTION;
1766 0 : sfi = (SFImage*)val->field.far_ptr;
1767 :
1768 0 : switch (magic) {
1769 0 : case 0: return JS_NewInt32(c, sfi->width);
1770 0 : case 1: return JS_NewInt32(c, sfi->height);
1771 0 : case 2: return JS_NewInt32(c, sfi->numComponents);
1772 0 : case 3:
1773 : {
1774 : u32 i, len;
1775 0 : JSValue an_obj = JS_NewObjectClass(c, MFInt32Class.class_id);
1776 0 : len = sfi->width*sfi->height*sfi->numComponents;
1777 0 : for (i=0; i<len; i++) {
1778 0 : JS_SetPropertyUint32(priv->js_ctx, an_obj, i, JS_NewInt32(c, sfi->pixels[i]) );
1779 : }
1780 : }
1781 0 : break;
1782 : default:
1783 : break;
1784 : }
1785 0 : return JS_UNDEFINED;
1786 : }
1787 :
1788 0 : static JSValue image_setProperty(JSContext *c, JSValueConst obj, JSValueConst value, int magic)
1789 : {
1790 : u32 ival;
1791 : Bool changed = 0;
1792 : SFImage *sfi;
1793 0 : GF_JSField *ptr = (GF_JSField *) JS_GetOpaque(obj, SFImageClass.class_id);
1794 0 : if (!ptr) return JS_EXCEPTION;
1795 :
1796 0 : sfi = (SFImage*)ptr->field.far_ptr;
1797 0 : switch (magic) {
1798 0 : case 0:
1799 0 : JS_ToInt32(c, &ival, value);
1800 0 : changed = ! (sfi->width == ival);
1801 0 : sfi->width = ival;
1802 0 : if (changed && sfi->pixels) {
1803 0 : gf_free(sfi->pixels);
1804 0 : sfi->pixels = NULL;
1805 : }
1806 : break;
1807 0 : case 1:
1808 0 : JS_ToInt32(c, &ival, value);
1809 0 : changed = ! (sfi->height == ival);
1810 0 : sfi->height = ival;
1811 0 : if (changed && sfi->pixels) {
1812 0 : gf_free(sfi->pixels);
1813 0 : sfi->pixels = NULL;
1814 : }
1815 : break;
1816 0 : case 2:
1817 0 : JS_ToInt32(c, &ival, value);
1818 0 : changed = ! (sfi->numComponents == ival);
1819 0 : sfi->numComponents = ival;
1820 0 : if (changed && sfi->pixels) {
1821 0 : gf_free(sfi->pixels);
1822 0 : sfi->pixels = NULL;
1823 : }
1824 : break;
1825 0 : case 3:
1826 : {
1827 : MFInt32 *pixels;
1828 : GF_JSField *sf;
1829 : u32 len, i;
1830 0 : sf = JS_GetOpaque(value, MFInt32Class.class_id);
1831 0 : if (!sf) return JS_EXCEPTION;
1832 0 : pixels = (MFInt32 *) sf->field.far_ptr;
1833 0 : if (sfi->pixels) gf_free(sfi->pixels);
1834 0 : len = sfi->width*sfi->height*sfi->numComponents;
1835 0 : sfi->pixels = (unsigned char *) gf_malloc(sizeof(char)*len);
1836 0 : len = MAX(len, pixels->count);
1837 0 : for (i=0; i<len; i++) sfi->pixels[i] = (u8) pixels->vals[i];
1838 : changed = 1;
1839 : break;
1840 : }
1841 0 : default:
1842 0 : return JS_UNDEFINED;
1843 : }
1844 0 : if (changed) Script_FieldChanged(c, NULL, ptr, NULL);
1845 0 : return JS_UNDEFINED;
1846 : }
1847 :
1848 : /*SFVec2f class functions */
1849 2925 : static GFINLINE GF_JSField *SFVec2f_Create(JSContext *c, JSValue obj, Fixed x, Fixed y)
1850 : {
1851 : GF_JSField *field;
1852 : SFVec2f *v;
1853 2925 : field = NewJSField(c);
1854 2925 : v = gf_sg_vrml_field_pointer_new(GF_SG_VRML_SFVEC2F);
1855 2925 : field->field_ptr = field->field.far_ptr = v;
1856 2925 : field->field.fieldType = GF_SG_VRML_SFVEC2F;
1857 2925 : v->x = x;
1858 2925 : v->y = y;
1859 2925 : JS_SetOpaque(obj, field);
1860 2925 : return field;
1861 : }
1862 :
1863 626 : static JSValue SFVec2fConstructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
1864 : {
1865 626 : Double x = 0.0, y = 0.0;
1866 626 : JSValue obj = JS_NewObjectClass(c, SFVec2fClass.class_id);
1867 :
1868 626 : if (argc > 0) JS_ToFloat64(c, &x, argv[0]);
1869 626 : if (argc > 1) JS_ToFloat64(c, &y, argv[1]);
1870 626 : SFVec2f_Create(c, obj, FLT2FIX( x), FLT2FIX( y));
1871 626 : return obj;
1872 : }
1873 :
1874 675 : static JSValue vec2f_getProperty(JSContext *c, JSValueConst obj, int magic)
1875 : {
1876 675 : GF_JSField *val = (GF_JSField *) JS_GetOpaque(obj, SFVec2fClass.class_id);
1877 675 : if (!val) return JS_EXCEPTION;
1878 :
1879 675 : switch (magic) {
1880 612 : case 0:
1881 612 : return JS_NewFloat64(c, FIX2FLT( ((SFVec2f*)val->field.far_ptr)->x));
1882 63 : case 1:
1883 63 : return JS_NewFloat64(c, FIX2FLT( ((SFVec2f*)val->field.far_ptr)->y));
1884 : default:
1885 : break;
1886 : }
1887 0 : return JS_UNDEFINED;
1888 : }
1889 :
1890 37100 : static JSValue vec2f_setProperty(JSContext *c, JSValueConst obj, JSValueConst value, int magic)
1891 : {
1892 : Double d;
1893 : Fixed v;
1894 : Bool changed = 0;
1895 37100 : GF_JSField *ptr = (GF_JSField *) JS_GetOpaque(obj, SFVec2fClass.class_id);
1896 37100 : if (!ptr) return JS_EXCEPTION;
1897 :
1898 37100 : if (JS_ToFloat64(c, &d, value)) {
1899 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[VRML JS] Value is not a number while assigning SFVec2f\n"));
1900 0 : return JS_FALSE;
1901 : }
1902 37100 : switch (magic) {
1903 19617 : case 0:
1904 19617 : v = FLT2FIX( d);
1905 19617 : changed = ! ( ((SFVec2f*)ptr->field.far_ptr)->x == v);
1906 19617 : ((SFVec2f*)ptr->field.far_ptr)->x = v;
1907 19617 : break;
1908 17483 : case 1:
1909 17483 : v = FLT2FIX( d);
1910 17483 : changed = ! ( ((SFVec2f*)ptr->field.far_ptr)->y == v);
1911 17483 : ((SFVec2f*)ptr->field.far_ptr)->y = v;
1912 17483 : break;
1913 0 : default:
1914 0 : return JS_UNDEFINED;
1915 : }
1916 37100 : if (changed) Script_FieldChanged(c, NULL, ptr, NULL);
1917 37100 : return JS_UNDEFINED;
1918 : }
1919 :
1920 0 : static JSValue vec2f_operand(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv, u32 op)
1921 : {
1922 : SFVec2f *v1, *v2;
1923 0 : Double d = 0.0;
1924 : JSValue pNew;
1925 : Fixed v;
1926 0 : GF_JSField *p1 = (GF_JSField *) JS_GetOpaque(obj, SFVec2fClass.class_id);
1927 0 : if (!p1) return JS_EXCEPTION;
1928 : GF_JSField *p2 = NULL;
1929 :
1930 0 : if (argc) {
1931 0 : if (JS_IsObject(argv[0])) {
1932 0 : p2 = (GF_JSField *) JS_GetOpaque(argv[0], SFVec2fClass.class_id);
1933 0 : } else if (JS_ToFloat64(c, &d, argv[0]))
1934 0 : return JS_EXCEPTION;
1935 : }
1936 :
1937 0 : v1 = ((GF_JSField *) p1)->field.far_ptr;
1938 0 : if (p2)
1939 0 : v2 = ((GF_JSField *) p2)->field.far_ptr;
1940 :
1941 0 : switch (op) {
1942 0 : case 5:
1943 0 : return JS_NewFloat64(c, FIX2FLT(gf_v2d_len(v1)) );
1944 0 : case 7:
1945 0 : if (!p2) return JS_EXCEPTION;
1946 0 : return JS_NewFloat64(c, FIX2FLT( gf_mulfix(v1->x, v2->x) + gf_mulfix(v1->y, v2->y) ) );
1947 0 : case 0:
1948 : case 1:
1949 0 : if (!p2) return JS_EXCEPTION;
1950 : }
1951 :
1952 0 : pNew = JS_NewObjectClass(c, SFVec2fClass.class_id);
1953 0 : switch (op) {
1954 0 : case 0:
1955 0 : SFVec2f_Create(c, pNew, v1->x + v2->x, v1->y + v2->y);
1956 0 : break;
1957 0 : case 1:
1958 0 : SFVec2f_Create(c, pNew, v1->x - v2->x, v1->y - v2->y);
1959 0 : break;
1960 0 : case 2:
1961 0 : SFVec2f_Create(c, pNew, -v1->x , -v1->y );
1962 0 : break;
1963 0 : case 3:
1964 0 : v = FLT2FIX(d);
1965 0 : SFVec2f_Create(c, pNew, gf_mulfix(v1->x , v), gf_mulfix(v1->y, v) );
1966 0 : break;
1967 0 : case 4:
1968 0 : v = FLT2FIX(d);
1969 0 : SFVec2f_Create(c, pNew, gf_divfix(v1->x, v), gf_divfix(v1->y, v));
1970 0 : break;
1971 0 : case 6:
1972 0 : v = gf_v2d_len(v1);
1973 0 : SFVec2f_Create(c, pNew, gf_divfix(v1->x, v), gf_divfix(v1->y, v) );
1974 0 : break;
1975 : }
1976 0 : return pNew;
1977 : }
1978 :
1979 0 : static JSValue vec2f_add(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1980 : {
1981 0 : return vec2f_operand(c, obj, argc, argv, 0);
1982 : }
1983 0 : static JSValue vec2f_subtract(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1984 : {
1985 0 : return vec2f_operand(c, obj, argc, argv, 1);
1986 : }
1987 0 : static JSValue vec2f_negate(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1988 : {
1989 0 : return vec2f_operand(c, obj, argc, argv, 2);
1990 : }
1991 0 : static JSValue vec2f_multiply(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1992 : {
1993 0 : return vec2f_operand(c, obj, argc, argv, 3);
1994 : }
1995 0 : static JSValue vec2f_divide(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1996 : {
1997 0 : return vec2f_operand(c, obj, argc, argv, 4);
1998 : }
1999 0 : static JSValue vec2f_length(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
2000 : {
2001 0 : return vec2f_operand(c, obj, argc, argv, 5);
2002 : }
2003 0 : static JSValue vec2f_normalize(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
2004 : {
2005 0 : return vec2f_operand(c, obj, argc, argv, 6);
2006 : }
2007 0 : static JSValue vec2f_dot(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
2008 : {
2009 0 : return vec2f_operand(c, obj, argc, argv, 7);
2010 : }
2011 :
2012 :
2013 : /*SFVec3f class functions */
2014 0 : static GFINLINE GF_JSField *SFVec3f_Create(JSContext *c, JSValue obj, Fixed x, Fixed y, Fixed z)
2015 : {
2016 : GF_JSField *field;
2017 : SFVec3f *v;
2018 0 : field = NewJSField(c);
2019 0 : v = gf_sg_vrml_field_pointer_new(GF_SG_VRML_SFVEC3F);
2020 0 : field->field_ptr = field->field.far_ptr = v;
2021 0 : field->field.fieldType = GF_SG_VRML_SFVEC3F;
2022 0 : v->x = x;
2023 0 : v->y = y;
2024 0 : v->z = z;
2025 0 : JS_SetOpaque(obj, field);
2026 0 : return field;
2027 : }
2028 :
2029 0 : static JSValue SFVec3fConstructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
2030 : {
2031 0 : Double x = 0.0, y = 0.0, z = 0.0;
2032 0 : JSValue obj = JS_NewObjectClass(c, SFVec3fClass.class_id);
2033 :
2034 0 : if (argc > 0) JS_ToFloat64(c, &x, argv[0]);
2035 0 : if (argc > 1) JS_ToFloat64(c, &y, argv[1]);
2036 0 : if (argc > 2) JS_ToFloat64(c, &z, argv[2]);
2037 0 : SFVec3f_Create(c, obj, FLT2FIX( x), FLT2FIX( y), FLT2FIX( z));
2038 0 : return obj;
2039 : }
2040 :
2041 120 : static JSValue vec3f_getProperty(JSContext *c, JSValueConst obj, int magic)
2042 : {
2043 120 : GF_JSField *val = (GF_JSField *) JS_GetOpaque(obj, SFVec3fClass.class_id);
2044 120 : if (!val) return JS_EXCEPTION;
2045 :
2046 120 : switch (magic) {
2047 120 : case 0:
2048 120 : return JS_NewFloat64(c, FIX2FLT( ((SFVec3f*)val->field.far_ptr)->x));
2049 0 : case 1:
2050 0 : return JS_NewFloat64(c, FIX2FLT( ((SFVec3f*)val->field.far_ptr)->y));
2051 0 : case 2:
2052 0 : return JS_NewFloat64(c, FIX2FLT( ((SFVec3f*)val->field.far_ptr)->z));
2053 : }
2054 0 : return JS_UNDEFINED;
2055 : }
2056 :
2057 0 : static JSValue vec3f_setProperty(JSContext *c, JSValueConst obj, JSValueConst value, int magic)
2058 : {
2059 : Double d;
2060 : Fixed v;
2061 : Bool changed = 0;
2062 0 : GF_JSField *ptr = (GF_JSField *) JS_GetOpaque(obj, SFVec3fClass.class_id);
2063 0 : if (!ptr) return JS_EXCEPTION;
2064 :
2065 0 : if (JS_ToFloat64(c, &d, value)) {
2066 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[VRML JS] Value is not a number while assigning SFVec3f\n"));
2067 0 : return JS_FALSE;
2068 : }
2069 :
2070 0 : switch (magic) {
2071 0 : case 0:
2072 0 : v = FLT2FIX( d);
2073 0 : changed = ! ( ((SFVec3f*)ptr->field.far_ptr)->x == v);
2074 0 : ((SFVec3f*)ptr->field.far_ptr)->x = v;
2075 0 : break;
2076 0 : case 1:
2077 0 : v = FLT2FIX( d);
2078 0 : changed = ! ( ((SFVec3f*)ptr->field.far_ptr)->y == v);
2079 0 : ((SFVec3f*)ptr->field.far_ptr)->y = v;
2080 0 : break;
2081 0 : case 2:
2082 0 : v = FLT2FIX( d);
2083 0 : changed = ! ( ((SFVec3f*)ptr->field.far_ptr)->z == v);
2084 0 : ((SFVec3f*)ptr->field.far_ptr)->z = v;
2085 0 : break;
2086 0 : default:
2087 0 : return JS_UNDEFINED;
2088 : }
2089 0 : if (changed) Script_FieldChanged(c, NULL, ptr, NULL);
2090 0 : return JS_UNDEFINED;
2091 : }
2092 :
2093 :
2094 0 : static JSValue vec3f_operand(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv, u32 op)
2095 : {
2096 : SFVec3f vec, wvec, *v1, *v2;
2097 0 : Double d=0;
2098 : JSValue pNew;
2099 : Fixed v;
2100 0 : GF_JSField *p1 = (GF_JSField *) JS_GetOpaque(obj, SFVec3fClass.class_id);
2101 0 : if (!p1) return JS_EXCEPTION;
2102 : GF_JSField *p2 = NULL;
2103 :
2104 0 : if (argc) {
2105 0 : if (JS_IsObject(argv[0])) {
2106 0 : p2 = (GF_JSField *) JS_GetOpaque(argv[0], SFVec3fClass.class_id);
2107 0 : } else if (JS_ToFloat64(c, &d, argv[0]))
2108 0 : return JS_EXCEPTION;
2109 : }
2110 :
2111 0 : v1 = ((GF_JSField *) p1)->field.far_ptr;
2112 0 : if (p2)
2113 0 : v2 = ((GF_JSField *) p2)->field.far_ptr;
2114 :
2115 0 : switch (op) {
2116 0 : case 0:
2117 : case 1:
2118 : case 8:
2119 0 : if (!p2) return JS_EXCEPTION;
2120 : case 5:
2121 0 : return JS_NewFloat64(c, FIX2FLT(gf_vec_len(*v1)) );
2122 0 : case 7:
2123 0 : vec = *v1;
2124 0 : if (!p2) return JS_EXCEPTION;
2125 0 : wvec = *v2;
2126 0 : return JS_NewFloat64(c, FIX2FLT(gf_vec_dot(vec, wvec)) );
2127 : }
2128 :
2129 0 : pNew = JS_NewObjectClass(c, SFVec3fClass.class_id);
2130 0 : switch (op) {
2131 0 : case 0:
2132 0 : SFVec3f_Create(c, pNew, v1->x + v2->x, v1->y + v2->y, v1->z + v2->z);
2133 0 : break;
2134 0 : case 1:
2135 0 : SFVec3f_Create(c, pNew, v1->x - v2->x, v1->y - v2->y, v1->z - v2->z);
2136 0 : break;
2137 0 : case 2:
2138 0 : SFVec3f_Create(c, pNew, -v1->x , -v1->y , -v1->z );
2139 0 : break;
2140 0 : case 3:
2141 0 : v = FLT2FIX(d);
2142 0 : SFVec3f_Create(c, pNew, gf_mulfix(v1->x, v), gf_mulfix(v1->y, v), gf_mulfix(v1->z, v) );
2143 0 : break;
2144 0 : case 4:
2145 0 : v = FLT2FIX(d);
2146 0 : SFVec3f_Create(c, pNew, gf_divfix(v1->x, v), gf_divfix(v1->y, v), gf_divfix(v1->z, v));
2147 0 : break;
2148 0 : case 6:
2149 0 : vec = *v1;
2150 0 : gf_vec_norm(&vec);
2151 0 : SFVec3f_Create(c, pNew, vec.x, vec.y, vec.z);
2152 0 : break;
2153 0 : case 8:
2154 0 : vec = gf_vec_cross(*v1, *v2);
2155 0 : SFVec3f_Create(c, pNew, vec.x, vec.y, vec.z);
2156 0 : break;
2157 : }
2158 0 : return pNew;
2159 : }
2160 :
2161 0 : static JSValue vec3f_add(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
2162 : {
2163 0 : return vec3f_operand(c, obj, argc, argv, 0);
2164 : }
2165 0 : static JSValue vec3f_subtract(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
2166 : {
2167 0 : return vec3f_operand(c, obj, argc, argv, 1);
2168 : }
2169 0 : static JSValue vec3f_negate(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
2170 : {
2171 0 : return vec3f_operand(c, obj, argc, argv, 2);
2172 : }
2173 0 : static JSValue vec3f_multiply(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
2174 : {
2175 0 : return vec3f_operand(c, obj, argc, argv, 3);
2176 : }
2177 0 : static JSValue vec3f_divide(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
2178 : {
2179 0 : return vec3f_operand(c, obj, argc, argv, 4);
2180 : }
2181 0 : static JSValue vec3f_length(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
2182 : {
2183 0 : return vec3f_operand(c, obj, argc, argv, 5);
2184 : }
2185 0 : static JSValue vec3f_normalize(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
2186 : {
2187 0 : return vec3f_operand(c, obj, argc, argv, 6);
2188 : }
2189 0 : static JSValue vec3f_dot(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
2190 : {
2191 0 : return vec3f_operand(c, obj, argc, argv, 7);
2192 : }
2193 0 : static JSValue vec3f_cross(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
2194 : {
2195 0 : return vec3f_operand(c, obj, argc, argv, 8);
2196 : }
2197 :
2198 :
2199 : /*SFRotation class*/
2200 0 : static GFINLINE GF_JSField *SFRotation_Create(JSContext *c, JSValue obj, Fixed x, Fixed y, Fixed z, Fixed q)
2201 : {
2202 : GF_JSField *field;
2203 : SFRotation *v;
2204 0 : field = NewJSField(c);
2205 0 : v = gf_sg_vrml_field_pointer_new(GF_SG_VRML_SFROTATION);
2206 0 : field->field_ptr = field->field.far_ptr = v;
2207 0 : field->field.fieldType = GF_SG_VRML_SFROTATION;
2208 0 : v->x = x;
2209 0 : v->y = y;
2210 0 : v->z = z;
2211 0 : v->q = q;
2212 0 : JS_SetOpaque(obj, field);
2213 0 : return field;
2214 : }
2215 :
2216 0 : static JSValue SFRotationConstructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
2217 : {
2218 : GF_JSField *f;
2219 : SFVec3f v1, v2;
2220 : Fixed l1, l2, dot;
2221 0 : Double x = 0.0, y = 0.0, z = 0.0, a = 0.0;
2222 0 : JSValue obj = JS_NewObjectClass(c, SFRotationClass.class_id);
2223 :
2224 0 : if (!argc) {
2225 0 : SFRotation_Create(c, obj, FLT2FIX(x), FLT2FIX(y), FIX_ONE, FLT2FIX(a) );
2226 0 : return obj;
2227 : }
2228 0 : if (JS_IsNumber(argv[0])) {
2229 0 : if (argc > 0) JS_ToFloat64(c, &x, argv[0]);
2230 0 : if (argc > 1) JS_ToFloat64(c, &y, argv[1]);
2231 0 : if (argc > 2) JS_ToFloat64(c, &z, argv[2]);
2232 0 : if (argc > 3) JS_ToFloat64(c, &a, argv[3]);
2233 0 : SFRotation_Create(c, obj, FLT2FIX(x), FLT2FIX(y), FLT2FIX(z), FLT2FIX(a));
2234 0 : return obj;
2235 : }
2236 :
2237 :
2238 0 : if (argc!=2) return JS_EXCEPTION;
2239 0 : if (!JS_IsObject(argv[0])) return JS_EXCEPTION;
2240 0 : f = JS_GetOpaque(argv[0], SFVec3fClass.class_id);
2241 0 : if (!f) return JS_EXCEPTION;
2242 :
2243 0 : v1 = * (SFVec3f *) (f)->field.far_ptr;
2244 0 : if (JS_IsNumber(argv[1])) {
2245 0 : JS_ToFloat64(c, &a, argv[1]);
2246 0 : SFRotation_Create(c, obj, v1.x, v1.y, v1.z, FLT2FIX(a));
2247 0 : return obj;
2248 : }
2249 :
2250 0 : if (!JS_IsObject(argv[1])) return JS_FALSE;
2251 0 : f = JS_GetOpaque(argv[1], SFVec3fClass.class_id);
2252 0 : if (!f) return JS_EXCEPTION;
2253 0 : v2 = * (SFVec3f *) (f)->field.far_ptr;
2254 0 : l1 = gf_vec_len(v1);
2255 0 : l2 = gf_vec_len(v2);
2256 0 : dot = gf_divfix(gf_vec_dot(v1, v2), gf_mulfix(l1, l2) );
2257 0 : a = gf_atan2(gf_sqrt(FIX_ONE - gf_mulfix(dot, dot)), dot);
2258 0 : SFRotation_Create(c, obj, gf_mulfix(v1.y, v2.z) - gf_mulfix(v2.y, v1.z),
2259 0 : gf_mulfix(v1.z, v2.x) - gf_mulfix(v2.z, v1.x),
2260 0 : gf_mulfix(v1.x, v2.y) - gf_mulfix(v2.x, v1.y),
2261 : FLT2FIX(a));
2262 0 : return obj;
2263 : }
2264 :
2265 0 : static JSValue rot_getProperty(JSContext *c, JSValueConst obj, int magic)
2266 : {
2267 0 : GF_JSField *val = (GF_JSField *) JS_GetOpaque(obj, SFRotationClass.class_id);
2268 0 : if (!val) return JS_EXCEPTION;
2269 0 : switch (magic) {
2270 0 : case 0:
2271 0 : return JS_NewFloat64(c, FIX2FLT( ((SFRotation*)val->field.far_ptr)->x));
2272 0 : case 1:
2273 0 : return JS_NewFloat64(c, FIX2FLT( ((SFRotation*)val->field.far_ptr)->y));
2274 0 : case 2:
2275 0 : return JS_NewFloat64(c, FIX2FLT( ((SFRotation*)val->field.far_ptr)->z));
2276 0 : case 3:
2277 0 : return JS_NewFloat64(c, FIX2FLT( ((SFRotation*)val->field.far_ptr)->q));
2278 : }
2279 0 : return JS_UNDEFINED;
2280 : }
2281 :
2282 0 : static JSValue rot_setProperty(JSContext *c, JSValueConst obj, JSValueConst value, int magic)
2283 : {
2284 : Double d;
2285 : Fixed v;
2286 : Bool changed = 0;
2287 0 : GF_JSField *ptr = (GF_JSField *) JS_GetOpaque(obj, SFRotationClass.class_id);
2288 0 : if (!ptr) return JS_EXCEPTION;
2289 :
2290 0 : if (JS_ToFloat64(c, &d, value)) {
2291 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[VRML JS] Value is not a number while assigning SFRotation\n"));
2292 0 : return JS_FALSE;
2293 : }
2294 :
2295 0 : switch (magic) {
2296 0 : case 0:
2297 0 : v = FLT2FIX(d);
2298 0 : changed = ! ( ((SFRotation*)ptr->field.far_ptr)->x == v);
2299 0 : ((SFRotation*)ptr->field.far_ptr)->x = v;
2300 0 : break;
2301 0 : case 1:
2302 0 : v = FLT2FIX(d);
2303 0 : changed = ! ( ((SFRotation*)ptr->field.far_ptr)->y == v);
2304 0 : ((SFRotation*)ptr->field.far_ptr)->y = v;
2305 0 : break;
2306 0 : case 2:
2307 0 : v = FLT2FIX(d);
2308 0 : changed = ! ( ((SFRotation*)ptr->field.far_ptr)->z == v);
2309 0 : ((SFRotation*)ptr->field.far_ptr)->z = v;
2310 0 : break;
2311 0 : case 3:
2312 0 : v = FLT2FIX(d);
2313 0 : changed = ! ( ((SFRotation*)ptr->field.far_ptr)->q == v);
2314 0 : ((SFRotation*)ptr->field.far_ptr)->q = v;
2315 0 : break;
2316 0 : default:
2317 0 : return JS_UNDEFINED;
2318 : }
2319 0 : if (changed) Script_FieldChanged(c, NULL, ptr, NULL);
2320 0 : return JS_UNDEFINED;
2321 : }
2322 :
2323 0 : static JSValue rot_getAxis(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
2324 : {
2325 : SFRotation r;
2326 : JSValue pNew;
2327 0 : GF_JSField *p = JS_GetOpaque(obj, SFRotationClass.class_id);
2328 0 : if (!p) return JS_EXCEPTION;
2329 0 : r = * (SFRotation *) (p)->field.far_ptr;
2330 0 : pNew = JS_NewObjectClass(c, SFVec3fClass.class_id);
2331 0 : SFVec3f_Create(c, pNew, r.x, r.y, r.z);
2332 0 : return pNew;
2333 : }
2334 0 : static JSValue rot_inverse(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
2335 : {
2336 : SFRotation r;
2337 : JSValue pNew;
2338 0 : GF_JSField *p = JS_GetOpaque(obj, SFRotationClass.class_id);
2339 0 : if (!p) return JS_EXCEPTION;
2340 0 : r = * (SFRotation *) (p)->field.far_ptr;
2341 0 : pNew = JS_NewObjectClass(c, SFRotationClass.class_id);
2342 0 : SFRotation_Create(c, pNew, r.x, r.y, r.z, r.q-GF_PI);
2343 0 : return pNew;
2344 : }
2345 :
2346 0 : static JSValue rot_multiply(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
2347 : {
2348 : SFRotation r1, r2;
2349 : SFVec4f q1, q2;
2350 : JSValue pNew;
2351 :
2352 0 : if (argc<=0 || !JS_IsObject(argv[0]))
2353 0 : return JS_EXCEPTION;
2354 0 : GF_JSField *p = JS_GetOpaque(obj, SFRotationClass.class_id);
2355 0 : if (!p) return JS_EXCEPTION;
2356 0 : r1 = * (SFRotation *) (p)->field.far_ptr;
2357 0 : p = JS_GetOpaque(argv[0], SFRotationClass.class_id);
2358 0 : if (!p) return JS_EXCEPTION;
2359 0 : r2 = * (SFRotation *) (p)->field.far_ptr;
2360 :
2361 0 : q1 = gf_quat_from_rotation(r1);
2362 0 : q2 = gf_quat_from_rotation(r2);
2363 0 : q1 = gf_quat_multiply(&q1, &q2);
2364 0 : r1 = gf_quat_to_rotation(&q1);
2365 :
2366 0 : pNew = JS_NewObjectClass(c, SFRotationClass.class_id);
2367 0 : SFRotation_Create(c, pNew, r1.x, r1.y, r1.z, r1.q);
2368 0 : return pNew;
2369 : }
2370 :
2371 0 : static JSValue rot_multVec(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
2372 : {
2373 : SFVec3f v;
2374 : SFRotation r;
2375 : GF_Matrix mx;
2376 : JSValue pNew;
2377 0 : if (argc<=0 || !JS_IsObject(argv[0]))
2378 0 : return JS_EXCEPTION;
2379 :
2380 0 : GF_JSField *p = JS_GetOpaque(obj, SFRotationClass.class_id);
2381 0 : if (!p) return JS_EXCEPTION;
2382 0 : r = * (SFRotation *) (p)->field.far_ptr;
2383 :
2384 0 : p = JS_GetOpaque(argv[0], SFVec3fClass.class_id);
2385 0 : if (!p) return JS_EXCEPTION;
2386 0 : v = * (SFVec3f *) (p)->field.far_ptr;
2387 :
2388 0 : gf_mx_init(mx);
2389 0 : gf_mx_add_rotation(&mx, r.q, r.x, r.y, r.z);
2390 0 : gf_mx_apply_vec(&mx, &v);
2391 0 : pNew = JS_NewObjectClass(c, SFVec3fClass.class_id);
2392 0 : SFVec3f_Create(c, pNew, v.x, v.y, v.z);
2393 0 : return pNew;
2394 : }
2395 0 : static JSValue rot_setAxis(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
2396 : {
2397 : SFVec3f v;
2398 : SFRotation *r;
2399 : GF_JSField *ptr;
2400 0 : if (argc<=0 || !JS_IsObject(argv[0]))
2401 0 : return JS_EXCEPTION;
2402 0 : ptr = JS_GetOpaque(obj, SFRotationClass.class_id);
2403 0 : if (!ptr) return JS_EXCEPTION;
2404 0 : r = (SFRotation *) (ptr)->field.far_ptr;
2405 :
2406 0 : GF_JSField *p = JS_GetOpaque(argv[0], SFVec3fClass.class_id);
2407 0 : if (!p) return JS_EXCEPTION;
2408 0 : v = * (SFVec3f *) (p)->field.far_ptr;
2409 :
2410 0 : r->x = v.x;
2411 0 : r->y = v.y;
2412 0 : r->z = v.z;
2413 0 : Script_FieldChanged(c, NULL, ptr, NULL);
2414 0 : return JS_TRUE;
2415 : }
2416 0 : static JSValue rot_slerp(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
2417 : {
2418 : SFRotation v1, v2, res;
2419 : SFVec4f q1, q2;
2420 : JSValue pNew;
2421 : Double d;
2422 : GF_JSField *p;
2423 :
2424 0 : if (argc<=0 || !JS_IsObject(argv[0]))
2425 0 : return JS_EXCEPTION;
2426 0 : p = JS_GetOpaque(obj, SFRotationClass.class_id);
2427 0 : if (!p) return JS_EXCEPTION;
2428 0 : v1 = * (SFRotation *) (p)->field.far_ptr;
2429 :
2430 0 : p = JS_GetOpaque(argv[0], SFRotationClass.class_id);
2431 0 : if (!p) return JS_EXCEPTION;
2432 0 : v2 = * (SFRotation *) (p)->field.far_ptr;
2433 :
2434 0 : if (JS_ToFloat64(c, &d, argv[1])) return JS_EXCEPTION;
2435 :
2436 0 : q1 = gf_quat_from_rotation(v1);
2437 0 : q2 = gf_quat_from_rotation(v2);
2438 0 : q1 = gf_quat_slerp(q1, q2, FLT2FIX( d));
2439 0 : res = gf_quat_to_rotation(&q1);
2440 0 : pNew = JS_NewObjectClass(c, SFRotationClass.class_id);
2441 0 : SFRotation_Create(c, pNew, res.x, res.y, res.z, res.q);
2442 0 : return pNew;
2443 : }
2444 :
2445 : /* SFColor class functions */
2446 595 : static GFINLINE GF_JSField *SFColor_Create(JSContext *c, JSValue obj, Fixed r, Fixed g, Fixed b)
2447 : {
2448 : GF_JSField *field;
2449 : SFColor *v;
2450 595 : field = NewJSField(c);
2451 595 : v = gf_sg_vrml_field_pointer_new(GF_SG_VRML_SFCOLOR);
2452 595 : field->field_ptr = field->field.far_ptr = v;
2453 595 : field->field.fieldType = GF_SG_VRML_SFCOLOR;
2454 595 : v->red = r;
2455 595 : v->green = g;
2456 595 : v->blue = b;
2457 595 : JS_SetOpaque(obj, field);
2458 595 : return field;
2459 : }
2460 589 : static JSValue SFColorConstructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
2461 : {
2462 589 : Double r = 0.0, g = 0.0, b = 0.0;
2463 589 : JSValue obj = JS_NewObjectClass(c, SFColorClass.class_id);
2464 :
2465 589 : if (argc > 0) JS_ToFloat64(c, &r, argv[0]);
2466 589 : if (argc > 1) JS_ToFloat64(c, &g, argv[1]);
2467 589 : if (argc > 2) JS_ToFloat64(c, &b, argv[2]);
2468 589 : SFColor_Create(c, obj, FLT2FIX( r), FLT2FIX( g), FLT2FIX( b));
2469 589 : return obj;
2470 : }
2471 797 : static JSValue color_getProperty(JSContext *c, JSValueConst obj, int magic)
2472 : {
2473 797 : GF_JSField *val = (GF_JSField *) JS_GetOpaque(obj, SFColorClass.class_id);
2474 797 : if (!val) return JS_EXCEPTION;
2475 797 : switch (magic) {
2476 307 : case 0:
2477 307 : return JS_NewFloat64(c, FIX2FLT( ((SFColor*)val->field.far_ptr)->red));
2478 307 : case 1:
2479 307 : return JS_NewFloat64(c, FIX2FLT( ((SFColor*)val->field.far_ptr)->green));
2480 : break;
2481 183 : case 2:
2482 183 : return JS_NewFloat64(c, FIX2FLT( ((SFColor*)val->field.far_ptr)->blue));
2483 : break;
2484 0 : default:
2485 0 : return JS_UNDEFINED;
2486 : }
2487 : return JS_UNDEFINED;
2488 : }
2489 :
2490 42 : static JSValue color_setProperty(JSContext *c, JSValueConst obj, JSValueConst value, int magic)
2491 : {
2492 : Double d;
2493 : Fixed v;
2494 : Bool changed = 0;
2495 42 : GF_JSField *ptr = (GF_JSField *) JS_GetOpaque(obj, SFColorClass.class_id);
2496 42 : if (!ptr) return JS_EXCEPTION;
2497 :
2498 42 : if (JS_ToFloat64(c, &d, value)) {
2499 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[VRML JS] Value is not a number while assigning SFColor\n"));
2500 0 : return JS_FALSE;
2501 : }
2502 :
2503 42 : switch (magic) {
2504 14 : case 0:
2505 14 : v = FLT2FIX(d);
2506 14 : changed = ! ( ((SFColor*)ptr->field.far_ptr)->red == v);
2507 14 : ((SFColor*)ptr->field.far_ptr)->red = v;
2508 14 : break;
2509 14 : case 1:
2510 14 : v = FLT2FIX(d);
2511 14 : changed = ! ( ((SFColor*)ptr->field.far_ptr)->green == v);
2512 14 : ((SFColor*)ptr->field.far_ptr)->green = v;
2513 14 : break;
2514 14 : case 2:
2515 14 : v = FLT2FIX(d);
2516 14 : changed = ! ( ((SFColor*)ptr->field.far_ptr)->blue == v);
2517 14 : ((SFColor*)ptr->field.far_ptr)->blue = v;
2518 14 : break;
2519 0 : default:
2520 0 : return JS_UNDEFINED;
2521 : }
2522 42 : if (changed) Script_FieldChanged(c, NULL, ptr, NULL);
2523 42 : return JS_UNDEFINED;
2524 : }
2525 :
2526 0 : static JSValue color_setHSV(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
2527 : {
2528 : SFColor *v1, hsv;
2529 0 : Double h=0, s=0, v=0;
2530 0 : GF_JSField *ptr = (GF_JSField *) JS_GetOpaque(obj, SFRotationClass.class_id);
2531 0 : if (!ptr) return JS_EXCEPTION;
2532 0 : if (argc != 3) return JS_FALSE;
2533 :
2534 0 : v1 = (ptr)->field.far_ptr;
2535 0 : if (JS_ToFloat64(c, &h, argv[0])) return JS_EXCEPTION;
2536 0 : if (JS_ToFloat64(c, &s, argv[1])) return JS_EXCEPTION;
2537 0 : if (JS_ToFloat64(c, &v, argv[2])) return JS_EXCEPTION;
2538 :
2539 0 : hsv.red = FLT2FIX( h);
2540 0 : hsv.green = FLT2FIX( s);
2541 0 : hsv.blue = FLT2FIX( v);
2542 0 : SFColor_fromHSV(&hsv);
2543 0 : gf_sg_vrml_field_copy(v1, &hsv, GF_SG_VRML_SFCOLOR);
2544 0 : Script_FieldChanged(c, NULL, ptr, NULL);
2545 0 : return JS_UNDEFINED;
2546 : }
2547 :
2548 0 : static JSValue color_getHSV(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
2549 : {
2550 : SFColor *v1, hsv;
2551 : JSValue arr;
2552 :
2553 0 : GF_JSField *ptr = (GF_JSField *) JS_GetOpaque(obj, SFRotationClass.class_id);
2554 0 : if (!ptr) return JS_EXCEPTION;
2555 0 : if (argc != 3) return JS_FALSE;
2556 :
2557 0 : v1 = (ptr)->field.far_ptr;
2558 0 : hsv = *v1;
2559 0 : SFColor_toHSV(&hsv);
2560 0 : arr = JS_NewArray(c);
2561 0 : if (JS_IsException(arr)) return arr;
2562 :
2563 0 : JS_SetPropertyUint32(c, arr, 0, JS_NewFloat64(c, FIX2FLT(hsv.red)) );
2564 0 : JS_SetPropertyUint32(c, arr, 1, JS_NewFloat64(c, FIX2FLT(hsv.green)) );
2565 0 : JS_SetPropertyUint32(c, arr, 2, JS_NewFloat64(c, FIX2FLT(hsv.blue)) );
2566 0 : return arr;
2567 : }
2568 :
2569 1813 : static JSValue genmf_Constructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv, JSClassID mf_class_id, JSClassID sf_class_id, u32 fieldType)
2570 : {
2571 : u32 i;
2572 : GF_JSField *ptr;
2573 1813 : JSValue obj = JS_NewObjectClass(c, mf_class_id);
2574 1813 : ptr = NewJSField(c);
2575 1813 : ptr->field.fieldType = fieldType;
2576 1813 : ptr->obj = obj;
2577 1813 : JS_SetOpaque(obj, ptr);
2578 :
2579 1813 : if (!argc || (fieldType==GF_SG_VRML_MFNODE)) return obj;
2580 0 : ptr->mfvals = gf_realloc(ptr->mfvals, sizeof(JSValue)*argc);
2581 0 : ptr->mfvals_count = argc;
2582 0 : for (i=0; i<(u32) argc; i++) {
2583 0 : if (sf_class_id) {
2584 0 : if (JS_IsObject(argv[i]) && JS_GetOpaque(argv[i], sf_class_id)) {
2585 0 : ptr->mfvals[i] = JS_DupValue(c, argv[i]);
2586 : } else {
2587 0 : ptr->mfvals[i] = JS_NewObjectClass(c, sf_class_id);
2588 : }
2589 : } else {
2590 0 : ptr->mfvals[i] = JS_DupValue(c, argv[i]);
2591 : }
2592 : }
2593 : //don't dup value since this may go directly in script
2594 0 : return obj;
2595 : }
2596 :
2597 0 : static JSValue MFBoolConstructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
2598 : {
2599 0 : return genmf_Constructor(c, new_target, argc, argv, MFBoolClass.class_id, 0, GF_SG_VRML_MFBOOL);
2600 : }
2601 209 : static JSValue MFInt32Constructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
2602 : {
2603 209 : return genmf_Constructor(c, new_target, argc, argv, MFInt32Class.class_id, 0, GF_SG_VRML_MFINT32);
2604 : }
2605 30 : static JSValue MFFloatConstructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
2606 : {
2607 30 : return genmf_Constructor(c, new_target, argc, argv, MFFloatClass.class_id, 0, GF_SG_VRML_MFFLOAT);
2608 : }
2609 0 : static JSValue MFTimeConstructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
2610 : {
2611 0 : return genmf_Constructor(c, new_target, argc, argv, MFTimeClass.class_id, 0, GF_SG_VRML_MFTIME);
2612 : }
2613 185 : static JSValue MFStringConstructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
2614 : {
2615 185 : return genmf_Constructor(c, new_target, argc, argv, MFStringClass.class_id, 0, GF_SG_VRML_MFSTRING);
2616 : }
2617 91 : static JSValue MFURLConstructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
2618 : {
2619 91 : return genmf_Constructor(c, new_target, argc, argv, MFUrlClass.class_id, 0, GF_SG_VRML_MFURL);
2620 : }
2621 1061 : static JSValue MFNodeConstructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
2622 : {
2623 1061 : return genmf_Constructor(c, new_target, argc, argv, MFNodeClass.class_id, 0, GF_SG_VRML_MFNODE);
2624 : }
2625 :
2626 6495 : static void array_finalize_ex(JSRuntime *rt, JSValue obj, Bool is_js_call)
2627 : {
2628 : u32 i;
2629 6495 : GF_JSField *ptr = JS_GetOpaque_Nocheck(obj);
2630 :
2631 6495 : JS_ObjectDestroyed(rt, obj, ptr, 1);
2632 6495 : if (!ptr) return;
2633 :
2634 1813 : GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[VRML JS] unregistering MFField %s\n", ptr->field.name));
2635 :
2636 : /*MF values*/
2637 1813 : if (ptr->mfvals) {
2638 4476 : for (i=0; i<ptr->mfvals_count; i++)
2639 4476 : JS_FreeValueRT(rt, ptr->mfvals[i] );
2640 744 : gf_free(ptr->mfvals);
2641 : }
2642 : /*MFNode*/
2643 1813 : if (ptr->temp_list) {
2644 0 : gf_node_unregister_children(ptr->owner, ptr->temp_list);
2645 : }
2646 1813 : if (ptr->field_ptr) {
2647 0 : gf_sg_vrml_field_pointer_del(ptr->field_ptr, ptr->field.fieldType);
2648 : }
2649 1813 : gf_free(ptr);
2650 : }
2651 :
2652 6257 : static void array_finalize(JSRuntime *rt, JSValue obj)
2653 : {
2654 6257 : array_finalize_ex(rt, obj, GF_TRUE);
2655 6257 : }
2656 :
2657 :
2658 13600 : static JSValue array_getElement(JSContext *c, JSValueConst obj, JSAtom atom, JSValueConst receiver)
2659 : {
2660 : u32 idx;
2661 13600 : GF_JSField *ptr = JS_GetOpaque_Nocheck(obj);
2662 :
2663 13600 : if (!JS_AtomIsArrayIndex(c, &idx, atom)) {
2664 2171 : JSValue ret = JS_UNDEFINED;
2665 2171 : const char *str = JS_AtomToCString(c, atom);
2666 2171 : if (!str) return ret;
2667 :
2668 2171 : if (!strcmp(str, "length")) {
2669 2166 : GF_JSField *f_ptr = JS_GetOpaque_Nocheck(obj);
2670 2166 : if (!f_ptr) {
2671 0 : ret = JS_EXCEPTION;
2672 2166 : } else if (f_ptr->field.fieldType==GF_SG_VRML_MFNODE) {
2673 750 : ret = JS_NewInt32(c, gf_node_list_get_count(*(GF_ChildNodeItem **)f_ptr->field.far_ptr) );
2674 : } else {
2675 1416 : ret = JS_NewInt32(c, f_ptr->mfvals_count);
2676 : }
2677 5 : } else if (!strcmp(str, "toString")) {
2678 : ret = JS_NewCFunction(c, field_toString, "toString", 0);
2679 : }
2680 2171 : JS_FreeCString(c, str);
2681 2171 : return ret;
2682 : }
2683 :
2684 11429 : if (ptr->field.fieldType==GF_SG_VRML_MFNODE) {
2685 10000 : GF_Node *node = gf_node_list_get_child(*(GF_ChildNodeItem **)ptr->field.far_ptr, idx);
2686 10000 : if (!node) return JS_NULL;
2687 10000 : return JS_DupValue(c, node_get_binding(JS_GetScriptStack(c), node));
2688 : } else {
2689 : JSValue val;
2690 1429 : if (idx>=ptr->mfvals_count) return JS_NULL;
2691 1429 : val = ptr->mfvals[idx];
2692 : // GF_JSField *sf = JS_GetOpaque_Nocheck(val);
2693 : return JS_DupValue(c, val);
2694 : }
2695 : return JS_NULL;
2696 : }
2697 :
2698 :
2699 308 : static int array_setLength(JSContext *c, GF_JSField *ptr, JSValueConst value)
2700 : {
2701 : u32 len, i, sftype, old_len;
2702 : GF_JSClass *the_sf_class;
2703 :
2704 616 : if (!JS_IsInteger(value) || !ptr) return -1;
2705 308 : len=-1;
2706 308 : JS_ToInt32(c, &len, value);
2707 308 : if ((s32)len<0) return -1;
2708 :
2709 308 : if (!len) {
2710 204 : if (ptr->field.fieldType==GF_SG_VRML_MFNODE) {
2711 48 : gf_node_unregister_children(ptr->owner, *(GF_ChildNodeItem**)ptr->field.far_ptr);
2712 48 : *(GF_ChildNodeItem**)ptr->field.far_ptr = NULL;
2713 : } else {
2714 156 : gf_sg_vrml_mf_reset(ptr->field.far_ptr, ptr->field.fieldType);
2715 : }
2716 335 : while (ptr->mfvals_count) {
2717 131 : ptr->mfvals_count--;
2718 131 : JS_FreeValue(c, ptr->mfvals[ptr->mfvals_count]);
2719 : }
2720 204 : gf_free(ptr->mfvals);
2721 204 : ptr->mfvals = NULL;
2722 204 : return 1;
2723 : }
2724 :
2725 104 : old_len = ptr->mfvals_count;
2726 104 : ptr->mfvals_count = len;
2727 104 : if (len == old_len) return 1;
2728 :
2729 : the_sf_class = NULL;
2730 104 : switch (ptr->field.fieldType) {
2731 104 : case GF_SG_VRML_MFVEC2F:
2732 : the_sf_class = &SFVec2fClass;
2733 104 : break;
2734 0 : case GF_SG_VRML_MFVEC3F:
2735 : the_sf_class = &SFVec3fClass;
2736 0 : break;
2737 0 : case GF_SG_VRML_MFCOLOR:
2738 : the_sf_class = &SFColorClass;
2739 0 : break;
2740 0 : case GF_SG_VRML_MFROTATION:
2741 : the_sf_class = &SFRotationClass;
2742 0 : break;
2743 0 : case GF_SG_VRML_MFNODE:
2744 : {
2745 0 : u32 nb_nodes = gf_node_list_get_count(*(GF_ChildNodeItem**)ptr->field.far_ptr);
2746 0 : while (len < nb_nodes) {
2747 0 : GF_Node *n = gf_node_list_del_child_idx((GF_ChildNodeItem**)ptr->field.far_ptr, nb_nodes - 1);
2748 0 : if (n) gf_node_unregister(n, ptr->owner);
2749 : nb_nodes--;
2750 : }
2751 0 : if (len>nb_nodes) {
2752 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[VRML] MFARRAY EXPANSION NOT SUPPORTED!!!\n"));
2753 : }
2754 : }
2755 : return 1;
2756 : }
2757 :
2758 104 : ptr->mfvals = gf_realloc(ptr->mfvals, sizeof(JSValue)*ptr->mfvals_count);
2759 104 : sftype = gf_sg_vrml_get_sf_type(ptr->field.fieldType);
2760 1352 : for (i=old_len; i<len; i++) {
2761 : JSValue a_val;
2762 1248 : if (the_sf_class) {
2763 : GF_JSField *slot;
2764 1248 : a_val = JS_NewObjectClass(c, the_sf_class->class_id);
2765 1248 : slot = SFVec2f_Create(c, a_val, 0, 0);
2766 1248 : if (slot) {
2767 1248 : slot->owner = ptr->owner;
2768 : }
2769 : } else {
2770 0 : switch (sftype) {
2771 0 : case GF_SG_VRML_SFBOOL:
2772 0 : a_val = JS_FALSE;
2773 0 : break;
2774 : case GF_SG_VRML_SFFLOAT:
2775 : case GF_SG_VRML_SFTIME:
2776 : a_val = JS_NewFloat64(c, 0);
2777 : break;
2778 0 : case GF_SG_VRML_SFSTRING:
2779 : case GF_SG_VRML_SFURL:
2780 0 : a_val = JS_NewString(c, "");
2781 0 : break;
2782 : case GF_SG_VRML_SFINT32:
2783 : default:
2784 : a_val = JS_NewInt32(c, 0);
2785 : break;
2786 : }
2787 : }
2788 1248 : ptr->mfvals[i] = a_val;
2789 1248 : gf_sg_vrml_mf_append(ptr->field.far_ptr, ptr->field.fieldType, NULL);
2790 : }
2791 : return 1;
2792 : }
2793 :
2794 : //this could be overloaded for each MF type...
2795 28802 : static int array_setElement(JSContext *c, JSValueConst obj, JSAtom atom, JSValueConst value, JSValueConst receiver, int flags)
2796 : {
2797 : u32 ind;
2798 : u32 len;
2799 : Double d;
2800 : s32 ival;
2801 : GF_JSField *from;
2802 : GF_JSClass *the_sf_class = NULL;
2803 : char *str_val;
2804 : void *sf_slot;
2805 : Bool is_append = 0;
2806 28802 : GF_JSField *ptr = (GF_JSField *) JS_GetOpaque_Nocheck(obj);
2807 28802 : if (!ptr) return -1;
2808 :
2809 28802 : if (!JS_AtomIsArrayIndex(c, &ind, atom)) {
2810 : int ret = 0;
2811 308 : const char *str = JS_AtomToCString(c, atom);
2812 308 : if (str && !strcmp(str, "length")) {
2813 308 : ret = array_setLength(c, ptr, value);
2814 : }
2815 308 : JS_FreeCString(c, str);
2816 308 : return ret;
2817 : }
2818 28494 : if (ptr->field.fieldType!=GF_SG_VRML_MFNODE) {
2819 26372 : len = ptr->mfvals_count;
2820 : } else {
2821 2122 : len = gf_node_list_get_count(*(GF_ChildNodeItem **)ptr->field.far_ptr);
2822 : }
2823 28494 : if (gf_sg_vrml_is_sf_field(ptr->field.fieldType))
2824 : return -1;
2825 :
2826 28494 : switch (ptr->field.fieldType) {
2827 15149 : case GF_SG_VRML_MFVEC2F:
2828 : the_sf_class = &SFVec2fClass;
2829 15149 : break;
2830 0 : case GF_SG_VRML_MFVEC3F:
2831 : the_sf_class = &SFVec3fClass;
2832 0 : break;
2833 81 : case GF_SG_VRML_MFCOLOR:
2834 : the_sf_class = &SFColorClass;
2835 81 : break;
2836 0 : case GF_SG_VRML_MFROTATION:
2837 : the_sf_class = &SFRotationClass;
2838 0 : break;
2839 : }
2840 : /*dynamic expend*/
2841 28494 : if (ind>=len) {
2842 : is_append = 1;
2843 : }
2844 3314 : if (is_append && (ptr->field.fieldType!=GF_SG_VRML_MFNODE)) {
2845 :
2846 1342 : ptr->mfvals = gf_realloc(ptr->mfvals, sizeof(JSValue)*(ind+1));
2847 1342 : ptr->mfvals_count = ind+1;
2848 :
2849 2684 : while (len<ind) {
2850 : JSValue a_val;
2851 0 : switch (ptr->field.fieldType) {
2852 : case GF_SG_VRML_MFBOOL:
2853 : a_val = JS_NewBool(c, 0);
2854 : break;
2855 : case GF_SG_VRML_MFINT32:
2856 : a_val = JS_NewInt32(c, 0);
2857 : break;
2858 : case GF_SG_VRML_MFFLOAT:
2859 : case GF_SG_VRML_MFTIME:
2860 : a_val = JS_NewFloat64(c, 0);
2861 : break;
2862 0 : case GF_SG_VRML_MFSTRING:
2863 : case GF_SG_VRML_MFURL:
2864 0 : a_val = JS_NewString(c, "");
2865 0 : break;
2866 0 : case GF_SG_VRML_MFVEC2F:
2867 : case GF_SG_VRML_MFVEC3F:
2868 : case GF_SG_VRML_MFCOLOR:
2869 : case GF_SG_VRML_MFROTATION:
2870 0 : a_val = JS_NewObjectClass(c, the_sf_class->class_id);
2871 0 : break;
2872 0 : default:
2873 0 : a_val = JS_NULL;
2874 0 : break;
2875 : }
2876 :
2877 0 : gf_sg_vrml_mf_insert(ptr->field.far_ptr, ptr->field.fieldType, &sf_slot, len);
2878 0 : ptr->mfvals[len] = a_val;
2879 :
2880 0 : len++;
2881 : }
2882 1342 : if (ptr->field.far_ptr)
2883 1342 : gf_sg_vrml_mf_insert(ptr->field.far_ptr, ptr->field.fieldType, &sf_slot, ind);
2884 :
2885 1342 : ptr->mfvals[ind] = JS_NULL;
2886 : }
2887 :
2888 28494 : if (ptr->field.far_ptr && (ptr->field.fieldType!=GF_SG_VRML_MFNODE)) {
2889 26372 : u32 items = ((GenMFField *)ptr->field.far_ptr)->count;
2890 52744 : while (ind>=items) {
2891 0 : gf_sg_vrml_mf_insert(ptr->field.far_ptr, ptr->field.fieldType, &sf_slot, ind);
2892 0 : items++;
2893 : }
2894 : }
2895 : /*assign object*/
2896 28494 : if (ptr->field.fieldType==GF_SG_VRML_MFNODE) {
2897 2122 : if (JS_IsUndefined(value)) return -1;
2898 2122 : if (JS_IsNull(value) ) return -1;
2899 2122 : if (!JS_GetOpaque(value, SFNodeClass.class_id) ) return -1;
2900 26372 : } else if (the_sf_class) {
2901 15230 : if (JS_IsUndefined(value)) return -1;
2902 15230 : if (!JS_GetOpaque(value, the_sf_class->class_id) ) return -1;
2903 11142 : } else if (ptr->field.fieldType==GF_SG_VRML_MFBOOL) {
2904 0 : if (!JS_IsBool(value)) return -1;
2905 11142 : } else if (ptr->field.fieldType==GF_SG_VRML_MFINT32) {
2906 10166 : if (!JS_IsInteger(value)) return -1;
2907 976 : } else if (ptr->field.fieldType==GF_SG_VRML_MFFLOAT) {
2908 75 : if (!JS_IsNumber(value)) return -1;
2909 901 : } else if (ptr->field.fieldType==GF_SG_VRML_MFTIME) {
2910 0 : if (!JS_IsNumber(value)) return -1;
2911 901 : } else if (ptr->field.fieldType==GF_SG_VRML_MFSTRING) {
2912 808 : if (!JS_IsString(value)) return -1;
2913 93 : } else if (ptr->field.fieldType==GF_SG_VRML_MFURL) {
2914 93 : if (!JS_IsString(value)) return -1;
2915 : }
2916 :
2917 :
2918 : /*rewrite MFNode entry*/
2919 28494 : if (ptr->field.fieldType==GF_SG_VRML_MFNODE) {
2920 : GF_Node *prev_n, *new_n;
2921 :
2922 2122 : if (!ptr->owner) return 1;
2923 :
2924 : /*get new node*/
2925 2122 : from = (GF_JSField *) JS_GetOpaque(value, SFNodeClass.class_id);
2926 2122 : new_n = *(GF_Node**)from->field.far_ptr;
2927 : prev_n = NULL;
2928 :
2929 2122 : if (!is_append) {
2930 : /*get and delete previous node if any, but unregister later*/
2931 150 : prev_n = gf_node_list_del_child_idx( (GF_ChildNodeItem **)ptr->field.far_ptr, ind);
2932 : }
2933 :
2934 2122 : if (new_n) {
2935 2122 : gf_node_list_insert_child( (GF_ChildNodeItem **)ptr->field.far_ptr , new_n, ind);
2936 2122 : gf_node_register(new_n, ptr->owner);
2937 :
2938 : /*node created from script and inserted in the tree, create binding*/
2939 2122 : node_get_binding(JS_GetScriptStack(c), new_n);
2940 : }
2941 : /*unregister previous node*/
2942 2122 : if (prev_n) gf_node_unregister(prev_n, ptr->owner);
2943 :
2944 2122 : Script_FieldChanged(c, NULL, ptr, NULL);
2945 2122 : return 1;
2946 : }
2947 :
2948 : //since this value comes from the script, ref count it
2949 26372 : JS_FreeValue(c, ptr->mfvals[ind]);
2950 52744 : ptr->mfvals[ind] = JS_DupValue(c, value);
2951 :
2952 26372 : if (!ptr->owner) return 1;
2953 26372 : if (!ptr->field.far_ptr) return -1;
2954 :
2955 : /*rewrite MF slot*/
2956 26372 : switch (ptr->field.fieldType) {
2957 0 : case GF_SG_VRML_MFBOOL:
2958 0 : ((MFBool *)ptr->field.far_ptr)->vals[ind] = (Bool) JS_ToBool(c, value);
2959 0 : break;
2960 10166 : case GF_SG_VRML_MFINT32:
2961 10166 : JS_ToInt32(c, &ival, value);
2962 10166 : ((MFInt32 *)ptr->field.far_ptr)->vals[ind] = ival;
2963 10166 : break;
2964 75 : case GF_SG_VRML_MFFLOAT:
2965 75 : JS_ToFloat64(c, &d, value);
2966 75 : ((MFFloat *)ptr->field.far_ptr)->vals[ind] = FLT2FIX(d);
2967 75 : break;
2968 0 : case GF_SG_VRML_MFTIME:
2969 0 : JS_ToFloat64(c, &d, value);
2970 0 : ((MFTime *)ptr->field.far_ptr)->vals[ind] = d;
2971 0 : break;
2972 808 : case GF_SG_VRML_MFSTRING:
2973 808 : if (((MFString *)ptr->field.far_ptr)->vals[ind]) {
2974 656 : gf_free(((MFString *)ptr->field.far_ptr)->vals[ind]);
2975 656 : ((MFString *)ptr->field.far_ptr)->vals[ind] = NULL;
2976 : }
2977 : str_val = (char *) JS_ToCString(c, value);
2978 808 : ((MFString *)ptr->field.far_ptr)->vals[ind] = gf_strdup(str_val);
2979 808 : JS_FreeCString(c, str_val);
2980 808 : break;
2981 :
2982 93 : case GF_SG_VRML_MFURL:
2983 93 : if (((MFURL *)ptr->field.far_ptr)->vals[ind].url) {
2984 2 : gf_free(((MFURL *)ptr->field.far_ptr)->vals[ind].url);
2985 2 : ((MFURL *)ptr->field.far_ptr)->vals[ind].url = NULL;
2986 : }
2987 : str_val = (char *) JS_ToCString(c, value);
2988 93 : ((MFURL *)ptr->field.far_ptr)->vals[ind].url = gf_strdup(str_val);
2989 93 : ((MFURL *)ptr->field.far_ptr)->vals[ind].OD_ID = 0;
2990 93 : JS_FreeCString(c, str_val);
2991 93 : break;
2992 :
2993 15149 : case GF_SG_VRML_MFVEC2F:
2994 15149 : from = (GF_JSField *) JS_GetOpaque(value, the_sf_class->class_id);
2995 15149 : gf_sg_vrml_field_copy(& ((MFVec2f *)ptr->field.far_ptr)->vals[ind], from->field.far_ptr, from->field.fieldType);
2996 15149 : break;
2997 0 : case GF_SG_VRML_MFVEC3F:
2998 0 : from = (GF_JSField *) JS_GetOpaque(value, the_sf_class->class_id);
2999 0 : gf_sg_vrml_field_copy(& ((MFVec3f *)ptr->field.far_ptr)->vals[ind], from->field.far_ptr, from->field.fieldType);
3000 0 : break;
3001 0 : case GF_SG_VRML_MFROTATION:
3002 0 : from = (GF_JSField *) JS_GetOpaque(value, the_sf_class->class_id);
3003 0 : gf_sg_vrml_field_copy(& ((MFRotation *)ptr->field.far_ptr)->vals[ind], from->field.far_ptr, from->field.fieldType);
3004 0 : break;
3005 81 : case GF_SG_VRML_MFCOLOR:
3006 81 : from = (GF_JSField *) JS_GetOpaque(value, the_sf_class->class_id);
3007 81 : gf_sg_vrml_field_copy(& ((MFColor *)ptr->field.far_ptr)->vals[ind], from->field.far_ptr, from->field.fieldType);
3008 81 : break;
3009 : }
3010 :
3011 26372 : Script_FieldChanged(c, NULL, ptr, NULL);
3012 26372 : return 1;
3013 : }
3014 :
3015 :
3016 : /* MFVec2f class constructor */
3017 206 : static JSValue MFVec2fConstructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
3018 : {
3019 206 : return genmf_Constructor(c, new_target, argc, argv, MFVec2fClass.class_id, SFVec2fClass.class_id, GF_SG_VRML_MFVEC2F);
3020 : }
3021 :
3022 : /* MFVec3f class constructor */
3023 0 : static JSValue MFVec3fConstructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
3024 : {
3025 0 : return genmf_Constructor(c, new_target, argc, argv, MFVec3fClass.class_id, SFVec3fClass.class_id, GF_SG_VRML_MFVEC3F);
3026 : }
3027 :
3028 : /* MFRotation class constructor */
3029 0 : static JSValue MFRotationConstructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
3030 : {
3031 0 : return genmf_Constructor(c, new_target, argc, argv, MFRotationClass.class_id, SFRotationClass.class_id, GF_SG_VRML_MFROTATION);
3032 : }
3033 :
3034 : /*MFColor class constructor */
3035 31 : static JSValue MFColorConstructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
3036 : {
3037 31 : return genmf_Constructor(c, new_target, argc, argv, MFColorClass.class_id, SFColorClass.class_id, GF_SG_VRML_MFCOLOR);
3038 : }
3039 :
3040 : #ifndef GPAC_DISABLE_SVG
3041 :
3042 : JSValue gf_sg_js_event_add_listener(JSContext *c, JSValueConst this_val, int argc, JSValueConst *argv, GF_Node *node);
3043 : JSValue gf_sg_js_event_remove_listener(JSContext *c, JSValueConst this_val, int argc, JSValueConst *argv, GF_Node *node);
3044 :
3045 37 : static JSValue vrml_event_add_listener(JSContext *c, JSValueConst this_val, int argc, JSValueConst *argv)
3046 : {
3047 : GF_Node *node;
3048 37 : GF_JSField *ptr = (GF_JSField *) JS_GetOpaque(this_val, SFNodeClass.class_id);
3049 37 : if (!ptr)
3050 0 : return JS_EXCEPTION;
3051 : assert(ptr->field.fieldType==GF_SG_VRML_SFNODE);
3052 37 : node = * ((GF_Node **)ptr->field.far_ptr);
3053 37 : return gf_sg_js_event_add_listener(c, this_val, argc, argv, node);
3054 : }
3055 :
3056 0 : static JSValue vrml_event_remove_listener(JSContext *c, JSValueConst this_val, int argc, JSValueConst *argv)
3057 : {
3058 : GF_Node *node;
3059 0 : GF_JSField *ptr = (GF_JSField *) JS_GetOpaque(this_val, SFNodeClass.class_id);
3060 0 : if (!ptr) return JS_EXCEPTION;
3061 : assert(ptr->field.fieldType==GF_SG_VRML_SFNODE);
3062 0 : node = * ((GF_Node **)ptr->field.far_ptr);
3063 0 : return gf_sg_js_event_remove_listener(c, this_val, argc, argv, node);
3064 : }
3065 : #endif
3066 :
3067 :
3068 : static const JSCFunctionListEntry Browser_funcs[] =
3069 : {
3070 : JS_CFUNC_DEF("getName", 0, getName),
3071 : JS_CFUNC_DEF("getVersion", 0, getVersion),
3072 : JS_CFUNC_DEF("getCurrentSpeed", 0, getCurrentSpeed),
3073 : JS_CFUNC_DEF("getCurrentFrameRate", 0, getCurrentFrameRate),
3074 : JS_CFUNC_DEF("getWorldURL", 0, getWorldURL),
3075 : JS_CFUNC_DEF("replaceWorld", 1, replaceWorld),
3076 : JS_CFUNC_DEF("addRoute", 4, addRoute),
3077 : JS_CFUNC_DEF("deleteRoute", 4, deleteRoute),
3078 : JS_CFUNC_DEF("loadURL", 1, loadURL),
3079 : JS_CFUNC_DEF("createVrmlFromString", 1, createVrmlFromString),
3080 : JS_CFUNC_DEF("setDescription", 1, setDescription),
3081 : JS_CFUNC_DEF("print", 1, js_print),
3082 : JS_CFUNC_DEF("alert", 1, js_print),
3083 : JS_CFUNC_DEF("getScript", 0, getScript),
3084 : JS_CFUNC_DEF("getProto", 0, getProto),
3085 : JS_CFUNC_DEF("loadScript", 1, loadScript),
3086 : JS_CFUNC_DEF("getElementById", 1, getElementById),
3087 : };
3088 :
3089 : static const JSCFunctionListEntry SFNode_funcs[] =
3090 : {
3091 : JS_CFUNC_DEF("toString", 0, node_toString),
3092 : };
3093 :
3094 : static JSClassExoticMethods SFNode_exotic =
3095 : {
3096 : .get_property = node_getProperty,
3097 : .set_property = node_setProperty,
3098 : };
3099 :
3100 : static const JSCFunctionListEntry SFVec2f_funcs[] =
3101 : {
3102 : JS_CGETSET_MAGIC_DEF("x", vec2f_getProperty, vec2f_setProperty, 0),
3103 : JS_CGETSET_MAGIC_DEF("y", vec2f_getProperty, vec2f_setProperty, 1),
3104 : JS_CFUNC_DEF("add", 1, vec2f_add),
3105 : JS_CFUNC_DEF("divide", 1, vec2f_divide),
3106 : JS_CFUNC_DEF("dot", 1, vec2f_dot),
3107 : JS_CFUNC_DEF("length", 0, vec2f_length),
3108 : JS_CFUNC_DEF("multiply", 1, vec2f_multiply),
3109 : JS_CFUNC_DEF("normalize", 0, vec2f_normalize),
3110 : JS_CFUNC_DEF("subtract", 1, vec2f_subtract),
3111 : JS_CFUNC_DEF("negate", 0, vec2f_negate),
3112 : JS_CFUNC_DEF("toString", 0, field_toString),
3113 : };
3114 :
3115 :
3116 : static const JSCFunctionListEntry SFVec3f_funcs[] =
3117 : {
3118 : JS_CGETSET_MAGIC_DEF("x", vec3f_getProperty, vec3f_setProperty, 0),
3119 : JS_CGETSET_MAGIC_DEF("y", vec3f_getProperty, vec3f_setProperty, 1),
3120 : JS_CGETSET_MAGIC_DEF("z", vec3f_getProperty, vec3f_setProperty, 2),
3121 :
3122 : JS_CFUNC_DEF("add", 1, vec3f_add),
3123 : JS_CFUNC_DEF("divide", 1, vec3f_divide),
3124 : JS_CFUNC_DEF("dot", 1, vec3f_dot),
3125 : JS_CFUNC_DEF("length", 0, vec3f_length),
3126 : JS_CFUNC_DEF("multiply", 1, vec3f_multiply),
3127 : JS_CFUNC_DEF("normalize", 0, vec3f_normalize),
3128 : JS_CFUNC_DEF("subtract", 1, vec3f_subtract),
3129 : JS_CFUNC_DEF("cross", 1, vec3f_cross),
3130 : JS_CFUNC_DEF("negate", 0, vec3f_negate),
3131 : JS_CFUNC_DEF("toString", 0, field_toString),
3132 : };
3133 :
3134 : static const JSCFunctionListEntry SFRotation_funcs[] =
3135 : {
3136 : JS_CGETSET_MAGIC_DEF("xAxis", rot_getProperty, rot_setProperty, 0),
3137 : JS_CGETSET_MAGIC_DEF("yAxis", rot_getProperty, rot_setProperty, 1),
3138 : JS_CGETSET_MAGIC_DEF("zAxis", rot_getProperty, rot_setProperty, 2),
3139 : JS_CGETSET_MAGIC_DEF("angle", rot_getProperty, rot_setProperty, 3),
3140 : JS_CFUNC_DEF("getAxis", 1, rot_getAxis),
3141 : JS_CFUNC_DEF("inverse", 1, rot_inverse),
3142 : JS_CFUNC_DEF("multiply", 1, rot_multiply),
3143 : JS_CFUNC_DEF("multVec", 0, rot_multVec),
3144 : JS_CFUNC_DEF("setAxis", 1, rot_setAxis),
3145 : JS_CFUNC_DEF("slerp", 0, rot_slerp),
3146 : JS_CFUNC_DEF("toString", 0, field_toString),
3147 : };
3148 :
3149 : static const JSCFunctionListEntry SFColor_funcs[] =
3150 : {
3151 : JS_CGETSET_MAGIC_DEF("r", color_getProperty, color_setProperty, 0),
3152 : JS_CGETSET_MAGIC_DEF("g", color_getProperty, color_setProperty, 1),
3153 : JS_CGETSET_MAGIC_DEF("b", color_getProperty, color_setProperty, 2),
3154 : JS_CFUNC_DEF("setHSV", 3, color_setHSV),
3155 : JS_CFUNC_DEF("getHSV", 0, color_getHSV),
3156 : JS_CFUNC_DEF("toString", 0, field_toString),
3157 : };
3158 :
3159 :
3160 : static const JSCFunctionListEntry SFImage_funcs[] =
3161 : {
3162 : JS_CGETSET_MAGIC_DEF("x", image_getProperty, image_setProperty, 0),
3163 : JS_CGETSET_MAGIC_DEF("y", image_getProperty, image_setProperty, 1),
3164 : JS_CGETSET_MAGIC_DEF("comp", image_getProperty, image_setProperty, 2),
3165 : JS_CGETSET_MAGIC_DEF("array", image_getProperty, image_setProperty, 3),
3166 : };
3167 :
3168 : static const JSCFunctionListEntry MFArray_funcs[] =
3169 : {
3170 : //ignored, kept for MSVC
3171 : JS_CGETSET_MAGIC_DEF("length", NULL, NULL, 0),
3172 : };
3173 : static JSClassExoticMethods MFArray_exotic =
3174 : {
3175 : .get_property = array_getElement,
3176 : .set_property = array_setElement,
3177 : };
3178 :
3179 2545954 : static void field_gc_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func)
3180 : {
3181 2545954 : GF_JSField *jsf = JS_GetOpaque_Nocheck(val);
3182 2545954 : if (!jsf) return;
3183 543708 : if (!JS_IsUndefined(jsf->obj) && jsf->owner) {
3184 267754 : JS_MarkValue(rt, jsf->obj, mark_func);
3185 : }
3186 271854 : if (jsf->node && jsf->node->sgprivate->interact->routes) {
3187 131982 : u32 i=0;
3188 : GF_RouteToScript *r;
3189 527972 : while ( (r = gf_list_enum(jsf->node->sgprivate->interact->routes, &i))) {
3190 264008 : if (r->script_route) {
3191 660 : JS_MarkValue(rt, r->fun, mark_func);
3192 660 : JS_MarkValue(rt, r->obj, mark_func);
3193 : }
3194 : }
3195 : }
3196 271854 : if (jsf->mfvals) {
3197 : u32 i;
3198 : assert(jsf->mfvals_count);
3199 5890 : for (i=0; i<jsf->mfvals_count; i++)
3200 5890 : JS_MarkValue(rt, jsf->mfvals[i], mark_func);
3201 : }
3202 : }
3203 :
3204 : #define SETUP_JSCLASS_BASIC(_class, _name) \
3205 : /*classes are global to runtime*/\
3206 : if (!_class.class_id) {\
3207 : JS_NewClassID(&(_class.class_id)); \
3208 : _class.class.class_name = _name; \
3209 : JS_NewClass(js_rt->js_runtime, _class.class_id, &(_class.class));\
3210 : }\
3211 :
3212 : #define SETUP_JSCLASS(_class, _name, _proto_funcs, _construct, _finalize, _exotic) \
3213 : /*classes are global to runtime*/\
3214 : if (!_class.class_id) {\
3215 : JS_NewClassID(&(_class.class_id)); \
3216 : _class.class.class_name = _name; \
3217 : _class.class.finalizer = _finalize;\
3218 : _class.class.exotic = _exotic;\
3219 : _class.class.gc_mark = field_gc_mark;\
3220 : JS_NewClass(js_rt->js_runtime, _class.class_id, &(_class.class));\
3221 : }\
3222 : { \
3223 : JSValue _proto_obj = JS_NewObjectClass(sc->js_ctx, _class.class_id);\
3224 : JS_SetPropertyFunctionList(sc->js_ctx, _proto_obj, _proto_funcs, countof(_proto_funcs));\
3225 : JS_SetClassProto(sc->js_ctx, _class.class_id, _proto_obj);\
3226 : sc->_class = JS_NewCFunction2(sc->js_ctx, _construct, _name, 1, JS_CFUNC_constructor, 0);\
3227 : JS_SetPropertyStr(sc->js_ctx, sc->js_obj, _name, sc->_class);\
3228 : }\
3229 :
3230 404 : static void vrml_js_init_api(GF_ScriptPriv *sc, GF_Node *script)
3231 : {
3232 404 : sc->js_obj = JS_GetGlobalObject(sc->js_ctx);
3233 :
3234 : //init all our classes
3235 404 : SETUP_JSCLASS_BASIC(globalClass, "global");
3236 404 : SETUP_JSCLASS_BASIC(AnyClass, "AnyClass");
3237 404 : SETUP_JSCLASS_BASIC(browserClass, "Browser");
3238 :
3239 404 : SETUP_JSCLASS(SFNodeClass, "SFNode", SFNode_funcs, SFNodeConstructor, node_finalize, &SFNode_exotic);
3240 404 : SETUP_JSCLASS(SFVec2fClass, "SFVec2f", SFVec2f_funcs, SFVec2fConstructor, field_finalize, NULL);
3241 404 : SETUP_JSCLASS(SFVec3fClass, "SFVec3f", SFVec3f_funcs, SFVec3fConstructor, field_finalize, NULL);
3242 404 : SETUP_JSCLASS(SFRotationClass, "SFRotation", SFRotation_funcs, SFRotationConstructor, field_finalize, NULL);
3243 404 : SETUP_JSCLASS(SFColorClass, "SFColor", SFColor_funcs, SFColorConstructor, field_finalize, NULL);
3244 404 : SETUP_JSCLASS(SFImageClass, "SFImage", SFImage_funcs, SFImageConstructor, field_finalize, NULL);
3245 :
3246 404 : SETUP_JSCLASS(MFInt32Class, "MFInt32", MFArray_funcs, MFInt32Constructor, array_finalize, &MFArray_exotic);
3247 404 : SETUP_JSCLASS(MFBoolClass, "MFBool", MFArray_funcs, MFBoolConstructor, array_finalize, &MFArray_exotic);
3248 404 : SETUP_JSCLASS(MFTimeClass, "MFTime", MFArray_funcs, MFTimeConstructor, array_finalize, &MFArray_exotic);
3249 404 : SETUP_JSCLASS(MFFloatClass, "MFFloat", MFArray_funcs, MFFloatConstructor, array_finalize, &MFArray_exotic);
3250 404 : SETUP_JSCLASS(MFUrlClass, "MFUrl", MFArray_funcs, MFURLConstructor, array_finalize, &MFArray_exotic);
3251 404 : SETUP_JSCLASS(MFStringClass, "MFString", MFArray_funcs, MFStringConstructor, array_finalize, &MFArray_exotic);
3252 404 : SETUP_JSCLASS(MFNodeClass, "MFNode", MFArray_funcs, MFNodeConstructor, array_finalize, &MFArray_exotic);
3253 404 : SETUP_JSCLASS(MFVec2fClass, "MFVec2f", MFArray_funcs, MFVec2fConstructor, array_finalize, &MFArray_exotic);
3254 404 : SETUP_JSCLASS(MFVec3fClass , "MFVec3f", MFArray_funcs, MFVec3fConstructor, array_finalize, &MFArray_exotic);
3255 404 : SETUP_JSCLASS(MFRotationClass, "MFRotation", MFArray_funcs, MFRotationConstructor, array_finalize, &MFArray_exotic);
3256 404 : SETUP_JSCLASS(MFColorClass, "MFColor", MFArray_funcs, MFColorConstructor, array_finalize, &MFArray_exotic);
3257 :
3258 :
3259 808 : JS_SetPropertyStr(sc->js_ctx, sc->js_obj, "print", JS_NewCFunction(sc->js_ctx, js_print, "print", 1));
3260 808 : JS_SetPropertyStr(sc->js_ctx, sc->js_obj, "alert", JS_NewCFunction(sc->js_ctx, js_print, "print", 1));
3261 808 : JS_SetPropertyStr(sc->js_ctx, sc->js_obj, "parseXML", JS_NewCFunction(sc->js_ctx, vrml_parse_xml, "parseXML", 1));
3262 :
3263 : /*remember pointer to scene graph!!*/
3264 404 : JS_SetOpaque(sc->js_obj, script->sgprivate->scenegraph);
3265 :
3266 404 : JS_SetPropertyStr(sc->js_ctx, sc->js_obj, "FALSE", JS_FALSE);
3267 404 : JS_SetPropertyStr(sc->js_ctx, sc->js_obj, "TRUE", JS_TRUE);
3268 404 : JS_SetPropertyStr(sc->js_ctx, sc->js_obj, "NULL", JS_NULL);
3269 404 : JS_SetPropertyStr(sc->js_ctx, sc->js_obj, "_this", JS_NULL);
3270 : // JS_DefineProperty(sc->js_ctx, sc->js_obj, "_this", PRIVATE_TO_JSVAL(script), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT );
3271 :
3272 404 : JSValue browser = JS_NewObjectClass(sc->js_ctx, browserClass.class_id);
3273 404 : JS_SetPropertyFunctionList(sc->js_ctx, browser, Browser_funcs, countof(Browser_funcs));
3274 404 : JS_SetPropertyStr(sc->js_ctx, sc->js_obj, "Browser", browser);
3275 :
3276 : /*TODO ? SFVec3f / MFVec4f support */
3277 :
3278 808 : sc->node_toString_fun = JS_NewCFunction(sc->js_ctx, node_toString, "toString", 0);
3279 808 : sc->node_getTime_fun = JS_NewCFunction(sc->js_ctx, node_getTime, "getTime", 0);
3280 : #ifndef GPAC_DISABLE_SVG
3281 808 : sc->node_addEventListener_fun = JS_NewCFunction(sc->js_ctx, vrml_event_add_listener, "addEventListener", 0);
3282 808 : sc->node_removeEventListener_fun = JS_NewCFunction(sc->js_ctx, vrml_event_remove_listener, "removeEventListener", 0);
3283 : #endif
3284 404 : }
3285 :
3286 :
3287 :
3288 20201 : void gf_sg_script_to_node_field(JSContext *c, JSValue val, GF_FieldInfo *field, GF_Node *owner, GF_JSField *parent)
3289 : {
3290 : Double d;
3291 : Bool changed;
3292 : const char *str_val;
3293 : GF_JSField *p, *from;
3294 : u32 len;
3295 : s32 ival;
3296 : JSValue item;
3297 : u32 i;
3298 :
3299 40402 : if (JS_IsUndefined(val)) return;
3300 20201 : if ((field->fieldType != GF_SG_VRML_SFNODE) && JS_IsNull(val)) return;
3301 :
3302 :
3303 20201 : switch (field->fieldType) {
3304 3668 : case GF_SG_VRML_SFBOOL:
3305 3668 : *((SFBool *) field->far_ptr) = JS_ToBool(c,val);
3306 3668 : Script_FieldChanged(c, owner, parent, field);
3307 3668 : return;
3308 :
3309 4324 : case GF_SG_VRML_SFINT32:
3310 4324 : if (!JS_ToInt32(c, ((SFInt32 *) field->far_ptr), val)) {
3311 4324 : Script_FieldChanged(c, owner, parent, field);
3312 4324 : return;
3313 :
3314 10335 : case GF_SG_VRML_SFFLOAT:
3315 10335 : if (!JS_ToFloat64(c, &d, val)) {
3316 10335 : *((SFFloat *) field->far_ptr) = FLT2FIX( d);
3317 10335 : Script_FieldChanged(c, owner, parent, field);
3318 : }
3319 : return;
3320 :
3321 127 : case GF_SG_VRML_SFTIME:
3322 127 : if (!JS_ToFloat64(c, &d, val)) {
3323 127 : *((SFTime *) field->far_ptr) = (Double) d;
3324 127 : Script_FieldChanged(c, owner, parent, field);
3325 : }
3326 : return;
3327 : }
3328 : case GF_SG_VRML_SFSTRING:
3329 : {
3330 3 : SFString *s = (SFString*)field->far_ptr;
3331 : const char *sval = JS_ToCString(c, val);
3332 :
3333 : /*we do filter strings since rebuilding a text is quite slow, so let's avoid killing the compositors*/
3334 3 : if (!s->buffer || strcmp(sval, s->buffer)) {
3335 3 : if ( s->buffer) gf_free(s->buffer);
3336 3 : s->buffer = gf_strdup(sval);
3337 3 : Script_FieldChanged(c, owner, parent, field);
3338 : }
3339 3 : JS_FreeCString(c, sval);
3340 3 : return;
3341 : }
3342 0 : case GF_SG_VRML_SFURL:
3343 : str_val = JS_ToCString(c, val);
3344 0 : if (((SFURL*)field->far_ptr)->url) gf_free(((SFURL*)field->far_ptr)->url);
3345 0 : ((SFURL*)field->far_ptr)->url = gf_strdup(str_val);
3346 0 : ((SFURL*)field->far_ptr)->OD_ID = 0;
3347 0 : Script_FieldChanged(c, owner, parent, field);
3348 0 : JS_FreeCString(c, str_val);
3349 0 : return;
3350 :
3351 0 : case GF_SG_VRML_MFSTRING:
3352 : {
3353 0 : GF_JSField *src = JS_GetOpaque(val, MFStringClass.class_id);
3354 0 : gf_sg_vrml_mf_reset(field->far_ptr, field->fieldType);
3355 0 : if (src && src->mfvals_count) {
3356 0 : gf_sg_vrml_mf_alloc(field->far_ptr, field->fieldType, src->mfvals_count);
3357 0 : for (i=0; i<src->mfvals_count; i++) {
3358 0 : str_val = JS_ToCString(c, src->mfvals[i]);
3359 0 : ((MFString*)field->far_ptr)->vals[i] = gf_strdup(str_val);
3360 0 : JS_FreeCString(c, str_val);
3361 : }
3362 : } else {
3363 0 : gf_sg_vrml_mf_alloc(field->far_ptr, field->fieldType, 1);
3364 : str_val = JS_ToCString(c, val);
3365 0 : ((MFString*)field->far_ptr)->vals[0] = gf_strdup(str_val);
3366 0 : JS_FreeCString(c, str_val);
3367 : }
3368 0 : Script_FieldChanged(c, owner, parent, field);
3369 : }
3370 0 : return;
3371 :
3372 0 : case GF_SG_VRML_MFURL:
3373 0 : gf_sg_vrml_mf_reset(field->far_ptr, field->fieldType);
3374 0 : gf_sg_vrml_mf_alloc(field->far_ptr, field->fieldType, 1);
3375 : str_val = JS_ToCString(c, val);
3376 0 : ((MFURL*)field->far_ptr)->vals[0].url = gf_strdup(str_val);
3377 0 : ((MFURL*)field->far_ptr)->vals[0].OD_ID = 0;
3378 0 : Script_FieldChanged(c, owner, parent, field);
3379 0 : JS_FreeCString(c, str_val);
3380 0 : return;
3381 :
3382 : default:
3383 : break;
3384 : }
3385 :
3386 : //from here we must have an object
3387 1744 : if (! JS_IsObject(val)) return;
3388 :
3389 1744 : switch (field->fieldType) {
3390 426 : case GF_SG_VRML_SFVEC2F:
3391 426 : p = (GF_JSField *) JS_GetOpaque(val, SFVec2fClass.class_id);
3392 426 : if (p) {
3393 426 : gf_sg_vrml_field_copy(field->far_ptr, p->field.far_ptr, GF_SG_VRML_SFVEC2F);
3394 426 : Script_FieldChanged(c, owner, parent, field);
3395 : }
3396 : return;
3397 :
3398 0 : case GF_SG_VRML_SFVEC3F:
3399 0 : p = (GF_JSField *) JS_GetOpaque(val, SFVec3fClass.class_id);
3400 0 : if (p) {
3401 0 : gf_sg_vrml_field_copy(field->far_ptr, p->field.far_ptr, GF_SG_VRML_SFVEC3F);
3402 0 : Script_FieldChanged(c, owner, parent, field);
3403 : }
3404 : return;
3405 :
3406 0 : case GF_SG_VRML_SFROTATION:
3407 0 : p = (GF_JSField *) JS_GetOpaque(val, SFRotationClass.class_id);
3408 0 : if (p) {
3409 0 : gf_sg_vrml_field_copy(field->far_ptr, p->field.far_ptr, GF_SG_VRML_SFROTATION);
3410 0 : Script_FieldChanged(c, owner, parent, field);
3411 : }
3412 : return;
3413 :
3414 537 : case GF_SG_VRML_SFCOLOR:
3415 537 : p = (GF_JSField *) JS_GetOpaque(val, SFColorClass.class_id);
3416 537 : if (p) {
3417 537 : gf_sg_vrml_field_copy(field->far_ptr, p->field.far_ptr, GF_SG_VRML_SFCOLOR);
3418 537 : Script_FieldChanged(c, owner, parent, field);
3419 : }
3420 : return;
3421 0 : case GF_SG_VRML_SFIMAGE:
3422 0 : p = (GF_JSField *) JS_GetOpaque(val, SFImageClass.class_id);
3423 0 : if (p) {
3424 0 : gf_sg_vrml_field_copy(field->far_ptr, p->field.far_ptr, GF_SG_VRML_SFIMAGE);
3425 0 : Script_FieldChanged(c, owner, parent, field);
3426 : }
3427 : return;
3428 :
3429 781 : case GF_SG_VRML_SFNODE:
3430 : /*replace object*/
3431 781 : if (*((GF_Node**)field->far_ptr)) {
3432 15 : gf_node_unregister(*((GF_Node**)field->far_ptr), owner);
3433 15 : *((GF_Node**)field->far_ptr) = NULL;
3434 : }
3435 :
3436 781 : p = (GF_JSField *) JS_GetOpaque(val, SFNodeClass.class_id);
3437 : if (JS_IsNull(val)) {
3438 : Script_FieldChanged(c, owner, parent, field);
3439 781 : } else if (p) {
3440 781 : GF_Node *n = * (GF_Node**) (p)->field.far_ptr;
3441 781 : * ((GF_Node **)field->far_ptr) = n;
3442 781 : gf_node_register(n, owner);
3443 781 : Script_FieldChanged(c, owner, parent, field);
3444 : }
3445 : return;
3446 : default:
3447 : break;
3448 : }
3449 :
3450 0 : p = (GF_JSField *) JS_GetOpaque_Nocheck(val);
3451 0 : if (!p) return;
3452 :
3453 0 : len = p->mfvals_count;
3454 : /*special handling for MF node, reset list first*/
3455 0 : if (field->fieldType == GF_SG_VRML_MFNODE) {
3456 : GF_Node *child;
3457 0 : GF_ChildNodeItem *last = NULL;
3458 0 : gf_node_unregister_children(owner, * (GF_ChildNodeItem **) field->far_ptr);
3459 0 : * (GF_ChildNodeItem **) field->far_ptr = NULL;
3460 :
3461 0 : for (i=0; i<len; i++) {
3462 0 : item = p->mfvals[i];
3463 0 : from = (GF_JSField *) JS_GetOpaque(item, SFNodeClass.class_id);
3464 0 : if (!from) continue;
3465 :
3466 0 : child = * ((GF_Node**)from->field.far_ptr);
3467 :
3468 0 : gf_node_list_add_child_last( (GF_ChildNodeItem **) field->far_ptr , child, &last);
3469 0 : gf_node_register(child, owner);
3470 : }
3471 0 : Script_FieldChanged(c, owner, parent, field);
3472 : /*and mark the field as changed*/
3473 0 : JSScript_NodeModified(owner->sgprivate->scenegraph, owner, field, NULL);
3474 : return;
3475 : }
3476 :
3477 : /*again, check text changes*/
3478 0 : changed = (field->fieldType == GF_SG_VRML_MFSTRING) ? 0 : 1;
3479 : /*gf_realloc*/
3480 0 : if (len != ((GenMFField *)field->far_ptr)->count) {
3481 0 : gf_sg_vrml_mf_reset(field->far_ptr, field->fieldType);
3482 0 : gf_sg_vrml_mf_alloc(field->far_ptr, field->fieldType, len);
3483 : changed = 1;
3484 : }
3485 : /*assign each slot*/
3486 0 : for (i=0; i<len; i++) {
3487 0 : item = p->mfvals[i];
3488 0 : switch (field->fieldType) {
3489 0 : case GF_SG_VRML_MFBOOL:
3490 0 : ((MFBool*)field->far_ptr)->vals[i] = (Bool) JS_ToBool(c,item);
3491 0 : break;
3492 0 : case GF_SG_VRML_MFINT32:
3493 0 : if (JS_ToInt32(c, &ival, item)) {
3494 0 : ((MFInt32 *)field->far_ptr)->vals[i] = ival;
3495 : }
3496 : break;
3497 0 : case GF_SG_VRML_MFFLOAT:
3498 0 : if (JS_ToFloat64(c, &d, item)) {
3499 0 : ((MFFloat *)field->far_ptr)->vals[i] = FLT2FIX( d);
3500 : }
3501 : break;
3502 0 : case GF_SG_VRML_MFTIME:
3503 0 : if (JS_ToFloat64(c, &d, item)) {
3504 0 : ((MFTime *)field->far_ptr)->vals[i] = d;
3505 : }
3506 : break;
3507 0 : case GF_SG_VRML_MFSTRING:
3508 : {
3509 0 : MFString *mfs = (MFString *) field->far_ptr;
3510 : str_val = JS_ToCString(c, item);
3511 0 : if (!mfs->vals[i] || strcmp(str_val, mfs->vals[i]) ) {
3512 0 : if (mfs->vals[i]) gf_free(mfs->vals[i]);
3513 0 : mfs->vals[i] = gf_strdup(str_val);
3514 : changed = 1;
3515 : }
3516 0 : JS_FreeCString(c, str_val);
3517 : }
3518 0 : break;
3519 0 : case GF_SG_VRML_MFURL:
3520 : {
3521 0 : MFURL *mfu = (MFURL *) field->far_ptr;
3522 0 : if (mfu->vals[i].url) gf_free(mfu->vals[i].url);
3523 : str_val = JS_ToCString(c, item);
3524 0 : mfu->vals[i].url = gf_strdup(str_val);
3525 0 : mfu->vals[i].OD_ID = 0;
3526 0 : JS_FreeCString(c, str_val);
3527 : }
3528 0 : break;
3529 :
3530 0 : case GF_SG_VRML_MFVEC2F:
3531 0 : from = (GF_JSField *) JS_GetOpaque(item, SFVec2fClass.class_id);
3532 0 : if (from) {
3533 0 : gf_sg_vrml_field_copy(& ((MFVec2f*)field->far_ptr)->vals[i], from->field.far_ptr, GF_SG_VRML_SFVEC2F);
3534 : }
3535 : break;
3536 0 : case GF_SG_VRML_MFVEC3F:
3537 0 : from = (GF_JSField *) JS_GetOpaque(item, SFVec3fClass.class_id);
3538 0 : if (from) {
3539 0 : gf_sg_vrml_field_copy(& ((MFVec3f*)field->far_ptr)->vals[i], from->field.far_ptr, GF_SG_VRML_SFVEC3F);
3540 : }
3541 : break;
3542 0 : case GF_SG_VRML_MFROTATION:
3543 0 : from = (GF_JSField *) JS_GetOpaque(item, SFRotationClass.class_id);
3544 0 : if (from) {
3545 0 : gf_sg_vrml_field_copy(& ((MFRotation*)field->far_ptr)->vals[i], from->field.far_ptr, GF_SG_VRML_SFROTATION);
3546 : }
3547 : break;
3548 0 : case GF_SG_VRML_MFCOLOR:
3549 0 : from = (GF_JSField *) JS_GetOpaque(item, SFColorClass.class_id);
3550 0 : if (from) {
3551 0 : gf_sg_vrml_field_copy(& ((MFColor*)field->far_ptr)->vals[i], from->field.far_ptr, GF_SG_VRML_SFCOLOR);
3552 : }
3553 : break;
3554 :
3555 : default:
3556 : break;
3557 : }
3558 : }
3559 0 : if (changed) Script_FieldChanged(c, owner, parent, field);
3560 : }
3561 :
3562 :
3563 1183 : static void gf_sg_script_update_cached_object(GF_ScriptPriv *priv, JSValue obj, GF_JSField *jsf, GF_FieldInfo *field, GF_Node *parent)
3564 : {
3565 : u32 i;
3566 :
3567 1183 : if ((jsf->field.fieldType != GF_SG_VRML_MFNODE) && ! gf_sg_vrml_is_sf_field(jsf->field.fieldType)) {
3568 0 : for (i=0; i<jsf->mfvals_count; i++) {
3569 0 : JS_FreeValue(priv->js_ctx, jsf->mfvals[i]);
3570 0 : jsf->mfvals[i] = JS_NULL;
3571 : }
3572 : }
3573 :
3574 : /*we need to rebuild MF types where SF is a native type.*/
3575 1183 : GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[VRML JS] Recomputing cached jsobj\n") );
3576 1183 : switch (jsf->field.fieldType) {
3577 0 : case GF_SG_VRML_MFBOOL:
3578 : {
3579 0 : MFBool *f = (MFBool *) field->far_ptr;
3580 0 : for (i=0; i<f->count; i++) {
3581 0 : jsf->mfvals[i] = JS_NewBool(priv->js_ctx, f->vals[i]);
3582 : }
3583 : }
3584 : break;
3585 0 : case GF_SG_VRML_MFINT32:
3586 : {
3587 0 : MFInt32 *f = (MFInt32 *) field->far_ptr;
3588 0 : for (i=0; i<f->count; i++) {
3589 0 : jsf->mfvals[i] = JS_NewInt32(priv->js_ctx, f->vals[i]);
3590 : }
3591 : }
3592 : break;
3593 0 : case GF_SG_VRML_MFFLOAT:
3594 : {
3595 0 : MFFloat *f = (MFFloat *) field->far_ptr;
3596 0 : for (i=0; i<f->count; i++) {
3597 0 : jsf->mfvals[i] = JS_NewFloat64(priv->js_ctx, FIX2FLT(f->vals[i]));
3598 : }
3599 : }
3600 : break;
3601 0 : case GF_SG_VRML_MFTIME:
3602 : {
3603 0 : MFTime *f = (MFTime *) field->far_ptr;
3604 0 : for (i=0; i<f->count; i++) {
3605 0 : jsf->mfvals[i] = JS_NewFloat64(priv->js_ctx, f->vals[i]);
3606 : }
3607 : }
3608 : break;
3609 0 : case GF_SG_VRML_MFSTRING:
3610 : {
3611 0 : MFString *f = (MFString *) field->far_ptr;
3612 0 : for (i=0; i<f->count; i++) {
3613 0 : jsf->mfvals[i] = JS_NewString(priv->js_ctx, f->vals[i]);
3614 : }
3615 : }
3616 : break;
3617 0 : case GF_SG_VRML_MFURL:
3618 : {
3619 0 : MFURL *f = (MFURL *) field->far_ptr;
3620 0 : for (i=0; i<f->count; i++) {
3621 0 : if (f->vals[i].OD_ID > 0) {
3622 : char msg[30];
3623 : sprintf(msg, "od:%d", f->vals[i].OD_ID);
3624 0 : jsf->mfvals[i] = JS_NewString(priv->js_ctx, (const char *) msg);
3625 : } else {
3626 0 : jsf->mfvals[i] = JS_NewString(priv->js_ctx, f->vals[i].url);
3627 : }
3628 : }
3629 : }
3630 : break;
3631 : /*
3632 : MFNode is tricky because in VRML/MPEG-4, SFNode are assigned by referenced, not copy.
3633 : We therefore need to make sure we reuse existing SFNode object rather than
3634 : blindly recreating them
3635 : */
3636 : case GF_SG_VRML_MFNODE:
3637 : {
3638 : #if 0
3639 : GF_ChildNodeItem *f = *(GF_ChildNodeItem **) field->far_ptr;
3640 : u32 j, count;
3641 : JSValue val;
3642 : /*1: find all existing objs for each node*/
3643 : val = JS_GetPropertyStr(priv->js_ctx, jsf->js_list, "length");
3644 : JS_ToInt32(priv->js_ctx, &count, val);
3645 : JS_FreeValue(priv->js_ctx, val);
3646 :
3647 : /*this may introduce bugs when a child is being replaced through an update command, but it is way
3648 : too costly to handle in script*/
3649 : if (gf_node_list_get_count(f)==count) return;
3650 :
3651 : JS_SetPropertyStr(priv->js_ctx, jsf->js_list, "length", JS_NewInt32(priv->js_ctx, 0));
3652 : count = 0;
3653 : while (f) {
3654 : GF_JSField *slot = NULL;
3655 : /*first look in the original array*/
3656 : for (j=0; j<count; j++) {
3657 : val = JS_GetPropertyUint32(priv->js_ctx, jsf->js_list, j);
3658 : slot = JS_GetOpaque(val, SFNodeClass.class_id);
3659 : if (slot && (slot->node==f->node)) {
3660 : JS_SetPropertyUint32(priv->js_ctx, jsf->js_list, count, JS_DupValue(priv->js_ctx, val));
3661 : count++;
3662 : break;
3663 : }
3664 : JS_FreeValue(priv->js_ctx, val);
3665 : slot = NULL;
3666 : }
3667 : if (!slot) {
3668 : JS_SetPropertyUint32(priv->js_ctx, jsf->js_list, count, node_get_binding(priv, f->node, 0) );
3669 : count++;
3670 : }
3671 : f = f->next;
3672 : }
3673 : JS_SetPropertyStr(priv->js_ctx, jsf->js_list, "length", JS_NewInt32(priv->js_ctx, count));
3674 : #endif
3675 : }
3676 : break;
3677 : }
3678 1183 : jsf->field.NDTtype = 0;
3679 1183 : }
3680 :
3681 :
3682 :
3683 : #define SETUP_FIELD \
3684 : jsf = NewJSField(priv->js_ctx); \
3685 : jsf->owner = parent; \
3686 : if(parent) gf_node_get_field(parent, field->fieldIndex, &jsf->field); \
3687 :
3688 : #define SETUP_MF_FIELD(_class) \
3689 : obj = JS_CallConstructor(priv->js_ctx, priv->_class, 0, NULL);\
3690 : if (JS_IsException(obj) ) return obj; \
3691 : jsf = (GF_JSField *) JS_GetOpaque(obj, _class.class_id); \
3692 : jsf->owner = parent; \
3693 : if (parent) gf_node_get_field(parent, field->fieldIndex, &jsf->field); \
3694 :
3695 :
3696 : static GF_JSClass *get_sf_class(u32 mftype)
3697 : {
3698 753 : switch (mftype) {
3699 : case GF_SG_VRML_MFNODE: return &SFNodeClass;
3700 206 : case GF_SG_VRML_MFVEC2F: return &SFVec2fClass;
3701 0 : case GF_SG_VRML_MFVEC3F: return &SFVec3fClass;
3702 0 : case GF_SG_VRML_MFROTATION: return &SFRotationClass;
3703 31 : case GF_SG_VRML_MFCOLOR: return &SFColorClass;
3704 0 : case GF_SG_VRML_MFIMAGE: return &SFImageClass;
3705 : }
3706 : return NULL;
3707 : }
3708 :
3709 49493 : JSValue gf_sg_script_to_qjs_field(GF_ScriptPriv *priv, GF_FieldInfo *field, GF_Node *parent, Bool force_evaluate)
3710 : {
3711 : u32 i;
3712 : GF_JSField *jsf = NULL;
3713 : GF_JSField *slot = NULL;
3714 : JSValue obj;
3715 : Bool was_found = GF_FALSE;
3716 : /*native types*/
3717 49493 : switch (field->fieldType) {
3718 700 : case GF_SG_VRML_SFBOOL:
3719 700 : return JS_NewBool(priv->js_ctx, * ((SFBool *) field->far_ptr) );
3720 3758 : case GF_SG_VRML_SFINT32:
3721 3758 : return JS_NewInt32(priv->js_ctx, * ((SFInt32 *) field->far_ptr));
3722 2560 : case GF_SG_VRML_SFFLOAT:
3723 2560 : return JS_NewFloat64(priv->js_ctx, FIX2FLT(* ((SFFloat *) field->far_ptr) ));
3724 2768 : case GF_SG_VRML_SFTIME:
3725 2768 : return JS_NewFloat64(priv->js_ctx, * ((SFTime *) field->far_ptr));
3726 3 : case GF_SG_VRML_SFSTRING:
3727 3 : return JS_NewString(priv->js_ctx, ((SFString *) field->far_ptr)->buffer);
3728 0 : case GF_SG_VRML_SFURL:
3729 : {
3730 0 : SFURL *url = (SFURL *)field->far_ptr;
3731 0 : if (url->OD_ID > 0) {
3732 : char msg[30];
3733 : sprintf(msg, "od:%d", url->OD_ID);
3734 0 : return JS_NewString(priv->js_ctx, (const char *) msg);
3735 : } else {
3736 0 : return JS_NewString(priv->js_ctx, (const char *) url->url);
3737 : }
3738 : }
3739 : }
3740 :
3741 39704 : obj = JS_NULL;
3742 :
3743 : /*look into object bank in case we already have this object*/
3744 39704 : if (parent && parent->sgprivate->interact && parent->sgprivate->interact->js_binding) {
3745 39304 : i=0;
3746 103285 : while ((jsf = gf_list_enum(parent->sgprivate->interact->js_binding->fields, &i))) {
3747 : was_found = GF_TRUE;
3748 48109 : obj = jsf->obj;
3749 :
3750 48109 : if (
3751 : /*make sure we use the same JS context*/
3752 48109 : (jsf->js_ctx == priv->js_ctx)
3753 48109 : && (jsf->owner == parent)
3754 48109 : && (jsf->field.far_ptr==field->far_ptr)
3755 : ) {
3756 : Bool do_rebuild = GF_FALSE;
3757 23432 : GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[VRML JS] found cached jsobj (field %s) in script %s bank (%d entries)\n", field->name, gf_node_get_log_name((GF_Node*)JS_GetScript(priv->js_ctx)), gf_list_count(priv->jsf_cache) ) );
3758 23432 : if (!force_evaluate && !jsf->field.NDTtype)
3759 : return JS_DupValue(priv->js_ctx, jsf->obj);
3760 :
3761 1183 : switch (field->fieldType) {
3762 : //we need to rewrite these
3763 0 : case GF_SG_VRML_MFVEC2F:
3764 : case GF_SG_VRML_MFVEC3F:
3765 : case GF_SG_VRML_MFROTATION:
3766 : case GF_SG_VRML_MFCOLOR:
3767 0 : if (force_evaluate) {
3768 0 : while (jsf->mfvals_count) {
3769 0 : JS_FreeValue(priv->js_ctx, jsf->mfvals[i]);
3770 0 : jsf->mfvals_count--;
3771 : }
3772 0 : gf_free(jsf->mfvals);
3773 0 : jsf->mfvals = NULL;
3774 : do_rebuild = GF_TRUE;
3775 : break;
3776 : }
3777 : default:
3778 : break;
3779 : }
3780 : if (do_rebuild) {
3781 : break;
3782 : }
3783 :
3784 1183 : gf_sg_script_update_cached_object(priv, jsf->obj, jsf, field, parent);
3785 : return JS_DupValue(priv->js_ctx, jsf->obj);
3786 : }
3787 : was_found = GF_FALSE;
3788 24677 : obj = JS_NULL;
3789 : }
3790 : }
3791 :
3792 15872 : if (!was_found) {
3793 16272 : GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[VRML JS] creating jsobj %s.%s\n", gf_node_get_name(parent), field->name) );
3794 : }
3795 :
3796 16272 : switch (field->fieldType) {
3797 665 : case GF_SG_VRML_SFVEC2F:
3798 665 : SETUP_FIELD
3799 665 : obj = JS_NewObjectClass(priv->js_ctx, SFVec2fClass.class_id);
3800 665 : break;
3801 5 : case GF_SG_VRML_SFVEC3F:
3802 5 : SETUP_FIELD
3803 5 : obj = JS_NewObjectClass(priv->js_ctx, SFVec3fClass.class_id);
3804 5 : break;
3805 0 : case GF_SG_VRML_SFROTATION:
3806 0 : SETUP_FIELD
3807 0 : obj = JS_NewObjectClass(priv->js_ctx, SFRotationClass.class_id);
3808 0 : break;
3809 97 : case GF_SG_VRML_SFCOLOR:
3810 97 : SETUP_FIELD
3811 97 : obj = JS_NewObjectClass(priv->js_ctx, SFColorClass.class_id);
3812 97 : break;
3813 0 : case GF_SG_VRML_SFIMAGE:
3814 0 : SETUP_FIELD
3815 0 : obj = JS_NewObjectClass(priv->js_ctx, SFImageClass.class_id);
3816 0 : break;
3817 13692 : case GF_SG_VRML_SFNODE:
3818 13692 : if (! *(GF_Node**) field->far_ptr)
3819 0 : return JS_NULL;
3820 :
3821 13692 : obj = node_get_binding(priv, *(GF_Node**) field->far_ptr);
3822 13692 : jsf = JS_GetOpaque(obj, SFNodeClass.class_id);
3823 13692 : if (!jsf->owner)
3824 1651 : jsf->owner = parent;
3825 : else
3826 : return JS_DupValue(priv->js_ctx, obj);
3827 : //return obj;
3828 1651 : break;
3829 :
3830 :
3831 0 : case GF_SG_VRML_MFBOOL:
3832 : {
3833 0 : MFBool *f = (MFBool *) field->far_ptr;
3834 0 : SETUP_MF_FIELD(MFBoolClass)
3835 0 : jsf->mfvals_count = f->count;
3836 0 : jsf->mfvals = gf_realloc(jsf->mfvals, sizeof(JSValue)*f->count);
3837 0 : for (i = 0; i<f->count; i++) {
3838 0 : jsf->mfvals[i] = JS_NewBool(priv->js_ctx, f->vals[i]);
3839 : }
3840 : break;
3841 : }
3842 209 : case GF_SG_VRML_MFINT32:
3843 : {
3844 209 : MFInt32 *f = (MFInt32 *) field->far_ptr;
3845 418 : SETUP_MF_FIELD(MFInt32Class)
3846 209 : jsf->mfvals_count = f->count;
3847 209 : jsf->mfvals = gf_realloc(jsf->mfvals, sizeof(JSValue)*f->count);
3848 991 : for (i=0; i<f->count; i++) {
3849 1564 : jsf->mfvals[i] = JS_NewInt32(priv->js_ctx, f->vals[i]);
3850 : }
3851 : break;
3852 : }
3853 30 : case GF_SG_VRML_MFFLOAT:
3854 : {
3855 30 : MFFloat *f = (MFFloat *) field->far_ptr;
3856 60 : SETUP_MF_FIELD(MFFloatClass)
3857 30 : jsf->mfvals_count = f->count;
3858 30 : jsf->mfvals = gf_realloc(jsf->mfvals, sizeof(JSValue)*f->count);
3859 30 : for (i=0; i<f->count; i++) {
3860 0 : jsf->mfvals[i] = JS_NewFloat64(priv->js_ctx, FIX2FLT(f->vals[i]) );
3861 : }
3862 : break;
3863 : }
3864 0 : case GF_SG_VRML_MFTIME:
3865 : {
3866 0 : MFTime *f = (MFTime *) field->far_ptr;
3867 0 : SETUP_MF_FIELD(MFTimeClass)
3868 0 : jsf->mfvals_count = f->count;
3869 0 : jsf->mfvals = gf_realloc(jsf->mfvals, sizeof(JSValue)*f->count);
3870 0 : for (i=0; i<f->count; i++) {
3871 0 : jsf->mfvals[i] = JS_NewFloat64(priv->js_ctx, f->vals[i] );
3872 : }
3873 : break;
3874 : }
3875 185 : case GF_SG_VRML_MFSTRING:
3876 : {
3877 185 : MFString *f = (MFString *) field->far_ptr;
3878 370 : SETUP_MF_FIELD(MFStringClass)
3879 185 : jsf->mfvals_count = f->count;
3880 185 : jsf->mfvals = gf_realloc(jsf->mfvals, sizeof(JSValue)*f->count);
3881 364 : for (i=0; i<f->count; i++) {
3882 179 : char *str = f->vals[i];
3883 179 : if (!str) str= "";
3884 179 : jsf->mfvals[i] = JS_NewString(priv->js_ctx, str );
3885 : }
3886 : break;
3887 : }
3888 91 : case GF_SG_VRML_MFURL:
3889 : {
3890 91 : MFURL *f = (MFURL *) field->far_ptr;
3891 182 : SETUP_MF_FIELD(MFUrlClass)
3892 91 : jsf->mfvals_count = f->count;
3893 91 : jsf->mfvals = gf_realloc(jsf->mfvals, sizeof(JSValue)*f->count);
3894 91 : for (i=0; i<f->count; i++) {
3895 0 : if (f->vals[i].OD_ID > 0) {
3896 : char msg[30];
3897 : sprintf(msg, "od:%d", f->vals[i].OD_ID);
3898 0 : jsf->mfvals[i] = JS_NewString(priv->js_ctx, (const char *) msg);
3899 : } else {
3900 0 : jsf->mfvals[i] = JS_NewString(priv->js_ctx, f->vals[i].url);
3901 : }
3902 : }
3903 : break;
3904 : }
3905 :
3906 206 : case GF_SG_VRML_MFVEC2F:
3907 : {
3908 206 : MFVec2f *f = (MFVec2f *) field->far_ptr;
3909 206 : if (!was_found) {
3910 412 : SETUP_MF_FIELD(MFVec2fClass)
3911 : }
3912 206 : jsf->mfvals_count = f->count;
3913 206 : jsf->mfvals = gf_realloc(jsf->mfvals, sizeof(JSValue)*f->count);
3914 1257 : for (i=0; i<f->count; i++) {
3915 1051 : JSValue pf = JS_NewObjectClass(priv->js_ctx, SFVec2fClass.class_id);
3916 1051 : slot = SFVec2f_Create(priv->js_ctx, pf, f->vals[i].x, f->vals[i].y);
3917 1051 : slot->owner = parent;
3918 1051 : jsf->mfvals[i] = pf;
3919 : }
3920 : break;
3921 : }
3922 0 : case GF_SG_VRML_MFVEC3F:
3923 : {
3924 0 : MFVec3f *f = (MFVec3f *) field->far_ptr;
3925 0 : if (!was_found) {
3926 0 : SETUP_MF_FIELD(MFVec3fClass)
3927 : }
3928 0 : jsf->mfvals_count = f->count;
3929 0 : jsf->mfvals = gf_realloc(jsf->mfvals, sizeof(JSValue)*f->count);
3930 0 : for (i=0; i<f->count; i++) {
3931 0 : JSValue pf = JS_NewObjectClass(priv->js_ctx, SFVec3fClass.class_id);
3932 0 : slot = SFVec3f_Create(priv->js_ctx, pf, f->vals[i].x, f->vals[i].y, f->vals[i].z);
3933 0 : slot->owner = parent;
3934 0 : jsf->mfvals[i] = pf;
3935 : }
3936 : break;
3937 : }
3938 0 : case GF_SG_VRML_MFROTATION:
3939 : {
3940 0 : MFRotation *f = (MFRotation*) field->far_ptr;
3941 0 : if (!was_found) {
3942 0 : SETUP_MF_FIELD(MFRotationClass)
3943 : }
3944 0 : jsf->mfvals_count = f->count;
3945 0 : jsf->mfvals = gf_realloc(jsf->mfvals, sizeof(JSValue)*f->count);
3946 0 : for (i=0; i<f->count; i++) {
3947 0 : JSValue pf = JS_NewObjectClass(priv->js_ctx, SFRotationClass.class_id);
3948 0 : slot = SFRotation_Create(priv->js_ctx, pf, f->vals[i].x, f->vals[i].y, f->vals[i].z, f->vals[i].q);
3949 0 : slot->owner = parent;
3950 0 : jsf->mfvals[i] = pf;
3951 : }
3952 : break;
3953 : }
3954 31 : case GF_SG_VRML_MFCOLOR:
3955 : {
3956 31 : MFColor *f = (MFColor *) field->far_ptr;
3957 31 : if (!was_found) {
3958 62 : SETUP_MF_FIELD(MFColorClass)
3959 : }
3960 31 : jsf->mfvals_count = f->count;
3961 31 : jsf->mfvals = gf_realloc(jsf->mfvals, sizeof(JSValue)*f->count);
3962 37 : for (i=0; i<f->count; i++) {
3963 6 : JSValue pf = JS_NewObjectClass(priv->js_ctx, SFColorClass.class_id);
3964 6 : slot = SFColor_Create(priv->js_ctx, pf, f->vals[i].red, f->vals[i].green, f->vals[i].blue);
3965 6 : slot->owner = parent;
3966 6 : jsf->mfvals[i] = pf;
3967 : }
3968 : break;
3969 : }
3970 :
3971 1061 : case GF_SG_VRML_MFNODE:
3972 : {
3973 : // GF_ChildNodeItem *f = * ((GF_ChildNodeItem **)field->far_ptr);
3974 2122 : SETUP_MF_FIELD(MFNodeClass)
3975 : assert(!jsf->mfvals);
3976 1061 : jsf->mfvals_count = 0;
3977 : /*we don't get the binding until the node is requested, and we don't store it in the MFVals value*/
3978 1061 : break;
3979 : }
3980 :
3981 : //not supported
3982 0 : default:
3983 0 : return JS_NULL;
3984 : }
3985 4231 : if (JS_IsNull(obj))
3986 0 : return obj;
3987 :
3988 4231 : if (!jsf) {
3989 0 : jsf = JS_GetOpaque_Nocheck(obj);
3990 : assert(jsf);
3991 : }
3992 : //store field associated with object if needed
3993 4231 : JS_SetOpaque(obj, jsf);
3994 4231 : if (!was_found) {
3995 4231 : jsf->obj = obj;
3996 : }
3997 : //for createVRMLFromString only, no parent
3998 4231 : if (!parent)
3999 0 : return obj;
4000 :
4001 : assert(jsf->owner);
4002 :
4003 : /*obj corresponding to an existing field/node, store it and prevent GC on object*/
4004 : /*remember the object*/
4005 4231 : if (!parent->sgprivate->interact) GF_SAFEALLOC(parent->sgprivate->interact, struct _node_interactive_ext);
4006 4231 : if (!parent->sgprivate->interact) {
4007 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[VRMLJS] Failed to create interact storage\n"));
4008 0 : return JS_NULL;
4009 : }
4010 4231 : if (!parent->sgprivate->interact->js_binding) {
4011 400 : GF_SAFEALLOC(parent->sgprivate->interact->js_binding, struct _node_js_binding);
4012 400 : if (!parent->sgprivate->interact->js_binding) {
4013 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[VRMLJS] Failed to create JS bindings storage\n"));
4014 0 : return JS_NULL;
4015 : }
4016 400 : parent->sgprivate->interact->js_binding->fields = gf_list_new();
4017 400 : if (!parent->sgprivate->interact->js_binding->fields) {
4018 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[VRMLJS] Failed to create JS bindings storage\n"));
4019 0 : return JS_NULL;
4020 : }
4021 : }
4022 :
4023 4231 : if ( gf_list_find(parent->sgprivate->interact->js_binding->fields, jsf) < 0) {
4024 : assert(jsf->owner == parent);
4025 4231 : gf_list_add(parent->sgprivate->interact->js_binding->fields, jsf);
4026 : }
4027 :
4028 4231 : if (!was_found) {
4029 4231 : if (gf_list_find(priv->jsf_cache, jsf)<0)
4030 2580 : gf_list_add(priv->jsf_cache, jsf);
4031 : } else {
4032 : assert (gf_list_find(priv->jsf_cache, jsf)>=0);
4033 : }
4034 :
4035 : /*our JS Array object (MFXXX) are always rooted and added to the cache upon construction*/
4036 4231 : if (jsf->mfvals) {
4037 : GF_JSClass *sf_class;
4038 752 : u32 count = jsf->mfvals_count;
4039 :
4040 752 : sf_class = get_sf_class(jsf->field.fieldType);
4041 : if (!sf_class) count=0;
4042 :
4043 1809 : for (i=0; i<count; i++) {
4044 1057 : JSValue item = jsf->mfvals[i];
4045 1057 : GF_JSField *afield = JS_GetOpaque(item, sf_class->class_id);
4046 1057 : if (afield) {
4047 1057 : if (afield->owner != parent) {
4048 0 : continue;
4049 : }
4050 1057 : if ( gf_list_find(parent->sgprivate->interact->js_binding->fields, afield) < 0) {
4051 1057 : gf_list_add(parent->sgprivate->interact->js_binding->fields, afield);
4052 : }
4053 : }
4054 : }
4055 : }
4056 4231 : parent->sgprivate->flags |= GF_NODE_HAS_BINDING;
4057 : return JS_DupValue(priv->js_ctx, jsf->obj);
4058 : }
4059 :
4060 404 : static void JS_ReleaseRootObjects(GF_ScriptPriv *priv)
4061 : {
4062 : /*pop the list rather than walk through it since unprotecting an element could trigger GC which in turn could modify this list content*/
4063 4140 : while (gf_list_count(priv->jsf_cache)) {
4064 : JSValue obj;
4065 3736 : GF_JSField *jsf = gf_list_pop_back(priv->jsf_cache);
4066 : assert(jsf);
4067 :
4068 : /* !!! WARNING !!!
4069 :
4070 : GC is handled at the JSRuntime level, not at the JSContext level.
4071 : Objects may not be finalized until the runtime is destroyed/GC'ed, which is not what we want.
4072 : We therefore destroy by hand all SFNode (obj rooted) and MFNode (for js_list)
4073 : */
4074 :
4075 3736 : obj = jsf->obj;
4076 3736 : jsf->obj = JS_UNDEFINED;
4077 3736 : if (jsf->mfvals)
4078 238 : array_finalize_ex(js_rt->js_runtime, obj, 0);
4079 3498 : else if (jsf->node)
4080 2713 : node_finalize_ex(js_rt->js_runtime, obj, 0);
4081 : else
4082 785 : jsf->js_ctx=NULL;
4083 :
4084 3736 : JS_FreeValue(priv->js_ctx, obj);
4085 : }
4086 404 : }
4087 :
4088 404 : static void JS_PreDestroy(GF_Node *node)
4089 : {
4090 : GF_SceneGraph *scene;
4091 404 : GF_ScriptPriv *priv = node->sgprivate->UserPrivate;
4092 404 : if (!priv) return;
4093 :
4094 404 : GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[Script] Destroying script node %s", gf_node_get_log_name(node) ));
4095 :
4096 : /*"shutdown" is no longer supported, as it is typically called when one of a parent node is destroyed through
4097 : a GC call.*/
4098 :
4099 404 : gf_js_lock(priv->js_ctx, 1);
4100 :
4101 404 : JS_FreeValue(priv->js_ctx, priv->the_event);
4102 :
4103 404 : JS_FreeValue(priv->js_ctx, priv->node_toString_fun);
4104 404 : JS_FreeValue(priv->js_ctx, priv->node_getTime_fun);
4105 : #ifndef GPAC_DISABLE_SVG
4106 404 : JS_FreeValue(priv->js_ctx, priv->node_addEventListener_fun);
4107 404 : JS_FreeValue(priv->js_ctx, priv->node_removeEventListener_fun);
4108 : #endif
4109 :
4110 : /*unprotect all cached objects from GC*/
4111 404 : JS_ReleaseRootObjects(priv);
4112 :
4113 : #ifndef GPAC_DISABLE_SVG
4114 404 : gf_sg_js_dom_pre_destroy(JS_GetRuntime(priv->js_ctx), node->sgprivate->scenegraph, NULL);
4115 : #endif
4116 :
4117 404 : JS_FreeValue(priv->js_ctx, priv->js_obj);
4118 :
4119 :
4120 404 : scene = JS_GetContextOpaque(priv->js_ctx);
4121 404 : if (scene && scene->__reserved_null) {
4122 404 : GF_Node *n = JS_GetContextOpaque(priv->js_ctx);
4123 404 : scene = n->sgprivate->scenegraph;
4124 : }
4125 404 : if (scene && scene->attached_session) {
4126 : void gf_fs_unload_js_api(JSContext *c, GF_FilterSession *fs);
4127 :
4128 4 : gf_fs_unload_js_api(priv->js_ctx, scene->attached_session);
4129 : }
4130 :
4131 :
4132 404 : gf_js_lock(priv->js_ctx, 0);
4133 :
4134 404 : gf_js_delete_context(priv->js_ctx);
4135 :
4136 : #ifndef GPAC_DISABLE_SVG
4137 404 : dom_js_unload();
4138 : #endif
4139 :
4140 404 : gf_list_del(priv->jsf_cache);
4141 :
4142 404 : priv->js_ctx = NULL;
4143 :
4144 : /*unregister script from parent scene (cf base_scenegraph::sg_reset) */
4145 404 : gf_list_del_item(node->sgprivate->scenegraph->scripts, node);
4146 : }
4147 :
4148 :
4149 404 : static void JS_InitScriptFields(GF_ScriptPriv *priv, GF_Node *sc)
4150 : {
4151 : u32 i;
4152 : GF_ScriptField *sf;
4153 : GF_FieldInfo info;
4154 : JSValue val;
4155 : JSAtom atom;
4156 :
4157 404 : i=0;
4158 3655 : while ((sf = gf_list_enum(priv->fields, &i))) {
4159 2847 : switch (sf->eventType) {
4160 : case GF_SG_EVENT_IN:
4161 : //nothing to do
4162 : break;
4163 375 : case GF_SG_EVENT_OUT:
4164 375 : gf_node_get_field(sc, sf->ALL_index, &info);
4165 : //do not assign a value, eventOut are write-only fields
4166 : /*create a setter function for this field to be notified whenever modified*/
4167 375 : sf->magic = i;
4168 375 : JSValue setter = JS_NewCFunction2(priv->js_ctx, (JSCFunction *) gf_sg_script_eventout_set_prop, sf->name, 1, JS_CFUNC_setter_magic, sf->magic);
4169 :
4170 375 : atom = JS_NewAtom(priv->js_ctx, sf->name);
4171 375 : JS_DefinePropertyGetSet(priv->js_ctx, priv->js_obj, atom, JS_UNDEFINED, setter, 0);
4172 375 : JS_FreeAtom(priv->js_ctx, atom);
4173 375 : break;
4174 1278 : default:
4175 : /*get field value and define in in global scope*/
4176 1278 : gf_node_get_field(sc, sf->ALL_index, &info);
4177 1278 : val = gf_sg_script_to_qjs_field(priv, &info, sc, 0);
4178 1278 : JS_SetPropertyStr(priv->js_ctx, priv->js_obj, (const char *) sf->name, val);
4179 1278 : break;
4180 : }
4181 : }
4182 404 : }
4183 :
4184 :
4185 : GF_EXPORT
4186 1637 : void gf_js_vrml_flush_event_out(GF_Node *node, GF_ScriptPriv *priv)
4187 : {
4188 : u32 i;
4189 : GF_ScriptField *sf;
4190 :
4191 : /*flush event out*/
4192 1637 : i=0;
4193 24482 : while ((sf = gf_list_enum(priv->fields, &i))) {
4194 21208 : if (sf->activate_event_out) {
4195 1012 : sf->activate_event_out = 0;
4196 1012 : gf_node_event_out(node, sf->ALL_index);
4197 : #ifndef GPAC_DISABLE_SVG
4198 1012 : if (node->sgprivate->interact && node->sgprivate->interact->dom_evt) {
4199 : GF_FieldInfo info;
4200 0 : if (gf_node_get_field(node, sf->ALL_index, &info)==GF_OK)
4201 0 : gf_node_changed(node, &info);
4202 : }
4203 : #endif
4204 : }
4205 : }
4206 1637 : }
4207 :
4208 1763 : static void JS_EventIn(GF_Node *node, GF_FieldInfo *in_field)
4209 : {
4210 : JSValue fval, rval;
4211 : Double time;
4212 : JSValue argv[2];
4213 : GF_ScriptField *sf;
4214 : GF_FieldInfo t_info;
4215 : GF_ScriptPriv *priv;
4216 : u32 i;
4217 1763 : priv = node->sgprivate->UserPrivate;
4218 :
4219 : /*no support for change of static fields*/
4220 1913 : if (in_field->fieldIndex<3) return;
4221 :
4222 1763 : i = (node->sgprivate->tag==TAG_MPEG4_Script) ? 3 : 4;
4223 1763 : sf = gf_list_get(priv->fields, in_field->fieldIndex - i);
4224 1763 : time = gf_node_get_scene_time(node);
4225 :
4226 : /*
4227 : if (sf->last_route_time == time) return;
4228 : */
4229 1763 : sf->last_route_time = time;
4230 :
4231 1763 : gf_js_lock(priv->js_ctx, 1);
4232 :
4233 1763 : fval = JS_GetPropertyStr(priv->js_ctx, priv->js_obj, sf->name);
4234 1763 : if (JS_IsUndefined(fval) || JS_IsNull(fval) || !JS_IsFunction(priv->js_ctx, fval)) {
4235 150 : gf_js_lock(priv->js_ctx, 0);
4236 150 : JS_FreeValue(priv->js_ctx, fval);
4237 : return;
4238 : }
4239 :
4240 1613 : argv[0] = gf_sg_script_to_qjs_field(priv, in_field, node, 1);
4241 :
4242 : memset(&t_info, 0, sizeof(GF_FieldInfo));
4243 1613 : t_info.far_ptr = &sf->last_route_time;
4244 1613 : t_info.fieldType = GF_SG_VRML_SFTIME;
4245 1613 : t_info.fieldIndex = -1;
4246 1613 : t_info.name = "timestamp";
4247 1613 : argv[1] = gf_sg_script_to_qjs_field(priv, &t_info, node, 1);
4248 :
4249 1613 : rval = JS_Call(priv->js_ctx, fval, priv->js_obj, 2, argv);
4250 1613 : if (JS_IsException(rval)) {
4251 80 : js_dump_error(priv->js_ctx);
4252 : }
4253 :
4254 1613 : JS_FreeValue(priv->js_ctx, fval);
4255 1613 : JS_FreeValue(priv->js_ctx, rval);
4256 1613 : JS_FreeValue(priv->js_ctx, argv[0]);
4257 1613 : JS_FreeValue(priv->js_ctx, argv[1]);
4258 :
4259 1613 : js_do_loop(priv->js_ctx);
4260 1613 : gf_js_lock(priv->js_ctx, 0);
4261 :
4262 1613 : gf_js_vrml_flush_event_out(node, priv);
4263 :
4264 1613 : do_js_gc(priv->js_ctx, node);
4265 : }
4266 :
4267 :
4268 36 : static Bool vrml_js_load_script(M_Script *script, char *file, Bool primary_script, JSValue *rval)
4269 : {
4270 : char *jsscript;
4271 : u32 fsize;
4272 : Bool success = 1;
4273 : u32 flags = JS_EVAL_TYPE_GLOBAL;
4274 : JSValue ret;
4275 : GF_Err e;
4276 36 : GF_ScriptPriv *priv = (GF_ScriptPriv *) script->sgprivate->UserPrivate;
4277 :
4278 36 : *rval = JS_UNDEFINED;
4279 :
4280 36 : e = gf_file_load_data(file, (u8 **) &jsscript, &fsize);
4281 36 : if (e) {
4282 : return GF_FALSE;
4283 : }
4284 :
4285 36 : if (!gf_opts_get_bool("core", "no-js-mods") && JS_DetectModule((const char *) jsscript, fsize )) {
4286 : flags = JS_EVAL_TYPE_MODULE;
4287 3 : priv->use_strict = GF_TRUE;
4288 : }
4289 : //we use JS_EVAL_TYPE_MODULE only for GUI at the current time. However the GUI script is still old and not
4290 : //compliant to strict mode, using a lot of global functions
4291 : //the code below is correct (we should always stick to strict if modules) but deactivated until we rewrite the GUI
4292 : // if (priv->use_strict)
4293 : // flags = JS_EVAL_TYPE_MODULE;
4294 :
4295 :
4296 36 : ret = JS_Eval(priv->js_ctx, jsscript, fsize, file, flags);
4297 36 : if (JS_IsException(ret)) {
4298 : success = 0;
4299 0 : js_dump_error(priv->js_ctx);
4300 : }
4301 36 : if (!success || primary_script)
4302 3 : JS_FreeValue(priv->js_ctx, ret);
4303 : else
4304 33 : *rval = ret;
4305 :
4306 36 : if (success && primary_script) {
4307 3 : JSValue fun = JS_GetPropertyStr(priv->js_ctx, priv->js_obj, "initialize");
4308 3 : if (JS_IsFunction(priv->js_ctx, fun)) {
4309 3 : ret = JS_Call(priv->js_ctx, fun, priv->js_obj, 0, NULL);
4310 3 : if (JS_IsException(ret)) {
4311 0 : js_dump_error(priv->js_ctx);
4312 : }
4313 3 : gf_js_vrml_flush_event_out((GF_Node *)script, priv);
4314 3 : JS_FreeValue(priv->js_ctx, ret);
4315 : }
4316 3 : JS_FreeValue(priv->js_ctx, fun);
4317 : }
4318 36 : gf_free(jsscript);
4319 36 : if (success)
4320 36 : js_do_loop(priv->js_ctx);
4321 : return success;
4322 : }
4323 :
4324 : /*fetches each listed URL and attempts to load the script - this is SYNCHRONOUS*/
4325 36 : Bool JSScriptFromFile(GF_Node *node, const char *opt_file, Bool no_complain, JSValue *rval)
4326 : {
4327 : GF_JSAPIParam par;
4328 : u32 i;
4329 : GF_DownloadManager *dnld_man;
4330 : GF_Err e;
4331 : M_Script *script = (M_Script *)node;
4332 :
4333 36 : e = GF_SCRIPT_ERROR;
4334 :
4335 36 : par.dnld_man = NULL;
4336 36 : ScriptAction(NULL, node->sgprivate->scenegraph, GF_JSAPI_OP_GET_DOWNLOAD_MANAGER, NULL, &par);
4337 36 : if (!par.dnld_man) return 0;
4338 : dnld_man = par.dnld_man;
4339 :
4340 0 : for (i=0; i<script->url.count; i++) {
4341 : char *url;
4342 : const char *ext;
4343 36 : char *_url = script->url.vals[i].script_text;
4344 36 : if (opt_file) {
4345 33 : if (strnicmp(_url+4, "script:", 7) && strnicmp(_url+5, "script:", 5)) {
4346 33 : _url = gf_url_concatenate(script->url.vals[i].script_text, opt_file);
4347 : } else {
4348 0 : _url = gf_strdup(opt_file);
4349 : }
4350 : }
4351 36 : par.uri.url = _url;
4352 36 : par.uri.nb_params = 0;
4353 36 : ScriptAction(NULL, node->sgprivate->scenegraph, GF_JSAPI_OP_RESOLVE_URI, node, &par);
4354 36 : if (opt_file) gf_free(_url);
4355 36 : url = (char *)par.uri.url;
4356 :
4357 36 : ext = gf_file_ext_start(url);
4358 36 : if (ext && strnicmp(ext, ".js", 3)) {
4359 0 : gf_free(url);
4360 0 : continue;
4361 : }
4362 :
4363 36 : if (!strstr(url, "://") || !strnicmp(url, "file://", 7)) {
4364 36 : Bool res = vrml_js_load_script(script, url, opt_file ? 0 : 1, rval);
4365 36 : gf_free(url);
4366 36 : if (res) return 1;
4367 0 : if (no_complain) return 0;
4368 : } else {
4369 0 : GF_DownloadSession *sess = gf_dm_sess_new(dnld_man, url, GF_NETIO_SESSION_NOT_THREADED, NULL, NULL, &e);
4370 0 : if (sess) {
4371 0 : e = gf_dm_sess_process(sess);
4372 0 : if (e==GF_OK) {
4373 0 : const char *szCache = gf_dm_sess_get_cache_name(sess);
4374 0 : if (!vrml_js_load_script(script, (char *) szCache, opt_file ? 0 : 1, rval))
4375 0 : e = GF_SCRIPT_ERROR;
4376 : }
4377 0 : gf_dm_sess_del(sess);
4378 : }
4379 0 : gf_free(url);
4380 0 : if (!e) return 1;
4381 : }
4382 : }
4383 :
4384 0 : par.info.e = GF_SCRIPT_ERROR;
4385 0 : par.info.msg = "Cannot fetch script";
4386 0 : ScriptAction(NULL, node->sgprivate->scenegraph, GF_JSAPI_OP_MESSAGE, NULL, &par);
4387 0 : return 0;
4388 : }
4389 :
4390 :
4391 404 : static void JSScript_LoadVRML(GF_Node *node)
4392 : {
4393 : char *str;
4394 : u32 i;
4395 : u32 flags = JS_EVAL_TYPE_GLOBAL;
4396 : Bool local_script;
4397 : JSValue rval;
4398 : M_Script *script = (M_Script *)node;
4399 404 : GF_ScriptPriv *priv = (GF_ScriptPriv *) node->sgprivate->UserPrivate;
4400 :
4401 407 : if (!priv || priv->is_loaded) return;
4402 404 : if (!script->url.count) return;
4403 404 : priv->is_loaded = 1;
4404 :
4405 : /*register script width parent scene (cf base_scenegraph::sg_reset) */
4406 404 : gf_list_add(node->sgprivate->scenegraph->scripts, node);
4407 :
4408 : str = NULL;
4409 407 : for (i=0; i<script->url.count; i++) {
4410 404 : str = script->url.vals[i].script_text;
4411 404 : while (strchr("\n\t ", str[0])) str++;
4412 :
4413 404 : if (!strnicmp(str, "javascript:", 11)) str += 11;
4414 4 : else if (!strnicmp(str, "vrmlscript:", 11)) str += 11;
4415 3 : else if (!strnicmp(str, "ecmascript:", 11)) str += 11;
4416 3 : else if (!strnicmp(str, "mpeg4script:", 12)) str += 12;
4417 : else str = NULL;
4418 401 : if (str) break;
4419 : }
4420 : local_script = str ? 1 : 0;
4421 :
4422 : /*lock runtime and set current thread before creating the context*/
4423 404 : priv->js_ctx = gf_js_create_context();
4424 404 : if (!priv->js_ctx) {
4425 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[VRML JS] Cannot allocate ECMAScript context for node\n"));
4426 : return;
4427 : }
4428 :
4429 404 : gf_js_lock(priv->js_ctx, 1);
4430 :
4431 404 : JS_SetContextOpaque(priv->js_ctx, node);
4432 404 : vrml_js_init_api(priv, node);
4433 :
4434 404 : qjs_module_init_gpaccore(priv->js_ctx);
4435 404 : qjs_module_init_scenejs(priv->js_ctx);
4436 404 : qjs_module_init_storage(priv->js_ctx);
4437 :
4438 404 : GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[VRML JS] Built-in classes initialized\n"));
4439 : #ifndef GPAC_DISABLE_SVG
4440 : /*initialize DOM*/
4441 404 : dom_js_load(node->sgprivate->scenegraph, priv->js_ctx);
4442 : /*create event object, and remember it*/
4443 404 : priv->the_event = dom_js_define_event(priv->js_ctx);
4444 404 : priv->the_event = JS_DupValue(priv->js_ctx, priv->the_event);
4445 : #endif
4446 404 : qjs_module_init_xhr_global(priv->js_ctx, priv->js_obj);
4447 :
4448 404 : priv->jsf_cache = gf_list_new();
4449 :
4450 : /*setup fields interfaces*/
4451 404 : JS_InitScriptFields(priv, node);
4452 404 : GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[VRML JS] script fields initialized\n"));
4453 :
4454 404 : priv->JS_PreDestroy = JS_PreDestroy;
4455 404 : priv->JS_EventIn = JS_EventIn;
4456 :
4457 404 : if (!local_script) {
4458 3 : JSScriptFromFile(node, NULL, 0, &rval);
4459 3 : gf_js_lock(priv->js_ctx, 0);
4460 3 : return;
4461 : }
4462 :
4463 401 : GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[VRML JS] Evaluating script %s\n", str));
4464 :
4465 401 : if (!gf_opts_get_bool("core", "no-js-mods") && JS_DetectModule((const char *) str, (u32) strlen(str) ))
4466 : flags = JS_EVAL_TYPE_MODULE;
4467 :
4468 401 : JSValue ret = JS_Eval(priv->js_ctx, str, (u32) strlen(str), "inline_script", flags);
4469 401 : if (!JS_IsException(ret)) {
4470 401 : JSValue fval = JS_GetPropertyStr(priv->js_ctx, priv->js_obj, "initialize");
4471 422 : if (JS_IsFunction(priv->js_ctx, fval) && !JS_IsUndefined(fval)) {
4472 : JSValue r, args[2];
4473 21 : args[0] = JS_TRUE;
4474 42 : args[1] = JS_NewFloat64(priv->js_ctx, gf_node_get_scene_time(node) );
4475 21 : r = JS_Call(priv->js_ctx, fval, priv->js_obj, 2, args);
4476 21 : if (JS_IsException(r)) {
4477 0 : js_dump_error(priv->js_ctx);
4478 : }
4479 21 : JS_FreeValue(priv->js_ctx, r);
4480 21 : JS_FreeValue(priv->js_ctx, args[0]);
4481 21 : JS_FreeValue(priv->js_ctx, args[1]);
4482 :
4483 21 : gf_js_vrml_flush_event_out(node, priv);
4484 : }
4485 401 : JS_FreeValue(priv->js_ctx, fval);
4486 : } else {
4487 0 : js_dump_error(priv->js_ctx);
4488 : }
4489 :
4490 401 : JS_FreeValue(priv->js_ctx, ret);
4491 401 : js_do_loop(priv->js_ctx);
4492 :
4493 401 : gf_js_lock(priv->js_ctx, 0);
4494 :
4495 401 : do_js_gc(priv->js_ctx, node);
4496 : }
4497 :
4498 407 : static void JSScript_Load(GF_Node *node)
4499 : {
4500 : #ifndef GPAC_DISABLE_SVG
4501 : void JSScript_LoadSVG(GF_Node *node);
4502 : #endif
4503 :
4504 407 : switch (node->sgprivate->tag) {
4505 404 : case TAG_MPEG4_Script:
4506 : #ifndef GPAC_DISABLE_X3D
4507 : case TAG_X3D_Script:
4508 : #endif
4509 404 : JSScript_LoadVRML(node);
4510 404 : break;
4511 : #ifndef GPAC_DISABLE_SVG
4512 3 : case TAG_SVG_script:
4513 : case TAG_SVG_handler:
4514 3 : JSScript_LoadSVG(node);
4515 3 : break;
4516 : #endif
4517 : default:
4518 : break;
4519 : }
4520 407 : }
4521 :
4522 :
4523 : #ifndef GPAC_DISABLE_PLAYER
4524 7688 : static void JSScript_NodeModified(GF_SceneGraph *sg, GF_Node *node, GF_FieldInfo *info, GF_Node *script)
4525 : {
4526 : u32 i;
4527 : GF_JSField *jsf;
4528 : /*this is REPLACE NODE signaling*/
4529 7688 : if (script) {
4530 : u32 count;
4531 0 : GF_ScriptPriv *priv = gf_node_get_private(script);
4532 0 : count = gf_list_count(priv->jsf_cache);
4533 :
4534 0 : for (i=0; i<count; i++) {
4535 0 : jsf = gf_list_get(priv->jsf_cache, i);
4536 : assert(jsf);
4537 0 : if (jsf->node && (jsf->node==node)) {
4538 : //detach node and its js binding
4539 0 : jsf->node = NULL;
4540 0 : node->sgprivate->interact->js_binding->pf = NULL;
4541 0 : node->sgprivate->interact->js_binding->obj = JS_UNDEFINED;
4542 : }
4543 : }
4544 7440 : return;
4545 : }
4546 :
4547 7688 : if (!info) {
4548 : /*handle DOM case*/
4549 7425 : if ((node->sgprivate->tag>=GF_NODE_FIRST_PARENT_NODE_TAG)
4550 176 : && node->sgprivate->interact
4551 176 : && node->sgprivate->interact->js_binding
4552 352 : && !JS_IsUndefined(node->sgprivate->interact->js_binding->obj))
4553 : {
4554 :
4555 28 : if (gf_list_del_item(sg->objects, node->sgprivate->interact->js_binding)>=0) {
4556 : #ifndef GPAC_DISABLE_SVG
4557 : void svg_free_node_binding(struct __tag_svg_script_ctx *svg_js, GF_Node *node);
4558 28 : svg_free_node_binding(sg->svg_js, node);
4559 : #endif
4560 : }
4561 : return;
4562 : }
4563 : /*this is unregister signaling*/
4564 7397 : if (!node->sgprivate->parents && node->sgprivate->interact && node->sgprivate->interact->js_binding && node->sgprivate->interact->js_binding->pf) {
4565 : GF_JSField *field = node->sgprivate->interact->js_binding->pf;
4566 : /*if this is the last occurence of the obj (no longer in JS), remove node binding*/
4567 715 : if (JS_VALUE_HAS_REF_COUNT(field->obj)) {
4568 715 : JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(field->obj);
4569 715 : if (p->ref_count==1) {
4570 : //store obj before calling JS_ObjectDestroyed which will set field->obj to JS_UNDEFINED
4571 6 : JSValue obj = field->obj;
4572 6 : JS_ObjectDestroyed(JS_GetRuntime(field->js_ctx), obj, field, 1);
4573 6 : JS_FreeValue(field->js_ctx, obj);
4574 6 : gf_free(field);
4575 : assert( node->sgprivate->interact->js_binding->pf==NULL);
4576 : //unregister node since we destroyed the binding
4577 6 : gf_node_unregister(node, NULL);
4578 : }
4579 : }
4580 : return;
4581 : }
4582 :
4583 : /*final destroy*/
4584 6682 : if (!node->sgprivate->num_instances) {
4585 3449 : i=0;
4586 6902 : while (node->sgprivate->interact && node->sgprivate->interact->js_binding && (jsf = gf_list_enum(node->sgprivate->interact->js_binding->fields, &i))) {
4587 4 : jsf->owner = NULL;
4588 :
4589 4 : if (jsf->mfvals) {
4590 : u32 j;
4591 :
4592 1 : if (jsf->field.fieldType != GF_SG_VRML_MFNODE) {
4593 1 : u32 count = jsf->mfvals_count;
4594 : GF_JSClass *sf_class = get_sf_class(jsf->field.fieldType);
4595 :
4596 : if (!sf_class) count=0;
4597 :
4598 1 : for (j=0; j<count; j++) {
4599 0 : JSValue item = jsf->mfvals[j];
4600 0 : GF_JSField *afield = JS_GetOpaque(item, sf_class->class_id);
4601 0 : if (afield) {
4602 0 : afield->owner = NULL;
4603 : }
4604 : }
4605 : }
4606 2 : while (jsf->mfvals_count) {
4607 1 : jsf->mfvals_count--;
4608 1 : JS_FreeValue(jsf->js_ctx, jsf->mfvals[jsf->mfvals_count]);
4609 : }
4610 1 : gf_free(jsf->mfvals);
4611 1 : jsf->mfvals = NULL;
4612 : }
4613 : }
4614 : }
4615 : return;
4616 : }
4617 : /*this is field modification signaling*/
4618 263 : if (!node->sgprivate->interact || !node->sgprivate->interact->js_binding) return;
4619 254 : i=0;
4620 1236 : while ((jsf = gf_list_enum(node->sgprivate->interact->js_binding->fields, &i))) {
4621 734 : if ((jsf->field.fieldIndex == info->fieldIndex) && (jsf->field.fieldType == info->fieldType)) {
4622 6 : jsf->field.NDTtype = 1;
4623 :
4624 : /*if field is a script field, rewrite it right away because script fields are exposed
4625 : as global variables within the script
4626 :
4627 : We also have a special value '-1' for the NDT for addChildren and removeChildren*/
4628 :
4629 : #if 0
4630 : if ((info->NDTtype == (u32) -1) || (node == (GF_Node*)JS_GetScript(jsf->js_ctx))) {
4631 : gf_sg_script_update_cached_object( JS_GetScriptStack(jsf->js_ctx), jsf->obj, jsf, &jsf->field, node);
4632 : }
4633 : #endif
4634 6 : return;
4635 : }
4636 : }
4637 : }
4638 : #endif
4639 :
4640 : GF_EXPORT
4641 94 : void gf_sg_handle_dom_event_for_vrml(GF_Node *node, GF_DOM_Event *event, GF_Node *observer)
4642 : {
4643 : #ifndef GPAC_DISABLE_SVG
4644 : GF_ScriptPriv *priv;
4645 : Bool prev_type;
4646 : //JSBool ret = JS_FALSE;
4647 : GF_DOM_Event *prev_event = NULL;
4648 : SVG_handlerElement *hdl;
4649 : JSValue ret;
4650 : JSValue evt;
4651 : JSValue argv[1];
4652 :
4653 : hdl = (SVG_handlerElement *) node;
4654 94 : if (!hdl->js_data) return;
4655 94 : if (!JS_IsFunction(hdl->js_data->ctx, hdl->js_data->fun_val) && !JS_IsUndefined(hdl->js_data->evt_listen_obj)) {
4656 : return;
4657 : }
4658 :
4659 94 : GF_LOG(GF_LOG_DEBUG, GF_LOG_INTERACT, ("[DOM Events] Executing script code from VRML handler\n"));
4660 :
4661 94 : priv = JS_GetScriptStack(hdl->js_data->ctx);
4662 94 : gf_js_lock(priv->js_ctx, 1);
4663 :
4664 94 : prev_event = JS_GetOpaque_Nocheck(priv->the_event);
4665 : /*break loops*/
4666 94 : if (prev_event && (prev_event->type==event->type) && (prev_event->target==event->target)) {
4667 0 : gf_js_lock(priv->js_ctx, 0);
4668 0 : return;
4669 : }
4670 :
4671 94 : evt = gf_dom_new_event(priv->js_ctx);
4672 94 : if (JS_IsUndefined(evt) || JS_IsNull(evt)) {
4673 0 : gf_js_lock(priv->js_ctx, 0);
4674 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[DOM Events] Cannot create JavaScript dom event for event type %d\n", event->type));
4675 : return;
4676 : }
4677 :
4678 94 : prev_type = event->is_vrml;
4679 94 : event->is_vrml = 1;
4680 94 : JS_SetOpaque(priv->the_event, event);
4681 :
4682 94 : JS_SetOpaque(evt, event);
4683 94 : argv[0] = evt;
4684 :
4685 94 : if (JS_IsFunction(hdl->js_data->ctx, hdl->js_data->fun_val)) {
4686 94 : JSValue listenObj = priv->js_obj;
4687 188 : if (!JS_IsNull(hdl->js_data->evt_listen_obj) && !JS_IsUndefined(hdl->js_data->evt_listen_obj))
4688 94 : listenObj = hdl->js_data->evt_listen_obj;
4689 :
4690 94 : ret = JS_Call(hdl->js_data->ctx, hdl->js_data->fun_val, listenObj, 1, argv);
4691 : } else {
4692 0 : JSValue fun = JS_GetPropertyStr(priv->js_ctx, hdl->js_data->evt_listen_obj, "handleEvent");
4693 0 : if (JS_IsFunction(priv->js_ctx, fun)) {
4694 0 : ret = JS_Call(priv->js_ctx, fun, hdl->js_data->evt_listen_obj, 1, argv);
4695 : } else {
4696 0 : ret = JS_UNDEFINED;
4697 : }
4698 0 : JS_FreeValue(priv->js_ctx, fun);
4699 : }
4700 94 : if (JS_IsException(ret)) {
4701 0 : js_dump_error(priv->js_ctx);
4702 : }
4703 94 : JS_FreeValue(priv->js_ctx, ret);
4704 94 : JS_FreeValue(priv->js_ctx, evt);
4705 :
4706 94 : event->is_vrml = prev_type;
4707 94 : JS_SetOpaque(priv->the_event, prev_event);
4708 :
4709 94 : js_do_loop(priv->js_ctx);
4710 94 : gf_js_lock(priv->js_ctx, 0);
4711 :
4712 : #endif
4713 : }
4714 :
4715 : #endif /*GPAC_DISABLE_VRML*/
4716 :
4717 : #endif /*GPAC_HAS_QJS*/
4718 :
4719 :
4720 :
4721 : /*set JavaScript interface*/
4722 655 : void gf_sg_set_script_action(GF_SceneGraph *scene, gf_sg_script_action script_act, void *cbk)
4723 : {
4724 655 : if (!scene) return;
4725 655 : scene->script_action = script_act;
4726 655 : scene->script_action_cbck = cbk;
4727 :
4728 : #if defined(GPAC_HAS_QJS) && !defined(GPAC_DISABLE_VRML)
4729 655 : scene->script_load = JSScript_Load;
4730 655 : scene->on_node_modified = JSScript_NodeModified;
4731 : #endif
4732 :
4733 : }
4734 :
4735 : #ifdef GPAC_HAS_QJS
4736 :
4737 : GF_EXPORT
4738 19 : GF_Node *gf_sg_js_get_node(JSContext *c, JSValue obj)
4739 : {
4740 : #ifndef GPAC_DISABLE_VRML
4741 19 : if (js_rt) {
4742 19 : GF_JSField *ptr = (GF_JSField *) JS_GetOpaque(obj, SFNodeClass.class_id);
4743 19 : if (ptr && (ptr->field.fieldType==GF_SG_VRML_SFNODE))
4744 19 : return * ((GF_Node **)ptr->field.far_ptr);
4745 : }
4746 : #endif
4747 :
4748 : #ifndef GPAC_DISABLE_SVG
4749 : {
4750 : Bool has_p = 0;
4751 0 : JSValue ns = JS_GetPropertyStr(c, obj, "namespaceURI");
4752 0 : if (!JS_IsNull(ns) && !JS_IsUndefined(ns)) has_p = 1;
4753 : JS_FreeValue(c, ns);
4754 0 : if (has_p) return dom_get_element(c, obj);
4755 : }
4756 : #endif
4757 0 : return NULL;
4758 : }
4759 :
4760 : #endif
4761 :
4762 : GF_EXPORT
4763 0 : Bool gf_sg_has_scripting()
4764 : {
4765 : #ifdef GPAC_HAS_QJS
4766 0 : return 1;
4767 : #else
4768 : return 0;
4769 : #endif
4770 : }
4771 :
4772 :
4773 : #ifdef GPAC_HAS_QJS
4774 :
4775 : /*
4776 : * locking/try-locking the JS context
4777 : *
4778 : * */
4779 : GF_EXPORT
4780 13717 : void gf_js_lock(struct JSContext *cx, Bool LockIt)
4781 : {
4782 13717 : if (!js_rt) return;
4783 :
4784 13717 : if (LockIt) {
4785 6831 : gf_mx_p(js_rt->mx);
4786 : } else {
4787 6886 : gf_mx_v(js_rt->mx);
4788 : }
4789 : }
4790 :
4791 : GF_EXPORT
4792 55 : Bool gf_js_try_lock(struct JSContext *cx)
4793 : {
4794 : assert(cx);
4795 55 : if (gf_mx_try_lock(js_rt->mx)) {
4796 : return 1;
4797 : }
4798 0 : return 0;
4799 : }
4800 :
4801 57 : JSRuntime *gf_js_get_rt()
4802 : {
4803 57 : if (!js_rt) return NULL;
4804 57 : return js_rt->js_runtime;
4805 : }
4806 :
4807 : #endif /* GPAC_HAS_QJS */
4808 :
4809 0 : GF_Err gf_scene_execute_script(GF_SceneGraph *sg, const char *com)
4810 : {
4811 : #if defined(GPAC_HAS_QJS) && !defined(GPAC_DISABLE_SVG)
4812 : u32 tag;
4813 : GF_Err e;
4814 0 : GF_Node *root = gf_sg_get_root_node(sg);
4815 0 : if (root) {
4816 0 : tag = gf_node_get_tag(root);
4817 0 : if (tag >= GF_NODE_RANGE_FIRST_SVG) {
4818 : GF_Err svg_exec_script(struct __tag_svg_script_ctx *svg_js, GF_SceneGraph *sg, const char *com);
4819 0 : return svg_exec_script(sg->svg_js, sg, (char *)com);
4820 : } else {
4821 : e = GF_NOT_SUPPORTED;
4822 : return e;
4823 : }
4824 : }
4825 : return GF_BAD_PARAM;
4826 : #else
4827 : return GF_NOT_SUPPORTED;
4828 : #endif
4829 : }
|