LCOV - code coverage report
Current view: top level - scenegraph - dom_js.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 801 1228 65.2 %
Date: 2021-04-29 23:48:07 Functions: 75 81 92.6 %

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

Generated by: LCOV version 1.13