LCOV - code coverage report
Current view: top level - scenegraph - vrml_js.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 1188 2430 48.9 %
Date: 2021-04-29 23:48:07 Functions: 70 138 50.7 %

          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             : }

Generated by: LCOV version 1.13