Coverage Report

Created: 2025-11-24 07:00

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