Coverage Report

Created: 2026-10-16 07:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gpac/src/scenegraph/dom_js.c
Line
Count
Source
1
/*
2
 *      GPAC - Multimedia Framework C SDK
3
 *
4
 *      Authors: Jean Le Feuvre
5
 *      Copyright (c) Telecom ParisTech 2007-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/setup.h>
27
#include <gpac/internal/scenegraph_dev.h>
28
#include "qjs_common.h"
29
30
/*base SVG type*/
31
#include <gpac/nodes_svg.h>
32
/*dom events*/
33
#include <gpac/events.h>
34
/*dom text event*/
35
#include <gpac/utf.h>
36
37
#include <gpac/download.h>
38
#include <gpac/network.h>
39
#include <gpac/xml.h>
40
41
#ifndef GPAC_DISABLE_SVG
42
43
#ifdef GPAC_HAS_QJS
44
45
#include <gpac/html5_mse.h>
46
47
#ifdef GPAC_CONFIG_ANDROID
48
#ifndef XP_UNIX
49
#define XP_UNIX
50
#endif /* XP_UNIX */
51
#endif
52
53
#include <gpac/html5_media.h>
54
55
/************************************************************
56
 *
57
 *        DOM3 core implementation
58
 *
59
 *  This is NOT a full dom implementation :)
60
 *
61
 *************************************************************/
62
63
GF_JSClass domDocumentClass;
64
GF_JSClass domNodeClass;
65
GF_JSClass domElementClass;
66
GF_JSClass domTextClass;
67
GF_JSClass domNodeListClass;
68
GF_JSClass domEventClass;
69
GF_JSClass htmlMediaElementClass;
70
71
72
typedef struct
73
{
74
  u32 nb_inst;
75
76
  GF_List *handlers;
77
} GF_DOMRuntime;
78
79
static GF_DOMRuntime *dom_rt = NULL;
80
81
typedef enum {
82
  NODE_JSPROPERTY_NODENAME        = -1,
83
  NODE_JSPROPERTY_NODEVALUE       = -2,
84
  NODE_JSPROPERTY_NODETYPE        = -3,
85
  NODE_JSPROPERTY_PARENTNODE        = -4,
86
  NODE_JSPROPERTY_CHILDNODES        = -5,
87
  NODE_JSPROPERTY_FIRSTCHILD        = -6,
88
  NODE_JSPROPERTY_LASTCHILD       = -7,
89
  NODE_JSPROPERTY_PREVIOUSSIBLING     = -8,
90
  NODE_JSPROPERTY_NEXTSIBLING       = -9,
91
  NODE_JSPROPERTY_ATTRIBUTES        = -10,
92
  NODE_JSPROPERTY_OWNERDOCUMENT     = -11,
93
  NODE_JSPROPERTY_NAMESPACEURI      = -12,
94
  NODE_JSPROPERTY_PREFIX          = -13,
95
  NODE_JSPROPERTY_LOCALNAME       = -14,
96
  NODE_JSPROPERTY_BASEURI         = -15,
97
  NODE_JSPROPERTY_TEXTCONTENT       = -16,
98
  NODE_JSPROPERTY_FIRSTELEMENTCHILD   = -17,
99
  NODE_JSPROPERTY_LASTELEMENTCHILD    = -18,
100
  NODE_JSPROPERTY_PREVIOUSELEMENTSIBLING  = -19,
101
  NODE_JSPROPERTY_NEXTELEMENTSIBLING    = -20
102
} GF_DOMNodeJSProperty;
103
104
typedef enum {
105
  DOCUMENT_JSPROPERTY_DOCTYPE       = -1,
106
  DOCUMENT_JSPROPERTY_IMPLEMENTATION    = -2,
107
  DOCUMENT_JSPROPERTY_DOCUMENTELEMENT   = -3,
108
  DOCUMENT_JSPROPERTY_INPUTENCODING   = -4,
109
  DOCUMENT_JSPROPERTY_XMLENCODING     = -5,
110
  DOCUMENT_JSPROPERTY_XMLSTANDALONE   = -6,
111
  DOCUMENT_JSPROPERTY_XMLVERSION      = -7,
112
  DOCUMENT_JSPROPERTY_STRICTERRORCHECKING = -8,
113
  DOCUMENT_JSPROPERTY_DOCUMENTURI     = -9,
114
  DOCUMENT_JSPROPERTY_LOCATION      = -10,
115
  DOCUMENT_JSPROPERTY_DOMCONFIG     = -11,
116
  DOCUMENT_JSPROPERTY_GLOBAL        = -12
117
} GF_DOMDocumentJSProperty;
118
119
typedef enum {
120
  ELEMENT_JSPROPERTY_TAGNAME      = -1,
121
  ELEMENT_JSPROPERTY_SCHEMATYPEINFO = -2,
122
} GF_DOMElementJSProperty;
123
124
typedef enum {
125
  DCCI_JSPROPERTY_VALUE           = -1,
126
  DCCI_JSPROPERTY_VALUETYPE         = -2,
127
  DCCI_JSPROPERTY_PROPERTYTYPE        = -3,
128
  DCCI_JSPROPERTY_READONLY          = -4,
129
  DCCI_JSPROPERTY_DCCIMETADATAINTERFACETYPE = -5,
130
  DCCI_JSPROPERTY_DCCIMETADATAINTERFACE   = -6,
131
  DCCI_JSPROPERTY_VERSION           = -7,
132
} GF_DCCIJSProperty;
133
134
typedef enum {
135
  NODELIST_JSPROPERTY_LENGTH = -1,
136
} GF_DOMNodeListJSProperty;
137
138
typedef enum {
139
  EVENT_JSPROPERTY_TYPE           = -1,
140
  EVENT_JSPROPERTY_TARGET           = -2,
141
  EVENT_JSPROPERTY_CURRENTTARGET        = -3,
142
  EVENT_JSPROPERTY_EVENTPHASE         = -4,
143
  EVENT_JSPROPERTY_BUBBLES          = -5,
144
  EVENT_JSPROPERTY_CANCELABLE         = -6,
145
  EVENT_JSPROPERTY_TIMESTAMP          = -7,
146
  EVENT_JSPROPERTY_NAMESPACEURI       = -8,
147
  EVENT_JSPROPERTY_DEFAULTPREVENTED     = -9,
148
  EVENT_JSPROPERTY_DETAIL           = -10,
149
  EVENT_JSPROPERTY_DATA           = -11,
150
  EVENT_JSPROPERTY_SCREENX          = -12,
151
  EVENT_JSPROPERTY_SCREENY          = -13,
152
  EVENT_JSPROPERTY_CLIENTX          = -14,
153
  EVENT_JSPROPERTY_CLIENTY          = -15,
154
  EVENT_JSPROPERTY_BUTTON           = -16,
155
  EVENT_JSPROPERTY_RELATEDTARGET        = -17,
156
  EVENT_JSPROPERTY_WHEELDELTA         = -18,
157
  EVENT_JSPROPERTY_KEYIDENTIFIER        = -19,
158
  EVENT_JSPROPERTY_KEYCHAR          = -20,
159
  EVENT_JSPROPERTY_CHARCODE         = -21,
160
  EVENT_JSPROPERTY_LENGTHCOMPUTABLE     = -22,
161
  EVENT_JSPROPERTY_TYPEARG          = -23,
162
  EVENT_JSPROPERTY_LOADED           = -24,
163
  EVENT_JSPROPERTY_TOTAL            = -25,
164
  EVENT_JSPROPERTY_BUFFER_ON          = -26,
165
  EVENT_JSPROPERTY_BUFFERLEVEL        = -27,
166
  EVENT_JSPROPERTY_BUFFERREMAININGTIME    = -28,
167
  EVENT_JSPROPERTY_STATUS           = -29,
168
  EVENT_JSPROPERTY_WIDTH            = -30,
169
  EVENT_JSPROPERTY_HEIGHT           = -31,
170
  EVENT_JSPROPERTY_OFFSETX          = -32,
171
  EVENT_JSPROPERTY_OFFSETY          = -33,
172
  EVENT_JSPROPERTY_VPWIDTH          = -34,
173
  EVENT_JSPROPERTY_VPHEIGHT         = -35,
174
  EVENT_JSPROPERTY_TRANSLATIONX       = -36,
175
  EVENT_JSPROPERTY_TRANSLATIONY       = -37,
176
  EVENT_JSPROPERTY_TYPE3D           = -38,
177
  EVENT_JSPROPERTY_ERROR            = -39,
178
  EVENT_JSPROPERTY_DYNAMIC_SCENE        = -40,
179
  EVENT_JSPROPERTY_URL              = -41,
180
} GF_DOMEventJSProperty;
181
182
typedef enum {
183
  TEXT_JSPROPERTY_DATA            = -1,
184
  TEXT_JSPROPERTY_LENGTH            = -2,
185
  TEXT_JSPROPERTY_ISELEMENTCONTENTWHITESPACE  = -3,
186
  TEXT_JSPROPERTY_WHOLETEXT         = -4,
187
} GF_DOMTextJSProperty;
188
189
190
191
JSValue xml_dom3_not_implemented(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
192
0
{
193
0
  return js_throw_err(ctx, GF_DOM_EXC_NOT_SUPPORTED_ERR);
194
0
}
195
196
197
#define JS_DOM3_uDOM_FIRST_PROP   18
198
199
void dom_node_changed(GF_Node *n, Bool child_modif, GF_FieldInfo *info)
200
0
{
201
0
  if (info) {
202
0
    if (child_modif) {
203
0
      gf_node_dirty_set(n, GF_SG_CHILD_DIRTY, GF_FALSE);
204
0
    }
205
0
#ifndef GPAC_DISABLE_SVG
206
0
    else {
207
0
      u32 flag = gf_svg_get_modification_flags((SVG_Element *)n, info);
208
0
      gf_node_dirty_set(n, flag, GF_FALSE);
209
0
    }
210
0
#endif
211
0
  }
212
0
  gf_node_changed(n, info);
213
0
}
214
215
static void define_dom_exception(JSContext *c, JSValue global)
216
0
{
217
0
#define DEF_EXC(_val) \
218
0
  JS_SetPropertyStr(c, obj, #_val, JS_NewInt32(c, GF_DOM_EXC_##_val));
219
220
0
  JSValue obj = JS_NewObject(c);
221
0
  JS_SetPropertyStr(c, global, "DOMException", obj);
222
223
0
  DEF_EXC(INDEX_SIZE_ERR);
224
0
  DEF_EXC(DOMSTRING_SIZE_ERR);
225
0
  DEF_EXC(HIERARCHY_REQUEST_ERR);
226
0
  DEF_EXC(WRONG_DOCUMENT_ERR);
227
0
  DEF_EXC(INVALID_CHARACTER_ERR);
228
0
  DEF_EXC(NO_DATA_ALLOWED_ERR);
229
0
  DEF_EXC(NO_MODIFICATION_ALLOWED_ERR);
230
0
  DEF_EXC(NOT_FOUND_ERR);
231
0
  DEF_EXC(NOT_SUPPORTED_ERR);
232
0
  DEF_EXC(INUSE_ATTRIBUTE_ERR);
233
0
  DEF_EXC(INVALID_STATE_ERR);
234
0
  DEF_EXC(SYNTAX_ERR);
235
0
  DEF_EXC(INVALID_MODIFICATION_ERR);
236
0
  DEF_EXC(NAMESPACE_ERR);
237
0
  DEF_EXC(INVALID_ACCESS_ERR);
238
0
  DEF_EXC(VALIDATION_ERR);
239
0
  DEF_EXC(TYPE_MISMATCH_ERR);
240
0
  DEF_EXC(SECURITY_ERR);
241
0
  DEF_EXC(NETWORK_ERR);
242
0
  DEF_EXC(ABORT_ERR);
243
0
  DEF_EXC(URL_MISMATCH_ERR);
244
0
  DEF_EXC(QUOTA_EXCEEDED_ERR);
245
0
  DEF_EXC(TIMEOUT_ERR);
246
0
  DEF_EXC(INVALID_NODE_TYPE_ERR);
247
0
  DEF_EXC(DATA_CLONE_ERR);
248
0
  DEF_EXC(SECURITY_ERR);
249
0
#undef  DEF_EXC
250
251
  //JS_AliasProperty(c, global, "DOMException", "e");
252
0
  obj = JS_NewObject(c);
253
0
  JS_SetPropertyStr(c, global, "EventException", obj);
254
0
  JS_SetPropertyStr(c, obj, "UNSPECIFIED_EVENT_TYPE_ERR", JS_NewInt32(c, 0));
255
0
  JS_SetPropertyStr(c, obj, "DISPATCH_REQUEST_ERR", JS_NewInt32(c, 1));
256
0
}
257
258
259
GF_Node *dom_get_node(JSValue obj)
260
0
{
261
0
  JSClassID _classID;
262
0
  GF_Node *n = (GF_Node *) JS_GetAnyOpaque(obj, &_classID);
263
0
  if (n && n->sgprivate) return n;
264
0
  return NULL;
265
0
}
266
267
GF_Node *dom_get_element(JSContext *c, JSValue obj)
268
0
{
269
0
  JSClassID _classID;
270
0
  GF_Node *n = (GF_Node *) JS_GetAnyOpaque(obj, &_classID);
271
0
  if (!n || !n->sgprivate) return NULL;
272
0
  if (n->sgprivate->tag==TAG_DOMText) return NULL;
273
0
  return n;
274
0
}
275
276
GF_SceneGraph *dom_get_doc(JSContext *c, JSValue obj)
277
0
{
278
0
  JSClassID _classID;
279
0
  GF_SceneGraph *sg = (GF_SceneGraph *) JS_GetAnyOpaque(obj, &_classID);
280
0
  if (sg && !sg->__reserved_null) return sg;
281
0
  return NULL;
282
0
}
283
typedef struct _dom_js_data
284
{
285
  JSValue document;
286
} GF_DOMJSData;
287
288
static void dom_js_define_document_ex(JSContext *c, JSValue global, GF_SceneGraph *doc, const char *name)
289
0
{
290
0
  JSClassID __class = 0;
291
0
  JSValue obj;
292
0
  GF_SceneGraph *par_sg;
293
0
  if (!doc || !doc->RootNode) return;
294
295
0
  if (doc->reference_count)
296
0
    doc->reference_count++;
297
  //no need to register root once more, it is already registered with graph
298
//  gf_node_register(doc->RootNode, NULL);
299
0
  par_sg = doc;
300
0
  while (par_sg && !par_sg->get_document_class)
301
0
    par_sg = par_sg->parent_scene;
302
303
0
  if (par_sg && par_sg->get_document_class)
304
0
    __class = par_sg->get_document_class(doc);
305
306
0
  if (!__class)
307
0
    __class = domDocumentClass.class_id;
308
309
0
  obj = JS_NewObjectClass(c, __class);
310
0
  JS_SetOpaque(obj, doc);
311
0
  GF_SAFEALLOC(doc->js_data, GF_DOMJSData);
312
0
  if (doc->js_data)
313
0
    doc->js_data->document = JS_DupValue(c, obj);
314
315
0
  JS_SetPropertyStr(c, global, name, obj);
316
0
}
317
318
void dom_js_define_document(JSContext *c, JSValue global, GF_SceneGraph *doc)
319
0
{
320
0
  dom_js_define_document_ex(c, global, doc, "document");
321
0
}
322
323
/* Constructs a new document based on the given context and scene graph, used for documents in XHR */
324
JSValue dom_document_construct(JSContext *c, GF_SceneGraph *sg)
325
0
{
326
0
  JSClassID __class=0;
327
0
  JSValue new_obj;
328
0
  GF_SceneGraph *par_sg = sg;
329
0
  if (!dom_rt) return GF_JS_EXCEPTION(c);
330
0
  if (sg->js_data) return JS_DupValue(c, sg->js_data->document);
331
332
0
  if (sg->reference_count)
333
0
    sg->reference_count++;
334
  //no need to register root once more, it is already registered with graph
335
//  gf_node_register(sg->RootNode, NULL);
336
337
0
  while (par_sg && !par_sg->get_element_class) {
338
0
    par_sg = par_sg->parent_scene;
339
0
  }
340
341
0
  if (par_sg && par_sg->get_document_class)
342
0
    __class = par_sg->get_document_class(sg);
343
344
0
  if (!__class)
345
0
    __class = domDocumentClass.class_id;
346
347
0
  new_obj = JS_NewObjectClass(c, __class);
348
0
  JS_SetOpaque(new_obj, sg);
349
0
  GF_SAFEALLOC(sg->js_data, GF_DOMJSData);
350
0
  if (sg->js_data)
351
0
    sg->js_data->document = JS_DupValue(c, new_obj);
352
0
  return new_obj;
353
0
}
354
355
JSValue dom_document_construct_external(JSContext *c, GF_SceneGraph *sg)
356
0
{
357
0
  return dom_document_construct(c, sg);
358
359
0
}
360
361
static JSValue dom_base_node_construct(JSContext *c, JSClassID class_id, GF_Node *n)
362
0
{
363
0
  GF_SceneGraph *sg;
364
0
  JSValue new_obj;
365
0
  if (!n || !n->sgprivate->scenegraph) return JS_NULL;
366
0
  if (n->sgprivate->tag<TAG_DOMText) return JS_NULL;
367
368
0
  sg = n->sgprivate->scenegraph;
369
370
0
  if (n->sgprivate->interact && n->sgprivate->interact->js_binding && !JS_IsUndefined(n->sgprivate->interact->js_binding->obj) ) {
371
0
    return JS_DupValue(c, n->sgprivate->interact->js_binding->obj);
372
0
  }
373
374
0
  if (n->sgprivate->scenegraph->reference_count)
375
0
    n->sgprivate->scenegraph->reference_count ++;
376
377
0
  gf_node_register(n, NULL);
378
0
  new_obj = JS_NewObjectClass(c, class_id);
379
0
  JS_SetOpaque(new_obj, n);
380
381
0
  if (n->sgprivate->tag == TAG_SVG_video || n->sgprivate->tag == TAG_SVG_audio)
382
0
  {
383
#ifdef GPAC_ENABLE_HTML5_MEDIA
384
    html_media_element_js_init(c, new_obj, n);
385
#endif
386
387
0
  }
388
0
  if (!n->sgprivate->interact) {
389
0
    GF_SAFEALLOC(n->sgprivate->interact, struct _node_interactive_ext);
390
0
    if (!n->sgprivate->interact) {
391
0
      return JS_NULL;
392
0
    }
393
0
  }
394
0
  if (!n->sgprivate->interact->js_binding) {
395
0
    GF_SAFEALLOC(n->sgprivate->interact->js_binding, struct _node_js_binding);
396
0
    if (!n->sgprivate->interact->js_binding) {
397
0
      return JS_NULL;
398
0
    }
399
0
  }
400
0
  n->sgprivate->interact->js_binding->obj = JS_DupValue(c, new_obj);
401
0
  gf_list_add(sg->objects, n->sgprivate->interact->js_binding);
402
0
  return new_obj;
403
0
}
404
static JSValue dom_node_construct(JSContext *c, GF_Node *n)
405
0
{
406
0
  JSClassID __class = 0;
407
0
  GF_SceneGraph *par_sg;
408
0
  if (!n) return JS_NULL;
409
0
  par_sg = n->sgprivate->scenegraph;
410
0
  while (par_sg && !par_sg->get_element_class)
411
0
    par_sg = par_sg->parent_scene;
412
413
0
  if (par_sg && par_sg->get_element_class)
414
0
    __class = par_sg->get_element_class(n);
415
416
0
  if (!__class)
417
0
    __class = domElementClass.class_id;
418
419
  /*in our implementation ONLY ELEMENTS are created, never attributes. We therefore always
420
  create Elements when asked to create a node !!*/
421
0
  return dom_base_node_construct(c, __class, n);
422
0
}
423
424
JSValue dom_element_construct(JSContext *c, GF_Node *n)
425
0
{
426
0
  JSClassID __class = 0;
427
0
  GF_SceneGraph *par_sg;
428
0
  if (!n) return JS_NULL;
429
0
  par_sg = n->sgprivate->scenegraph;
430
0
  while (par_sg && !par_sg->get_element_class)
431
0
    par_sg = par_sg->parent_scene;
432
433
0
  if (par_sg && par_sg->get_element_class)
434
0
    __class = par_sg->get_element_class(n);
435
436
0
  if (!__class)
437
0
    __class = domElementClass.class_id;
438
439
0
  return dom_base_node_construct(c, __class, n);
440
0
}
441
442
static JSValue dom_text_construct(JSContext *c, GF_Node *n)
443
0
{
444
0
  return dom_base_node_construct(c, domTextClass.class_id, n);
445
0
}
446
447
static void dom_unregister_node(GF_Node *n)
448
0
{
449
0
  GF_SceneGraph *sg = n->sgprivate->scenegraph;
450
0
  if (!sg) return;
451
  /*!! node is being deleted !! */
452
0
  if (!n->sgprivate->num_instances) return;
453
454
0
  gf_node_unregister(n, NULL);
455
0
  if (sg->reference_count) {
456
0
    sg->reference_count--;
457
0
    if (!sg->reference_count)
458
0
      gf_sg_del(sg);
459
0
  }
460
0
}
461
462
JSValue dom_node_get_sibling(JSContext *c, GF_Node *n, Bool is_prev, Bool elt_only)
463
0
{
464
0
  GF_Node *val;
465
0
  GF_ChildNodeItem *child;
466
0
  s32 idx, cur;
467
0
  GF_ParentNode *par;
468
0
  if (!n) return JS_NULL;
469
0
  par = (GF_ParentNode *)gf_node_get_parent(n, 0);
470
0
  if (!par) return JS_NULL;
471
472
0
  idx = gf_node_list_find_child(par->children, n);
473
0
  if (idx<0) return JS_NULL;
474
475
0
  if (!elt_only) {
476
0
    if (is_prev) {
477
0
      idx--;
478
0
      if (idx<0) return JS_NULL;
479
0
    }
480
0
    else idx++;
481
0
    return dom_node_construct(c, gf_node_list_get_child(par->children, idx) );
482
0
  }
483
0
  cur = 0;
484
0
  val = NULL;
485
0
  child = par->children;
486
0
  while (child) {
487
0
    if ((idx!=cur) && (child->node->sgprivate->tag!=TAG_DOMText)) {
488
0
      val = child->node;
489
0
    }
490
0
    if (is_prev) {
491
0
      if (idx<=cur)
492
0
        break;
493
0
    } else {
494
0
      if (idx>=cur) val = NULL;
495
0
      if (val && idx<cur)
496
0
        break;
497
0
    }
498
0
    child = child->next;
499
0
    cur++;
500
0
  }
501
0
  return dom_node_construct(c, val);
502
0
}
503
504
505
506
/*dom3 NodeList/NamedNodeMap*/
507
typedef struct
508
{
509
  /*set if the object is a childList from an existing node*/
510
  GF_ParentNode *owner;
511
  /*child list*/
512
  GF_ChildNodeItem *child;
513
} DOMNodeList;
514
515
516
static JSValue dom_nodelist_construct(JSContext *c, GF_ParentNode *n)
517
0
{
518
0
  DOMNodeList *nl;
519
0
  JSValue new_obj;
520
0
  if (!n) return JS_NULL;
521
0
  GF_SAFEALLOC(nl, DOMNodeList);
522
0
  if (!nl) return GF_JS_EXCEPTION(c);
523
  
524
0
  nl->owner = n;
525
0
  if (n->sgprivate->scenegraph->reference_count)
526
0
    n->sgprivate->scenegraph->reference_count++;
527
528
0
  gf_node_register((GF_Node*)n, NULL);
529
0
  new_obj = JS_NewObjectClass(c, domNodeListClass.class_id);
530
0
  JS_SetOpaque(new_obj, nl);
531
0
  return new_obj;
532
0
}
533
534
static void dom_nodelist_finalize(JSRuntime *rt, JSValue obj)
535
0
{
536
0
  DOMNodeList *nl = JS_GetOpaque(obj, domNodeListClass.class_id);
537
0
  if (!nl) return;
538
539
0
  if (nl->owner) {
540
0
    dom_unregister_node((GF_Node*)nl->owner);
541
0
  } else {
542
    /*unregister all nodes for created lists*/
543
0
    while (nl->child) {
544
0
      GF_ChildNodeItem *child = nl->child;
545
0
      nl->child = child->next;
546
0
      dom_unregister_node(child->node);
547
0
      gf_free(child);
548
0
    }
549
0
  }
550
0
  gf_free(nl);
551
0
}
552
553
static JSValue dom_nodelist_item(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
554
0
{
555
0
  GF_Node *n;
556
0
  u32 count;
557
0
  s32 idx;
558
0
  DOMNodeList *nl;
559
560
0
  nl = (DOMNodeList *) JS_GetOpaque(obj, domNodeListClass.class_id);
561
0
  if (!nl || (argc!=1) || JS_ToInt32(c, &idx, argv[0]))
562
0
    return GF_JS_EXCEPTION(c);
563
564
0
  count = gf_node_list_get_count(nl->owner ? nl->owner->children : nl->child);
565
0
  if ((idx<0) || ((u32) idx>=count)) {
566
0
    return JS_NULL;
567
0
  }
568
0
  n = gf_node_list_get_child(nl->owner ? nl->owner->children : nl->child, idx);
569
0
  return dom_node_construct(c, n);
570
0
}
571
572
static JSValue dom_nodelist_getProperty(JSContext *c, JSValueConst obj, int magic)
573
0
{
574
0
  DOMNodeList *nl;
575
0
  u32 count;
576
0
  nl = (DOMNodeList *) JS_GetOpaque(obj, domNodeListClass.class_id);
577
0
  if (!nl) return GF_JS_EXCEPTION(c);
578
0
  count = gf_node_list_get_count(nl->owner ? nl->owner->children : nl->child);
579
580
0
  switch (magic) {
581
0
  case NODELIST_JSPROPERTY_LENGTH:
582
0
    return JS_NewInt32(c, count);
583
0
  default:
584
0
    break;
585
0
  }
586
0
  return js_throw_err(c, GF_DOM_EXC_SYNTAX_ERR);
587
0
}
588
589
590
591
592
static void dom_handler_remove(GF_Node *node, void *rs, Bool is_destroy)
593
0
{
594
0
  if (is_destroy) {
595
0
    SVG_handlerElement *handler = (SVG_handlerElement *)node;
596
0
    if (handler->js_data) {
597
0
      if (handler->js_data->ctx && !JS_IsUndefined(handler->js_data->fun_val)) {
598
        /*unprotect the function*/
599
0
        JS_FreeValue(handler->js_data->ctx, handler->js_data->fun_val);
600
0
        gf_list_del_item(dom_rt->handlers, handler);
601
0
      }
602
0
      gf_free(handler->js_data);
603
0
      handler->js_data = NULL;
604
0
    }
605
0
  }
606
0
}
607
608
static void sg_js_get_event_target(JSContext *c, JSValue obj, GF_EventType evtType, GF_Node *vrml_node,
609
                                     GF_SceneGraph **sg, GF_DOMEventTarget **target, GF_Node **n)
610
0
{
611
0
  Bool is_svg_document_class(JSContext *c, JSValue obj);
612
0
  Bool is_svg_element_class(JSContext *c, JSValue obj);
613
0
  Bool gf_mse_is_mse_object(JSContext *c, JSValue obj);
614
0
  *target = NULL;
615
0
  *sg = NULL;
616
0
  *n = NULL;
617
618
#ifdef GPAC_ENABLE_HTML5_MEDIA
619
  if (gf_dom_event_get_category(evtType) == GF_DOM_EVENT_MEDIA) {
620
    void gf_html_media_get_event_target(JSContext *c, JSValue obj, GF_DOMEventTarget **target, GF_SceneGraph **sg);
621
    gf_html_media_get_event_target(c, obj, target, sg);
622
    if (*target && *sg) return;
623
  }
624
625
  if (gf_dom_event_get_category(evtType) == GF_DOM_EVENT_MEDIASOURCE) {
626
    void gf_mse_get_event_target(JSContext *c, JSValue obj, GF_DOMEventTarget **target, GF_SceneGraph **sg);
627
    gf_mse_get_event_target(c, obj, target, sg);
628
    if (*target && *sg) return;
629
  }
630
#endif
631
632
633
0
  if (JS_GetOpaque(obj, domDocumentClass.class_id) || is_svg_document_class(c, obj)) {
634
    /*document interface*/
635
0
    *sg = dom_get_doc(c, obj);
636
0
    if (*sg) {
637
0
#ifndef GPAC_DISABLE_SVG
638
0
      *target = (*sg)->dom_evt;
639
#else
640
      return;
641
#endif
642
0
    } else {
643
0
      return;
644
0
    }
645
0
  } else if (JS_GetOpaque(obj, domElementClass.class_id) || is_svg_element_class(c, obj) || vrml_node) {
646
    /*Element interface*/
647
0
    if (vrml_node) {
648
0
      *n = vrml_node;
649
0
    } else {
650
0
      *n = dom_get_element(c, obj);
651
0
    }
652
0
    if (*n) {
653
0
      *sg = (*n)->sgprivate->scenegraph;
654
0
      if (!(*n)->sgprivate->interact->dom_evt) {
655
0
        (*n)->sgprivate->interact->dom_evt = gf_dom_event_target_new(GF_DOM_EVENT_TARGET_NODE, *n);
656
0
      }
657
0
      *target = (*n)->sgprivate->interact->dom_evt;
658
0
    }
659
0
  } else {
660
0
#if !defined(GPAC_DISABLE_SVG) && defined(GPAC_USE_DOWNLOADER)
661
0
    void xhr_get_event_target(JSContext *c, JSValue obj, GF_SceneGraph **sg, GF_DOMEventTarget **target);
662
663
0
    xhr_get_event_target(c, obj, sg, target);
664
0
#endif
665
0
  }
666
0
}
667
668
static GF_Err sg_js_parse_event_args(JSContext *c, JSValue obj, int argc, JSValue *argv,
669
                                     GF_EventType *evtType,
670
0
                                     char **callback, JSValue *funval, JSValue *evt_handler) {
671
0
  u32 offset = 0;
672
0
  const char *type = NULL;
673
0
  const char *inNS = NULL;
674
675
0
  *evtType = GF_EVENT_UNKNOWN;
676
0
  *callback = NULL;
677
0
  *funval = JS_NULL;
678
0
  if (evt_handler) *evt_handler = obj;
679
680
  /*NS version (4 args in DOM2, 5 in DOM3 for evt_group param)*/
681
0
  if (argc>=4) {
682
0
    if (!JS_CHECK_STRING(argv[0])) return GF_BAD_PARAM;
683
0
    inNS = JS_ToCString(c, argv[0]);
684
0
    offset = 1;
685
0
  }
686
687
0
  if (!JS_CHECK_STRING(argv[offset])) goto err_exit;
688
0
  type = JS_ToCString(c, argv[offset]);
689
690
0
  if (JS_CHECK_STRING(argv[offset+1])) {
691
0
    const char *str = JS_ToCString(c, argv[offset+1]);
692
0
    if (!str) goto err_exit;
693
0
    *callback = gf_strdup(str);
694
0
    JS_FreeCString(c, str);
695
0
  } else if (JS_IsFunction(c, argv[offset+1])) {
696
0
    *funval = argv[offset+1];
697
698
0
  } else if (JS_IsObject(argv[offset+1])) {
699
0
    JSValue evt_fun;
700
0
    if (evt_handler) {
701
0
      *evt_handler = JS_DupValue(c, argv[offset+1]);
702
0
    } else {
703
0
      goto err_exit;
704
0
    }
705
0
    evt_fun = JS_GetPropertyStr(c, *evt_handler, "handleEvent");
706
0
    if (!JS_IsFunction(c, evt_fun) ) goto err_exit;
707
0
    *funval = evt_fun;
708
0
  }
709
710
0
  *evtType = gf_dom_event_type_by_name(type);
711
0
  if (*evtType==GF_EVENT_UNKNOWN) goto err_exit;
712
713
0
  JS_FreeCString(c, type);
714
0
  JS_FreeCString(c, inNS);
715
0
  return GF_OK;
716
717
0
err_exit:
718
0
  if (type) JS_FreeCString(c, type);
719
0
  if (inNS) JS_FreeCString(c, inNS);
720
0
  if (callback) gf_free(*callback);
721
0
  *callback = NULL;
722
0
  *evtType = GF_EVENT_UNKNOWN;
723
0
  *funval = JS_NULL;
724
0
  if (evt_handler) *evt_handler = JS_UNDEFINED;
725
0
  return GF_BAD_PARAM;
726
0
}
727
728
void svg_execute_handler(GF_Node *node, GF_DOM_Event *event, GF_Node *observer);
729
730
static GF_Node *create_listener(GF_SceneGraph *sg, GF_EventType evtType, GF_Node *n, GF_Node *vrml_node,
731
                                JSContext *c, char *callback, JSValue funval, JSValue evt_handler)
732
0
{
733
0
  GF_FieldInfo info;
734
0
  GF_Node *listener;
735
0
  SVG_handlerElement *handler;
736
737
0
  listener = gf_node_new(sg, TAG_SVG_listener);
738
  /*we don't register the listener with the parent node , it will be registered
739
  on gf_dom_listener_add*/
740
741
  /*!!! create the handler in the scene owning the script context !!! */
742
0
  handler = (SVG_handlerElement *) gf_node_new(sg, TAG_SVG_handler);
743
  /*we register the handler with the listener node to avoid modifying the DOM*/
744
0
  gf_node_register((GF_Node *)handler, listener);
745
0
  gf_node_list_add_child(& ((GF_ParentNode *)listener)->children, (GF_Node*)handler);
746
747
0
  if (!callback) {
748
0
    GF_SAFEALLOC(handler->js_data, struct js_handler_context)
749
0
    if (handler->js_data) {
750
0
      handler->js_data->fun_val = funval;
751
0
      handler->js_data->ctx = c;
752
0
      if (JS_IsFunction(c, funval)) {
753
        /*protect the function - we don't know how it was passed to us, so prevent it from being GCed*/
754
0
        handler->js_data->fun_val = JS_DupValue(c, funval);
755
0
        handler->sgprivate->UserCallback = dom_handler_remove;
756
0
        gf_list_add(dom_rt->handlers, handler);
757
0
        handler->handle_event = svg_execute_handler;
758
0
      }
759
0
      handler->js_data->evt_listen_obj = evt_handler;
760
0
    }
761
0
  }
762
763
  /*create attributes if needed*/
764
0
  gf_node_get_attribute_by_tag(listener, TAG_XMLEV_ATT_event, GF_TRUE, GF_FALSE, &info);
765
0
  ((XMLEV_Event*)info.far_ptr)->type = evtType;
766
0
  gf_node_get_attribute_by_tag(listener, TAG_XMLEV_ATT_handler, GF_TRUE, GF_FALSE, &info);
767
0
  ((XMLRI*)info.far_ptr)->target = (GF_Node*)handler;
768
0
  if (n) {
769
0
    gf_node_get_attribute_by_tag(listener, TAG_XMLEV_ATT_target, GF_TRUE, GF_FALSE, &info);
770
0
    ((XMLRI*)info.far_ptr)->target = n;
771
0
  }
772
773
0
  gf_node_get_attribute_by_tag((GF_Node*)handler, TAG_XMLEV_ATT_event, GF_TRUE, GF_FALSE, &info);
774
0
  ((XMLEV_Event*)info.far_ptr)->type = evtType;
775
776
0
  if (callback) gf_dom_add_text_node((GF_Node *)handler, gf_strdup(callback));
777
778
0
#ifndef GPAC_DISABLE_SVG
779
0
  if (handler->sgprivate->scenegraph->svg_js && !handler->handle_event)
780
0
    handler->handle_event = gf_sg_handle_dom_event;
781
0
#endif
782
783
0
  if (vrml_node) {
784
0
    gf_assert(handler->js_data);
785
0
    handler->js_data->ctx = c;
786
0
#ifndef GPAC_DISABLE_VRML
787
0
    if (vrml_node->sgprivate->tag <= GF_NODE_RANGE_LAST_VRML)
788
0
      handler->handle_event = gf_sg_handle_dom_event_for_vrml;
789
0
#endif
790
0
  }
791
792
0
  return listener;
793
0
}
794
795
/*eventListeners routines used by document, element and XHR interfaces*/
796
JSValue gf_sg_js_event_add_listener(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv, GF_Node *vrml_node)
797
0
{
798
0
  GF_DOMEventTarget *target = NULL;
799
0
  GF_Node *listener = NULL;
800
0
  GF_EventType evtType = GF_EVENT_UNKNOWN;
801
0
  GF_SceneGraph *sg = NULL;
802
0
  char *callback = NULL;
803
0
  JSValue funval = JS_UNDEFINED;
804
0
  GF_Node *n = NULL;
805
0
  JSValue evt_handler = JS_UNDEFINED;
806
0
  GF_Err e;
807
808
  /* Determine the event type and handler params */
809
0
  e = sg_js_parse_event_args(c, obj, argc, argv, &evtType, &callback, &funval, &evt_handler);
810
0
  if (e != GF_OK) goto err_exit;
811
812
  /* First retrieve the scenegraph and the GF_DOMEventTarget object */
813
0
  sg_js_get_event_target(c, obj, evtType, vrml_node, &sg, &target, &n);
814
0
  if (!sg || !target) goto err_exit;
815
816
0
  listener = create_listener(sg, evtType, n, vrml_node, c, callback, funval, evt_handler);
817
0
  if (!listener) goto err_exit;
818
819
  /*don't add listener directly, post it and wait for event processing*/
820
0
  if (n) {
821
0
    gf_sg_listener_post_add((GF_Node *) n, listener);
822
0
  } else {
823
0
    gf_sg_listener_associate(listener, target);
824
0
  }
825
826
0
err_exit:
827
0
  if (callback) gf_free(callback);
828
0
  if (e)
829
0
    return js_throw_err(c, GF_DOM_EXC_SYNTAX_ERR);
830
0
  return JS_UNDEFINED;
831
0
}
832
833
834
JSValue dom_event_add_listener(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
835
0
{
836
0
  return gf_sg_js_event_add_listener(c, obj, argc, argv, NULL);
837
0
}
838
839
JSValue gf_sg_js_event_remove_listener(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv, GF_Node *vrml_node)
840
0
{
841
0
#ifndef GPAC_DISABLE_SVG
842
0
  char *callback = NULL;
843
0
  GF_EventType evtType = GF_EVENT_UNKNOWN;
844
0
  u32 i, count;
845
0
  GF_Node *node = NULL;
846
0
  JSValue funval = JS_UNDEFINED;
847
0
  GF_SceneGraph *sg = NULL;
848
0
  GF_DOMEventTarget *target = NULL;
849
0
  GF_Err e;
850
851
  /* Determine the event type and handler params */
852
0
  e = sg_js_parse_event_args(c, obj, argc, argv, &evtType, &callback, &funval, NULL);
853
0
  if (e != GF_OK) goto err_exit;
854
855
  /* First retrieve the scenegraph and the GF_DOMEventTarget object */
856
0
  sg_js_get_event_target(c, obj, evtType, vrml_node, &sg, &target, &node);
857
0
  if (!sg || !target) return JS_TRUE;
858
859
  /*flush all pending add_listener*/
860
0
  gf_dom_listener_process_add(sg);
861
862
0
  count = gf_list_count(target->listeners);
863
0
  for (i=0; i<count; i++) {
864
0
    GF_FieldInfo info;
865
0
    GF_DOMText *txt;
866
0
    SVG_handlerElement *hdl;
867
0
    GF_Node *el = (GF_Node *)gf_list_get(target->listeners, i);
868
869
0
    gf_node_get_attribute_by_tag(el, TAG_XMLEV_ATT_event, GF_FALSE, GF_FALSE, &info);
870
0
    if (!info.far_ptr) continue;
871
0
    if (((XMLEV_Event*)info.far_ptr)->type != evtType) continue;
872
873
0
    gf_node_get_attribute_by_tag(el, TAG_XMLEV_ATT_handler, GF_FALSE, GF_FALSE, &info);
874
0
    if (!info.far_ptr) continue;
875
0
    hdl = (SVG_handlerElement *) ((XMLRI*)info.far_ptr)->target;
876
0
    if (!hdl) continue;
877
0
    if (! JS_IsNull(funval) ) {
878
0
      Bool is_same = GF_FALSE;
879
880
0
      const char * f1 = JS_ToCString(c, funval);
881
0
      const char * f2 = JS_ToCString(c, funval);
882
0
      if (f1 && f2 && !strcmp(f1, f2)) is_same = GF_TRUE;
883
0
      JS_FreeCString(c, f1);
884
0
      JS_FreeCString(c, f2);
885
0
      if (!is_same) continue;
886
0
    } else if (hdl->children) {
887
0
      txt = (GF_DOMText *) hdl->children->node;
888
0
      if (txt->sgprivate->tag != TAG_DOMText) continue;
889
0
      if (!txt->textContent || !callback || strcmp(txt->textContent, callback)) continue;
890
0
    } else {
891
0
      continue;
892
0
    }
893
894
    /*this will destroy the listener and its child handler*/
895
0
    gf_dom_listener_del(el, target);
896
0
    break;
897
0
  }
898
0
#endif
899
900
0
err_exit:
901
0
  if (callback) gf_free(callback);
902
0
  return JS_UNDEFINED;
903
0
}
904
905
JSValue dom_event_remove_listener(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
906
0
{
907
0
  return gf_sg_js_event_remove_listener(c, obj, argc, argv, NULL);
908
0
}
909
910
/*dom3 node*/
911
static void dom_node_finalize(JSRuntime *rt, JSValue obj)
912
0
{
913
0
  JSClassID _classID;
914
0
  GF_Node *n = (GF_Node *) JS_GetAnyOpaque(obj, &_classID);
915
  /*the JS proto of the svgClass or a destroyed object*/
916
0
  if (!n) return;
917
0
  if (!n->sgprivate) return;
918
919
0
  JS_SetOpaque(obj, NULL);
920
0
  if (n->sgprivate->interact)
921
0
    gf_list_del_item(n->sgprivate->scenegraph->objects, n->sgprivate->interact->js_binding);
922
0
  gf_sg_js_dom_pre_destroy(rt, n->sgprivate->scenegraph, n);
923
0
  dom_unregister_node(n);
924
0
}
925
926
static Bool check_dom_parents(JSContext *c, GF_Node *n, GF_Node *parent)
927
0
{
928
0
  GF_ParentList *par = n->sgprivate->parents;
929
0
  if (n->sgprivate->scenegraph != parent->sgprivate->scenegraph) {
930
0
    js_throw_err(c, GF_DOM_EXC_WRONG_DOCUMENT_ERR);
931
0
    return GF_FALSE;
932
0
  }
933
0
  while (par) {
934
0
    if (par->node==parent) {
935
0
      js_throw_err(c, GF_DOM_EXC_HIERARCHY_REQUEST_ERR);
936
0
      return GF_FALSE;
937
0
    }
938
0
    if (!check_dom_parents(c, par->node, parent))
939
0
      return GF_FALSE;
940
0
    par = par->next;
941
0
  }
942
0
  return GF_TRUE;
943
0
}
944
945
static void dom_node_inserted(JSContext *c, GF_Node *n, GF_Node *parent, s32 pos)
946
0
{
947
0
  GF_ParentNode *old_parent;
948
0
  Bool do_init = GF_FALSE;
949
950
  /*if node is already in graph, remove it from its parent*/
951
0
  old_parent = (GF_ParentNode*)gf_node_get_parent(n, 0);
952
0
  if (old_parent) {
953
    /*prevent destruction when removing node*/
954
0
    n->sgprivate->num_instances++;
955
0
    gf_node_list_del_child(&old_parent->children, n);
956
0
    gf_node_unregister(n, (GF_Node*)old_parent);
957
0
    n->sgprivate->num_instances--;
958
0
  } else {
959
0
    do_init = (n->sgprivate->UserCallback || n->sgprivate->UserPrivate) ? GF_FALSE : GF_TRUE;
960
961
0
  }
962
963
0
  if (pos<0) gf_node_list_add_child( & ((GF_ParentNode *)parent)->children, n);
964
0
  else gf_node_list_insert_child( & ((GF_ParentNode *)parent)->children, n, (u32) pos);
965
0
  gf_node_register(n, parent);
966
967
0
  if (do_init) {
968
    /*node is a handler, create listener*/
969
0
    if (parent && (n->sgprivate->tag==TAG_SVG_handler)) {
970
0
      gf_dom_listener_build_ex(parent, 0, 0, n, NULL);
971
0
    }
972
0
    gf_node_init(n);
973
974
975
0
#ifndef GPAC_DISABLE_SVG
976
0
    if (n->sgprivate->interact && n->sgprivate->interact->dom_evt) {
977
0
      GF_DOM_Event evt;
978
0
      memset(&evt, 0, sizeof(GF_DOM_Event));
979
0
      evt.type = GF_EVENT_LOAD;
980
0
      gf_dom_event_fire(n, &evt);
981
0
    }
982
0
#endif
983
0
  }
984
  /*node is being re-inserted, activate it in case*/
985
0
  if (!old_parent) gf_node_activate(n);
986
987
0
  dom_node_changed(parent, GF_TRUE, NULL);
988
0
}
989
990
static JSValue xml_node_insert_before(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
991
0
{
992
0
  s32 idx;
993
0
  u32 tag;
994
0
  GF_Node *n, *target, *new_node;
995
0
  GF_ParentNode *par;
996
997
0
  if (!argc || !JS_IsObject(argv[0]))
998
0
    return GF_JS_EXCEPTION(c);
999
1000
0
  n = dom_get_node(obj);
1001
0
  if (!n) {
1002
0
    return js_throw_err(c, GF_DOM_EXC_HIERARCHY_REQUEST_ERR);
1003
0
  }
1004
1005
0
  new_node = dom_get_node(argv[0]);
1006
0
  if (!new_node)
1007
0
    return js_throw_err(c, GF_DOM_EXC_SYNTAX_ERR);
1008
1009
0
  target = NULL;
1010
0
  if ((argc==2) && JS_IsObject(argv[1]) && !JS_IsNull(argv[1])) {
1011
0
    target = dom_get_node(argv[1]);
1012
0
    if (!target) return JS_NULL;
1013
0
  }
1014
0
  tag = gf_node_get_tag(n);
1015
0
  if (tag==TAG_DOMText)
1016
0
    return js_throw_err(c, GF_DOM_EXC_SYNTAX_ERR);
1017
1018
0
  if (!check_dom_parents(c, n, new_node))
1019
0
    return js_throw_err(c, GF_DOM_EXC_VALIDATION_ERR);
1020
0
  par = (GF_ParentNode*)n;
1021
0
  idx = -1;
1022
0
  if (target) {
1023
0
    idx = gf_node_list_find_child(par->children, target);
1024
0
    if (idx<0) {
1025
0
      return js_throw_err(c, GF_DOM_EXC_NOT_FOUND_ERR);
1026
0
    }
1027
0
  }
1028
0
  dom_node_inserted(c, new_node, n, idx);
1029
0
  return JS_DupValue(c, argv[0] );
1030
0
}
1031
1032
static JSValue xml_node_append_child(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1033
0
{
1034
0
  u32 tag;
1035
0
  GF_Node *n, *new_node;
1036
1037
0
  if (!argc || !JS_IsObject(argv[0])) {
1038
0
    return GF_JS_EXCEPTION(c);
1039
0
  }
1040
0
  n = dom_get_node(obj);
1041
0
  if (!n) {
1042
0
    return js_throw_err(c, GF_DOM_EXC_HIERARCHY_REQUEST_ERR);
1043
0
  }
1044
1045
0
  new_node = dom_get_node(argv[0]);
1046
0
  if (!new_node) {
1047
0
    return js_throw_err(c, GF_DOM_EXC_SYNTAX_ERR);
1048
0
  }
1049
0
  tag = gf_node_get_tag(n);
1050
0
  if (tag==TAG_DOMText) {
1051
0
    return js_throw_err(c, GF_DOM_EXC_SYNTAX_ERR);
1052
0
  }
1053
1054
0
  if (!check_dom_parents(c, n, new_node)) {
1055
0
    return js_throw_err(c, GF_DOM_EXC_VALIDATION_ERR);
1056
0
  }
1057
1058
0
  dom_node_inserted(c, new_node, n, -1);
1059
0
  return JS_DupValue(c, argv[0]);
1060
0
}
1061
1062
void svg_mark_gc(struct __tag_svg_script_ctx *svg_js);
1063
1064
static JSValue xml_node_replace_child(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1065
0
{
1066
0
  s32 idx;
1067
0
  u32 tag;
1068
0
  GF_Node *n, *new_node, *old_node;
1069
0
  GF_ParentNode *par;
1070
1071
0
  if ((argc!=2) || !JS_IsObject(argv[0]) || !JS_IsObject(argv[1])) return GF_JS_EXCEPTION(c);
1072
0
  n = dom_get_node(obj);
1073
0
  if (!n) return js_throw_err(c, GF_DOM_EXC_HIERARCHY_REQUEST_ERR);
1074
1075
0
  new_node = dom_get_node(argv[0]);
1076
0
  if (!new_node) return js_throw_err(c, GF_DOM_EXC_HIERARCHY_REQUEST_ERR);
1077
0
  old_node = dom_get_node(argv[1]);
1078
0
  if (!old_node) return js_throw_err(c, GF_DOM_EXC_HIERARCHY_REQUEST_ERR);
1079
0
  tag = gf_node_get_tag(n);
1080
0
  if (tag==TAG_DOMText) return js_throw_err(c, GF_DOM_EXC_HIERARCHY_REQUEST_ERR);
1081
0
  par = (GF_ParentNode*)n;
1082
0
  idx = gf_node_list_find_child(par->children, old_node);
1083
0
  if (idx<0) return js_throw_err(c, GF_DOM_EXC_HIERARCHY_REQUEST_ERR);
1084
1085
0
  gf_node_list_del_child(&par->children, old_node);
1086
0
  gf_node_unregister(old_node, n);
1087
1088
0
  dom_node_inserted(c, new_node, n, -1);
1089
1090
  /*whenever we remove a node from the tree, call GC to cleanup the JS binding if any. Not doing so may screw up node IDs*/
1091
0
  svg_mark_gc(n->sgprivate->scenegraph->svg_js);
1092
0
  return JS_DupValue(c, argv[0]);
1093
0
}
1094
1095
static JSValue xml_node_remove_child(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1096
0
{
1097
0
  u32 tag;
1098
0
  GF_Node *n, *old_node;
1099
0
  GF_ParentNode *par;
1100
0
  if (!argc || !JS_IsObject(argv[0])) return JS_TRUE;
1101
1102
0
  n = dom_get_node(obj);
1103
0
  if (!n) return js_throw_err(c, GF_DOM_EXC_HIERARCHY_REQUEST_ERR);
1104
1105
0
  old_node = dom_get_node(argv[0]);
1106
0
  if (!old_node) return js_throw_err(c, GF_DOM_EXC_HIERARCHY_REQUEST_ERR);
1107
0
  tag = gf_node_get_tag(n);
1108
0
  if (tag==TAG_DOMText) return js_throw_err(c, GF_DOM_EXC_HIERARCHY_REQUEST_ERR);
1109
0
  par = (GF_ParentNode*)n;
1110
1111
  /*if node is present in parent, unregister*/
1112
0
  if (gf_node_list_del_child(&par->children, old_node)) {
1113
0
    gf_node_unregister(old_node, n);
1114
0
  } else {
1115
0
    return js_throw_err(c, GF_DOM_EXC_NOT_FOUND_ERR);
1116
0
  }
1117
1118
0
  dom_node_changed(n, GF_TRUE, NULL);
1119
  /*whenever we remove a node from the tree, call GC to cleanup the JS binding if any. Not doing so may screw up node IDs*/
1120
0
  svg_mark_gc(n->sgprivate->scenegraph->svg_js);
1121
0
  return JS_DupValue(c, argv[0]);
1122
0
}
1123
1124
static JSValue xml_clone_node(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1125
0
{
1126
0
  Bool deep;
1127
0
  GF_Node *n, *clone;
1128
1129
0
  n = dom_get_node(obj);
1130
0
  if (!n) return js_throw_err(c, GF_DOM_EXC_HIERARCHY_REQUEST_ERR);
1131
0
  deep = argc ? (JS_ToBool(c, argv[0]) ? GF_TRUE : GF_FALSE) : GF_FALSE;
1132
1133
0
  clone = gf_node_clone(n->sgprivate->scenegraph, n, NULL, "", deep);
1134
0
  return dom_node_construct(c, clone);
1135
0
}
1136
1137
static JSValue xml_node_has_children(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1138
0
{
1139
0
  GF_Node *n;
1140
0
  n = dom_get_node(obj);
1141
0
  if (!n) return js_throw_err(c, GF_DOM_EXC_HIERARCHY_REQUEST_ERR);
1142
0
  return ((GF_ParentNode*)n)->children ? JS_TRUE : JS_FALSE;
1143
0
}
1144
1145
static JSValue xml_node_has_attributes(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1146
0
{
1147
0
  u32 tag;
1148
0
  GF_Node *n;
1149
1150
0
  n = dom_get_node(obj);
1151
0
  if (!n) return js_throw_err(c, GF_DOM_EXC_HIERARCHY_REQUEST_ERR);
1152
0
  tag = gf_node_get_tag(n);
1153
0
  if (tag>=GF_NODE_FIRST_DOM_NODE_TAG) {
1154
0
    return ((GF_DOMNode*)n)->attributes ? JS_TRUE : JS_FALSE;
1155
0
  }
1156
  /*not supported for other node types*/
1157
0
  return JS_FALSE;
1158
0
}
1159
1160
static JSValue xml_node_is_same_node(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1161
0
{
1162
0
  GF_Node *n, *a_node;
1163
1164
0
  if (!argc || !JS_IsObject(argv[0])) return JS_TRUE;
1165
0
  n = dom_get_node(obj);
1166
0
  if (!n) return js_throw_err(c, GF_DOM_EXC_HIERARCHY_REQUEST_ERR);
1167
1168
0
  a_node = dom_get_node(argv[0]);
1169
0
  if (!a_node) return js_throw_err(c, GF_DOM_EXC_HIERARCHY_REQUEST_ERR);
1170
0
  return (a_node==n) ? JS_TRUE : JS_FALSE;
1171
0
}
1172
1173
static const char *node_get_local_name(GF_Node *node)
1174
0
{
1175
0
  const char *res;
1176
0
  GF_List *l;
1177
0
  if (!node) return NULL;
1178
0
  l = node->sgprivate->scenegraph->ns;
1179
0
  node->sgprivate->scenegraph->ns = NULL;
1180
0
  res = gf_node_get_class_name(node);
1181
0
  node->sgprivate->scenegraph->ns = l;
1182
0
  return res;
1183
0
}
1184
1185
static const char *node_lookup_namespace_by_tag(GF_Node *node, u32 tag)
1186
0
{
1187
  /*browse attributes*/
1188
0
  GF_DOMAttribute *att;
1189
0
  if (!node) return NULL;
1190
0
  if (node->sgprivate->tag < GF_NODE_FIRST_DOM_NODE_TAG) return NULL;
1191
1192
0
  att = ((SVG_Element*)node)->attributes;
1193
0
  while (att) {
1194
0
    if (att->tag==TAG_DOM_ATT_any) {
1195
0
      GF_DOMFullAttribute *datt = (GF_DOMFullAttribute*)att;
1196
0
      if (datt->name && !strncmp(datt->name, "xmlns", 5)) {
1197
0
        char *xmlns = *(DOM_String *) datt->data;
1198
0
        u32 crc = gf_crc_32(xmlns, (u32) strlen(xmlns));
1199
0
        if (tag==crc)
1200
0
          return xmlns;
1201
0
        if (!tag && !strcmp(datt->name, "xmlns"))
1202
0
          return xmlns;
1203
0
      }
1204
0
    }
1205
0
    att = att->next;
1206
0
  }
1207
  /*browse for parent*/
1208
0
  return node_lookup_namespace_by_tag(gf_node_get_parent(node, 0), tag);
1209
0
}
1210
1211
#ifdef GPAC_UNUSED_FUNC
1212
/**
1213
 * FIXME : function is not used by anybody
1214
 */
1215
static u32 get_namespace_code_by_prefix(GF_Node *node, char *prefix)
1216
{
1217
  /*browse attributes*/
1218
  GF_DOMAttribute *att;
1219
  if (!node) return 0;
1220
  att = ((SVG_Element*)node)->attributes;
1221
  while (att) {
1222
    if (att->tag==TAG_DOM_ATT_any) {
1223
      GF_DOMFullAttribute *datt = (GF_DOMFullAttribute*)att;
1224
      if (!prefix) {
1225
        if (!strcmp(datt->name, "xmlns")) return datt->xmlns;
1226
      } else if (!strncmp(datt->name, "xmlns:", 6)) {
1227
        if (!strcmp(datt->name+6, prefix)) return datt->xmlns;
1228
      }
1229
    }
1230
    att = att->next;
1231
  }
1232
  /*browse for parent*/
1233
  return get_namespace_code_by_prefix(gf_node_get_parent(node, 0), prefix);
1234
}
1235
#endif /*GPAC_UNUSED_FUNC*/
1236
1237
static JSValue dom_node_getProperty(JSContext *c, JSValueConst obj, int magic)
1238
0
{
1239
0
  u32 tag;
1240
0
  GF_Node *n;
1241
0
  GF_SceneGraph *sg = NULL;
1242
0
  GF_ParentNode *par;
1243
1244
0
  n = dom_get_node(obj);
1245
0
  if (!n) {
1246
0
    sg = dom_get_doc(c, obj);
1247
0
    if (!sg) return GF_JS_EXCEPTION(c);
1248
0
  }
1249
0
  tag = n ? gf_node_get_tag(n) : 0;
1250
0
  par = (GF_ParentNode*)n;
1251
1252
0
  switch (magic) {
1253
0
  case NODE_JSPROPERTY_NODENAME:
1254
0
    if (sg) {
1255
0
      return JS_NewString(c, "#document");
1256
0
    }
1257
0
    else if (tag==TAG_DOMText) {
1258
0
      GF_DOMText *txt = (GF_DOMText *)n;
1259
0
      if (txt->type==GF_DOM_TEXT_CDATA) return JS_NewString(c, "#cdata-section");
1260
0
      else return JS_NewString(c, "#text");
1261
0
    }
1262
0
    return JS_NewString(c, gf_node_get_class_name(n) );
1263
1264
0
  case NODE_JSPROPERTY_NODEVALUE:
1265
0
    if (tag==TAG_DOMText) {
1266
0
      GF_DOMText *txt = (GF_DOMText *)n;
1267
0
      return JS_NewString(c, txt->textContent);
1268
0
    }
1269
0
    return JS_NULL;
1270
0
  case NODE_JSPROPERTY_NODETYPE:
1271
0
    if (sg) return JS_NewInt32(c, 9);
1272
0
    else if (tag==TAG_DOMText) {
1273
0
      GF_DOMText *txt = (GF_DOMText *)n;
1274
0
      if (txt->type==GF_DOM_TEXT_CDATA) return JS_NewInt32(c, 4);
1275
0
      else return JS_NewInt32(c, 3);
1276
0
    }
1277
0
    return JS_NewInt32(c, 1);
1278
1279
0
  case NODE_JSPROPERTY_PARENTNODE:
1280
0
    if (sg) {
1281
0
      return JS_NULL;
1282
0
    }
1283
    /*if root node of the tree, the parentNode is the document*/
1284
0
    else if (n->sgprivate->scenegraph->RootNode==n) {
1285
0
      return dom_document_construct(c, n->sgprivate->scenegraph);
1286
0
    } else {
1287
0
      return dom_node_construct(c, gf_node_get_parent(n, 0) );
1288
0
    }
1289
1290
0
  case NODE_JSPROPERTY_CHILDNODES:
1291
    /*NOT SUPPORTED YET*/
1292
0
    if (sg) return JS_UNDEFINED;
1293
0
    else if (tag==TAG_DOMText) return JS_NULL;
1294
0
    else return dom_nodelist_construct(c, par);
1295
1296
0
  case NODE_JSPROPERTY_FIRSTCHILD:
1297
0
    if (sg) return dom_node_construct(c, sg->RootNode);
1298
0
    else if (tag==TAG_DOMText) return JS_NULL;
1299
0
    else if (!par->children) return JS_NULL;
1300
0
    else return dom_node_construct(c, par->children->node);
1301
1302
0
  case NODE_JSPROPERTY_LASTCHILD:
1303
0
    if (sg) return dom_node_construct(c, sg->RootNode);
1304
0
    else if ((tag==TAG_DOMText) || !par->children) return JS_NULL;
1305
0
    else return dom_node_construct(c, gf_node_list_get_child(par->children, -1) );
1306
1307
0
  case NODE_JSPROPERTY_PREVIOUSSIBLING:
1308
    /*works for doc as well since n is NULL*/
1309
0
    return dom_node_get_sibling(c, n, GF_TRUE, GF_FALSE);
1310
1311
0
  case NODE_JSPROPERTY_NEXTSIBLING:
1312
0
    return dom_node_get_sibling(c, n, GF_FALSE, GF_FALSE);
1313
1314
0
  case NODE_JSPROPERTY_ATTRIBUTES:
1315
    /*NOT SUPPORTED YET*/
1316
0
    return JS_UNDEFINED;
1317
1318
0
  case NODE_JSPROPERTY_OWNERDOCUMENT:
1319
0
    if (sg) return JS_NULL;
1320
0
    else return dom_document_construct(c, n->sgprivate->scenegraph);
1321
1322
0
  case NODE_JSPROPERTY_NAMESPACEURI:
1323
0
    if (!sg) {
1324
0
      const char *xmlns;
1325
0
      tag = gf_xml_get_element_namespace(n);
1326
0
      xmlns = gf_sg_get_namespace(n->sgprivate->scenegraph, tag);
1327
0
      if (!xmlns) xmlns = node_lookup_namespace_by_tag(n, tag);
1328
0
      return xmlns ? JS_NewString(c, xmlns) : JS_NULL;
1329
0
    }
1330
0
    return JS_NULL;
1331
0
  case NODE_JSPROPERTY_PREFIX:
1332
0
    if (sg) tag = gf_sg_get_namespace_code(sg, NULL);
1333
0
    else tag = gf_xml_get_element_namespace(n);
1334
1335
0
    if (tag) {
1336
0
      char *xmlns = (char *)gf_sg_get_namespace_qname(sg ? sg : n->sgprivate->scenegraph, tag);
1337
0
      if (xmlns) return JS_NewString(c, xmlns);
1338
0
    }
1339
0
    return JS_NULL;
1340
0
  case NODE_JSPROPERTY_LOCALNAME:
1341
0
    if (!sg && (tag!=TAG_DOMText)) {
1342
0
      return JS_NewString(c, node_get_local_name(n) );
1343
0
    }
1344
0
    return JS_NULL;
1345
0
  case NODE_JSPROPERTY_BASEURI:
1346
    /*NOT SUPPORTED YET*/
1347
0
    return JS_NULL;
1348
1349
0
  case NODE_JSPROPERTY_TEXTCONTENT:
1350
0
    if (!sg)  {
1351
0
      char *res = gf_dom_flatten_textContent(n);
1352
0
      JSValue ret = JS_NewString(c, res ? res : "");
1353
0
      if (res) gf_free(res);
1354
0
      return ret;
1355
0
    }
1356
0
    return JS_NewString(c, "");
1357
1358
0
  case NODE_JSPROPERTY_FIRSTELEMENTCHILD:
1359
0
    if (n->sgprivate->tag!=TAG_DOMText) {
1360
0
      GF_ChildNodeItem *child = ((GF_ParentNode*)n)->children;
1361
0
      while (child) {
1362
0
        if (child->node->sgprivate->tag != TAG_DOMText) {
1363
0
          return dom_element_construct(c, child->node);
1364
0
        }
1365
0
        child = child->next;
1366
0
      }
1367
0
    }
1368
0
    return JS_NULL;
1369
1370
0
  case NODE_JSPROPERTY_LASTELEMENTCHILD:
1371
0
    if (n->sgprivate->tag!=TAG_DOMText) {
1372
0
      GF_Node *last = NULL;
1373
0
      GF_ChildNodeItem *child = ((GF_ParentNode*)n)->children;
1374
0
      while (child) {
1375
0
        if (child->node->sgprivate->tag != TAG_DOMText) {
1376
0
          last = child->node;
1377
0
        }
1378
0
        child = child->next;
1379
0
      }
1380
0
      if (last) return dom_element_construct(c, last);
1381
0
    }
1382
0
    return JS_NULL;
1383
0
  case NODE_JSPROPERTY_PREVIOUSELEMENTSIBLING:
1384
0
    return dom_node_get_sibling(c, n, GF_TRUE, GF_TRUE);
1385
1386
0
  case NODE_JSPROPERTY_NEXTELEMENTSIBLING:
1387
0
    return dom_node_get_sibling(c, n, GF_FALSE, GF_TRUE);
1388
0
  }
1389
0
  return JS_UNDEFINED;
1390
0
}
1391
1392
void dom_node_set_textContent(GF_Node *n, char *text)
1393
0
{
1394
0
  GF_FieldInfo info;
1395
0
  gf_dom_set_textContent(n, text);
1396
1397
0
  gf_node_dirty_set(n, GF_SG_CHILD_DIRTY, GF_FALSE);
1398
0
  memset(&info, 0, sizeof(GF_FieldInfo));
1399
0
  info.fieldIndex = (u32) -1;
1400
0
  gf_node_changed(n, &info);
1401
0
}
1402
1403
static JSValue dom_node_setProperty(JSContext *c, JSValueConst obj, JSValueConst value, int magic)
1404
0
{
1405
0
  u32 tag;
1406
0
  GF_Node *n;
1407
1408
0
  n = dom_get_node(obj);
1409
  /*note an element - we don't support property setting on document yet*/
1410
0
  if (!n) return GF_JS_EXCEPTION(c);
1411
1412
0
  tag = n ? gf_node_get_tag(n) : 0;
1413
0
  switch (magic) {
1414
0
  case NODE_JSPROPERTY_NODEVALUE:
1415
0
    if ((tag==TAG_DOMText) && JS_CHECK_STRING(value)) {
1416
0
      const char *str;
1417
0
      GF_DOMText *txt = (GF_DOMText *)n;
1418
0
      if (txt->textContent) gf_free(txt->textContent);
1419
0
      str = JS_ToCString(c, value);
1420
0
      txt->textContent = str ? gf_strdup(str) : NULL;
1421
0
      JS_FreeCString(c, str);
1422
0
      dom_node_changed(n, GF_TRUE, NULL);
1423
0
    }
1424
    /*we only support element and sg in the Node interface, no set*/
1425
0
    return JS_TRUE;
1426
0
  case NODE_JSPROPERTY_PREFIX:
1427
    /*NOT SUPPORTED YET*/
1428
0
    return JS_TRUE;
1429
0
  case NODE_JSPROPERTY_TEXTCONTENT:
1430
0
  {
1431
0
    const char *txt = JS_ToCString(c, value);
1432
0
    dom_node_set_textContent(n, (char *) txt);
1433
0
    if (txt) JS_FreeCString(c, txt);
1434
0
  }
1435
0
  return JS_TRUE;
1436
0
  }
1437
  /*not supported*/
1438
0
  return JS_TRUE;
1439
0
}
1440
1441
1442
/*dom3 document*/
1443
1444
/*don't attempt to do anything with the scenegraph, it may be destroyed
1445
fortunately a sg cannot be created like that...*/
1446
void dom_document_finalize(JSRuntime *rt, JSValue obj)
1447
0
{
1448
0
  GF_SceneGraph *sg;
1449
0
  JSClassID _classID;
1450
0
  sg = (GF_SceneGraph*) JS_GetAnyOpaque(obj, &_classID);
1451
  /*the JS proto of the svgClass or a destroyed object*/
1452
0
  if (!sg) return;
1453
1454
0
  if (sg->js_data) {
1455
0
    JS_SetOpaque(sg->js_data->document, NULL);
1456
0
    JS_FreeValueRT(rt, sg->js_data->document);
1457
0
    gf_free(sg->js_data);
1458
0
    sg->js_data = NULL;
1459
0
  }
1460
1461
  //no need to unregister root, we did not registered it in the constructor
1462
//  if (sg->RootNode) {
1463
//    gf_node_unregister(sg->RootNode, NULL);
1464
//  }
1465
1466
0
  if (sg->reference_count) {
1467
0
    sg->reference_count--;
1468
0
    if (!sg->reference_count)
1469
0
      gf_sg_del(sg);
1470
0
  }
1471
0
}
1472
1473
static JSValue dom_document_getProperty(JSContext *c, JSValueConst obj, int magic)
1474
0
{
1475
0
  GF_SceneGraph *sg = dom_get_doc(c, obj);
1476
0
  if (!sg) return GF_JS_EXCEPTION(c);
1477
1478
0
  switch (magic) {
1479
0
  case DOCUMENT_JSPROPERTY_IMPLEMENTATION:
1480
0
  { /*FIXME, this is wrong, we should have our own implementation
1481
    but at the current time we rely on the global object to provide it*/
1482
0
    JSValue global = JS_GetGlobalObject(c);
1483
0
    JSValue ret = JS_GetPropertyStr(c, global, "Window");
1484
0
    JS_FreeValue(c, global);
1485
0
    return ret;
1486
0
  }
1487
1488
0
  case DOCUMENT_JSPROPERTY_DOCUMENTELEMENT:
1489
0
    return dom_element_construct(c, sg->RootNode);
1490
1491
0
  case DOCUMENT_JSPROPERTY_GLOBAL:
1492
0
    return JS_GetGlobalObject(c);
1493
1494
0
  case DOCUMENT_JSPROPERTY_DOCTYPE:
1495
0
  case DOCUMENT_JSPROPERTY_INPUTENCODING:
1496
0
  case DOCUMENT_JSPROPERTY_XMLENCODING:
1497
0
  case DOCUMENT_JSPROPERTY_XMLSTANDALONE:
1498
0
  case DOCUMENT_JSPROPERTY_XMLVERSION:
1499
0
  case DOCUMENT_JSPROPERTY_STRICTERRORCHECKING:
1500
0
  case DOCUMENT_JSPROPERTY_DOCUMENTURI:
1501
0
  case DOCUMENT_JSPROPERTY_LOCATION:
1502
0
  case DOCUMENT_JSPROPERTY_DOMCONFIG:
1503
0
    return JS_NULL;
1504
0
  }
1505
0
  return JS_UNDEFINED;
1506
0
}
1507
1508
static JSValue dom_document_setProperty(JSContext *c, JSValueConst obj, JSValueConst value, int magic)
1509
0
{
1510
0
  GF_SceneGraph *sg = dom_get_doc(c, obj);
1511
0
  if (!sg) return GF_JS_EXCEPTION(c);
1512
0
  return js_throw_err(c, GF_DOM_EXC_NOT_SUPPORTED_ERR);
1513
0
}
1514
1515
static JSValue xml_document_create_element(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1516
0
{
1517
0
  u32 tag, ns;
1518
0
  JSValue ret = JS_NULL;
1519
0
  const char *name;
1520
0
  const char *xmlns;
1521
0
  GF_SceneGraph *sg = dom_get_doc(c, obj);
1522
1523
0
  if (!sg || !argc || !JS_CHECK_STRING(argv[0]) )
1524
0
    return GF_JS_EXCEPTION(c);
1525
1526
0
  name = NULL;
1527
  /*NS version*/
1528
0
  ns = 0;
1529
0
  xmlns = NULL;
1530
0
  if (argc==2) {
1531
0
    if (!JS_CHECK_STRING(argv[1])) return GF_JS_EXCEPTION(c);
1532
0
    xmlns = JS_ToCString(c, argv[0]);
1533
0
    if (xmlns) ns = gf_sg_get_namespace_code_from_name(sg, (char *) xmlns);
1534
0
    name = JS_ToCString(c, argv[1]);
1535
0
  } else {
1536
0
    name = JS_ToCString(c, argv[0]);
1537
0
  }
1538
1539
0
  if (name) {
1540
0
    GF_Node *n;
1541
    /*browse all our supported DOM implementations*/
1542
0
    tag = gf_xml_get_element_tag(name, ns);
1543
0
    if (!tag) tag = TAG_DOMFullNode;
1544
0
    n = gf_node_new(sg, tag);
1545
0
    if (n && (tag == TAG_DOMFullNode)) {
1546
0
      GF_DOMFullNode *elt = (GF_DOMFullNode *)n;
1547
0
      elt->name = gf_strdup(name);
1548
0
      if (xmlns) {
1549
0
        gf_sg_add_namespace(sg, (char *) xmlns, NULL);
1550
0
        elt->ns = gf_sg_get_namespace_code_from_name(sg, (char *) xmlns);
1551
0
      }
1552
0
    }
1553
0
    ret = dom_element_construct(c, n);
1554
0
  }
1555
0
  JS_FreeCString(c, name);
1556
0
  JS_FreeCString(c, xmlns);
1557
0
  return ret;
1558
0
}
1559
1560
static JSValue xml_document_create_text(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1561
0
{
1562
0
  GF_Node *n;
1563
0
  GF_SceneGraph *sg = dom_get_doc(c, obj);
1564
0
  if (!sg) return GF_JS_EXCEPTION(c);
1565
1566
0
  n = gf_node_new(sg, TAG_DOMText);
1567
0
  if (argc) {
1568
0
    const char *str = JS_ToCString(c, argv[0]);
1569
0
    char *ntext = gf_strdup(str ? str : "");
1570
0
    ((GF_DOMText*)n)->textContent = ntext;
1571
0
    JS_FreeCString(c, str);
1572
0
  }
1573
0
  return dom_text_construct(c, n);
1574
0
}
1575
1576
static void xml_doc_gather_nodes(GF_ParentNode *node, char *name, DOMNodeList *nl)
1577
0
{
1578
0
  Bool bookmark = GF_TRUE;
1579
0
  GF_ChildNodeItem *child;
1580
0
  if (!node) return;
1581
0
  if (name) {
1582
0
    const char *node_name = gf_node_get_class_name((GF_Node*)node);
1583
0
    if (strcmp(node_name, name)) bookmark = GF_FALSE;
1584
0
  }
1585
0
  if (bookmark) {
1586
0
    gf_node_register((GF_Node*)node, NULL);
1587
0
    if (node->sgprivate->scenegraph->reference_count)
1588
0
      node->sgprivate->scenegraph->reference_count++;
1589
0
    gf_node_list_add_child(&nl->child, (GF_Node*)node);
1590
0
  }
1591
0
  if (node->sgprivate->tag<GF_NODE_FIRST_PARENT_NODE_TAG) return;
1592
0
  child = node->children;
1593
0
  while (child) {
1594
0
    xml_doc_gather_nodes((GF_ParentNode*)child->node, name, nl);
1595
0
    child = child->next;
1596
0
  }
1597
0
}
1598
1599
static JSValue xml_document_elements_by_tag(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1600
0
{
1601
0
  DOMNodeList *nl;
1602
0
  JSValue new_obj;
1603
0
  const char *name;
1604
0
  GF_SceneGraph *sg = dom_get_doc(c, obj);
1605
0
  if (!sg) return GF_JS_EXCEPTION(c);
1606
1607
0
  if (!argc || !JS_CHECK_STRING(argv[0])) return GF_JS_EXCEPTION(c);
1608
1609
  /*NS version - TODO*/
1610
0
  if (argc==2) {
1611
0
    if (!JS_CHECK_STRING(argv[1])) return GF_JS_EXCEPTION(c);
1612
0
    name = JS_ToCString(c, argv[1]);
1613
0
  } else {
1614
0
    name = JS_ToCString(c, argv[0]);
1615
0
  }
1616
1617
0
  GF_SAFEALLOC(nl, DOMNodeList);
1618
0
  if (!nl) return GF_JS_EXCEPTION(c);
1619
1620
0
  if (name && !strcmp(name, "*"))
1621
0
    xml_doc_gather_nodes((GF_ParentNode*)sg->RootNode, NULL, nl);
1622
0
  else
1623
0
    xml_doc_gather_nodes((GF_ParentNode*)sg->RootNode, (char *) name, nl);
1624
0
  new_obj = JS_NewObjectClass(c, domNodeListClass.class_id);
1625
0
  JS_SetOpaque(new_obj, nl);
1626
0
  JS_FreeCString(c, name);
1627
0
  return new_obj;
1628
0
}
1629
1630
1631
static JSValue xml_document_element_by_id(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1632
0
{
1633
0
  NodeIDedItem *reg_node;
1634
0
  GF_Node *n;
1635
0
  const char *id;
1636
0
  JSValue ret;
1637
1638
0
  GF_SceneGraph *sg = dom_get_doc(c, obj);
1639
0
  if (!sg) return GF_JS_EXCEPTION(c);
1640
0
  if (!argc || !JS_CHECK_STRING(argv[0])) return GF_JS_EXCEPTION(c);
1641
0
  id = JS_ToCString(c, argv[0]);
1642
1643
  /*we don't use the regular gf_sg_find_node_by_name because we may have nodes defined with the
1644
  same ID and we need to locate the first one which is inserted in the tree*/
1645
0
  n = NULL;
1646
0
  reg_node = sg->id_node;
1647
0
  while (reg_node) {
1648
0
    if (reg_node->NodeName && !strcmp(reg_node->NodeName, id)) {
1649
0
      n = reg_node->node;
1650
      /*element is not inserted - fixme, we should check all parents*/
1651
0
      if (n && (n->sgprivate->scenegraph->RootNode!=n) && !n->sgprivate->parents) n = NULL;
1652
0
      else break;
1653
0
    }
1654
0
    reg_node = reg_node->next;
1655
0
  }
1656
0
  ret = dom_element_construct(c, n);
1657
0
  JS_FreeCString(c, id);
1658
0
  return ret;
1659
0
}
1660
1661
/*dom3 element*/
1662
void dom_element_finalize(JSRuntime *rt, JSValue obj)
1663
0
{
1664
0
  dom_node_finalize(rt, obj);
1665
0
}
1666
1667
static JSValue dom_element_getProperty(JSContext *c, JSValueConst obj, int magic)
1668
0
{
1669
0
  GF_Node *n = dom_get_node(obj);
1670
0
  if (!n) return JS_TRUE;
1671
1672
0
  switch (magic) {
1673
0
  case ELEMENT_JSPROPERTY_TAGNAME:
1674
0
    return JS_NewString(c, gf_node_get_class_name(n) );
1675
1676
0
  case ELEMENT_JSPROPERTY_SCHEMATYPEINFO:
1677
    /*NOT SUPPORTED YET*/
1678
0
    return JS_NULL;
1679
0
  }
1680
0
  return JS_UNDEFINED;
1681
0
}
1682
1683
static JSValue xml_element_get_attribute(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1684
0
{
1685
0
  const char *name;
1686
0
  const char *ns;
1687
0
  JSValue ret = JS_NULL;
1688
1689
0
  GF_Node *n = dom_get_node(obj);
1690
0
  if (!n) return GF_JS_EXCEPTION(c);
1691
1692
0
  if (!argc || !JS_CHECK_STRING(argv[0]))
1693
0
    return JS_TRUE;
1694
0
  ns = NULL;
1695
  /*NS version*/
1696
0
  if (argc==2) {
1697
0
    if (!JS_CHECK_STRING(argv[1]))
1698
0
      return JS_TRUE;
1699
0
    ns = JS_ToCString(c, argv[0]);
1700
0
    name = JS_ToCString(c, argv[1]);
1701
0
  } else {
1702
0
    name = JS_ToCString(c, argv[0]);
1703
0
  }
1704
0
  if (!name) goto exit;
1705
1706
  /*ugly ugly hack ...*/
1707
0
  if (!strcmp(name, "id") || !strcmp(name, "xml:id") ) {
1708
0
    char *sID = (char *) gf_node_get_name(n);
1709
0
    if (sID) {
1710
0
      ret = JS_NewString(c, sID);
1711
0
      goto exit;
1712
0
    }
1713
0
  }
1714
1715
0
  if (n->sgprivate->tag==TAG_DOMFullNode) {
1716
0
    GF_DOMFullNode *node = (GF_DOMFullNode*)n;
1717
0
    GF_DOMFullAttribute *att = (GF_DOMFullAttribute*)node->attributes;
1718
0
    u32 ns_code = ns ? gf_sg_get_namespace_code_from_name(n->sgprivate->scenegraph, (char *) ns) : 0;
1719
0
    while (att) {
1720
0
      if (att->tag==TAG_DOM_ATT_any) {
1721
0
        Bool found = GF_FALSE;
1722
0
        if (!strcmp(att->name, name) && (ns_code == att->xmlns))
1723
0
          found = GF_TRUE;
1724
0
        else if (ns) {
1725
0
          char *nssep = strchr(att->name, ':');
1726
0
          if (nssep && !strcmp(nssep+1, name)) {
1727
0
            u32 ns_len = (u32) (nssep - att->name);
1728
            //todo check namespace
1729
0
            if (!strcmp(ns, "*") || !strncmp(ns, att->name, ns_len))
1730
0
              found = GF_TRUE;
1731
0
          }
1732
0
        }
1733
0
        if (found) {
1734
0
          ret = JS_NewString(c, *(char**)att->data );
1735
0
          goto exit;
1736
0
        }
1737
0
      }
1738
0
      att = (GF_DOMFullAttribute *) att->next;
1739
0
    }
1740
0
  }
1741
0
  else if (n->sgprivate->tag==TAG_DOMText) {
1742
1743
0
  } else if (n->sgprivate->tag<=GF_NODE_RANGE_LAST_SVG) {
1744
0
    GF_FieldInfo info;
1745
0
    u32 ns_code = 0;
1746
0
    if (ns) {
1747
0
      ns_code = gf_sg_get_namespace_code_from_name(n->sgprivate->scenegraph, (char *) ns);
1748
0
      if (!ns_code) ns_code = gf_crc_32(ns, (u32) strlen(ns));
1749
0
    }
1750
0
    else {
1751
0
      ns_code = gf_xml_get_element_namespace(n);
1752
0
    }
1753
1754
0
    if (gf_node_get_attribute_by_name(n, (char *) name, ns_code, GF_FALSE, GF_FALSE, &info)==GF_OK) {
1755
0
      char *szAtt = gf_svg_dump_attribute(n, &info);
1756
0
      ret = JS_NewString(c, szAtt);
1757
0
      if (szAtt) gf_free(szAtt);
1758
0
      goto exit;
1759
0
    }
1760
0
  }
1761
0
  ret = JS_NewString(c, "");
1762
0
exit:
1763
0
  JS_FreeCString(c, name);
1764
0
  JS_FreeCString(c, ns);
1765
0
  return ret;
1766
0
}
1767
1768
static JSValue xml_element_has_attribute(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1769
0
{
1770
0
  const char *name;
1771
0
  const char *ns;
1772
0
  JSValue ret = JS_NULL;
1773
1774
0
  GF_Node *n = dom_get_node(obj);
1775
0
  if (!n) return GF_JS_EXCEPTION(c);
1776
1777
0
  if (!argc || !JS_CHECK_STRING(argv[0])) return GF_JS_EXCEPTION(c);
1778
0
  ns = NULL;
1779
  /*NS version*/
1780
0
  if (argc==2) {
1781
0
    if (!JS_CHECK_STRING(argv[1])) return GF_JS_EXCEPTION(c);
1782
0
    ns = JS_ToCString(c, argv[0]);
1783
0
    name = JS_ToCString(c, argv[1]);
1784
0
  } else {
1785
0
    name = JS_ToCString(c, argv[0]);
1786
0
  }
1787
0
  if (!name) goto exit;
1788
1789
0
  if (n->sgprivate->tag==TAG_DOMFullNode) {
1790
0
    GF_DOMFullNode *node = (GF_DOMFullNode*)n;
1791
0
    GF_DOMFullAttribute *att = (GF_DOMFullAttribute*)node->attributes;
1792
0
    while (att) {
1793
0
      if ((att->tag==TAG_DOM_ATT_any) && !strcmp(att->name, name)) {
1794
0
        ret = JS_TRUE;
1795
0
        goto exit;
1796
0
      }
1797
0
      att = (GF_DOMFullAttribute *) att->next;
1798
0
    }
1799
0
  }
1800
0
  else if (n->sgprivate->tag==TAG_DOMText) {
1801
0
  }
1802
0
  else if (n->sgprivate->tag<=GF_NODE_RANGE_LAST_SVG) {
1803
0
    GF_FieldInfo info;
1804
0
    u32 ns_code = 0;
1805
0
    if (ns) ns_code = gf_sg_get_namespace_code_from_name(n->sgprivate->scenegraph, (char *) ns);
1806
0
    else ns_code = gf_sg_get_namespace_code(n->sgprivate->scenegraph, NULL);
1807
1808
0
    if (gf_node_get_attribute_by_name(n, (char *) name, ns_code, GF_FALSE, GF_FALSE, &info)==GF_OK) {
1809
0
      ret = JS_TRUE;
1810
0
      goto exit;
1811
0
    }
1812
0
  }
1813
0
  ret = JS_FALSE;
1814
0
exit:
1815
0
  JS_FreeCString(c, name);
1816
0
  JS_FreeCString(c, ns);
1817
0
  return ret;
1818
0
}
1819
1820
1821
1822
static JSValue xml_element_remove_attribute(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1823
0
{
1824
0
  u32 tag;
1825
0
  GF_DOMFullNode *node;
1826
0
  GF_DOMFullAttribute *prev, *att;
1827
0
  const char *name, *ns;
1828
1829
0
  GF_Node *n = dom_get_node(obj);
1830
0
  if (!n) return GF_JS_EXCEPTION(c);
1831
1832
0
  if (!argc || !JS_CHECK_STRING(argv[0])) return GF_JS_EXCEPTION(c);
1833
0
  ns = NULL;
1834
  /*NS version*/
1835
0
  if (argc==2) {
1836
0
    if (!JS_CHECK_STRING(argv[1])) return GF_JS_EXCEPTION(c);
1837
0
    ns = JS_ToCString(c, argv[0]);
1838
0
    name = JS_ToCString(c, argv[1]);
1839
0
  } else {
1840
0
    name = JS_ToCString(c, argv[0]);
1841
0
  }
1842
0
  if (!name) goto exit;
1843
1844
0
  tag = TAG_DOM_ATT_any;
1845
0
  node = (GF_DOMFullNode*)n;
1846
0
  prev = NULL;
1847
0
  att = (GF_DOMFullAttribute*)node->attributes;
1848
1849
0
  if (n->sgprivate->tag==TAG_DOMFullNode) tag = TAG_DOM_ATT_any;
1850
0
  else if (n->sgprivate->tag==TAG_DOMText) {
1851
0
    goto exit;
1852
0
  } else if (n->sgprivate->tag<=GF_NODE_RANGE_LAST_SVG) {
1853
0
    u32 ns_code = 0;
1854
0
    if (ns) ns_code = gf_sg_get_namespace_code_from_name(n->sgprivate->scenegraph, (char *) ns);
1855
0
    else if (!strchr(name, ':'))
1856
0
      ns_code = gf_sg_get_namespace_code(n->sgprivate->scenegraph, NULL);
1857
1858
0
    tag = gf_xml_get_attribute_tag(n, (char *)name, ns_code);
1859
0
  }
1860
1861
0
  while (att) {
1862
0
    if (att->tag==TAG_DOM_ATT_any) {
1863
0
      if (!strcmp(att->name, name)) {
1864
0
        DOM_String *s;
1865
0
        if (prev) prev->next = att->next;
1866
0
        else node->attributes = att->next;
1867
0
        s = att->data;
1868
0
        if (*s) gf_free(*s);
1869
0
        gf_free(s);
1870
0
        gf_free(att->name);
1871
0
        gf_free(att);
1872
0
        dom_node_changed(n, GF_FALSE, NULL);
1873
0
        goto exit;
1874
0
      }
1875
0
    } else if (tag==att->tag) {
1876
0
      if (prev) prev->next = att->next;
1877
0
      else node->attributes = att->next;
1878
0
      gf_svg_delete_attribute_value(att->data_type, att->data, n->sgprivate->scenegraph);
1879
0
      gf_free(att);
1880
0
      dom_node_changed(n, GF_FALSE, NULL);
1881
0
      goto exit;
1882
0
    }
1883
0
    prev = att;
1884
0
    att = (GF_DOMFullAttribute *) att->next;
1885
0
  }
1886
0
exit:
1887
0
  JS_FreeCString(c, name);
1888
0
  JS_FreeCString(c, ns);
1889
0
  return JS_TRUE;
1890
0
}
1891
1892
static void gf_dom_add_handler_listener(GF_Node *n, u32 evtType, char *handlerCode)
1893
0
{
1894
  /*check if we're modifying an existing listener*/
1895
0
  SVG_handlerElement *handler;
1896
0
  u32 i, count = gf_dom_listener_count(n);
1897
0
  for (i=0; i<count; i++) {
1898
0
    GF_FieldInfo info;
1899
0
    GF_DOMText *text;
1900
0
    GF_Node *listen = gf_dom_listener_get(n, i);
1901
1902
0
    gf_node_get_attribute_by_tag(listen, TAG_XMLEV_ATT_event, GF_FALSE, GF_FALSE, &info);
1903
0
    if (!info.far_ptr || (((XMLEV_Event*)info.far_ptr)->type != evtType)) continue;
1904
1905
    /* found a listener for this event, override the handler
1906
    TODO: FIX this, there may be a listener/handler already set with JS, why overriding ? */
1907
0
    gf_node_get_attribute_by_tag(listen, TAG_XMLEV_ATT_handler, GF_FALSE, GF_FALSE, &info);
1908
0
    if (!info.far_ptr) {
1909
0
      gf_assert(0);
1910
0
      return;
1911
0
    }
1912
0
    handler = (SVG_handlerElement *) ((XMLRI*)info.far_ptr)->target;
1913
0
    text = (GF_DOMText*)handler->children->node;
1914
0
    if (text->sgprivate->tag==TAG_DOMText) {
1915
0
      if (text->textContent) gf_free(text->textContent);
1916
0
      text->textContent = gf_strdup(handlerCode);
1917
0
    }
1918
0
    return;
1919
0
  }
1920
  /*nope, create a listener*/
1921
0
  handler = gf_dom_listener_build(n, evtType, 0);
1922
0
  gf_dom_add_text_node((GF_Node*)handler, gf_strdup(handlerCode));
1923
0
  return;
1924
0
}
1925
1926
static void gf_dom_full_set_attribute(GF_DOMFullNode *node, char *attribute_name, char *attribute_content)
1927
0
{
1928
0
  GF_DOMFullAttribute *prev = NULL;
1929
0
  GF_DOMFullAttribute *att = (GF_DOMFullAttribute*)node->attributes;
1930
0
  while (att) {
1931
0
    if ((att->tag==TAG_DOM_ATT_any) && !strcmp(att->name, attribute_name)) {
1932
0
      if ((att->data_type == DOM_String_datatype) && att->data) {
1933
0
        DOM_String *s;
1934
0
        s = (DOM_String *) att->data;
1935
0
        if ( *s ) gf_free( *s);
1936
0
        *s = gf_strdup(attribute_content);
1937
0
        dom_node_changed((GF_Node *)node, GF_FALSE, NULL);
1938
0
      } else {
1939
0
        gf_assert(0);
1940
0
      }
1941
0
      return;
1942
0
    }
1943
0
    prev = att;
1944
0
    att = (GF_DOMFullAttribute *) att->next;
1945
0
  }
1946
  /*create new att*/
1947
0
  GF_SAFEALLOC(att, GF_DOMFullAttribute);
1948
0
  if (!att) {
1949
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[DOMJS] Failed to allocate DOM attribute\n"));
1950
0
    return;
1951
0
  }
1952
0
  att->tag = TAG_DOM_ATT_any;
1953
0
  att->name = gf_strdup(attribute_name);
1954
0
  att->data_type = (u16) DOM_String_datatype;
1955
0
  att->data = gf_svg_create_attribute_value(att->data_type);
1956
0
  *((char **)att->data) = gf_strdup(attribute_content);
1957
1958
0
  if (prev) prev->next = (GF_DOMAttribute*) att;
1959
0
  else node->attributes = (GF_DOMAttribute*) att;
1960
0
  return;
1961
0
}
1962
1963
void gf_svg_set_attributeNS(GF_Node *n, u32 ns_code, char *name, char *val)
1964
0
{
1965
0
  GF_FieldInfo info;
1966
0
  u32 anim_value_type = 0;
1967
1968
0
  if (!strcmp(name, "attributeName")) {
1969
0
    if (gf_node_get_attribute_by_tag(n, TAG_SVG_ATT_attributeName, GF_FALSE, GF_FALSE, &info) == GF_OK) {
1970
0
      SMIL_AttributeName *attname = (SMIL_AttributeName *)info.far_ptr;
1971
1972
      /*parse the attribute name even if the target is not found, because a namespace could be specified and
1973
      only valid for the current node*/
1974
0
      if (!attname->type) {
1975
0
        char *sep;
1976
0
        char *a_name = attname->name;
1977
0
        sep = strchr(a_name, ':');
1978
0
        if (sep) {
1979
0
          sep[0] = 0;
1980
0
          attname->type = gf_sg_get_namespace_code(n->sgprivate->scenegraph, a_name);
1981
0
          sep[0] = ':';
1982
0
          a_name = gf_strdup(sep+1);
1983
0
          gf_free(attname->name);
1984
0
          attname->name = a_name;
1985
0
        }
1986
0
      }
1987
0
    }
1988
0
  }
1989
1990
0
  if ((n->sgprivate->tag == TAG_SVG_animateTransform) && (strstr(name, "from") || strstr(name, "to")) ) {
1991
0
    if (gf_node_get_attribute_by_tag((GF_Node *)n, TAG_SVG_ATT_transform_type, GF_TRUE, GF_FALSE, &info) != GF_OK) {
1992
0
      GF_LOG(GF_LOG_WARNING, GF_LOG_SCRIPT, ("Cannot retrieve attribute 'type' from animateTransform\n"));
1993
0
      return;
1994
0
    }
1995
1996
0
    switch(*(SVG_TransformType *) info.far_ptr) {
1997
0
    case SVG_TRANSFORM_TRANSLATE:
1998
0
      anim_value_type = SVG_Transform_Translate_datatype;
1999
0
      break;
2000
0
    case SVG_TRANSFORM_SCALE:
2001
0
      anim_value_type = SVG_Transform_Scale_datatype;
2002
0
      break;
2003
0
    case SVG_TRANSFORM_ROTATE:
2004
0
      anim_value_type = SVG_Transform_Rotate_datatype;
2005
0
      break;
2006
0
    case SVG_TRANSFORM_SKEWX:
2007
0
      anim_value_type = SVG_Transform_SkewX_datatype;
2008
0
      break;
2009
0
    case SVG_TRANSFORM_SKEWY:
2010
0
      anim_value_type = SVG_Transform_SkewY_datatype;
2011
0
      break;
2012
0
    case SVG_TRANSFORM_MATRIX:
2013
0
      anim_value_type = SVG_Transform_datatype;
2014
0
      break;
2015
0
    default:
2016
0
      return;
2017
0
    }
2018
0
  }
2019
2020
0
  if (gf_node_get_attribute_by_name(n, name, ns_code,  GF_TRUE, GF_TRUE, &info)==GF_OK) {
2021
0
    GF_Err e;
2022
0
    if (!strcmp(name, "from") || !strcmp(name, "to") || !strcmp(name, "values") ) {
2023
0
      GF_FieldInfo attType;
2024
0
      SMIL_AttributeName *attname;
2025
0
      if (gf_node_get_attribute_by_tag((GF_Node *)n, TAG_SVG_ATT_attributeName, GF_FALSE, GF_FALSE, &attType) != GF_OK) {
2026
0
        GF_LOG(GF_LOG_WARNING, GF_LOG_SCRIPT, ("Cannot retrieve attribute 'attributeName'\n"));
2027
0
        return;
2028
0
      }
2029
2030
0
      attname = (SMIL_AttributeName *)attType.far_ptr;
2031
0
      if (!attname->type && attname->name) {
2032
0
        GF_Node *anim_target = gf_smil_anim_get_target(n);
2033
0
        if (anim_target) {
2034
0
          gf_node_get_attribute_by_name((GF_Node *)anim_target, attname->name, attname->type, GF_FALSE, GF_FALSE, &attType);
2035
0
          attname->type = attType.fieldType;
2036
0
        } else {
2037
0
          GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[DOM] Cannot find target of the animation to parse attribute %s\n", attname->name));
2038
0
          return;
2039
0
        }
2040
0
      }
2041
2042
0
      anim_value_type = attname->type;
2043
0
    }
2044
0
    e = gf_svg_parse_attribute(n, &info, val, anim_value_type);
2045
0
    if (e != GF_OK) {
2046
0
      GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[DOM] Error parsing attribute\n"));
2047
0
    }
2048
2049
0
    if (info.fieldType==SVG_ID_datatype) {
2050
0
      char *idname = *(SVG_String*)info.far_ptr;
2051
0
      gf_svg_parse_element_id(n, idname, GF_FALSE);
2052
0
    }
2053
0
    if (info.fieldType==XMLRI_datatype) {
2054
0
      gf_node_dirty_set(n, GF_SG_SVG_XLINK_HREF_DIRTY, GF_FALSE);
2055
0
    }
2056
0
    dom_node_changed(n, GF_FALSE, &info);
2057
0
    return;
2058
0
  }
2059
0
}
2060
2061
void gf_svg_set_attribute(GF_Node *n, char * ns, char *name, char *val)
2062
0
{
2063
0
  u32 ns_code = 0;
2064
0
  if (ns) {
2065
0
    ns_code = gf_sg_get_namespace_code_from_name(n->sgprivate->scenegraph, ns);
2066
0
  } else {
2067
0
    ns_code = gf_xml_get_element_namespace(n);
2068
0
  }
2069
0
  gf_svg_set_attributeNS(n, ns_code, name, val);
2070
0
}
2071
2072
static JSValue xml_element_set_attribute(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
2073
0
{
2074
0
  u32 idx;
2075
0
  const char *name, *_val, *val, *ns;
2076
0
  char szVal[100];
2077
2078
0
  GF_Node *n = dom_get_node(obj);
2079
0
  if (!n) return GF_JS_EXCEPTION(c);
2080
0
  if ((argc < 2)) return GF_JS_EXCEPTION(c);
2081
2082
0
  if (!JS_CHECK_STRING(argv[0]))
2083
0
    return GF_JS_EXCEPTION(c);
2084
2085
0
  idx = 1;
2086
0
  name = _val = val = NULL;
2087
0
  ns = NULL;
2088
  /*NS version*/
2089
0
  if (argc==3) {
2090
0
    char *sep;
2091
0
    if (!JS_CHECK_STRING(argv[1]))
2092
0
      return GF_JS_EXCEPTION(c);
2093
0
    ns = JS_ToCString(c, argv[0]);
2094
0
    gf_sg_add_namespace(n->sgprivate->scenegraph, (char *) ns, NULL);
2095
0
    name = JS_ToCString(c, argv[1]);
2096
0
    idx = 2;
2097
2098
0
    sep = strchr(name, ':');
2099
0
    if (sep) name = sep+1;
2100
2101
0
  } else {
2102
0
    name = JS_ToCString(c, argv[0]);
2103
0
  }
2104
2105
0
  val = NULL;
2106
0
  if (JS_CHECK_STRING(argv[idx])) {
2107
0
    val = _val = JS_ToCString(c, argv[idx]);
2108
0
  } else if (JS_IsBool(argv[idx])) {
2109
0
    sprintf(szVal, "%s", JS_ToBool(c, argv[idx]) ? "true" : "false");
2110
0
    val = szVal;
2111
0
  } else if (JS_IsNumber(argv[idx])) {
2112
0
    Double d;
2113
0
    JS_ToFloat64(c, &d, argv[idx]);
2114
0
    sprintf(szVal, "%g", d);
2115
0
    val = szVal;
2116
0
  } else if (JS_IsInteger(argv[idx])) {
2117
0
    u32 i;
2118
0
    JS_ToInt32(c, &i, argv[idx]);
2119
0
    sprintf(szVal, "%d", i);
2120
0
    val = szVal;
2121
0
  } else {
2122
0
    goto exit;
2123
0
  }
2124
0
  if (!name || !val)
2125
0
    goto exit;
2126
2127
2128
  /* For on* attribute (e.g. onclick), we create a couple listener/handler elements on setting the attribute */
2129
0
  if ((name[0]=='o') && (name[1]=='n')) {
2130
0
    u32 evtType = gf_dom_event_type_by_name(name + 2);
2131
0
    if (evtType != GF_EVENT_UNKNOWN) {
2132
0
      gf_dom_add_handler_listener(n, evtType, (char *) val);
2133
0
      goto exit;
2134
0
    }
2135
0
  }
2136
2137
0
  if (n->sgprivate->tag==TAG_DOMFullNode) {
2138
0
    gf_dom_full_set_attribute((GF_DOMFullNode*)n, (char *) name, (char *) val);
2139
0
    goto exit;
2140
0
  }
2141
2142
0
  if (n->sgprivate->tag==TAG_DOMText) {
2143
0
    goto exit;
2144
0
  }
2145
2146
0
  if (n->sgprivate->tag<=GF_NODE_RANGE_LAST_SVG) {
2147
0
    gf_svg_set_attribute(n, (char *) ns, (char *) name, (char *) val);
2148
0
  }
2149
0
exit:
2150
0
  JS_FreeCString(c, name);
2151
0
  JS_FreeCString(c, ns);
2152
0
  JS_FreeCString(c, _val);
2153
0
  return JS_TRUE;
2154
0
}
2155
2156
2157
static JSValue xml_element_elements_by_tag(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
2158
0
{
2159
0
  DOMNodeList *nl;
2160
0
  JSValue new_obj;
2161
0
  const char *name;
2162
2163
0
  GF_Node *n = dom_get_node(obj);
2164
0
  if (!n) return GF_JS_EXCEPTION(c);
2165
2166
0
  if (!argc || !JS_CHECK_STRING(argv[0])) return GF_JS_EXCEPTION(c);
2167
2168
  /*NS version*/
2169
0
  if (argc==2) {
2170
0
    if (!JS_CHECK_STRING(argv[1])) return GF_JS_EXCEPTION(c);
2171
0
    name = JS_ToCString(c, argv[1]);
2172
0
  } else {
2173
0
    name = JS_ToCString(c, argv[0]);
2174
0
  }
2175
0
  GF_SAFEALLOC(nl, DOMNodeList);
2176
0
  if (!nl) return GF_JS_EXCEPTION(c);
2177
2178
0
  if (name && !strcmp(name, "*")) {
2179
0
    JS_FreeCString(c, name);
2180
0
    name = NULL;
2181
0
  }
2182
0
  xml_doc_gather_nodes((GF_ParentNode*)n, (char *)name, nl);
2183
0
  new_obj = JS_NewObjectClass(c, domNodeListClass.class_id);
2184
0
  JS_SetOpaque(new_obj, nl);
2185
0
  JS_FreeCString(c, name);
2186
0
  return new_obj;
2187
0
}
2188
2189
static JSValue xml_element_set_id(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
2190
0
{
2191
0
  u32 node_id;
2192
0
  const char *name;
2193
0
  Bool is_id;
2194
2195
0
  GF_Node *n = dom_get_node(obj);
2196
0
  if (!n) return GF_JS_EXCEPTION(c);
2197
2198
0
  if ((argc<2) || !JS_CHECK_STRING(argv[0])) return GF_JS_EXCEPTION(c);
2199
2200
  /*NS version*/
2201
0
  if (argc==3) {
2202
0
    if (!JS_CHECK_STRING(argv[1])) return GF_JS_EXCEPTION(c);
2203
0
    name = JS_ToCString(c, argv[1]);
2204
0
    is_id = JS_ToBool(c, argv[2]) ? GF_TRUE : GF_FALSE;
2205
0
  } else {
2206
0
    name = JS_ToCString(c, argv[0]);
2207
0
    is_id = JS_ToBool(c, argv[1]) ? GF_TRUE : GF_FALSE;
2208
0
  }
2209
0
  gf_node_get_name_and_id(n, &node_id);
2210
0
  if (node_id && is_id) {
2211
    /*we only support ONE ID per node*/
2212
0
    JS_FreeCString(c, name);
2213
0
    return GF_JS_EXCEPTION(c);
2214
0
  }
2215
0
  if (is_id) {
2216
0
    if (!name) return GF_JS_EXCEPTION(c);
2217
0
    gf_node_set_id(n, gf_sg_get_max_node_id(n->sgprivate->scenegraph) + 1, name);
2218
0
  } else if (node_id) {
2219
0
    gf_node_remove_id(n);
2220
0
  }
2221
0
  JS_FreeCString(c, name);
2222
0
  return JS_TRUE;
2223
0
}
2224
static void gather_text(GF_ParentNode *n, char **out_str)
2225
0
{
2226
0
  if (n->sgprivate->tag==TAG_DOMText) {
2227
0
    GF_DOMText *dom_text = (GF_DOMText *)n;
2228
0
    if (dom_text->textContent) gf_dynstrcat(out_str, dom_text->textContent, NULL);
2229
0
  } else {
2230
0
    GF_ChildNodeItem *child = ((GF_ParentNode *) n)->children;
2231
0
    while (child) {
2232
0
      gather_text((GF_ParentNode *)child->node, out_str);
2233
0
      child = child->next;
2234
0
    }
2235
0
  }
2236
0
}
2237
static JSValue xml_element_to_string(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
2238
0
{
2239
0
  JSValue ret;
2240
0
  char *out_str = NULL;
2241
0
  GF_Node *n = dom_get_node(obj);
2242
0
  if (!n) return GF_JS_EXCEPTION(c);
2243
2244
0
  GF_ChildNodeItem *child;
2245
0
  child = ((GF_ParentNode *) n)->children;
2246
0
  while (child) {
2247
0
    gather_text((GF_ParentNode *)child->node, &out_str);
2248
0
    child = child->next;
2249
0
  }
2250
0
  if (!out_str) {
2251
0
    const char *node_name = gf_node_get_class_name(n);
2252
0
    if (node_name)
2253
0
      return JS_NewString(c, node_name);
2254
0
    return JS_NULL;
2255
0
  }
2256
0
  ret = JS_NewString(c, out_str);
2257
0
  gf_free(out_str);
2258
0
  return ret;
2259
0
}
2260
2261
/*dom3 character/text/comment*/
2262
static JSValue dom_text_getProperty(JSContext *c, JSValueConst obj, int magic)
2263
0
{
2264
0
  GF_DOMText *txt = (GF_DOMText*)dom_get_node(obj);
2265
0
  if (!txt || (txt->sgprivate->tag != TAG_DOMText)) return GF_JS_EXCEPTION(c);
2266
2267
0
  switch (magic) {
2268
0
  case TEXT_JSPROPERTY_DATA:
2269
0
    if (txt->textContent) return JS_NewString(c, txt->textContent);
2270
0
    else return JS_NewString(c, "");
2271
2272
0
  case TEXT_JSPROPERTY_LENGTH:
2273
0
    return JS_NewInt32(c, txt->textContent ? (u32) strlen(txt->textContent) : 0);
2274
2275
0
  case TEXT_JSPROPERTY_ISELEMENTCONTENTWHITESPACE:
2276
0
    return JS_FALSE;
2277
2278
0
  case TEXT_JSPROPERTY_WHOLETEXT:
2279
    /*FIXME - this is wrong we should serialize adjacent text strings as well*/
2280
0
    if (txt->textContent) return JS_NewString(c, txt->textContent);
2281
0
    else return JS_NewString(c, "");
2282
0
  }
2283
0
  return JS_UNDEFINED;
2284
0
}
2285
2286
static JSValue dom_text_setProperty(JSContext *c, JSValueConst obj, JSValueConst value, int magic)
2287
0
{
2288
0
  GF_DOMText *txt = (GF_DOMText*)dom_get_node(obj);
2289
0
  if (!txt || (txt->sgprivate->tag != TAG_DOMText)) return GF_JS_EXCEPTION(c);
2290
2291
0
  switch (magic) {
2292
0
  case TEXT_JSPROPERTY_DATA:
2293
0
    if (txt->textContent) gf_free(txt->textContent);
2294
0
    txt->textContent = NULL;
2295
0
    if (JS_CHECK_STRING(value)) {
2296
0
      const char *str = JS_ToCString(c, value);
2297
0
      txt->textContent = gf_strdup(str ? str : "");
2298
0
      JS_FreeCString(c, str);
2299
0
    }
2300
0
    dom_node_changed((GF_Node*)txt, GF_FALSE, NULL);
2301
0
    return JS_TRUE;
2302
    /*the rest is read-only*/
2303
0
  }
2304
0
  return JS_UNDEFINED;
2305
0
}
2306
2307
static JSValue event_stop_propagation(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
2308
0
{
2309
0
  GF_DOM_Event *evt = JS_GetOpaque(obj, domEventClass.class_id);
2310
0
  if (!evt) return GF_JS_EXCEPTION(c);
2311
0
  evt->event_phase |= GF_DOM_EVENT_PHASE_CANCEL;
2312
0
  return JS_TRUE;
2313
0
}
2314
static JSValue event_stop_immediate_propagation(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
2315
0
{
2316
0
  GF_DOM_Event *evt = JS_GetOpaque(obj, domEventClass.class_id);
2317
0
  if (!evt) return GF_JS_EXCEPTION(c);
2318
0
  evt->event_phase |= GF_DOM_EVENT_PHASE_CANCEL_ALL;
2319
0
  return JS_TRUE;
2320
0
}
2321
static JSValue event_prevent_default(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
2322
0
{
2323
0
  GF_DOM_Event *evt = JS_GetOpaque(obj, domEventClass.class_id);
2324
0
  if (!evt) return GF_JS_EXCEPTION(c);
2325
0
  evt->event_phase |= GF_DOM_EVENT_PHASE_PREVENT;
2326
0
  return JS_TRUE;
2327
0
}
2328
2329
static JSValue event_getProperty(JSContext *c, JSValueConst obj, int magic)
2330
0
{
2331
0
  GF_DOM_Event *evt = JS_GetOpaque(obj, domEventClass.class_id);
2332
0
  if (evt==NULL) return JS_TRUE;
2333
2334
0
  switch (magic) {
2335
0
  case EVENT_JSPROPERTY_TYPE:
2336
0
    return JS_NewString(c, gf_dom_event_get_name(evt->type) );
2337
2338
0
  case EVENT_JSPROPERTY_TARGET:
2339
0
    if (evt->is_vrml) return JS_TRUE;
2340
0
    switch (evt->target_type) {
2341
0
    case GF_DOM_EVENT_TARGET_NODE:
2342
0
      return dom_element_construct(c, (GF_Node*) evt->target);
2343
2344
0
    case GF_DOM_EVENT_TARGET_DOCUMENT:
2345
0
      return dom_document_construct(c, (GF_SceneGraph *) evt->target);
2346
2347
#ifdef GPAC_ENABLE_HTML5_MEDIA
2348
    case GF_DOM_EVENT_TARGET_MSE_MEDIASOURCE:
2349
      return OBJECT_TO_JSValue(((GF_HTML_MediaSource *)evt->target)->_this);
2350
    case GF_DOM_EVENT_TARGET_MSE_SOURCEBUFFER:
2351
      return OBJECT_TO_JSValue(((GF_HTML_SourceBuffer *)evt->target)->_this);
2352
    case GF_DOM_EVENT_TARGET_MSE_SOURCEBUFFERLIST:
2353
      return OBJECT_TO_JSValue(((GF_HTML_SourceBufferList *)evt->target)->_this);
2354
#endif
2355
0
    default:
2356
0
      break;
2357
0
    }
2358
0
    return JS_TRUE;
2359
0
  case EVENT_JSPROPERTY_CURRENTTARGET:
2360
0
    if (evt->is_vrml) return JS_NULL;
2361
0
    switch (evt->currentTarget->ptr_type) {
2362
0
    case GF_DOM_EVENT_TARGET_NODE:
2363
0
      return dom_element_construct(c, (GF_Node*) evt->currentTarget->ptr);
2364
0
    case GF_DOM_EVENT_TARGET_DOCUMENT:
2365
0
      return dom_document_construct(c, (GF_SceneGraph *) evt->currentTarget->ptr);
2366
#ifdef GPAC_ENABLE_HTML5_MEDIA
2367
    case GF_DOM_EVENT_TARGET_MSE_MEDIASOURCE:
2368
      return OBJECT_TO_JSValue(((GF_HTML_MediaSource *)evt->target)->_this);
2369
    case GF_DOM_EVENT_TARGET_MSE_SOURCEBUFFER:
2370
      return OBJECT_TO_JSValue(((GF_HTML_SourceBuffer *)evt->target)->_this);
2371
      break;
2372
    case GF_DOM_EVENT_TARGET_MSE_SOURCEBUFFERLIST:
2373
      return OBJECT_TO_JSValue(((GF_HTML_SourceBufferList *)evt->target)->_this);
2374
#endif
2375
0
    default:
2376
0
      break;
2377
0
    }
2378
0
    return JS_TRUE;
2379
0
  case EVENT_JSPROPERTY_EVENTPHASE:
2380
0
    return JS_NewInt32(c, (evt->event_phase & 0x3) );
2381
2382
0
  case EVENT_JSPROPERTY_BUBBLES:
2383
0
    return evt->bubbles ? JS_TRUE : JS_FALSE;
2384
2385
0
  case EVENT_JSPROPERTY_CANCELABLE:
2386
0
    return evt->cancelable ? JS_TRUE : JS_FALSE;
2387
0
  case EVENT_JSPROPERTY_NAMESPACEURI:
2388
0
    return JS_NULL;
2389
2390
0
  case EVENT_JSPROPERTY_TIMESTAMP:
2391
0
    return JS_NULL;
2392
2393
0
  case EVENT_JSPROPERTY_DEFAULTPREVENTED:
2394
0
    return (evt->event_phase & GF_DOM_EVENT_PHASE_PREVENT) ? JS_TRUE : JS_FALSE;
2395
2396
0
  case EVENT_JSPROPERTY_DETAIL:
2397
0
    return JS_NewInt32(c, evt->detail);
2398
2399
0
  case EVENT_JSPROPERTY_DATA:
2400
0
  {
2401
0
    u32 len;
2402
0
    s16 txt[2];
2403
0
    const u16 *srcp;
2404
0
    char szData[5];
2405
0
    txt[0] = evt->detail;
2406
0
    txt[1] = 0;
2407
0
    srcp = (const u16 *) txt;
2408
0
    len = gf_utf8_wcstombs(szData, 5, &srcp);
2409
0
    if (len == GF_UTF8_FAIL) len = 0;
2410
0
    szData[len] = 0;
2411
0
    return JS_NewString(c, szData);
2412
0
  }
2413
2414
0
  case EVENT_JSPROPERTY_SCREENX:
2415
0
    return JS_NewInt32(c, evt->screenX);
2416
0
  case EVENT_JSPROPERTY_SCREENY:
2417
0
    return JS_NewInt32(c, evt->screenY);
2418
0
  case EVENT_JSPROPERTY_CLIENTX:
2419
0
    return JS_NewInt32(c, evt->clientX);
2420
0
  case EVENT_JSPROPERTY_CLIENTY:
2421
0
    return JS_NewInt32(c, evt->clientY);
2422
0
  case EVENT_JSPROPERTY_BUTTON:
2423
0
    return JS_NewInt32(c, evt->button);
2424
0
  case EVENT_JSPROPERTY_RELATEDTARGET:
2425
0
    if (evt->is_vrml) return JS_NULL;
2426
0
    return dom_element_construct(c, evt->relatedTarget);
2427
0
  case EVENT_JSPROPERTY_WHEELDELTA:
2428
0
    return JS_NewInt32(c, FIX2INT(evt->new_scale) );
2429
0
  case EVENT_JSPROPERTY_KEYIDENTIFIER:
2430
0
    return JS_NewString(c, gf_dom_get_key_name(evt->detail) );
2431
  /*Mozilla keyChar, charCode: wrap up to same value*/
2432
0
  case EVENT_JSPROPERTY_KEYCHAR:
2433
0
  case EVENT_JSPROPERTY_CHARCODE:
2434
0
    return JS_NewInt32(c, evt->detail);
2435
0
  case EVENT_JSPROPERTY_LOADED:
2436
0
    return JS_NewInt64(c, evt->media_event.loaded_size);
2437
0
  case EVENT_JSPROPERTY_TOTAL:
2438
0
    return JS_NewInt64(c, evt->media_event.total_size);
2439
0
  case EVENT_JSPROPERTY_BUFFER_ON:
2440
0
    return evt->media_event.bufferValid ? JS_TRUE : JS_FALSE;
2441
0
  case EVENT_JSPROPERTY_BUFFERLEVEL:
2442
0
    return JS_NewInt32(c, evt->media_event.level);
2443
0
  case EVENT_JSPROPERTY_BUFFERREMAININGTIME:
2444
0
    return JS_NewFloat64(c, evt->media_event.remaining_time);
2445
0
  case EVENT_JSPROPERTY_STATUS:
2446
0
    return JS_NewInt32(c, evt->media_event.status);
2447
2448
  /*VRML ones*/
2449
0
  case EVENT_JSPROPERTY_WIDTH:
2450
0
    return JS_NewFloat64(c, FIX2FLT(evt->screen_rect.width) );
2451
0
  case EVENT_JSPROPERTY_HEIGHT:
2452
0
    return JS_NewFloat64(c, FIX2FLT(evt->screen_rect.height) );
2453
0
  case EVENT_JSPROPERTY_OFFSETX:
2454
0
    return JS_NewFloat64(c, FIX2FLT(evt->screen_rect.x) );
2455
0
  case EVENT_JSPROPERTY_OFFSETY:
2456
0
    return JS_NewFloat64(c, FIX2FLT(evt->screen_rect.y) );
2457
0
  case EVENT_JSPROPERTY_VPWIDTH:
2458
0
    return JS_NewFloat64(c, FIX2FLT(evt->prev_translate.x) );
2459
0
  case EVENT_JSPROPERTY_VPHEIGHT:
2460
0
    return JS_NewFloat64(c, FIX2FLT(evt->prev_translate.y) );
2461
0
  case EVENT_JSPROPERTY_TRANSLATIONX:
2462
0
    return JS_NewFloat64(c, FIX2FLT(evt->new_translate.x) );
2463
0
  case EVENT_JSPROPERTY_TRANSLATIONY:
2464
0
    return JS_NewFloat64(c, FIX2FLT(evt->new_translate.y) );
2465
0
  case EVENT_JSPROPERTY_TYPE3D:
2466
0
    return JS_NewInt32(c, evt->detail);
2467
0
  case EVENT_JSPROPERTY_ERROR:
2468
0
    return JS_NewInt32(c, evt->error_state);
2469
0
  case EVENT_JSPROPERTY_DYNAMIC_SCENE:
2470
0
    return JS_NewInt32(c, evt->key_flags);
2471
0
  case EVENT_JSPROPERTY_URL:
2472
0
    return JS_NewString(c, evt->addon_url ? evt->addon_url : "");
2473
2474
0
  default:
2475
0
    break;
2476
0
  }
2477
0
  return JS_UNDEFINED;
2478
0
}
2479
2480
2481
#define SETUP_JSCLASS(_class, _name, _proto_funcs, _construct, _finalize, _proto_class_id) \
2482
0
  if (! _class.class_id) {\
2483
0
    JS_NewClassID(&(_class.class_id)); \
2484
0
    _class.class.class_name = _name; \
2485
0
    _class.class.finalizer = _finalize;\
2486
0
    JS_NewClass(jsrt, _class.class_id, &(_class.class));\
2487
0
  }\
2488
0
  proto = JS_NewObjectClass(c, _proto_class_id ? _proto_class_id : _class.class_id);\
2489
0
      JS_SetPropertyFunctionList(c, proto, _proto_funcs, countof(_proto_funcs));\
2490
0
      JS_SetClassProto(c, _class.class_id, proto);\
2491
0
      if (_construct) {\
2492
0
    JSValue ctor = JS_NewCFunction2(c, _construct, _name, 1, JS_CFUNC_constructor, 0);\
2493
0
        JS_SetPropertyStr(c, global, _name, ctor);\
2494
0
  }\
2495
2496
2497
/*SVGMatrix class*/
2498
static const JSCFunctionListEntry node_Funcs[] =
2499
{
2500
  JS_CGETSET_MAGIC_DEF("nodeName", dom_node_getProperty, NULL, NODE_JSPROPERTY_NODENAME),
2501
  JS_CGETSET_MAGIC_DEF("nodeValue", dom_node_getProperty, dom_node_setProperty, NODE_JSPROPERTY_NODEVALUE),
2502
2503
  JS_CGETSET_MAGIC_DEF("nodeType", dom_node_getProperty, NULL, NODE_JSPROPERTY_NODETYPE),
2504
  JS_CGETSET_MAGIC_DEF("parentNode", dom_node_getProperty, NULL, NODE_JSPROPERTY_PARENTNODE),
2505
  JS_CGETSET_MAGIC_DEF("childNodes", dom_node_getProperty, NULL, NODE_JSPROPERTY_CHILDNODES),
2506
  JS_CGETSET_MAGIC_DEF("firstChild", dom_node_getProperty, NULL, NODE_JSPROPERTY_FIRSTCHILD),
2507
  JS_CGETSET_MAGIC_DEF("lastChild", dom_node_getProperty, NULL, NODE_JSPROPERTY_LASTCHILD),
2508
  JS_CGETSET_MAGIC_DEF("previousSibling", dom_node_getProperty, NULL, NODE_JSPROPERTY_PREVIOUSSIBLING),
2509
  JS_CGETSET_MAGIC_DEF("nextSibling", dom_node_getProperty, NULL, NODE_JSPROPERTY_NEXTSIBLING),
2510
  JS_CGETSET_MAGIC_DEF("attributes", dom_node_getProperty, NULL, NODE_JSPROPERTY_ATTRIBUTES),
2511
  JS_CGETSET_MAGIC_DEF("ownerDocument", dom_node_getProperty, NULL, NODE_JSPROPERTY_OWNERDOCUMENT),
2512
  JS_CGETSET_MAGIC_DEF("namespaceURI", dom_node_getProperty, NULL, NODE_JSPROPERTY_NAMESPACEURI),
2513
  JS_CGETSET_MAGIC_DEF("prefix", dom_node_getProperty, dom_node_setProperty, NODE_JSPROPERTY_PREFIX),
2514
  JS_CGETSET_MAGIC_DEF("localName", dom_node_getProperty, NULL, NODE_JSPROPERTY_LOCALNAME),
2515
  JS_CGETSET_MAGIC_DEF("baseURI", dom_node_getProperty, NULL, NODE_JSPROPERTY_BASEURI),
2516
  JS_CGETSET_MAGIC_DEF("textContent", dom_node_getProperty, dom_node_setProperty, NODE_JSPROPERTY_TEXTCONTENT),
2517
  /*elementTraversal interface*/
2518
  JS_CGETSET_MAGIC_DEF("firstElementChild", dom_node_getProperty, NULL, NODE_JSPROPERTY_FIRSTELEMENTCHILD),
2519
  JS_CGETSET_MAGIC_DEF("lastElementChild", dom_node_getProperty, NULL, NODE_JSPROPERTY_LASTELEMENTCHILD),
2520
  JS_CGETSET_MAGIC_DEF("previousElementSibling", dom_node_getProperty, NULL, NODE_JSPROPERTY_PREVIOUSELEMENTSIBLING),
2521
  JS_CGETSET_MAGIC_DEF("nextElementSibling", dom_node_getProperty, NULL, NODE_JSPROPERTY_NEXTELEMENTSIBLING),
2522
2523
  JS_CFUNC_DEF("insertBefore", 2, xml_node_insert_before),
2524
  JS_CFUNC_DEF("replaceChild", 2, xml_node_replace_child),
2525
  JS_CFUNC_DEF("removeChild", 1, xml_node_remove_child),
2526
  JS_CFUNC_DEF("appendChild", 1, xml_node_append_child),
2527
  JS_CFUNC_DEF("hasChildNodes", 0, xml_node_has_children),
2528
  JS_CFUNC_DEF("cloneNode", 1, xml_clone_node),
2529
  JS_CFUNC_DEF("normalize", 0, xml_dom3_not_implemented),
2530
  JS_CFUNC_DEF("isSupported", 2, xml_dom3_not_implemented),
2531
  JS_CFUNC_DEF("hasAttributes", 0, xml_node_has_attributes),
2532
  JS_CFUNC_DEF("compareDocumentPosition", 1, xml_dom3_not_implemented),
2533
  JS_CFUNC_DEF("isSameNode", 1, xml_node_is_same_node),
2534
  JS_CFUNC_DEF("lookupPrefix", 1, xml_dom3_not_implemented),
2535
  JS_CFUNC_DEF("isDefaultNamespace", 1, xml_dom3_not_implemented),
2536
  JS_CFUNC_DEF("lookupNamespaceURI", 1, xml_dom3_not_implemented),
2537
  /*we don't support full node compare*/
2538
  JS_CFUNC_DEF("isEqualNode", 1, xml_node_is_same_node),
2539
  JS_CFUNC_DEF("getFeature", 2, xml_dom3_not_implemented),
2540
  JS_CFUNC_DEF("setUserData", 3, xml_dom3_not_implemented),
2541
  JS_CFUNC_DEF("getUserData", 1, xml_dom3_not_implemented),
2542
};
2543
2544
2545
static const JSCFunctionListEntry document_Funcs[] =
2546
{
2547
  JS_CGETSET_MAGIC_DEF("doctype", dom_document_getProperty, NULL, DOCUMENT_JSPROPERTY_DOCTYPE),
2548
  JS_CGETSET_MAGIC_DEF("implementation", dom_document_getProperty, NULL, DOCUMENT_JSPROPERTY_IMPLEMENTATION),
2549
  JS_CGETSET_MAGIC_DEF("documentElement", dom_document_getProperty, NULL, DOCUMENT_JSPROPERTY_DOCUMENTELEMENT),
2550
  JS_CGETSET_MAGIC_DEF("inputEncoding", dom_document_getProperty, NULL, DOCUMENT_JSPROPERTY_INPUTENCODING),
2551
  JS_CGETSET_MAGIC_DEF("xmlEncoding", dom_document_getProperty, NULL, DOCUMENT_JSPROPERTY_XMLENCODING),
2552
  JS_CGETSET_MAGIC_DEF("xmlStandalone", dom_document_getProperty, dom_document_setProperty, DOCUMENT_JSPROPERTY_XMLSTANDALONE),
2553
  JS_CGETSET_MAGIC_DEF("xmlVersion", dom_document_getProperty, dom_document_setProperty, DOCUMENT_JSPROPERTY_XMLVERSION),
2554
  JS_CGETSET_MAGIC_DEF("strictErrorChecking", dom_document_getProperty, dom_document_setProperty, DOCUMENT_JSPROPERTY_STRICTERRORCHECKING),
2555
  JS_CGETSET_MAGIC_DEF("documentURI", dom_document_getProperty, dom_document_setProperty, DOCUMENT_JSPROPERTY_DOCUMENTURI),
2556
  JS_CGETSET_MAGIC_DEF("location", dom_document_getProperty, dom_document_setProperty, DOCUMENT_JSPROPERTY_LOCATION),
2557
  JS_CGETSET_MAGIC_DEF("domConfig", dom_document_getProperty, dom_document_setProperty, DOCUMENT_JSPROPERTY_DOMCONFIG),
2558
  JS_CGETSET_MAGIC_DEF("global", dom_document_getProperty, dom_document_setProperty, DOCUMENT_JSPROPERTY_GLOBAL),
2559
2560
  JS_CFUNC_DEF("createElement", 1, xml_document_create_element),
2561
  JS_CFUNC_DEF("createDocumentFragment", 0, xml_dom3_not_implemented),
2562
  JS_CFUNC_DEF("createTextNode", 1, xml_document_create_text),
2563
  JS_CFUNC_DEF("createComment", 1, xml_dom3_not_implemented),
2564
  JS_CFUNC_DEF("createCDATASection", 1, xml_dom3_not_implemented),
2565
  JS_CFUNC_DEF("createProcessingInstruction", 2, xml_dom3_not_implemented),
2566
  JS_CFUNC_DEF("createAttribute", 1, xml_dom3_not_implemented),
2567
  JS_CFUNC_DEF("createEntityReference", 1, xml_dom3_not_implemented),
2568
  JS_CFUNC_DEF("getElementsByTagName", 1, xml_document_elements_by_tag),
2569
  JS_CFUNC_DEF("importNode", 2, xml_dom3_not_implemented),
2570
  JS_CFUNC_DEF("createElementNS", 2, xml_document_create_element),
2571
  JS_CFUNC_DEF("createAttributeNS", 2, xml_dom3_not_implemented),
2572
  JS_CFUNC_DEF("getElementsByTagNameNS", 2, xml_document_elements_by_tag),
2573
  JS_CFUNC_DEF("getElementById", 1, xml_document_element_by_id),
2574
  JS_CFUNC_DEF("adoptNode", 1, xml_dom3_not_implemented),
2575
  JS_CFUNC_DEF("normalizeDocument", 0, xml_dom3_not_implemented),
2576
  JS_CFUNC_DEF("renameNode", 3, xml_dom3_not_implemented),
2577
  /*eventTarget interface*/
2578
  JS_DOM3_EVENT_TARGET_INTERFACE
2579
  /*DocumentEvent interface*/
2580
  JS_CFUNC_DEF("createEvent", 1, xml_dom3_not_implemented),
2581
};
2582
2583
static const JSCFunctionListEntry element_Funcs[] =
2584
{
2585
  JS_CGETSET_MAGIC_DEF("tagName", dom_element_getProperty, NULL, ELEMENT_JSPROPERTY_TAGNAME),
2586
  JS_CGETSET_MAGIC_DEF("schemaTypeInfo", dom_element_getProperty, NULL, ELEMENT_JSPROPERTY_SCHEMATYPEINFO),
2587
2588
  JS_CFUNC_DEF("getAttribute", 1, xml_element_get_attribute),
2589
  JS_CFUNC_DEF("setAttribute", 2, xml_element_set_attribute),
2590
  JS_CFUNC_DEF("removeAttribute", 1, xml_element_remove_attribute),
2591
  JS_CFUNC_DEF("getAttributeNS", 2, xml_element_get_attribute),
2592
  JS_CFUNC_DEF("setAttributeNS", 3, xml_element_set_attribute),
2593
  JS_CFUNC_DEF("removeAttributeNS", 2, xml_element_remove_attribute),
2594
  JS_CFUNC_DEF("hasAttribute", 1, xml_element_has_attribute),
2595
  JS_CFUNC_DEF("hasAttributeNS", 2, xml_element_has_attribute),
2596
  JS_CFUNC_DEF("getElementsByTagName", 1, xml_element_elements_by_tag),
2597
  JS_CFUNC_DEF("getElementsByTagNameNS", 2, xml_element_elements_by_tag),
2598
  JS_CFUNC_DEF("setIdAttribute", 2, xml_element_set_id),
2599
  JS_CFUNC_DEF("setIdAttributeNS", 3, xml_element_set_id),
2600
  JS_CFUNC_DEF("toString", 0, xml_element_to_string),
2601
  JS_CFUNC_DEF("getAttributeNode", 1, xml_dom3_not_implemented),
2602
  JS_CFUNC_DEF("setAttributeNode", 1, xml_dom3_not_implemented),
2603
  JS_CFUNC_DEF("removeAttributeNode", 1, xml_dom3_not_implemented),
2604
  JS_CFUNC_DEF("getAttributeNodeNS", 2, xml_dom3_not_implemented),
2605
  JS_CFUNC_DEF("setAttributeNodeNS", 1, xml_dom3_not_implemented),
2606
  JS_CFUNC_DEF("setIdAttributeNode", 2, xml_dom3_not_implemented),
2607
  /*eventTarget interface*/
2608
  JS_DOM3_EVENT_TARGET_INTERFACE
2609
};
2610
2611
static const JSCFunctionListEntry text_Funcs[] =
2612
{
2613
  JS_CGETSET_MAGIC_DEF("data", dom_text_getProperty, dom_text_setProperty, TEXT_JSPROPERTY_DATA),
2614
  JS_CGETSET_MAGIC_DEF("length", dom_text_getProperty, NULL, TEXT_JSPROPERTY_LENGTH),
2615
  JS_CGETSET_MAGIC_DEF("isElementContentWhitespace", dom_text_getProperty, NULL, TEXT_JSPROPERTY_ISELEMENTCONTENTWHITESPACE),
2616
  JS_CGETSET_MAGIC_DEF("wholeText", dom_text_getProperty, NULL, TEXT_JSPROPERTY_WHOLETEXT),
2617
#if 0
2618
  JS_CFUNC_DEF("substringData", 2, xml_dom3_not_implemented),
2619
  JS_CFUNC_DEF("appendData", 1, xml_dom3_not_implemented),
2620
  JS_CFUNC_DEF("insertData", 2, xml_dom3_not_implemented),
2621
  JS_CFUNC_DEF("deleteData", 2, xml_dom3_not_implemented),
2622
  JS_CFUNC_DEF("replaceData", 3, xml_dom3_not_implemented),
2623
  JS_CFUNC_DEF("splitText", 1, xml_dom3_not_implemented),
2624
  JS_CFUNC_DEF("replaceWholeText", 1, xml_dom3_not_implemented),
2625
#endif
2626
};
2627
2628
static const JSCFunctionListEntry event_Funcs[] =
2629
{
2630
  JS_CGETSET_MAGIC_DEF("type", event_getProperty, NULL, EVENT_JSPROPERTY_TYPE),
2631
  JS_CGETSET_MAGIC_DEF("target", event_getProperty, NULL, EVENT_JSPROPERTY_TARGET),
2632
  JS_CGETSET_MAGIC_DEF("currentTarget", event_getProperty, NULL, EVENT_JSPROPERTY_CURRENTTARGET),
2633
  JS_CGETSET_MAGIC_DEF("eventPhase", event_getProperty, NULL, EVENT_JSPROPERTY_EVENTPHASE),
2634
  JS_CGETSET_MAGIC_DEF("bubbles", event_getProperty, NULL, EVENT_JSPROPERTY_BUBBLES),
2635
  JS_CGETSET_MAGIC_DEF("cancelable", event_getProperty, NULL, EVENT_JSPROPERTY_CANCELABLE),
2636
  JS_CGETSET_MAGIC_DEF("timeStamp", event_getProperty, NULL, EVENT_JSPROPERTY_TIMESTAMP),
2637
  JS_CGETSET_MAGIC_DEF("namespaceURI", event_getProperty, NULL, EVENT_JSPROPERTY_NAMESPACEURI),
2638
  JS_CGETSET_MAGIC_DEF("defaultPrevented", event_getProperty, NULL, EVENT_JSPROPERTY_DEFAULTPREVENTED),
2639
  /*UIEvent*/
2640
  JS_CGETSET_MAGIC_DEF("detail", event_getProperty, NULL, EVENT_JSPROPERTY_DETAIL),
2641
  /*text, connectionEvent*/
2642
  JS_CGETSET_MAGIC_DEF("data", event_getProperty, NULL, EVENT_JSPROPERTY_DATA),
2643
  /*MouseEvent*/
2644
  JS_CGETSET_MAGIC_DEF("screenX", event_getProperty, NULL, EVENT_JSPROPERTY_SCREENX),
2645
  JS_CGETSET_MAGIC_DEF("screenY", event_getProperty, NULL, EVENT_JSPROPERTY_SCREENY),
2646
  JS_CGETSET_MAGIC_DEF("clientX", event_getProperty, NULL, EVENT_JSPROPERTY_CLIENTX),
2647
  JS_CGETSET_MAGIC_DEF("clientY", event_getProperty, NULL, EVENT_JSPROPERTY_CLIENTY),
2648
  JS_CGETSET_MAGIC_DEF("button", event_getProperty, NULL, EVENT_JSPROPERTY_BUTTON),
2649
  JS_CGETSET_MAGIC_DEF("relatedTarget", event_getProperty, NULL, EVENT_JSPROPERTY_RELATEDTARGET),
2650
  /*wheelEvent*/
2651
  JS_CGETSET_MAGIC_DEF("wheelDelta", event_getProperty, NULL, EVENT_JSPROPERTY_WHEELDELTA),
2652
  /*keyboard*/
2653
  JS_CGETSET_MAGIC_DEF("keyIdentifier", event_getProperty, NULL, EVENT_JSPROPERTY_KEYIDENTIFIER),
2654
  JS_CGETSET_MAGIC_DEF("keyChar", event_getProperty, NULL, EVENT_JSPROPERTY_KEYCHAR),
2655
  JS_CGETSET_MAGIC_DEF("charCode", event_getProperty, NULL, EVENT_JSPROPERTY_CHARCODE),
2656
  /*progress*/
2657
  JS_CGETSET_MAGIC_DEF("lengthComputable", event_getProperty, NULL, EVENT_JSPROPERTY_LENGTHCOMPUTABLE),
2658
  JS_CGETSET_MAGIC_DEF("typeArg", event_getProperty, NULL, EVENT_JSPROPERTY_TYPEARG),
2659
  JS_CGETSET_MAGIC_DEF("loaded", event_getProperty, NULL, EVENT_JSPROPERTY_LOADED),
2660
  JS_CGETSET_MAGIC_DEF("total", event_getProperty, NULL, EVENT_JSPROPERTY_TOTAL),
2661
  JS_CGETSET_MAGIC_DEF("buffering", event_getProperty, NULL, EVENT_JSPROPERTY_BUFFER_ON),
2662
  JS_CGETSET_MAGIC_DEF("bufferLevel", event_getProperty, NULL, EVENT_JSPROPERTY_BUFFERLEVEL),
2663
  JS_CGETSET_MAGIC_DEF("bufferRemainingTime", event_getProperty, NULL, EVENT_JSPROPERTY_BUFFERREMAININGTIME),
2664
  JS_CGETSET_MAGIC_DEF("status", event_getProperty, NULL, EVENT_JSPROPERTY_STATUS),
2665
  /*used by vrml*/
2666
  JS_CGETSET_MAGIC_DEF("width", event_getProperty, NULL, EVENT_JSPROPERTY_WIDTH),
2667
  JS_CGETSET_MAGIC_DEF("height", event_getProperty, NULL, EVENT_JSPROPERTY_HEIGHT),
2668
  JS_CGETSET_MAGIC_DEF("offset_x", event_getProperty, NULL, EVENT_JSPROPERTY_OFFSETX),
2669
  JS_CGETSET_MAGIC_DEF("offset_y", event_getProperty, NULL, EVENT_JSPROPERTY_OFFSETY),
2670
  JS_CGETSET_MAGIC_DEF("vp_width", event_getProperty, NULL, EVENT_JSPROPERTY_VPWIDTH),
2671
  JS_CGETSET_MAGIC_DEF("vp_height", event_getProperty, NULL, EVENT_JSPROPERTY_VPHEIGHT),
2672
  JS_CGETSET_MAGIC_DEF("translation_x", event_getProperty, NULL, EVENT_JSPROPERTY_TRANSLATIONX),
2673
  JS_CGETSET_MAGIC_DEF("translation_y", event_getProperty, NULL, EVENT_JSPROPERTY_TRANSLATIONY),
2674
  JS_CGETSET_MAGIC_DEF("type3d", event_getProperty, NULL, EVENT_JSPROPERTY_TYPE3D),
2675
  JS_CGETSET_MAGIC_DEF("error", event_getProperty, NULL, EVENT_JSPROPERTY_ERROR),
2676
  JS_CGETSET_MAGIC_DEF("dynamic_scene", event_getProperty, NULL, EVENT_JSPROPERTY_DYNAMIC_SCENE),
2677
  JS_CGETSET_MAGIC_DEF("url", event_getProperty, NULL, EVENT_JSPROPERTY_URL),
2678
2679
  JS_CFUNC_DEF("stopPropagation", 0, event_stop_propagation),
2680
  JS_CFUNC_DEF("stopImmediatePropagation", 0, event_stop_immediate_propagation),
2681
  JS_CFUNC_DEF("preventDefault", 0, event_prevent_default),
2682
#if 0
2683
  JS_CFUNC_DEF("initEvent", 3, event_prevent_default),
2684
  JS_CFUNC_DEF("initEventNS", 4, event_prevent_default),
2685
#endif
2686
};
2687
2688
static const JSCFunctionListEntry nodeList_Funcs[] =
2689
{
2690
  JS_CGETSET_MAGIC_DEF("length", dom_nodelist_getProperty, NULL, NODELIST_JSPROPERTY_LENGTH),
2691
  JS_CFUNC_DEF("item", 1, dom_nodelist_item),
2692
};
2693
2694
2695
void domDocument_gc_mark(JSRuntime *rt, JSValueConst obj, JS_MarkFunc *mark_func)
2696
0
{
2697
0
  GF_SceneGraph *sg;
2698
0
  JSClassID _classID;
2699
0
  sg = (GF_SceneGraph*) JS_GetAnyOpaque(obj, &_classID);
2700
  /*the JS proto of the svgClass or a destroyed object*/
2701
0
  if (!sg || !sg->js_data) return;
2702
0
  if (!JS_IsUndefined(sg->js_data->document))
2703
0
    JS_MarkValue(rt, sg->js_data->document, mark_func);
2704
0
}
2705
void domElement_gc_mark(JSRuntime *rt, JSValueConst obj, JS_MarkFunc *mark_func)
2706
0
{
2707
0
  JSClassID _classID;
2708
0
  GF_Node *n = (GF_Node *) JS_GetAnyOpaque(obj, &_classID);
2709
  /*the JS proto of the svgClass or a destroyed object*/
2710
0
  if (!n) return;
2711
0
  if (!n->sgprivate || !n->sgprivate->interact || !n->sgprivate->interact->js_binding) return;
2712
2713
0
  if (!JS_IsUndefined(n->sgprivate->interact->js_binding->obj))
2714
0
    JS_MarkValue(rt, n->sgprivate->interact->js_binding->obj, mark_func);
2715
0
}
2716
2717
void dom_js_load(GF_SceneGraph *scene, JSContext *c)
2718
0
{
2719
0
  JSValue proto;
2720
0
  JSValue global = JS_GetGlobalObject(c);
2721
0
  JSRuntime *jsrt = JS_GetRuntime(c);
2722
2723
0
  if (!dom_rt) {
2724
0
    GF_SAFEALLOC(dom_rt, GF_DOMRuntime);
2725
0
    if (!dom_rt) {
2726
0
      GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[DOMJS] Failed to allocate DOM runtime\n"));
2727
0
      return;
2728
0
    }
2729
0
    dom_rt->handlers = gf_list_new();
2730
0
    GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[DOMCore] dom run-time allocated\n"));
2731
0
  }
2732
0
  dom_rt->nb_inst++;
2733
2734
0
  define_dom_exception(c, global);
2735
2736
0
  domNodeClass.class.gc_mark = domElement_gc_mark;
2737
0
  SETUP_JSCLASS(domNodeClass, "Node", node_Funcs, NULL, dom_node_finalize, 0);
2738
0
  JS_SetPropertyStr(c, proto, "ELEMENT_NODE", JS_NewInt32(c, 1) );
2739
0
  JS_SetPropertyStr(c, proto, "TEXT_NODE", JS_NewInt32(c, 3));
2740
0
  JS_SetPropertyStr(c, proto, "CDATA_SECTION_NODE", JS_NewInt32(c, 4));
2741
0
  JS_SetPropertyStr(c, proto, "DOCUMENT_NODE", JS_NewInt32(c, 9));
2742
2743
0
  domDocumentClass.class.gc_mark = domDocument_gc_mark;
2744
0
  SETUP_JSCLASS(domDocumentClass, "Document", document_Funcs, NULL, dom_document_finalize, domNodeClass.class_id);
2745
0
  domElementClass.class.gc_mark = domElement_gc_mark;
2746
0
  SETUP_JSCLASS(domElementClass, "Element", element_Funcs, NULL, dom_node_finalize, domNodeClass.class_id);
2747
0
  domTextClass.class.gc_mark = domElement_gc_mark;
2748
0
  SETUP_JSCLASS(domTextClass, "Text", text_Funcs, NULL, dom_node_finalize, domNodeClass.class_id);
2749
2750
0
  SETUP_JSCLASS(domEventClass, "Event", event_Funcs, NULL, NULL, 0);
2751
0
  JS_SetPropertyStr(c, proto, "CAPTURING_PHASE", JS_NewInt32(c, 1));
2752
0
  JS_SetPropertyStr(c, proto, "AT_TARGET", JS_NewInt32(c, 2));
2753
0
  JS_SetPropertyStr(c, proto, "BUBBLING_PHASE", JS_NewInt32(c, 3));
2754
0
  JS_SetPropertyStr(c, proto, "DOM_KEY_LOCATION_STANDARD ", JS_NewInt32(c, 0));
2755
0
  JS_SetPropertyStr(c, proto, "DOM_KEY_LOCATION_LEFT", JS_NewInt32(c, 1));
2756
0
  JS_SetPropertyStr(c, proto, "DOM_KEY_LOCATION_RIGHT", JS_NewInt32(c, 2));
2757
0
  JS_SetPropertyStr(c, proto, "DOM_KEY_LOCATION_NUMPAD", JS_NewInt32(c, 3));
2758
2759
2760
0
  SETUP_JSCLASS(domNodeListClass, "NodeList", nodeList_Funcs, NULL, dom_nodelist_finalize, 0);
2761
2762
0
  JS_FreeValue(c, global);
2763
0
}
2764
2765
#ifdef GPAC_ENABLE_HTML5_MEDIA
2766
void html_media_element_js_finalize(JSContext *c, GF_Node *n);
2767
#endif
2768
2769
GF_EXPORT
2770
void gf_sg_js_dom_pre_destroy(JSRuntime *rt, GF_SceneGraph *sg, GF_Node *n)
2771
0
{
2772
0
  u32 i, count;
2773
0
  if (n) {
2774
0
    if (n->sgprivate->interact && n->sgprivate->interact->js_binding && !JS_IsUndefined(n->sgprivate->interact->js_binding->obj) ) {
2775
0
      JS_SetOpaque(n->sgprivate->interact->js_binding->obj, NULL);
2776
0
      if (gf_list_del_item(sg->objects, n->sgprivate->interact->js_binding)>=0) {
2777
0
        JS_SetOpaque(n->sgprivate->interact->js_binding->obj, NULL);
2778
0
        JS_FreeValueRT(rt, n->sgprivate->interact->js_binding->obj);
2779
0
      }
2780
0
      n->sgprivate->interact->js_binding->obj = JS_UNDEFINED;
2781
0
    }
2782
0
    return;
2783
0
  }
2784
2785
  /*force cleanup of scripts/handlers not destroyed - this usually happens when a script/handler node has been created in a script
2786
  but not inserted in the graph*/
2787
0
  while (gf_list_count(sg->objects)) {
2788
0
    GF_Node *js_node;
2789
0
    struct _node_js_binding *js_bind = (struct _node_js_binding *)gf_list_get(sg->objects, 0);
2790
0
    js_node = dom_get_node(js_bind->obj);
2791
0
    if (js_node) {
2792
0
      if (js_node->sgprivate->tag == TAG_SVG_video || js_node->sgprivate->tag == TAG_SVG_audio) {
2793
#ifdef GPAC_ENABLE_HTML5_MEDIA
2794
        html_media_element_js_finalize(c, js_node);
2795
#endif
2796
0
      }
2797
0
      JS_SetOpaque(js_bind->obj, NULL);
2798
0
      JS_FreeValueRT(rt, js_node->sgprivate->interact->js_binding->obj);
2799
0
      js_node->sgprivate->interact->js_binding->obj=JS_UNDEFINED;
2800
0
      gf_node_unregister(js_node, NULL);
2801
0
    }
2802
0
    gf_list_rem(sg->objects, 0);
2803
0
  }
2804
2805
0
  count = dom_rt ? gf_list_count(dom_rt->handlers) : 0;
2806
0
  for (i=0; i<count; i++) {
2807
0
    SVG_handlerElement *handler = (SVG_handlerElement *)gf_list_get(dom_rt->handlers, i);
2808
0
    if (!handler->js_data) continue;
2809
2810
    /*if same context and same document, discard handler*/
2811
0
    if (!sg || (handler->sgprivate->scenegraph==sg)) {
2812
      /*unprotect the function*/
2813
0
      JS_FreeValueRT(rt, handler->js_data->fun_val);
2814
0
      gf_free(handler->js_data);
2815
0
      handler->js_data = NULL;
2816
0
      gf_list_rem(dom_rt->handlers, i);
2817
0
      i--;
2818
0
      count--;
2819
0
    }
2820
0
  }
2821
0
  if (sg->js_data) {
2822
0
    if (!JS_IsUndefined(sg->js_data->document)) {
2823
      //detach sg from object and free it
2824
0
      JS_SetOpaque(sg->js_data->document, NULL);
2825
0
      JS_FreeValueRT(rt, sg->js_data->document);
2826
0
    }
2827
0
    gf_free(sg->js_data);
2828
0
    sg->js_data = NULL;
2829
0
  }
2830
0
}
2831
2832
void dom_js_unload()
2833
0
{
2834
0
  if (!dom_rt) return;
2835
0
  dom_rt->nb_inst--;
2836
0
  if (!dom_rt->nb_inst) {
2837
0
    gf_list_del(dom_rt->handlers);
2838
0
    gf_free(dom_rt);
2839
0
    dom_rt = NULL;
2840
0
  }
2841
0
}
2842
2843
JSValue dom_js_define_event(JSContext *c)
2844
0
{
2845
0
  JSValue global = JS_GetGlobalObject(c);
2846
2847
0
  JSValue obj = JS_NewObjectClass(c, domEventClass.class_id);
2848
0
  JS_SetPropertyStr(c, global, "evt", obj);
2849
//  JS_DefinePropertyStr(c, global, "event", obj);
2850
0
  JS_FreeValue(c, global);
2851
0
  return obj;
2852
0
}
2853
GF_DOM_Event *dom_get_evt_private(JSValue v)
2854
0
{
2855
0
  return JS_GetOpaque(v, domEventClass.class_id);
2856
0
}
2857
2858
JSValue gf_dom_new_event(JSContext *c)
2859
0
{
2860
0
  return JS_NewObjectClass(c, domEventClass.class_id);
2861
0
}
2862
2863
JSClassID dom_js_get_element_proto(JSContext *c)
2864
0
{
2865
0
  return domElementClass.class_id;
2866
0
}
2867
JSClassID dom_js_get_document_proto(JSContext *c)
2868
0
{
2869
0
  return domDocumentClass.class_id;
2870
0
}
2871
#endif  /*GPAC_HAS_QJS*/
2872
2873
2874
2875
#if 0 //unused
2876
/*! parses the given XML document and returns a scene graph composed of GF_DOMFullNode
2877
\param src the source file
2878
\param scene set to the new scene graph
2879
\return error if any
2880
*/
2881
GF_Err gf_sg_new_from_xml_doc(const char *src, GF_SceneGraph **scene)
2882
{
2883
#ifdef GPAC_HAS_QJS
2884
  GF_Err e;
2885
  XMLHTTPContext *ctx;
2886
  GF_SceneGraph *sg;
2887
2888
  GF_SAFEALLOC(ctx, XMLHTTPContext);
2889
  if (!ctx) return GF_OUT_OF_MEM;
2890
2891
  ctx->sax = gf_xml_sax_new(xml_http_sax_start, xml_http_sax_end, xml_http_sax_text, ctx);
2892
  ctx->node_stack = gf_list_new();
2893
  sg = gf_sg_new();
2894
  ctx->document = sg;
2895
  e = gf_xml_sax_parse_file(ctx->sax, src, NULL);
2896
  gf_xml_sax_del(ctx->sax);
2897
  gf_list_del(ctx->node_stack);
2898
  gf_free(ctx);
2899
2900
  *scene = NULL;
2901
  if (e<0) {
2902
    gf_sg_del(sg);
2903
    return e;
2904
  }
2905
  *scene = sg;
2906
  return GF_OK;
2907
#else
2908
  return GF_NOT_SUPPORTED;
2909
#endif
2910
}
2911
#endif
2912
2913
2914
2915
#if 0 //unused
2916
2917
typedef struct
2918
{
2919
  GF_SAXParser *sax;
2920
  GF_List *node_stack;
2921
  /*dom graph*/
2922
  GF_SceneGraph *document;
2923
} XMLReloadContext;
2924
2925
static void xml_reload_node_start(void *sax_cbck, const char *node_name, const char *name_space, const GF_XMLAttribute *attributes, u32 nb_attributes)
2926
{
2927
  GF_DOM_Event evt;
2928
  Bool is_root = GF_FALSE;
2929
  Bool modified = GF_FALSE;
2930
  Bool atts_modified = GF_FALSE;
2931
  Bool new_node = GF_FALSE;
2932
  u32 i;
2933
  GF_DOMFullAttribute *prev = NULL;
2934
  GF_DOMFullNode *par = NULL;
2935
  XMLReloadContext *ctx = (XMLReloadContext *)sax_cbck;
2936
  GF_DOMFullNode *node;
2937
2938
  node = (GF_DOMFullNode *)gf_list_last(ctx->node_stack);
2939
  if (!node) {
2940
    is_root = GF_TRUE;
2941
    node = (GF_DOMFullNode *) ctx->document->RootNode;
2942
  }
2943
2944
  if (is_root) {
2945
    Bool same_node = GF_TRUE;
2946
    if (strcmp(node->name, node_name)) same_node = GF_FALSE;
2947
    if (node->ns && name_space && strcmp(gf_sg_get_namespace(node->sgprivate->scenegraph, node->ns), name_space)) same_node = GF_FALSE;
2948
2949
    if (!same_node) {
2950
      if (node->name) gf_free(node->name);
2951
      node->name = gf_strdup(node_name);
2952
      node->ns = 0;
2953
      if (name_space) {
2954
        gf_sg_add_namespace(node->sgprivate->scenegraph, (char *) name_space, NULL);
2955
        node->ns = gf_sg_get_namespace_code_from_name(node->sgprivate->scenegraph, (char*)name_space);
2956
      }
2957
      modified = GF_TRUE;
2958
    }
2959
  } else {
2960
    GF_ChildNodeItem *child = node->children;
2961
    node = NULL;
2962
    /*locate the node in the children*/
2963
    while (child) {
2964
      Bool same_node = GF_TRUE;
2965
      node = (GF_DOMFullNode*)child->node;
2966
      if (strcmp(node->name, node_name)) same_node = GF_FALSE;
2967
      if (node->ns && name_space && strcmp(gf_sg_get_namespace(node->sgprivate->scenegraph, node->ns), name_space)) same_node = GF_FALSE;
2968
      if (same_node) {
2969
        break;
2970
      }
2971
      node = NULL;
2972
      child = child->next;
2973
    }
2974
    if (!node) {
2975
      modified = GF_TRUE;
2976
2977
      /*create the new node*/
2978
      node = (GF_DOMFullNode *) gf_node_new(ctx->document, TAG_DOMFullNode);
2979
      node->name = gf_strdup(node_name);
2980
      if (name_space) {
2981
        gf_sg_add_namespace(node->sgprivate->scenegraph, (char *)name_space, NULL);
2982
        node->ns = gf_sg_get_namespace_code(node->sgprivate->scenegraph, (char *)name_space);
2983
      }
2984
2985
      par = (GF_DOMFullNode *)gf_list_last(ctx->node_stack);
2986
      gf_node_register((GF_Node*)node, (GF_Node*)par);
2987
      gf_node_list_add_child(&par->children, (GF_Node*)node);
2988
      new_node = GF_TRUE;
2989
    }
2990
  }
2991
  gf_list_add(ctx->node_stack, node);
2992
2993
  if (!modified) {
2994
    u32 count = 0;
2995
    GF_DOMFullAttribute *att = (GF_DOMFullAttribute *)node->attributes;
2996
    while (att) {
2997
      Bool found = GF_FALSE;
2998
      for (i=0; i<nb_attributes; i++) {
2999
        if (!stricmp(attributes[i].name, "xml:id")) {
3000
          const char *id = gf_node_get_name((GF_Node*)node);
3001
          if (!id || strcmp(id, attributes[i].value))
3002
            modified = GF_TRUE;
3003
        } else if (!strcmp(att->name, attributes[i].name)) {
3004
          found = GF_TRUE;
3005
          if (strcmp((const char *)att->data, attributes[i].value)) {
3006
            atts_modified = GF_TRUE;
3007
            gf_free(att->data);
3008
            att->data = gf_strdup(attributes[i].value);
3009
          }
3010
        }
3011
      }
3012
      if (!found) {
3013
        modified = GF_TRUE;
3014
        break;
3015
      }
3016
      count++;
3017
      att = (GF_DOMFullAttribute *)att->next;
3018
    }
3019
    if (count != nb_attributes)
3020
      modified = GF_TRUE;
3021
  }
3022
3023
3024
  if (modified) {
3025
    GF_DOMFullAttribute *tmp, *att;
3026
    att = (GF_DOMFullAttribute *)node->attributes;
3027
    while (att) {
3028
      if (att->name) gf_free(att->name);
3029
      if (att->data) gf_free(att->data);
3030
      tmp = att;
3031
      att = (GF_DOMFullAttribute *)att->next;
3032
      gf_free(tmp);
3033
    }
3034
3035
    /*parse all atts*/
3036
    for (i=0; i<nb_attributes; i++) {
3037
      if (!stricmp(attributes[i].name, "xml:id")) {
3038
        u32 id = gf_sg_get_max_node_id(ctx->document) + 1;
3039
        gf_node_set_id((GF_Node *)node, id, attributes[i].value);
3040
      } else {
3041
        GF_DOMFullAttribute *att;
3042
        GF_SAFEALLOC(att, GF_DOMFullAttribute);
3043
        if (!att) {
3044
          GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[DOMJS] Failed to allocate DOM attribute\n"));
3045
          continue;
3046
        }
3047
        att->tag = TAG_DOM_ATT_any;
3048
        att->name = gf_strdup(attributes[i].name);
3049
        att->data = gf_strdup(attributes[i].value);
3050
        if (prev) prev->next = (GF_DOMAttribute*)att;
3051
        else node->attributes = (GF_DOMAttribute*)att;
3052
        prev = att;
3053
      }
3054
    }
3055
    atts_modified = GF_TRUE;
3056
  }
3057
3058
  if (atts_modified || new_node) {
3059
    memset(&evt, 0, sizeof(GF_DOM_Event));
3060
    evt.type = new_node ? GF_EVENT_NODE_INSERTED : GF_EVENT_ATTR_MODIFIED;
3061
    evt.bubbles = 1;
3062
    evt.relatedNode = (GF_Node*) ( (evt.type == GF_EVENT_NODE_INSERTED) ? par : node);
3063
    gf_dom_event_fire((GF_Node*)node, &evt);
3064
  }
3065
}
3066
3067
static void xml_reload_node_end(void *sax_cbck, const char *node_name, const char *name_space)
3068
{
3069
  XMLReloadContext *ctx = (XMLReloadContext *)sax_cbck;
3070
  GF_DOMFullNode *par = (GF_DOMFullNode *)gf_list_last(ctx->node_stack);
3071
  if (par) {
3072
    /*depth mismatch*/
3073
    if (strcmp(par->name, node_name)) return;
3074
    gf_list_rem_last(ctx->node_stack);
3075
  }
3076
}
3077
static void xml_reload_text_content(void *sax_cbck, const char *content, Bool is_cdata)
3078
{
3079
  GF_DOM_Event evt;
3080
  u32 i, len;
3081
  GF_ChildNodeItem *child;
3082
  GF_DOMText *txt = NULL;
3083
  XMLReloadContext *ctx = (XMLReloadContext *)sax_cbck;
3084
  GF_DOMFullNode *par = (GF_DOMFullNode *)gf_list_last(ctx->node_stack);
3085
  if (!par) return;
3086
3087
  /*basic check, remove all empty text nodes*/
3088
  len = (u32) strlen(content);
3089
  for (i=0; i<len; i++) {
3090
    if (!strchr(" \n\r\t", content[i])) break;
3091
  }
3092
  if (i==len) return;
3093
3094
  child = par->children;
3095
  while (child) {
3096
    if (child->node->sgprivate->tag == TAG_DOMText) {
3097
      txt = (GF_DOMText *)child->node;
3098
      if (!strcmp(txt->textContent, content) && ((txt->type==GF_DOM_TEXT_REGULAR) || is_cdata))
3099
        return;
3100
      if (txt->textContent) gf_free(txt->textContent);
3101
      txt->textContent = gf_strdup(content);
3102
      txt->type = is_cdata ? GF_DOM_TEXT_CDATA : GF_DOM_TEXT_REGULAR;
3103
      break;
3104
    }
3105
    child = child->next;
3106
  }
3107
  if (!txt) {
3108
    txt = gf_dom_add_text_node((GF_Node *)par, gf_strdup(content) );
3109
    txt->type = is_cdata ? GF_DOM_TEXT_CDATA : GF_DOM_TEXT_REGULAR;
3110
3111
    memset(&evt, 0, sizeof(GF_DOM_Event));
3112
    evt.type = GF_EVENT_NODE_INSERTED;
3113
    evt.bubbles = 1;
3114
    evt.relatedNode = (GF_Node*) par;
3115
    gf_dom_event_fire((GF_Node*)txt, &evt);
3116
  }
3117
3118
  memset(&evt, 0, sizeof(GF_DOM_Event));
3119
  evt.type = GF_EVENT_CHAR_DATA_MODIFIED;
3120
  evt.bubbles = 1;
3121
  evt.relatedNode = (GF_Node*)par;
3122
  gf_dom_event_fire((GF_Node*)par, &evt);
3123
3124
  memset(&evt, 0, sizeof(GF_DOM_Event));
3125
  evt.type = GF_EVENT_DCCI_PROP_CHANGE;
3126
  evt.bubbles = 1;
3127
  evt.relatedNode = (GF_Node*)par;
3128
  gf_dom_event_fire((GF_Node*)par, &evt);
3129
}
3130
3131
GF_Err gf_sg_reload_xml_doc(const char *src, GF_SceneGraph *scene)
3132
{
3133
  GF_Err e;
3134
  XMLReloadContext ctx;
3135
3136
  if (!src || !scene) return GF_BAD_PARAM;
3137
  memset(&ctx, 0, sizeof(XMLReloadContext));
3138
  ctx.document = scene;
3139
  ctx.node_stack = gf_list_new();
3140
3141
  ctx.sax = gf_xml_sax_new(xml_reload_node_start, xml_reload_node_end, xml_reload_text_content, &ctx);
3142
  e = gf_xml_sax_parse_file(ctx.sax, src, NULL);
3143
  gf_list_del(ctx.node_stack);
3144
  gf_xml_sax_del(ctx.sax);
3145
  if (e<0) return e;
3146
  return GF_OK;
3147
}
3148
#endif
3149
3150
3151
#endif  /*GPAC_DISABLE_SVG*/