LCOV - code coverage report
Current view: top level - scenegraph - base_scenegraph.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 932 1068 87.3 %
Date: 2021-04-29 23:48:07 Functions: 93 96 96.9 %

          Line data    Source code
       1             : /*
       2             :  *                      GPAC - Multimedia Framework C SDK
       3             :  *
       4             :  *                      Authors: Jean Le Feuvre
       5             :  *                      Copyright (c) Telecom ParisTech 2000-2012
       6             :  *                                      All rights reserved
       7             :  *
       8             :  *  This file is part of GPAC / Scene Graph sub-project
       9             :  *
      10             :  *  GPAC is free software; you can redistribute it and/or modify
      11             :  *  it under the terms of the GNU Lesser General Public License as published by
      12             :  *  the Free Software Foundation; either version 2, or (at your option)
      13             :  *  any later version.
      14             :  *
      15             :  *  GPAC is distributed in the hope that it will be useful,
      16             :  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
      17             :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      18             :  *  GNU Lesser General Public License for more details.
      19             :  *
      20             :  *  You should have received a copy of the GNU Lesser General Public
      21             :  *  License along with this library; see the file COPYING.  If not, write to
      22             :  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
      23             :  *
      24             :  */
      25             : 
      26             : #include <gpac/internal/scenegraph_dev.h>
      27             : /*svg proto*/
      28             : #include <gpac/scenegraph_svg.h>
      29             : /*MPEG4 tags (for internal nodes)*/
      30             : #include <gpac/nodes_mpeg4.h>
      31             : /*X3D tags (for internal nodes)*/
      32             : #include <gpac/nodes_x3d.h>
      33             : #include <gpac/events.h>
      34             : #include <gpac/nodes_svg.h>
      35             : 
      36             : #ifdef GPAC_HAS_QJS
      37             : #include "qjs_common.h"
      38             : #endif
      39             : static void ReplaceDEFNode(GF_Node *FromNode, GF_Node *node, GF_Node *newNode, Bool updateOrderedGroup);
      40             : 
      41             : #ifndef GPAC_DISABLE_SVG
      42             : static void ReplaceIRINode(GF_Node *FromNode, GF_Node *oldNode, GF_Node *newNode);
      43             : #endif
      44             : 
      45          93 : static void node_modif_stub(GF_SceneGraph *sg, GF_Node *node, GF_FieldInfo *info, GF_Node *script)
      46             : {
      47          93 : }
      48             : 
      49             : GF_EXPORT
      50        2899 : GF_SceneGraph *gf_sg_new()
      51             : {
      52             :         GF_SceneGraph *tmp;
      53        2899 :         GF_SAFEALLOC(tmp, GF_SceneGraph);
      54        2899 :         if (!tmp) return NULL;
      55             : 
      56        2899 :         tmp->exported_nodes = gf_list_new();
      57             : 
      58             : #ifndef GPAC_DISABLE_VRML
      59        2899 :         tmp->protos = gf_list_new();
      60        2899 :         tmp->unregistered_protos = gf_list_new();
      61        2899 :         tmp->Routes = gf_list_new();
      62        2899 :         tmp->routes_to_activate = gf_list_new();
      63        2899 :         tmp->routes_to_destroy = gf_list_new();
      64             : #endif
      65             : 
      66             : #ifndef GPAC_DISABLE_SVG
      67        2899 :         tmp->dom_evt_mx = gf_mx_new("DOMEvent");
      68        2899 :         tmp->dom_evt = gf_dom_event_target_new(GF_DOM_EVENT_TARGET_DOCUMENT, tmp);
      69        2899 :         tmp->xlink_hrefs = gf_list_new();
      70        2899 :         tmp->smil_timed_elements = gf_list_new();
      71        2899 :         tmp->modified_smil_timed_elements = gf_list_new();
      72        2899 :         tmp->listeners_to_add = gf_list_new();
      73             : #endif
      74             : 
      75             : #ifdef GPAC_HAS_QJS
      76        2899 :         tmp->scripts = gf_list_new();
      77        2899 :         tmp->objects = gf_list_new();
      78             : #endif
      79        2899 :         tmp->on_node_modified = node_modif_stub;
      80             : 
      81             :         //test some functions only used in weird smil anim or dom JS, and not addressed in test suite
      82             : #ifdef GPAC_ENABLE_COVERAGE
      83        2899 :         if (gf_sys_is_cov_mode()) {
      84             :                 node_modif_stub(NULL, NULL, NULL, NULL);
      85        2807 :                 gf_node_remove_id(NULL);
      86        2807 :                 gf_node_list_get_child(NULL, 0);
      87        2807 :                 gf_node_get_parent_count(NULL);
      88             :                 gf_node_get_num_instances(NULL);
      89             :                 gf_node_get_log_name(NULL);
      90             :                 gf_sg_get_namespace(NULL, 0);
      91        2807 :                 gf_node_parent_of(NULL, NULL);
      92             :                 gf_sg_get_parent(tmp);
      93        2807 :                 gf_node_get_attribute_count(NULL);
      94        2807 :                 gf_xml_node_clone(NULL, NULL, NULL, NULL, 0);
      95        2807 :                 gf_dom_flatten_textContent(NULL);
      96        2807 :                 gf_smil_timing_pause(NULL);
      97        2807 :                 gf_smil_timing_resume(NULL);
      98        2807 :                 gf_smil_get_media_duration(NULL);
      99             :         }
     100             : #endif
     101             :         return tmp;
     102             : }
     103             : 
     104             : GF_EXPORT
     105        2202 : GF_SceneGraph *gf_sg_new_subscene(GF_SceneGraph *scene)
     106             : {
     107             :         GF_SceneGraph *tmp;
     108        2202 :         if (!scene) return NULL;
     109        2202 :         tmp = gf_sg_new();
     110        2202 :         if (!tmp) return NULL;
     111        2202 :         tmp->parent_scene = scene;
     112        2202 :         tmp->script_action = scene->script_action;
     113        2202 :         tmp->script_action_cbck = scene->script_action_cbck;
     114        2202 :         tmp->script_load = scene->script_load;
     115        2202 :         tmp->on_node_modified = scene->on_node_modified;
     116             : 
     117             :         /*by default use the same callbacks*/
     118        2202 :         tmp->userpriv = scene->userpriv;
     119        2202 :         tmp->GetSceneTime = scene->GetSceneTime;
     120        2202 :         tmp->NodeCallback = scene->NodeCallback;
     121             : 
     122             : #ifndef GPAC_DISABLE_VRML
     123        2202 :         tmp->GetExternProtoLib = scene->GetExternProtoLib;
     124             : #endif
     125        2202 :         return tmp;
     126             : }
     127             : 
     128             : 
     129             : GF_EXPORT
     130        1094 : void gf_sg_set_node_callback(GF_SceneGraph *sg, gf_sg_node_init_callback NodeCallback)
     131             : {
     132        1094 :         sg->NodeCallback = NodeCallback;
     133        1094 : }
     134             : 
     135             : GF_EXPORT
     136         709 : void gf_sg_set_scene_time_callback(GF_SceneGraph *sg, Double (*GetSceneTime)(void *user_priv))
     137             : {
     138         709 :         sg->GetSceneTime = GetSceneTime;
     139         709 : }
     140             : 
     141             : 
     142             : GF_EXPORT
     143       49435 : Double gf_node_get_scene_time(GF_Node *node)
     144             : {
     145       49435 :         if (!node || !node->sgprivate->scenegraph->GetSceneTime) return 0.0;
     146       49435 :         return node->sgprivate->scenegraph->GetSceneTime(node->sgprivate->scenegraph->userpriv);
     147             : }
     148             : 
     149             : 
     150             : GF_EXPORT
     151        2899 : void gf_sg_del(GF_SceneGraph *sg)
     152             : {
     153        2899 :         if (!sg) return;
     154             : 
     155             : #ifndef GPAC_DISABLE_VRML
     156        2899 :         if (sg->global_qp) {
     157          24 :                 gf_node_unregister(sg->global_qp, NULL);
     158          24 :                 sg->global_qp = NULL;
     159             :         }
     160             : #endif
     161             : 
     162        2899 :         gf_sg_reset(sg);
     163             : 
     164             : #ifndef GPAC_DISABLE_SVG
     165             : 
     166        2899 :         gf_dom_event_target_del(sg->dom_evt);
     167        2899 :         gf_list_del(sg->xlink_hrefs);
     168        2899 :         gf_list_del(sg->smil_timed_elements);
     169        2899 :         gf_list_del(sg->modified_smil_timed_elements);
     170        2899 :         gf_list_del(sg->listeners_to_add);
     171        2899 :         gf_mx_del(sg->dom_evt_mx);
     172             : #endif
     173             : 
     174             : #ifdef GPAC_HAS_QJS
     175        2899 :         gf_list_del(sg->scripts);
     176        2899 :         sg->scripts = NULL;
     177        2899 :         gf_list_del(sg->objects);
     178        2899 :         sg->objects = NULL;
     179             : #ifndef GPAC_DISABLE_SVG
     180        2899 :         if (sg->svg_js) {
     181             :                 void gf_svg_script_context_del(void *svg_js, GF_SceneGraph *scenegraph);
     182           0 :                 gf_svg_script_context_del(sg->svg_js, sg);
     183             :         }
     184             : #endif
     185             : 
     186             : #endif //GPAC_HAS_QJS
     187             : 
     188             : #ifndef GPAC_DISABLE_VRML
     189        2899 :         gf_list_del(sg->Routes);
     190        2899 :         gf_list_del(sg->protos);
     191        2899 :         gf_list_del(sg->unregistered_protos);
     192        2899 :         gf_list_del(sg->routes_to_activate);
     193        2899 :         gf_list_del(sg->routes_to_destroy);
     194             : #endif
     195        2899 :         gf_list_del(sg->exported_nodes);
     196        2899 :         gf_free(sg);
     197             : }
     198             : 
     199             : /*recursive traverse of the whole graph to check for scope mixes (nodes from an inline graph
     200             : inserted in a parent graph through bind or routes). We must do this otherwise we're certain to get random
     201             : crashes or mem leaks.*/
     202           0 : void SG_GraphRemoved(GF_Node *node, GF_SceneGraph *sg)
     203             : {
     204             :         u32 i, count;
     205             :         GF_FieldInfo info;
     206             :         u32 tag;
     207             : 
     208             : 
     209           0 :         tag = node->sgprivate->tag;
     210           0 :         if (tag == TAG_ProtoNode) return;
     211             : 
     212             :         /*not possible in DOM ?*/
     213           0 :         if (tag>GF_NODE_RANGE_LAST_VRML) return;
     214             : 
     215           0 :         count = gf_node_get_field_count(node);
     216           0 :         for (i=0; i<count; i++) {
     217           0 :                 gf_node_get_field(node, i, &info);
     218           0 :                 if (info.fieldType==GF_SG_VRML_SFNODE) {
     219           0 :                         GF_Node *n = *(GF_Node **) info.far_ptr;
     220           0 :                         if (n) {
     221           0 :                                 if (n->sgprivate->scenegraph==sg) {
     222             :                                         /*if root of graph, skip*/
     223           0 :                                         if (sg->RootNode!=n) {
     224           0 :                                                 gf_node_unregister(n, node);
     225             :                                                 /*don't forget to remove node...*/
     226           0 :                                                 *(GF_Node **) info.far_ptr = NULL;
     227             :                                         }
     228             :                                 } else {
     229           0 :                                         SG_GraphRemoved(n, sg);
     230             :                                 }
     231             :                         }
     232             :                 }
     233           0 :                 else if (info.fieldType==GF_SG_VRML_MFNODE) {
     234           0 :                         GF_ChildNodeItem *cur, *prev, *list = *(GF_ChildNodeItem **) info.far_ptr;
     235             :                         prev = NULL;
     236           0 :                         while (list) {
     237           0 :                                 if (list->node->sgprivate->scenegraph==sg) {
     238           0 :                                         gf_node_unregister(list->node, node);
     239             : 
     240             :                                         if (prev) prev->next = list->next;
     241           0 :                                         else *(GF_ChildNodeItem **) info.far_ptr = list->next;
     242             :                                         cur = list;
     243           0 :                                         gf_free(cur);
     244             :                                 } else {
     245           0 :                                         SG_GraphRemoved(list->node, sg);
     246             :                                 }
     247           0 :                                 list = list->next;
     248             :                         }
     249             :                 }
     250             :                 /*for uncompressed files (bt/xmt/laserML), also reset command lists*/
     251           0 :                 else if (info.fieldType==GF_SG_VRML_SFCOMMANDBUFFER) {
     252             :                         u32 j, count2;
     253           0 :                         SFCommandBuffer *cb = (SFCommandBuffer*)info.far_ptr;
     254           0 :                         count2 = gf_list_count(cb->commandList);
     255           0 :                         for (j=0; j<count2; j++) {
     256           0 :                                 u32 k = 0;
     257             :                                 GF_CommandField *f;
     258           0 :                                 GF_Command *com = gf_list_get(cb->commandList, j);
     259           0 :                                 while ((f=gf_list_enum(com->command_fields, &k))) {
     260           0 :                                         switch (f->fieldType) {
     261           0 :                                         case GF_SG_VRML_SFNODE:
     262           0 :                                                 if (f->new_node) {
     263           0 :                                                         if (f->new_node->sgprivate->scenegraph==sg) {
     264             :                                                                 /*if root of graph, skip*/
     265           0 :                                                                 if (sg->RootNode!=f->new_node) {
     266           0 :                                                                         gf_node_unregister(f->new_node, node);
     267             :                                                                         /*don't forget to remove node...*/
     268           0 :                                                                         f->new_node = NULL;
     269             :                                                                 }
     270             :                                                         } else {
     271           0 :                                                                 SG_GraphRemoved(f->new_node, sg);
     272             :                                                         }
     273             :                                                 }
     274             :                                                 break;
     275           0 :                                         case GF_SG_VRML_MFNODE:
     276           0 :                                                 if (f->node_list) {
     277             :                                                         GF_ChildNodeItem *cur, *prev, *list = f->node_list;
     278             :                                                         prev = NULL;
     279           0 :                                                         while (list) {
     280           0 :                                                                 if (list->node->sgprivate->scenegraph==sg) {
     281           0 :                                                                         gf_node_unregister(list->node, node);
     282             : 
     283             :                                                                         if (prev) prev->next = list->next;
     284           0 :                                                                         else f->node_list = list->next;
     285             :                                                                         cur = list;
     286           0 :                                                                         gf_free(cur);
     287             :                                                                 } else {
     288           0 :                                                                         SG_GraphRemoved(list->node, sg);
     289             :                                                                 }
     290           0 :                                                                 list = list->next;
     291             :                                                         }
     292             :                                                 }
     293             :                                                 break;
     294             :                                         }
     295             :                                 }
     296             :                         }
     297             :                 }
     298             :         }
     299             : }
     300             : 
     301             : GFINLINE GF_Node *SG_SearchForNode(GF_SceneGraph *sg, GF_Node *node)
     302             : {
     303             :         NodeIDedItem *reg_node = sg->id_node;
     304             :         while (reg_node) {
     305             :                 if (reg_node->node == node) return reg_node->node;
     306             :                 reg_node = reg_node->next;
     307             :         }
     308             :         return NULL;
     309             : }
     310             : 
     311             : static GFINLINE u32 get_num_id_nodes(GF_SceneGraph *sg)
     312             : {
     313             :         u32 count = 0;
     314             :         NodeIDedItem *reg_node = sg->id_node;
     315          79 :         while (reg_node) {
     316          63 :                 count++;
     317          63 :                 reg_node = reg_node->next;
     318             :         }
     319             :         return count;
     320             : }
     321             : 
     322             : GF_EXPORT
     323        5599 : void gf_sg_reset(GF_SceneGraph *sg)
     324             : {
     325             :         GF_SceneGraph *par;
     326             :         GF_List *gc;
     327             : #ifndef GPAC_DISABLE_SVG
     328             :         u32 type;
     329             : #endif
     330             :         u32 count;
     331             :         NodeIDedItem *reg_node;
     332        5599 :         if (!sg) return;
     333             : 
     334        5599 :         GF_LOG(GF_LOG_DEBUG, GF_LOG_SCENE, ("[SceneGraph] Reseting scene graph\n"));
     335             : #if 0
     336             :         /*inlined graph, remove any of this graph nodes from the parent graph*/
     337             :         if (!sg->pOwningProto && sg->parent_scene) {
     338             :                 GF_SceneGraph *par = sg->parent_scene;
     339             :                 while (par->parent_scene) par = par->parent_scene;
     340             :                 if (par->RootNode) SG_GraphRemoved(par->RootNode, sg);
     341             :         }
     342             : #endif
     343             : 
     344        5599 :         gc = gf_list_new();
     345             : #ifdef GPAC_HAS_QJS
     346             :         /*scripts are the first source of cylic references in the graph. In order to clean properly
     347             :         force a remove of all script nodes, this will release all references to nodes in JS*/
     348       11237 :         while (gf_list_count(sg->scripts)) {
     349          39 :                 GF_Node *n = gf_list_get(sg->scripts, 0);
     350          39 :                 gf_list_rem(sg->scripts, 0);
     351             :                 /*prevent destroy*/
     352          39 :                 gf_node_register(n, NULL);
     353             :                 /*remove from all parents*/
     354          39 :                 gf_node_replace(n, NULL, 0);
     355             :                 /*FORCE destroy in case the script refers to itself*/
     356          39 :                 n->sgprivate->num_instances=1;
     357          39 :                 gf_node_unregister(n, NULL);
     358             :                 /*remember this script was forced to be destroyed*/
     359          39 :                 gf_list_add(gc, n);
     360             :         }
     361             : #endif
     362             : 
     363             : #ifndef GPAC_DISABLE_SVG
     364             : 
     365        5599 :         gf_mx_p(sg->dom_evt_mx);
     366             :         /*remove listeners attached to the doc*/
     367        5599 :         gf_dom_event_remove_all_listeners(sg->dom_evt);
     368             :         /*flush any pending add_listener*/
     369        5599 :         gf_dom_listener_reset_deferred(sg);
     370        5599 :         gf_mx_v(sg->dom_evt_mx);
     371             : #endif
     372             : 
     373             : #ifndef GPAC_DISABLE_VRML
     374       11202 :         while (gf_list_count(sg->routes_to_activate)) {
     375           4 :                 gf_list_rem(sg->routes_to_activate, 0);
     376             :         }
     377             : 
     378             :         /*destroy all routes*/
     379        7512 :         while (gf_list_count(sg->Routes)) {
     380        1913 :                 GF_Route *r = (GF_Route*)gf_list_get(sg->Routes, 0);
     381             :                 /*this will unregister the route from the graph, so don't delete the chain entry*/
     382        1913 :                 gf_sg_route_del(r);
     383             : 
     384             :         }
     385             : #endif
     386             : 
     387             : 
     388             :         /*reset all exported symbols */
     389        5796 :         while (gf_list_count(sg->exported_nodes)) {
     390         197 :                 GF_Node *n = gf_list_get(sg->exported_nodes, 0);
     391         197 :                 gf_list_rem(sg->exported_nodes, 0);
     392         197 :                 gf_node_replace(n, NULL, 0);
     393             :         }
     394             :         /*reassign the list of exported nodes to our garbage collected nodes*/
     395        5599 :         gf_list_del(sg->exported_nodes);
     396        5599 :         sg->exported_nodes = gc;
     397             : 
     398             :         /*reset the main tree*/
     399        5599 :         if (sg->RootNode) gf_node_unregister(sg->RootNode, NULL);
     400        5599 :         sg->RootNode = NULL;
     401             : 
     402        9286 :         if (!sg->pOwningProto && gf_list_count(sg->protos) && sg->GetExternProtoLib)
     403          46 :                 sg->GetExternProtoLib(sg->userpriv, NULL);
     404             : 
     405             :         /*WATCHOUT: we may have cyclic dependencies due to
     406             :         1- a node referencing itself (forbidden in VRML)
     407             :         2- nodes referred to in commands of conditionals children of this node (MPEG-4 is mute about that)
     408             :         we recursively preocess from last declared DEF node to first one
     409             :         */
     410        5561 : restart:
     411        5607 :         reg_node = sg->id_node;
     412       11214 :         while (reg_node) {
     413             : #if 0
     414             :                 Bool ignore = 0;
     415             : #endif
     416             :                 GF_ParentList *nlist;
     417             : 
     418           8 :                 GF_Node *node = reg_node->node;
     419           8 :                 if (!node
     420             : #ifndef GPAC_DISABLE_VRML
     421           8 :                         || (node==sg->global_qp)
     422             : #endif
     423             :                    ) {
     424           0 :                         reg_node = reg_node->next;
     425           0 :                         continue;
     426             :                 }
     427             : 
     428             :                 /*first replace all instances in parents by NULL WITHOUT UNREGISTERING (to avoid destroying the node).
     429             :                 This will take care of nodes referencing themselves*/
     430           8 :                 nlist = node->sgprivate->parents;
     431             : #ifndef GPAC_DISABLE_SVG
     432           8 :                 type = (node->sgprivate->tag>GF_NODE_RANGE_LAST_VRML) ? 1 : 0;
     433             : #endif
     434          26 :                 while (nlist) {
     435          10 :                         GF_ParentList *next = nlist->next;
     436             : #if 0
     437             :                         /*parent is a DEF'ed node, try to clean-up properly?*/
     438             :                         if ((nlist->node!=node) && SG_SearchForNode(sg, nlist->node) != NULL) {
     439             :                                 ignore = 1;
     440             :                                 break;
     441             :                         }
     442             : #endif
     443             : 
     444             : #ifndef GPAC_DISABLE_SVG
     445          10 :                         if (type) {
     446           0 :                                 ReplaceIRINode(nlist->node, node, NULL);
     447             :                         } else
     448             : #endif
     449          10 :                                 ReplaceDEFNode(nlist->node, reg_node->node, NULL, 0);
     450             : 
     451             :                         /*direct cyclic reference to ourselves, make sure we update the parentList to the next entry before freeing it
     452             :                         since the next parent node could be reg_node again (reg_node->reg_node)*/
     453          10 :                         if (nlist->node==node) {
     454           0 :                                 node->sgprivate->parents = next;
     455             :                         }
     456             : 
     457          10 :                         gf_free(nlist);
     458             :                         nlist = next;
     459             : #if 0
     460             :                         if (ignore) {
     461             :                                 node->sgprivate->parents = nlist;
     462             :                                 continue;
     463             :                         }
     464             : #endif
     465             :                 }
     466             : 
     467           8 :                 node->sgprivate->parents = NULL;
     468             : 
     469             :                 //sg->node_registry[i-1] = NULL;
     470           8 :                 count = get_num_id_nodes(sg);
     471           8 :                 node->sgprivate->num_instances = 1;
     472             :                 /*remember this node was forced to be destroyed*/
     473           8 :                 gf_list_add(sg->exported_nodes, node);
     474           8 :                 gf_node_unregister(node, NULL);
     475          16 :                 if (count != get_num_id_nodes(sg)) goto restart;
     476           0 :                 reg_node = reg_node->next;
     477             :         }
     478             : 
     479             :         /*reset the forced destroy ndoes*/
     480        5599 :         gf_list_reset(sg->exported_nodes);
     481             : 
     482             : #ifndef GPAC_DISABLE_VRML
     483             :         /*destroy all proto*/
     484       11262 :         while (gf_list_count(sg->protos)) {
     485          64 :                 GF_Proto *p = (GF_Proto *)gf_list_get(sg->protos, 0);
     486             :                 /*this will unregister the proto from the graph, so don't delete the chain entry*/
     487          64 :                 gf_sg_proto_del(p);
     488             :         }
     489             :         /*destroy all unregistered proto*/
     490        5599 :         while (gf_list_count(sg->unregistered_protos)) {
     491           0 :                 GF_Proto *p = (GF_Proto *)gf_list_get(sg->unregistered_protos, 0);
     492             :                 /*this will unregister the proto from the graph, so don't delete the chain entry*/
     493           0 :                 gf_sg_proto_del(p);
     494             :         }
     495             : 
     496             :         /*last destroy all routes*/
     497        5599 :         gf_sg_destroy_routes(sg);
     498        5599 :         sg->simulation_tick = 0;
     499             : 
     500             : #endif /*GPAC_DISABLE_VRML*/
     501             : 
     502             : #ifndef GPAC_DISABLE_SVG
     503             : //      assert(gf_list_count(sg->xlink_hrefs) == 0);
     504             : #endif
     505             : 
     506       11285 :         while (gf_list_count(sg->ns)) {
     507          87 :                 GF_XMLNS *ns = gf_list_get(sg->ns, 0);
     508          87 :                 gf_list_rem(sg->ns, 0);
     509          87 :                 if (ns->name) gf_free(ns->name);
     510          87 :                 if (ns->qname) gf_free(ns->qname);
     511          87 :                 gf_free(ns);
     512             :         }
     513        5599 :         gf_list_del(sg->ns);
     514        5599 :         sg->ns = 0;
     515             : 
     516             :         par = sg;
     517        5599 :         while (par->parent_scene) par = par->parent_scene;
     518             : 
     519             : #ifndef GPAC_DISABLE_SVG
     520        5599 :         if (par != sg) {
     521             :                 u32 i;
     522        4248 :                 count = gf_list_count(par->smil_timed_elements);
     523        4352 :                 for (i=0; i<count; i++) {
     524         104 :                         SMIL_Timing_RTI *rti = gf_list_get(par->smil_timed_elements, i);
     525         104 :                         if (rti->timed_elt->sgprivate->scenegraph == sg) {
     526           0 :                                 gf_list_rem(par->smil_timed_elements, i);
     527           0 :                                 i--;
     528           0 :                                 count--;
     529             :                         }
     530             :                 }
     531             :         }
     532             : #endif
     533             : 
     534             : #ifdef GF_SELF_REPLACE_ENABLE
     535             :         sg->graph_has_been_reset = 1;
     536             : #endif
     537        5599 :         GF_LOG(GF_LOG_DEBUG, GF_LOG_SCENE, ("[SceneGraph] Scene graph has been reset\n"));
     538             : }
     539             : 
     540             : 
     541             : GFINLINE GF_Node *SG_SearchForDuplicateNodeID(GF_SceneGraph *sg, u32 nodeID, GF_Node *toExclude)
     542             : {
     543             :         NodeIDedItem *reg_node = sg->id_node;
     544             :         while (reg_node) {
     545             :                 if ((reg_node->node != toExclude) && (reg_node->NodeID == nodeID)) return reg_node->node;
     546             :                 reg_node = reg_node->next;
     547             :         }
     548             :         return NULL;
     549             : }
     550             : 
     551             : #if 0 //unused
     552             : void *gf_node_get_name_address(GF_Node*node)
     553             : {
     554             :         NodeIDedItem *reg_node;
     555             :         if (!(node->sgprivate->flags & GF_NODE_IS_DEF)) return NULL;
     556             :         reg_node = node->sgprivate->scenegraph->id_node;
     557             :         while (reg_node) {
     558             :                 if (reg_node->node == node) return &reg_node->NodeName;
     559             :                 reg_node = reg_node->next;
     560             :         }
     561             :         return NULL;
     562             : }
     563             : #endif
     564             : 
     565             : GF_EXPORT
     566         682 : void gf_sg_set_private(GF_SceneGraph *sg, void *ptr)
     567             : {
     568         682 :         if (sg) sg->userpriv = ptr;
     569         682 : }
     570             : 
     571             : GF_EXPORT
     572       39346 : void *gf_sg_get_private(GF_SceneGraph *sg)
     573             : {
     574       39346 :         return sg ? sg->userpriv : NULL;
     575             : }
     576             : 
     577             : 
     578             : GF_EXPORT
     579        2032 : void gf_sg_set_scene_size_info(GF_SceneGraph *sg, u32 width, u32 height, Bool usePixelMetrics)
     580             : {
     581        2032 :         if (!sg) return;
     582        2032 :         if (width && height) {
     583        1961 :                 sg->width = width;
     584        1961 :                 sg->height = height;
     585             :         } else {
     586          71 :                 sg->width = sg->height = 0;
     587             :         }
     588        2032 :         sg->usePixelMetrics = usePixelMetrics;
     589             : }
     590             : 
     591             : GF_EXPORT
     592        7396 : Bool gf_sg_use_pixel_metrics(GF_SceneGraph *sg)
     593             : {
     594        7396 :         if (sg) {
     595             : #ifndef GPAC_DISABLE_VRML
     596           0 :                 while (sg->pOwningProto) sg = sg->parent_scene;
     597             : #endif
     598        7396 :                 return sg->usePixelMetrics;
     599             :         }
     600             :         return 0;
     601             : }
     602             : 
     603             : GF_EXPORT
     604        7096 : Bool gf_sg_get_scene_size_info(GF_SceneGraph *sg, u32 *width, u32 *height)
     605             : {
     606        7096 :         if (!sg) return 0;
     607        7096 :         *width = sg->width;
     608        7096 :         *height = sg->height;
     609        7096 :         return (sg->width && sg->height) ? 1 : 0;
     610             : }
     611             : 
     612             : 
     613             : GF_EXPORT
     614      132405 : GF_Node *gf_sg_get_root_node(GF_SceneGraph *sg)
     615             : {
     616      132405 :         return sg ? sg->RootNode : NULL;
     617             : }
     618             : 
     619             : GF_EXPORT
     620         374 : void gf_sg_set_root_node(GF_SceneGraph *sg, GF_Node *node)
     621             : {
     622         374 :         if (sg) sg->RootNode = node;
     623         374 : }
     624             : 
     625       15730 : void remove_node_id(GF_SceneGraph *sg, GF_Node *node)
     626             : {
     627       15730 :         NodeIDedItem *reg_node = sg->id_node;
     628       15730 :         if (!reg_node) return;
     629             :         
     630       15730 :         if (reg_node->node==node) {
     631       10544 :                 sg->id_node = reg_node->next;
     632       10544 :                 if (sg->id_node_last==reg_node)
     633        1558 :                         sg->id_node_last = reg_node->next;
     634       10544 :                 if (reg_node->NodeName) gf_free(reg_node->NodeName);
     635       10544 :                 gf_free(reg_node);
     636             :         } else {
     637             :                 NodeIDedItem *to_del;
     638      115729 :                 while (reg_node->next) {
     639      115729 :                         if (reg_node->next->node!=node) {
     640             :                                 reg_node = reg_node->next;
     641      110543 :                                 continue;
     642             :                         }
     643             :                         to_del = reg_node->next;
     644        5186 :                         reg_node->next = to_del->next;
     645        5186 :                         if (sg->id_node_last==to_del) {
     646         705 :                                 sg->id_node_last = reg_node->next ? reg_node->next : reg_node;
     647             :                         }
     648        5186 :                         if (to_del->NodeName) gf_free(to_del->NodeName);
     649        5186 :                         to_del->NodeName = NULL;
     650        5186 :                         gf_free(to_del);
     651        5186 :                         break;
     652             :                 }
     653             :         }
     654             : }
     655             : 
     656       10614 : GF_Err gf_node_try_destroy(GF_SceneGraph *sg, GF_Node *pNode, GF_Node *parentNode)
     657             : {
     658       10614 :         if (!sg) return GF_BAD_PARAM;
     659             :         /*if node has been destroyed, don't even look at it*/
     660       10614 :         if (gf_list_find(sg->exported_nodes, pNode)>=0) return GF_OK;
     661       10610 :         if (!pNode || !pNode->sgprivate->num_instances) return GF_OK;
     662       10610 :         return gf_node_unregister(pNode, parentNode);
     663             : }
     664             : 
     665             : GF_EXPORT
     666      145225 : GF_Err gf_node_unregister(GF_Node *pNode, GF_Node *parentNode)
     667             : {
     668             : #ifndef GPAC_DISABLE_VRML
     669             :         u32 j;
     670             : #endif
     671             : #ifdef GPAC_HAS_QJS
     672             :         Bool detach=0;
     673             : #endif
     674             :         GF_SceneGraph *pSG;
     675             : 
     676      145225 :         if (!pNode) return GF_OK;
     677      111584 :         pSG = pNode->sgprivate->scenegraph;
     678             : 
     679      111584 :         if (parentNode) {
     680       65947 :                 GF_ParentList *nlist = pNode->sgprivate->parents;
     681       65947 :                 if (nlist) {
     682             :                         GF_ParentList *prev = NULL;
     683      295243 :                         while (nlist) {
     684      295243 :                                 if (nlist->node != parentNode) {
     685             :                                         prev = nlist;
     686      230918 :                                         nlist = nlist->next;
     687      230918 :                                         continue;
     688             :                                 }
     689       64325 :                                 if (prev) prev->next = nlist->next;
     690       58280 :                                 else pNode->sgprivate->parents = nlist->next;
     691       64325 :                                 gf_free(nlist);
     692             : #ifdef GPAC_HAS_QJS
     693       64325 :                                 if (pNode->sgprivate->parents==NULL) detach=1;
     694             : #endif
     695             :                                 break;
     696             :                         }
     697             :                 }
     698       65947 :                 if (parentNode->sgprivate->scenegraph != pSG) {
     699        2390 :                         gf_list_del_item(pSG->exported_nodes, pNode);
     700             :                 }
     701             :         }
     702             : 
     703             : #ifndef GPAC_DISABLE_VRML
     704             :         /*if this is a proto its is registered in its parent graph, not the current*/
     705      111584 :         if (pSG && (pNode == (GF_Node*)pSG->pOwningProto)) pSG = pSG->parent_scene;
     706             : #endif
     707             : 
     708             :         /*unregister the instance*/
     709             :         assert(pNode->sgprivate->num_instances);
     710      111584 :         pNode->sgprivate->num_instances -= 1;
     711             : 
     712             :         /*this is just an instance removed*/
     713      111584 :         if (pNode->sgprivate->num_instances) {
     714             : #ifdef GPAC_HAS_QJS
     715       31834 :                 if (pNode->sgprivate->num_instances==1) detach=1;
     716       31834 :                 if (pSG && pNode->sgprivate->scenegraph->on_node_modified && detach && pNode->sgprivate->interact && pNode->sgprivate->interact->js_binding) {
     717        4022 :                         pNode->sgprivate->scenegraph->on_node_modified(pNode->sgprivate->scenegraph, pNode, NULL, NULL);
     718             :                 }
     719             : #endif
     720             :                 return GF_OK;
     721             :         }
     722             : 
     723             :         assert(pNode->sgprivate->parents==NULL);
     724             : 
     725       79750 :         if (pSG) {
     726             :                 GF_Route *r;
     727             :                 /*if def, remove from sg def table*/
     728       79146 :                 if (pNode->sgprivate->flags & GF_NODE_IS_DEF) {
     729       15727 :                         remove_node_id(pSG, pNode);
     730             :                 }
     731             : 
     732             : #ifndef GPAC_DISABLE_VRML
     733             :                 /*check all routes from or to this node and destroy them - cf spec*/
     734       79146 :                 j=0;
     735      298232 :                 while ((r = (GF_Route *)gf_list_enum(pSG->Routes, &j))) {
     736      139940 :                         if ( (r->ToNode == pNode) || (r->FromNode == pNode)) {
     737       13622 :                                 gf_sg_route_del(r);
     738       13622 :                                 j--;
     739             :                         }
     740             :                 }
     741             : #endif
     742             : 
     743             : #ifndef GPAC_DISABLE_SVG
     744       79146 :                 if (pSG->use_stack && (gf_list_del_item(pSG->use_stack, pNode)>=0)) {
     745           0 :                         pSG->abort_bubbling = 1;
     746             :                 }
     747             : #endif
     748             : 
     749             :         }
     750             :         /*delete the node*/
     751       79750 :         if (pNode->sgprivate->scenegraph && (pNode->sgprivate->scenegraph->RootNode==pNode)) {
     752             :                 pSG = pNode->sgprivate->scenegraph;
     753         733 :                 gf_node_del(pNode);
     754         733 :                 pSG->RootNode = NULL;
     755             :         } else {
     756       79017 :                 gf_node_del(pNode);
     757             :         }
     758             :         return GF_OK;
     759             : }
     760             : 
     761             : GF_EXPORT
     762      111598 : GF_Err gf_node_register(GF_Node *node, GF_Node *parentNode)
     763             : {
     764      111598 :         if (!node) return GF_OK;
     765             : 
     766      111567 :         node->sgprivate->num_instances ++;
     767             :         /*parent may be NULL (top node and proto)*/
     768      111567 :         if (parentNode) {
     769       64335 :                 if (!node->sgprivate->parents) {
     770       51839 :                         node->sgprivate->parents = (GF_ParentList*)gf_malloc(sizeof(GF_ParentList));
     771       51839 :                         node->sgprivate->parents->next = NULL;
     772       51839 :                         node->sgprivate->parents->node = parentNode;
     773             :                 } else {
     774             :                         GF_ParentList *item, *nlist = node->sgprivate->parents;
     775      423935 :                         while (nlist->next) nlist = nlist->next;
     776       12496 :                         item = (GF_ParentList*)gf_malloc(sizeof(GF_ParentList));
     777       12496 :                         item->next = NULL;
     778       12496 :                         item->node = parentNode;
     779       12496 :                         nlist->next = item;
     780             :                 }
     781       64335 :                 if (parentNode->sgprivate->scenegraph != node->sgprivate->scenegraph) {
     782        2390 :                         gf_list_add(node->sgprivate->scenegraph->exported_nodes, node);
     783             :                 }
     784             :         }
     785             :         return GF_OK;
     786             : }
     787             : 
     788             : /*replace or remove node instance in the given node (eg in all GF_Node or MFNode fields)
     789             : this doesn't propagate in the scene graph. If updateOrderedGroup and new_node is NULL, the order field of OG
     790             : is updated*/
     791         460 : static void ReplaceDEFNode(GF_Node *FromNode, GF_Node *node, GF_Node *newNode, Bool updateOrderedGroup)
     792             : {
     793             :         u32 i, j, count;
     794             :         GF_Node *p;
     795             :         GF_ChildNodeItem *list;
     796             :         GF_FieldInfo field;
     797             : 
     798             :         /*browse all fields*/
     799         460 :         count = gf_node_get_field_count(FromNode);
     800         901 :         for (i=0; i<count; i++) {
     801         901 :                 gf_node_get_field(FromNode, i, &field);
     802         901 :                 switch (field.fieldType) {
     803         386 :                 case GF_SG_VRML_SFNODE:
     804             :                         /*set to NULL for SFFields*/
     805         386 :                         p = *((GF_Node **) field.far_ptr);
     806             :                         /*this is a USE / DEF*/
     807         386 :                         if (p == node) {
     808         340 :                                 *((GF_Node **) field.far_ptr) = NULL;
     809         340 :                                 if (newNode) {
     810           9 :                                         *((GF_Node **) field.far_ptr) = newNode;
     811             :                                 }
     812             :                                 goto exit;
     813             :                         }
     814             :                         break;
     815         342 :                 case GF_SG_VRML_MFNODE:
     816         342 :                         list = *(GF_ChildNodeItem **) field.far_ptr;
     817             :                         j=0;
     818        2886 :                         while (list) {
     819             :                                 /*replace nodes different from newNode but with same ID*/
     820        2322 :                                 if ((newNode == list->node) || (list->node != node)) {
     821        2202 :                                         list = list->next;
     822        2202 :                                         j++;
     823        2202 :                                         continue;
     824             :                                 }
     825         120 :                                 if (newNode) {
     826          37 :                                         list->node = newNode;
     827             :                                 } else {
     828          83 :                                         gf_node_list_del_child( (GF_ChildNodeItem **) field.far_ptr, list->node);
     829             : #ifndef GPAC_DISABLE_VRML
     830          83 :                                         if (updateOrderedGroup && (FromNode->sgprivate->tag==TAG_MPEG4_OrderedGroup)) {
     831             :                                                 GF_FieldInfo info;
     832             :                                                 M_OrderedGroup *og = (M_OrderedGroup *)FromNode;
     833          12 :                                                 info.fieldIndex = 3;
     834          12 :                                                 info.fieldType = GF_SG_VRML_MFFLOAT;
     835          12 :                                                 info.on_event_in = NULL;
     836          12 :                                                 info.far_ptr = &og->order;
     837          12 :                                                 gf_sg_vrml_mf_remove(&og->order, GF_SG_VRML_SFINT32, j);
     838          12 :                                                 gf_node_changed_internal(FromNode, &info, 1);
     839             :                                         }
     840             : #endif
     841             :                                 }
     842             :                                 goto exit;
     843             :                         }
     844             :                         break;
     845             :                 /*not a node, continue*/
     846         173 :                 default:
     847         173 :                         continue;
     848             :                 }
     849             :         }
     850             :         /*since we don't filter parent nodes this is called once per USE, not per container, so return if found*/
     851           0 : exit:
     852             : 
     853             : #ifndef GPAC_DISABLE_VRML
     854             :         /*Notify all scripts that the node is being removed from its parent - we have to do that because nodes used in scripts are not
     855             :         always registered in MF/SFNodes fields of the script.*/
     856         460 :         switch (FromNode->sgprivate->tag) {
     857           0 :         case TAG_MPEG4_Script:
     858             : #ifndef GPAC_DISABLE_X3D
     859             :         case TAG_X3D_Script:
     860             : #endif
     861           0 :                 if (FromNode->sgprivate->scenegraph->on_node_modified)
     862           0 :                         FromNode->sgprivate->scenegraph->on_node_modified(FromNode->sgprivate->scenegraph, node, NULL, FromNode);
     863             :                 break;
     864             :         }
     865             : #endif
     866             : 
     867         460 :         gf_node_changed(FromNode, &field);
     868         460 : }
     869             : 
     870             : #ifndef GPAC_DISABLE_SVG
     871             : 
     872           4 : static void Replace_IRI(GF_SceneGraph *sg, GF_Node *old_node, GF_Node *newNode)
     873             : {
     874             :         u32 i, count;
     875           4 :         count = gf_list_count(sg->xlink_hrefs);
     876          80 :         for (i=0; i<count; i++) {
     877          80 :                 XMLRI *iri = (XMLRI *)gf_list_get(sg->xlink_hrefs, i);
     878          80 :                 if (iri->target == old_node) {
     879           0 :                         iri->target = newNode;
     880           0 :                         if (!newNode) {
     881           0 :                                 gf_list_rem(sg->xlink_hrefs, i);
     882           0 :                                 i--;
     883           0 :                                 count--;
     884             :                         }
     885             :                 }
     886             :         }
     887           4 : }
     888             : 
     889             : /*replace or remove node instance in the given node (eg in all IRI)*/
     890           4 : static void ReplaceIRINode(GF_Node *FromNode, GF_Node *old_node, GF_Node *newNode)
     891             : {
     892             :         GF_ChildNodeItem *prev = NULL;
     893           4 :         GF_ChildNodeItem *child = ((SVG_Element *)FromNode)->children;
     894          93 :         while (child) {
     895          89 :                 if (child->node != old_node) {
     896             :                         prev = child;
     897          85 :                         child = child->next;
     898          85 :                         continue;
     899             :                 }
     900           4 :                 if (newNode) {
     901           0 :                         child->node = newNode;
     902             :                 } else {
     903           4 :                         if (prev) prev->next = child->next;
     904           2 :                         else ((SVG_Element *)FromNode)->children = child->next;
     905           4 :                         gf_free(child);
     906             :                 }
     907             :                 break;
     908             :         }
     909           4 : }
     910             : #endif
     911             : 
     912             : /*get all parents of the node and replace, the instance of the node and finally destroy the node*/
     913             : GF_EXPORT
     914         340 : GF_Err gf_node_replace(GF_Node *node, GF_Node *new_node, Bool updateOrderedGroup)
     915             : {
     916             : #ifndef GPAC_DISABLE_SVG
     917             :         u32 type;
     918             : #endif
     919             : #ifndef GPAC_DISABLE_VRML
     920             :         Bool replace_proto;
     921             : #endif
     922             :         Bool replace_root;
     923             : 
     924             : #ifndef GPAC_DISABLE_SVG
     925         340 :         type = (node->sgprivate->tag>GF_NODE_RANGE_LAST_VRML) ? 1 : 0;
     926         340 :         if (type) {
     927           4 :                 Replace_IRI(node->sgprivate->scenegraph, node, new_node);
     928             :         }
     929             : #endif
     930             : 
     931             :         /*first check if this is the root node*/
     932         340 :         replace_root = (node->sgprivate->scenegraph->RootNode == node) ? 1 : 0;
     933             : 
     934             : #ifndef GPAC_DISABLE_VRML
     935             :         /*check for proto replacement*/
     936             :         replace_proto = 0;
     937         340 :         if (node->sgprivate->scenegraph->pOwningProto
     938           1 :                 && (gf_list_find(node->sgprivate->scenegraph->pOwningProto->node_code, node)>=0)) {
     939             :                 replace_proto = 1;
     940             :         }
     941             : #endif
     942             : 
     943         454 :         while (node->sgprivate->parents) {
     944         454 :                 Bool do_break = node->sgprivate->parents->next ? 0 : 1;
     945         454 :                 GF_Node *par = node->sgprivate->parents->node;
     946             : 
     947             : #ifndef GPAC_DISABLE_SVG
     948         454 :                 if (type)
     949           4 :                         ReplaceIRINode(par, node, new_node);
     950             :                 else
     951             : #endif
     952         450 :                         ReplaceDEFNode(par, node, new_node, updateOrderedGroup);
     953             : 
     954         454 :                 if (new_node) gf_node_register(new_node, par);
     955         454 :                 gf_node_unregister(node, par);
     956         454 :                 gf_node_changed(par, NULL);
     957         454 :                 if (do_break) break;
     958             :         }
     959             : 
     960         340 :         if (replace_root) {
     961           0 :                 GF_SceneGraph *pSG = node->sgprivate->scenegraph;
     962           0 :                 gf_node_unregister(node, NULL);
     963           0 :                 pSG->RootNode = new_node;
     964             :         }
     965             : #ifndef GPAC_DISABLE_VRML
     966         340 :         if (replace_proto) {
     967           0 :                 GF_SceneGraph *pSG = node->sgprivate->scenegraph;
     968           0 :                 gf_list_del_item(pSG->pOwningProto->node_code, node);
     969           0 :                 if (pSG->pOwningProto->RenderingNode==node) pSG->pOwningProto->RenderingNode = NULL;
     970           0 :                 gf_node_unregister(node, NULL);
     971             :         }
     972             : #endif
     973         340 :         return GF_OK;
     974             : }
     975             : 
     976       15730 : static GFINLINE void insert_node_def(GF_SceneGraph *sg, GF_Node *def, u32 ID, const char *name)
     977             : {
     978             :         NodeIDedItem *reg_node, *cur;
     979             : 
     980       15730 :         reg_node = (NodeIDedItem *) gf_malloc(sizeof(NodeIDedItem));
     981       15730 :         reg_node->node = def;
     982       15730 :         reg_node->NodeID = ID;
     983       15730 :         reg_node->NodeName = name ? gf_strdup(name) : NULL;
     984             : 
     985       15730 :         if (!sg->id_node) {
     986        1558 :                 sg->id_node = reg_node;
     987        1558 :                 sg->id_node_last = sg->id_node;
     988        1558 :                 reg_node->next = NULL;
     989       14172 :         } else if (sg->id_node_last->NodeID < ID) {
     990       14049 :                 sg->id_node_last->next = reg_node;
     991       14049 :                 sg->id_node_last = reg_node;
     992       14049 :                 reg_node->next = NULL;
     993         123 :         } else if (sg->id_node->NodeID>ID) {
     994           8 :                 reg_node->next = sg->id_node;
     995           8 :                 sg->id_node = reg_node;
     996             :         } else {
     997             :                 cur = sg->id_node;
     998        1186 :                 while (cur->next) {
     999        1185 :                         if (cur->next->NodeID>ID) {
    1000         114 :                                 reg_node->next = cur->next;
    1001         114 :                                 cur->next = reg_node;
    1002             :                                 return;
    1003             :                         }
    1004             :                         cur = cur->next;
    1005             :                 }
    1006           1 :                 cur->next = reg_node;
    1007           1 :                 sg->id_node_last = reg_node;
    1008           1 :                 reg_node->next = NULL;
    1009             :         }
    1010             : }
    1011             : 
    1012             : 
    1013             : 
    1014             : GF_EXPORT
    1015       15730 : GF_Err gf_node_set_id(GF_Node *p, u32 ID, const char *name)
    1016             : {
    1017             :         GF_SceneGraph *pSG;
    1018       15730 :         if (!ID || !p || !p->sgprivate->scenegraph) return GF_BAD_PARAM;
    1019             : 
    1020             :         pSG = p->sgprivate->scenegraph;
    1021             : #ifndef GPAC_DISABLE_VRML
    1022             :         /*if this is a proto register to the parent graph, not the current*/
    1023       15730 :         if (p == (GF_Node*)pSG->pOwningProto) pSG = pSG->parent_scene;
    1024             : #endif
    1025             : 
    1026             :         /*new DEF ID*/
    1027       15730 :         if (!(p->sgprivate->flags & GF_NODE_IS_DEF) ) {
    1028       15728 :                 p->sgprivate->flags |= GF_NODE_IS_DEF;
    1029       15728 :                 insert_node_def(pSG, p, ID, name);
    1030             :         }
    1031             :         /*reassigning ID, remove node def*/
    1032             :         else {
    1033           2 :                 char *_name = gf_strdup(name);
    1034           2 :                 remove_node_id(pSG, p);
    1035           2 :                 insert_node_def(pSG, p, ID, _name);
    1036           2 :                 gf_free(_name);
    1037             :         }
    1038             :         return GF_OK;
    1039             : }
    1040             : 
    1041             : GF_EXPORT
    1042        2808 : GF_Err gf_node_remove_id(GF_Node *p)
    1043             : {
    1044             :         GF_SceneGraph *pSG;
    1045        2808 :         if (!p) return GF_BAD_PARAM;
    1046             : 
    1047           1 :         pSG = p->sgprivate->scenegraph;
    1048             : #ifndef GPAC_DISABLE_VRML
    1049             :         /*if this is a proto register to the parent graph, not the current*/
    1050           1 :         if (p == (GF_Node*)pSG->pOwningProto) pSG = pSG->parent_scene;
    1051             : #endif
    1052             : 
    1053             :         /*new DEF ID*/
    1054           1 :         if (p->sgprivate->flags & GF_NODE_IS_DEF) {
    1055           1 :                 remove_node_id(pSG, p);
    1056           1 :                 p->sgprivate->flags &= ~GF_NODE_IS_DEF;
    1057           1 :                 return GF_OK;
    1058             :         }
    1059             :         return GF_BAD_PARAM;
    1060             : }
    1061             : 
    1062             : /*calls RenderNode on this node*/
    1063             : GF_EXPORT
    1064     1750225 : void gf_node_traverse(GF_Node *node, void *renderStack)
    1065             : {
    1066     1750225 :         if (!node || !node->sgprivate) return;
    1067             : 
    1068     1749499 :         if (node->sgprivate->flags & GF_NODE_IS_DEACTIVATED) return;
    1069             : 
    1070     1749499 :         if (node->sgprivate->UserCallback) {
    1071             : #ifdef GF_CYCLIC_TRAVERSE_ON
    1072     1565611 :                 if (node->sgprivate->flags & GF_NODE_IN_TRAVERSE) return;
    1073     1565609 :                 node->sgprivate->flags |= GF_NODE_IN_TRAVERSE;
    1074             :                 assert(node->sgprivate->flags);
    1075             : #endif
    1076     1565609 :                 GF_LOG(GF_LOG_DEBUG, GF_LOG_SCENE, ("[SceneGraph] Traversing node %s (ID %s)\n", gf_node_get_class_name(node) , gf_node_get_name(node) ));
    1077     1565609 :                 node->sgprivate->UserCallback(node, renderStack, 0);
    1078             : #ifdef GF_CYCLIC_TRAVERSE_ON
    1079     1565609 :                 node->sgprivate->flags &= ~GF_NODE_IN_TRAVERSE;
    1080             : #endif
    1081     1565609 :                 return;
    1082             :         }
    1083             : 
    1084             : #ifndef GPAC_DISABLE_VRML
    1085      183888 :         if (node->sgprivate->tag != TAG_ProtoNode) return;
    1086             : 
    1087             :         /*if no rendering function is assigned this is a real proto (otherwise this is an hardcoded one)*/
    1088             :         if (!node->sgprivate->UserCallback) {
    1089             : 
    1090             :                 /*if a rendering node is assigned use it*/
    1091       92957 :                 if (((GF_ProtoInstance *) node)->RenderingNode) {
    1092             :                         node = ((GF_ProtoInstance *) node)->RenderingNode;
    1093             :                         /*if rendering node is a proto and not a hardcoded proto, traverse it*/
    1094       92766 :                         if (!node->sgprivate->UserCallback && (node->sgprivate->tag == TAG_ProtoNode)) {
    1095             :                                 gf_node_traverse(node, renderStack);
    1096             :                                 return;
    1097             :                         }
    1098             :                 }
    1099             :                 /*if no rendering node, check if the proto is fully instantiated (externProto)*/
    1100             :                 else {
    1101             :                         GF_ProtoInstance *proto_inst = (GF_ProtoInstance *) node;
    1102         191 :                         gf_node_dirty_clear(node, 0);
    1103             :                         /*proto has been deleted or dummy proto (without node code)*/
    1104         191 :                         if (!proto_inst->proto_interface || (proto_inst->flags & GF_SG_PROTO_LOADED) ) return;
    1105             :                         /*try to load the code*/
    1106         191 :                         gf_sg_proto_instantiate(proto_inst);
    1107             : 
    1108             :                         /*if user callback is set, this is an hardcoded proto. If not, locate the first traversable node*/
    1109         191 :                         if (!node->sgprivate->UserCallback) {
    1110         191 :                                 if (!proto_inst->RenderingNode) {
    1111         176 :                                         gf_node_dirty_set(node, 0, 1);
    1112         176 :                                         return;
    1113             :                                 }
    1114             :                                 /*signal we have been loaded*/
    1115          15 :                                 node->sgprivate->scenegraph->NodeCallback(node->sgprivate->scenegraph->userpriv, GF_SG_CALLBACK_MODIFIED, node, NULL);
    1116             :                         }
    1117             :                 }
    1118             :         }
    1119             : 
    1120       92781 :         if (node->sgprivate->UserCallback) {
    1121             : #ifdef GF_CYCLIC_TRAVERSE_ON
    1122       92766 :                 if (node->sgprivate->flags & GF_NODE_IN_TRAVERSE) return;
    1123       92766 :                 node->sgprivate->flags |= GF_NODE_IN_TRAVERSE;
    1124             : #endif
    1125       92766 :                 GF_LOG(GF_LOG_DEBUG, GF_LOG_SCENE, ("[SceneGraph] Traversing node %s\n", gf_node_get_class_name(node) ));
    1126       92766 :                 node->sgprivate->UserCallback(node, renderStack, 0);
    1127             : #ifdef GF_CYCLIC_TRAVERSE_ON
    1128       92766 :                 node->sgprivate->flags &= ~GF_NODE_IN_TRAVERSE;
    1129             : #endif
    1130             :         }
    1131             : 
    1132             : #endif /*GPAC_DISABLE_VRML*/
    1133             : }
    1134             : 
    1135             : GF_EXPORT
    1136        4262 : void gf_node_allow_cyclic_traverse(GF_Node *node)
    1137             : {
    1138             : #ifdef GF_CYCLIC_TRAVERSE_ON
    1139        4262 :         if (node) node->sgprivate->flags &= ~GF_NODE_IN_TRAVERSE;
    1140             : #endif
    1141        4262 : }
    1142             : 
    1143             : 
    1144             : GF_EXPORT
    1145        9258 : Bool gf_node_set_cyclic_traverse_flag(GF_Node *node, Bool on)
    1146             : {
    1147             :         Bool ret = 1;
    1148             : #ifdef GF_CYCLIC_TRAVERSE_ON
    1149        9258 :         if (node) {
    1150        9258 :                 ret = (node->sgprivate->flags & GF_NODE_IN_TRAVERSE) ? 0 : 1;
    1151        9258 :                 if (on) {
    1152        4629 :                         node->sgprivate->flags |= GF_NODE_IN_TRAVERSE;
    1153             :                 } else {
    1154        4629 :                         node->sgprivate->flags &= ~GF_NODE_IN_TRAVERSE;
    1155             :                 }
    1156             :         }
    1157             : #endif
    1158        9258 :         return ret;
    1159             : }
    1160             : 
    1161             : /*blindly calls RenderNode on all nodes in the "children" list*/
    1162             : GF_EXPORT
    1163        2372 : void gf_node_traverse_children(GF_Node *node, void *renderStack)
    1164             : {
    1165             :         GF_ChildNodeItem *child;
    1166             : 
    1167             :         assert(node);
    1168        2372 :         child = ((GF_ParentNode *)node)->children;
    1169        7899 :         while (child) {
    1170        3155 :                 gf_node_traverse(child->node, renderStack);
    1171        3155 :                 child = child->next;
    1172             :         }
    1173        2372 : }
    1174             : 
    1175             : 
    1176             : GF_EXPORT
    1177       39689 : GF_SceneGraph *gf_node_get_graph(GF_Node *node)
    1178             : {
    1179       39689 :         return (node ? node->sgprivate->scenegraph : NULL);
    1180             : }
    1181             : 
    1182             : GF_EXPORT
    1183       21151 : GF_Node *gf_sg_find_node(GF_SceneGraph *sg, u32 nodeID)
    1184             : {
    1185       21151 :         NodeIDedItem *reg_node = sg->id_node;
    1186     1191206 :         while (reg_node) {
    1187     1169584 :                 if (reg_node->NodeID == nodeID) return reg_node->node;
    1188     1148904 :                 reg_node = reg_node->next;
    1189             :         }
    1190             :         return NULL;
    1191             : }
    1192             : 
    1193             : GF_EXPORT
    1194       31139 : GF_Node *gf_sg_find_node_by_name(GF_SceneGraph *sg, char *name)
    1195             : {
    1196       31139 :         if (name) {
    1197       31139 :                 NodeIDedItem *reg_node = sg->id_node;
    1198     1212934 :                 while (reg_node) {
    1199     1171895 :                         if (reg_node->NodeName && !strcmp(reg_node->NodeName, name)) return reg_node->node;
    1200     1150656 :                         reg_node = reg_node->next;
    1201             :                 }
    1202             :         }
    1203             :         return NULL;
    1204             : }
    1205             : 
    1206             : 
    1207             : GF_EXPORT
    1208        6095 : u32 gf_sg_get_next_available_node_id(GF_SceneGraph *sg)
    1209             : {
    1210             :         u32 ID;
    1211             :         NodeIDedItem *reg_node;
    1212        6095 :         if (!sg->id_node) return 1;
    1213             :         reg_node = sg->id_node;
    1214        5463 :         ID = reg_node->NodeID;
    1215             :         /*nodes are sorted*/
    1216      165565 :         while (reg_node->next) {
    1217      154652 :                 if (ID+1<reg_node->next->NodeID) return ID+1;
    1218             :                 ID = reg_node->next->NodeID;
    1219             :                 reg_node = reg_node->next;
    1220             :         }
    1221        5450 :         return ID+1;
    1222             : }
    1223             : 
    1224             : GF_EXPORT
    1225        2360 : u32 gf_sg_get_max_node_id(GF_SceneGraph *sg)
    1226             : {
    1227             :         NodeIDedItem *reg_node;
    1228        2360 :         if (!sg->id_node) return 0;
    1229        2287 :         if (sg->id_node_last) return sg->id_node_last->NodeID;
    1230             :         reg_node = sg->id_node;
    1231           0 :         while (reg_node->next) reg_node = reg_node->next;
    1232           0 :         return reg_node->NodeID;
    1233             : }
    1234             : 
    1235       79751 : void gf_node_setup(GF_Node *p, u32 tag)
    1236             : {
    1237       79751 :         if (!p) {
    1238           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[SceneGraph] Failed to setup NULL node\n"));
    1239             :                 return;
    1240             :         }
    1241       79751 :         GF_SAFEALLOC(p->sgprivate, NodePriv);
    1242       79751 :         if (!p->sgprivate) {
    1243           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[SceneGraph] Failed to allocate node scenegraph private handler\n"));
    1244             :                 return;
    1245             :         }
    1246       79751 :         p->sgprivate->tag = tag;
    1247       79751 :         p->sgprivate->flags = GF_SG_NODE_DIRTY;
    1248             : }
    1249             : 
    1250         614 : GF_Node *gf_sg_new_base_node()
    1251             : {
    1252         614 :         GF_Node *newnode = (GF_Node *)gf_malloc(sizeof(GF_Node));
    1253         614 :         gf_node_setup(newnode, TAG_UndefinedNode);
    1254         614 :         return newnode;
    1255             : }
    1256             : GF_EXPORT
    1257     1683951 : u32 gf_node_get_tag(GF_Node*p)
    1258             : {
    1259             :         assert(p);
    1260     1683951 :         return p->sgprivate->tag;
    1261             : }
    1262             : GF_EXPORT
    1263       57255 : u32 gf_node_get_id(GF_Node*p)
    1264             : {
    1265             :         NodeIDedItem *reg_node;
    1266             :         GF_SceneGraph *sg;
    1267             :         assert(p);
    1268       57255 :         if (!(p->sgprivate->flags & GF_NODE_IS_DEF)) return 0;
    1269       39408 :         sg = p->sgprivate->scenegraph;
    1270             : #ifndef GPAC_DISABLE_VRML
    1271             :         /*if this is a proto, look in parent graph*/
    1272       39408 :         if (p == (GF_Node*)sg->pOwningProto) sg = sg->parent_scene;
    1273             : #endif
    1274             : 
    1275       39408 :         reg_node = sg->id_node;
    1276     2380120 :         while (reg_node) {
    1277     2340712 :                 if (reg_node->node==p) return reg_node->NodeID;
    1278     2301304 :                 reg_node = reg_node->next;
    1279             :         }
    1280             :         return 0;
    1281             : }
    1282             : 
    1283             : GF_EXPORT
    1284        1733 : const char *gf_node_get_name(GF_Node*p)
    1285             : {
    1286             :         GF_SceneGraph *sg;
    1287             :         NodeIDedItem *reg_node;
    1288        1733 :         if (!p || !(p->sgprivate->flags & GF_NODE_IS_DEF)) return NULL;
    1289             : 
    1290        1719 :         sg = p->sgprivate->scenegraph;
    1291             : #ifndef GPAC_DISABLE_VRML
    1292             :         /*if this is a proto, look in parent graph*/
    1293        1719 :         if (p == (GF_Node*)sg->pOwningProto) sg = sg->parent_scene;
    1294             : #endif
    1295             : 
    1296        1719 :         reg_node = sg->id_node;
    1297      188958 :         while (reg_node) {
    1298      187239 :                 if (reg_node->node==p) return reg_node->NodeName;
    1299      185520 :                 reg_node = reg_node->next;
    1300             :         }
    1301             :         return NULL;
    1302             : }
    1303             : 
    1304             : GF_EXPORT
    1305       31435 : const char *gf_node_get_name_and_id(GF_Node*p, u32 *id)
    1306             : {
    1307             :         GF_SceneGraph *sg;
    1308             :         NodeIDedItem *reg_node;
    1309             :         assert(p);
    1310       31435 :         if (!(p->sgprivate->flags & GF_NODE_IS_DEF)) {
    1311       22297 :                 *id = 0;
    1312       22297 :                 return NULL;
    1313             :         }
    1314             : 
    1315        9138 :         sg = p->sgprivate->scenegraph;
    1316             : #ifndef GPAC_DISABLE_VRML
    1317             :         /*if this is a proto, look in parent graph*/
    1318        9138 :         if (p == (GF_Node*)sg->pOwningProto) sg = sg->parent_scene;
    1319             : #endif
    1320             : 
    1321        9138 :         reg_node = sg->id_node;
    1322      262124 :         while (reg_node) {
    1323      252986 :                 if (reg_node->node==p) {
    1324        9138 :                         *id = reg_node->NodeID;
    1325        9138 :                         return reg_node->NodeName;
    1326             :                 }
    1327      243848 :                 reg_node = reg_node->next;
    1328             :         }
    1329           0 :         *id = 0;
    1330           0 :         return NULL;
    1331             : }
    1332             : 
    1333             : GF_EXPORT
    1334     1611070 : void *gf_node_get_private(GF_Node*p)
    1335             : {
    1336             :         assert(p);
    1337     1611070 :         return p->sgprivate->UserPrivate;
    1338             : }
    1339             : GF_EXPORT
    1340       23581 : void gf_node_set_private(GF_Node*p, void *pr)
    1341             : {
    1342             :         assert(p);
    1343       23581 :         p->sgprivate->UserPrivate = pr;
    1344       23581 : }
    1345             : GF_EXPORT
    1346       27769 : GF_Err gf_node_set_callback_function(GF_Node *p, void (*TraverseNode)(GF_Node *node, void *render_stack, Bool is_destroy) )
    1347             : {
    1348             :         assert(p);
    1349       27769 :         p->sgprivate->UserCallback = TraverseNode;
    1350       27769 :         return GF_OK;
    1351             : }
    1352             : 
    1353        5179 : void gf_sg_parent_setup(GF_Node *node)
    1354             : {
    1355        5179 :         ((GF_ParentNode *)node)->children = NULL;
    1356        5179 :         node->sgprivate->flags |= GF_SG_CHILD_DIRTY;
    1357        5179 : }
    1358             : 
    1359             : GF_EXPORT
    1360       64001 : void gf_node_unregister_children(GF_Node *container, GF_ChildNodeItem *child)
    1361             : {
    1362      162981 :         while (child) {
    1363             :                 GF_ChildNodeItem *cur;
    1364       34979 :                 gf_node_unregister(child->node, container);
    1365             :                 cur = child;
    1366       34979 :                 child = child->next;
    1367       34979 :                 gf_free(cur);
    1368             :         }
    1369       64001 : }
    1370             : 
    1371             : GF_EXPORT
    1372        2194 : GF_Err gf_node_list_insert_child(GF_ChildNodeItem **list, GF_Node *n, u32 pos)
    1373             : {
    1374             :         GF_ChildNodeItem *child, *cur, *prev;
    1375             :         u32 cur_pos = 0;
    1376             : 
    1377             :         assert(pos != (u32) -1);
    1378             : 
    1379        2194 :         child = *list;
    1380             : 
    1381        2194 :         cur = (GF_ChildNodeItem*) gf_malloc(sizeof(GF_ChildNodeItem));
    1382        2194 :         if (!cur) return GF_OUT_OF_MEM;
    1383        2194 :         cur->node = n;
    1384        2194 :         cur->next = NULL;
    1385             :         prev = NULL;
    1386       87475 :         while (child) {
    1387       84631 :                 if (pos==cur_pos) break;
    1388             :                 /*append*/
    1389       84449 :                 if (!child->next) {
    1390        1362 :                         child->next = cur;
    1391        1362 :                         return GF_OK;
    1392             :                 }
    1393             :                 prev = child;
    1394             :                 child = child->next;
    1395       83087 :                 cur_pos++;
    1396             :         }
    1397         832 :         cur->next = child;
    1398         832 :         if (prev) prev->next = cur;
    1399         712 :         else *list = cur;
    1400             :         return GF_OK;
    1401             : }
    1402             : 
    1403             : #if 0 //unused
    1404             : GF_Err gf_node_list_append_child(GF_ChildNodeItem **list, GF_ChildNodeItem **last_child, GF_Node *n)
    1405             : {
    1406             :         GF_ChildNodeItem *child, *cur;
    1407             : 
    1408             :         child = *list;
    1409             : 
    1410             :         cur = (GF_ChildNodeItem*) gf_malloc(sizeof(GF_ChildNodeItem));
    1411             :         if (!cur) return GF_OUT_OF_MEM;
    1412             :         cur->node = n;
    1413             :         cur->next = NULL;
    1414             : 
    1415             :         if (!child) {
    1416             :                 *list = cur;
    1417             :                 *last_child = cur;
    1418             :         } else {
    1419             :                 if (! *last_child) {
    1420             :                         while (child->next) {
    1421             :                                 child = child->next;
    1422             :                         }
    1423             :                         *last_child = child;
    1424             :                 }
    1425             :                 (*last_child)->next = cur;
    1426             :                 *last_child = cur;
    1427             :         }
    1428             :         return GF_OK;
    1429             : }
    1430             : #endif
    1431             : 
    1432             : GF_EXPORT
    1433       39957 : GF_Node *gf_node_list_get_child(GF_ChildNodeItem *list, s32 pos)
    1434             : {
    1435             :         s32 cur_pos = 0;
    1436       92551 :         while (list) {
    1437       49767 :                 if (pos==cur_pos) return list->node;
    1438       12649 :                 if ((pos<0) && !list->next) return list->node;
    1439       12637 :                 list = list->next;
    1440       12637 :                 cur_pos++;
    1441             :         }
    1442             :         return NULL;
    1443             : }
    1444             : 
    1445             : GF_EXPORT
    1446           2 : s32 gf_node_list_find_child(GF_ChildNodeItem *list, GF_Node *n)
    1447             : {
    1448             :         s32 res = 0;
    1449          13 :         while (list) {
    1450          11 :                 if (list->node==n) return res;
    1451           9 :                 list = list->next;
    1452           9 :                 res++;
    1453             :         }
    1454             :         return -1;
    1455             : }
    1456             : 
    1457             : GF_EXPORT
    1458        9844 : GF_Err gf_node_list_add_child(GF_ChildNodeItem **list, GF_Node *n)
    1459             : {
    1460             :         GF_ChildNodeItem *child, *cur;
    1461             : 
    1462        9844 :         child = *list;
    1463             : 
    1464        9844 :         cur = (GF_ChildNodeItem*) gf_malloc(sizeof(GF_ChildNodeItem));
    1465        9844 :         if (!cur) return GF_OUT_OF_MEM;
    1466        9844 :         cur->node = n;
    1467        9844 :         cur->next = NULL;
    1468        9844 :         if (child) {
    1469      104478 :                 while (child->next) child = child->next;
    1470        4094 :                 child->next = cur;
    1471             :         } else {
    1472        5750 :                 *list = cur;
    1473             :         }
    1474             :         return GF_OK;
    1475             : }
    1476             : 
    1477             : 
    1478             : GF_EXPORT
    1479       23471 : GF_Err gf_node_list_add_child_last(GF_ChildNodeItem **list, GF_Node *n, GF_ChildNodeItem **last_child)
    1480             : {
    1481             :         GF_ChildNodeItem *child, *cur;
    1482             : 
    1483       23471 :         child = *list;
    1484             : 
    1485       23471 :         cur = (GF_ChildNodeItem*) gf_malloc(sizeof(GF_ChildNodeItem));
    1486       23471 :         if (!cur) return GF_OUT_OF_MEM;
    1487       23471 :         cur->node = n;
    1488       23471 :         cur->next = NULL;
    1489       23471 :         if (child) {
    1490       14296 :                 if (last_child && (*last_child) ) {
    1491          45 :                         while ((*last_child)->next) (*last_child) = (*last_child)->next;
    1492       14106 :                         (*last_child)->next = cur;
    1493       14106 :                         (*last_child) = (*last_child)->next;
    1494             :                 } else {
    1495         489 :                         while (child->next) child = child->next;
    1496         190 :                         child->next = cur;
    1497         190 :                         if (last_child) *last_child = child->next;
    1498             :                 }
    1499             :         } else {
    1500        9175 :                 *list = cur;
    1501        9175 :                 if (last_child)
    1502        8510 :                         *last_child = *list;
    1503             :         }
    1504             :         return GF_OK;
    1505             : }
    1506             : 
    1507             : GF_EXPORT
    1508          88 : Bool gf_node_list_del_child(GF_ChildNodeItem **list, GF_Node *n)
    1509             : {
    1510             :         GF_ChildNodeItem *child, *cur;
    1511             : 
    1512          88 :         child = *list;
    1513          88 :         if (!child) return 0;
    1514          88 :         if (child->node==n) {
    1515          11 :                 *list = child->next;
    1516          11 :                 gf_free(child);
    1517          11 :                 return 1;
    1518             :         }
    1519             : 
    1520         933 :         while (child->next) {
    1521         933 :                 if (child->next->node!=n) {
    1522             :                         child = child->next;
    1523         856 :                         continue;
    1524             :                 }
    1525             :                 cur = child->next;
    1526          77 :                 child->next = cur->next;
    1527          77 :                 gf_free(cur);
    1528          77 :                 return 1;
    1529             :         }
    1530             :         return 0;
    1531             : }
    1532             : 
    1533             : GF_EXPORT
    1534         150 : GF_Node *gf_node_list_del_child_idx(GF_ChildNodeItem **list, u32 pos)
    1535             : {
    1536             :         u32 cur_pos = 0;
    1537             :         GF_Node *ret = NULL;
    1538             :         GF_ChildNodeItem *child, *cur;
    1539             : 
    1540         150 :         child = *list;
    1541         150 :         if (!child) return 0;
    1542         150 :         if (!pos) {
    1543          40 :                 *list = child->next;
    1544          40 :                 ret = child->node;
    1545          40 :                 gf_free(child);
    1546          40 :                 return ret;
    1547             :         }
    1548             : 
    1549         110 :         while (child->next) {
    1550         110 :                 if (cur_pos+1 != pos) {
    1551             :                         child = child->next;
    1552             :                         cur_pos++;
    1553           0 :                         continue;
    1554             :                 }
    1555             :                 cur = child->next;
    1556         110 :                 child->next = cur->next;
    1557         110 :                 ret = cur->node;
    1558         110 :                 gf_free(cur);
    1559         110 :                 return ret;
    1560             :         }
    1561             :         return NULL;
    1562             : }
    1563             : 
    1564             : GF_EXPORT
    1565        8379 : u32 gf_node_list_get_count(GF_ChildNodeItem *list)
    1566             : {
    1567             :         u32 count = 0;
    1568      187844 :         while (list) {
    1569      171086 :                 count++;
    1570      171086 :                 list = list->next;
    1571             :         }
    1572        8379 :         return count;
    1573             : }
    1574             : 
    1575        6428 : void gf_sg_parent_reset(GF_Node *node)
    1576             : {
    1577        6428 :         gf_node_unregister_children(node, ((GF_ParentNode *)node)->children);
    1578        6428 :         ((GF_ParentNode *)node)->children = NULL;
    1579        6428 : }
    1580             : 
    1581       79750 : void gf_node_free(GF_Node *node)
    1582             : {
    1583       79750 :         if (!node) return;
    1584             : 
    1585       79750 :         if (node->sgprivate->UserCallback) node->sgprivate->UserCallback(node, NULL, 1);
    1586             : 
    1587       79750 :         if (node->sgprivate->scenegraph && node->sgprivate->scenegraph->NodeCallback)
    1588       37533 :                 node->sgprivate->scenegraph->NodeCallback(node->sgprivate->scenegraph->userpriv, GF_SG_CALLBACK_NODE_DESTROY, node, NULL);
    1589             : 
    1590       79750 :         if (node->sgprivate->interact) {
    1591       10400 :                 if (node->sgprivate->interact->routes) {
    1592           0 :                         gf_list_del(node->sgprivate->interact->routes);
    1593             :                 }
    1594             : #ifndef GPAC_DISABLE_SVG
    1595       10400 :                 if (node->sgprivate->interact->dom_evt) {
    1596         518 :                         gf_dom_event_remove_all_listeners(node->sgprivate->interact->dom_evt);
    1597         518 :                         gf_dom_event_target_del(node->sgprivate->interact->dom_evt);
    1598             :                 }
    1599       10400 :                 if (node->sgprivate->interact->animations) {
    1600           0 :                         gf_list_del(node->sgprivate->interact->animations);
    1601             :                 }
    1602             : #endif
    1603             : #ifdef GPAC_HAS_QJS
    1604       10400 :                 if (node->sgprivate->interact->js_binding) {
    1605        3496 :                         if (node->sgprivate->scenegraph && node->sgprivate->scenegraph->on_node_modified)
    1606        3496 :                                 node->sgprivate->scenegraph->on_node_modified(node->sgprivate->scenegraph, node, NULL, NULL);
    1607        3496 :                         gf_list_del(node->sgprivate->interact->js_binding->fields);
    1608        3496 :                         gf_free(node->sgprivate->interact->js_binding);
    1609             :                 }
    1610             : #endif
    1611       10400 :                 gf_free(node->sgprivate->interact);
    1612             :         }
    1613             :         assert(! node->sgprivate->parents);
    1614       79750 :         gf_free(node->sgprivate);
    1615       79750 :         gf_free(node);
    1616             : }
    1617             : 
    1618             : GF_EXPORT
    1619        2807 : u32 gf_node_get_parent_count(GF_Node *node)
    1620             : {
    1621             :         u32 count = 0;
    1622        2807 :         GF_ParentList *nlist = node ? node->sgprivate->parents : 0;
    1623        2807 :         while (nlist) {
    1624           0 :                 count++;
    1625           0 :                 nlist = nlist->next;
    1626             :         }
    1627        2807 :         return count;
    1628             : }
    1629             : 
    1630             : GF_EXPORT
    1631        1374 : GF_Node *gf_node_get_parent(GF_Node *node, u32 idx)
    1632             : {
    1633             : 
    1634        1374 :         GF_ParentList *nlist = node->sgprivate->parents;
    1635             :         /*break cyclic graphs*/
    1636        1374 :         if (node->sgprivate->scenegraph->RootNode==node) return NULL;
    1637             : #ifndef GPAC_DISABLE_VRML
    1638         577 :         if (node->sgprivate->scenegraph->pOwningProto && node->sgprivate->scenegraph->pOwningProto->RenderingNode==node)
    1639             :                 return NULL;
    1640             : #endif
    1641         577 :         if (!nlist) return NULL;
    1642         457 :         while (idx) {
    1643           0 :                 nlist = nlist->next;
    1644           0 :                 idx--;
    1645             :         }
    1646         457 :         return nlist ? nlist->node : NULL;
    1647             : }
    1648             : 
    1649       21377 : static void dirty_children(GF_Node *node)
    1650             : {
    1651             :         u32 i, count;
    1652             :         GF_FieldInfo info;
    1653       29559 :         if (!node) return;
    1654             : 
    1655       13195 :         node->sgprivate->flags &= GF_NODE_INTERNAL_FLAGS;
    1656       13195 :         if (node->sgprivate->tag>=GF_NODE_RANGE_LAST_VRML) {
    1657           0 :                 GF_ChildNodeItem *child = ((GF_ParentNode*)node)->children;
    1658           0 :                 while (child) {
    1659           0 :                         dirty_children(child->node);
    1660           0 :                         child = child->next;
    1661             :                 }
    1662             :         } else {
    1663       13195 :                 count = gf_node_get_field_count(node);
    1664       89346 :                 for (i=0; i<count; i++) {
    1665       76151 :                         gf_node_get_field(node, i, &info);
    1666       76151 :                         if (info.fieldType==GF_SG_VRML_SFNODE) dirty_children(*(GF_Node **)info.far_ptr);
    1667       61266 :                         else if (info.fieldType==GF_SG_VRML_MFNODE) {
    1668        1794 :                                 GF_ChildNodeItem *list = *(GF_ChildNodeItem **) info.far_ptr;
    1669        4196 :                                 while (list) {
    1670         608 :                                         dirty_children(list->node);
    1671         608 :                                         list = list->next;
    1672             :                                 }
    1673             :                         }
    1674             :                 }
    1675             :         }
    1676             : }
    1677      174189 : static void dirty_parents(GF_Node *node)
    1678             : {
    1679             :         Bool check_root = 1;
    1680             :         GF_ParentList *nlist;
    1681             : #if defined GPAC_CONFIG_ANDROID
    1682             :         if ( !node || !node->sgprivate )
    1683             :                 return;
    1684             : #else
    1685      176778 :         if (!node) return;
    1686             : #endif
    1687      176778 :         nlist = node->sgprivate->parents;
    1688      511591 :         while (nlist) {
    1689      158035 :                 GF_Node *p = nlist->node;
    1690      158035 :                 if (! (p->sgprivate->flags & GF_SG_CHILD_DIRTY)) {
    1691       95275 :                         p->sgprivate->flags |= GF_SG_CHILD_DIRTY;
    1692       95275 :                         dirty_parents(p);
    1693             :                 }
    1694             :                 check_root = 0;
    1695      158035 :                 nlist = nlist->next;
    1696             :         }
    1697             :         /*propagate to parent scene graph */
    1698             : #if defined GPAC_CONFIG_ANDROID
    1699             :         if (check_root && node->sgprivate->scenegraph) {
    1700             : #else
    1701      176778 :         if (check_root) {
    1702             : #endif
    1703             :                 /*if root node of the scenegraph*/
    1704       28908 :                 if (node->sgprivate->scenegraph->NodeCallback && (node==node->sgprivate->scenegraph->RootNode) ) {
    1705       23108 :                         node->sgprivate->scenegraph->NodeCallback(node->sgprivate->scenegraph->userpriv, GF_SG_CALLBACK_GRAPH_DIRTY, NULL, NULL);
    1706             :                 }
    1707             : #ifndef GPAC_DISABLE_VRML
    1708             :                 /*or if parent graph is a protoinstance but the node is not this proto*/
    1709        5800 :                 else if (node->sgprivate->scenegraph->pOwningProto) {
    1710             :                         GF_Node *the_node = (GF_Node *) node->sgprivate->scenegraph->pOwningProto;
    1711        3183 :                         if (the_node != node) dirty_parents(the_node);
    1712             :                 }
    1713             : #endif
    1714             :         }
    1715             : }
    1716             : GF_EXPORT
    1717        5536 : void gf_node_dirty_parent_graph(GF_Node *node)
    1718             : {
    1719             :         /*if root node of the scenegraph*/
    1720        5536 :         if (node->sgprivate->scenegraph->NodeCallback ) {
    1721        5536 :                 node->sgprivate->scenegraph->NodeCallback(node->sgprivate->scenegraph->userpriv, GF_SG_CALLBACK_GRAPH_DIRTY, NULL, NULL);
    1722             :         }
    1723        5536 : }
    1724             : 
    1725             : GF_EXPORT
    1726       77244 : void gf_node_dirty_set(GF_Node *node, u32 flags, Bool and_dirty_parents)
    1727             : {
    1728       77244 :         if (!node) return;
    1729             : 
    1730       77244 :         if (flags) node->sgprivate->flags |= (flags & (~GF_NODE_INTERNAL_FLAGS) );
    1731       68820 :         else node->sgprivate->flags |= GF_SG_NODE_DIRTY;
    1732             : 
    1733       77244 :         if (and_dirty_parents) dirty_parents(node);
    1734             : }
    1735             : 
    1736             : GF_EXPORT
    1737        4575 : void gf_node_dirty_parents(GF_Node *node)
    1738             : {
    1739        4575 :         dirty_parents(node);
    1740        4575 : }
    1741             : 
    1742             : GF_EXPORT
    1743      882328 : void gf_node_dirty_clear(GF_Node *node, u32 flag_to_remove)
    1744             : {
    1745      882328 :         if (!node) return;
    1746      882328 :         if (flag_to_remove) node->sgprivate->flags &= ~ (flag_to_remove & ~GF_NODE_INTERNAL_FLAGS);
    1747      386611 :         else node->sgprivate->flags &= GF_NODE_INTERNAL_FLAGS;
    1748             : }
    1749             : 
    1750             : GF_EXPORT
    1751     1548614 : u32 gf_node_dirty_get(GF_Node *node)
    1752             : {
    1753     1548614 :         if (node) return (node->sgprivate->flags & ~GF_NODE_INTERNAL_FLAGS);
    1754             :         return 0;
    1755             : }
    1756             : 
    1757             : 
    1758             : GF_EXPORT
    1759      130279 : void gf_node_dirty_reset(GF_Node *node, Bool reset_children)
    1760             : {
    1761      142875 :         if (!node) return;
    1762      136024 :         if (node->sgprivate->flags & ~GF_NODE_INTERNAL_FLAGS) {
    1763       18499 :                 node->sgprivate->flags &= GF_NODE_INTERNAL_FLAGS;
    1764       18499 :                 if (reset_children) {
    1765        5884 :                         dirty_children(node);
    1766             : #ifndef GPAC_DISABLE_VRML
    1767       12615 :                 } else if (node->sgprivate->tag==TAG_MPEG4_Appearance) {
    1768       12596 :                         gf_node_dirty_reset( ((M_Appearance*)node)->material, 1);
    1769             : #endif
    1770             :                 }
    1771             :         }
    1772             : }
    1773             : 
    1774             : 
    1775             : 
    1776             : GF_EXPORT
    1777       54632 : void gf_node_init(GF_Node *node)
    1778             : {
    1779       54632 :         GF_SceneGraph *pSG = node->sgprivate->scenegraph;
    1780             :         assert(pSG);
    1781             :         /*no user-defined init, consider the scenegraph is only used for parsing/encoding/decoding*/
    1782       54632 :         if (!pSG->NodeCallback) return;
    1783             : 
    1784             :         /*internal nodes*/
    1785             : #ifndef GPAC_DISABLE_VRML
    1786       35411 :         if (gf_sg_vrml_node_init(node)) return;
    1787             : #endif
    1788             : 
    1789             : #ifndef GPAC_DISABLE_SVG
    1790       32928 :         if (gf_svg_node_init(node)) return;
    1791             : #endif
    1792             : 
    1793             :         /*user defined init*/
    1794       32920 :         pSG->NodeCallback(pSG->userpriv, GF_SG_CALLBACK_INIT, node, NULL);
    1795             : }
    1796             : 
    1797             : 
    1798       86880 : void gf_node_changed_internal(GF_Node *node, GF_FieldInfo *field, Bool notify_scripts)
    1799             : {
    1800             :         GF_SceneGraph *sg;
    1801       86880 :         if (!node) return;
    1802             : 
    1803       86880 :         sg = node->sgprivate->scenegraph;
    1804             :         assert(sg);
    1805             : 
    1806             : #ifndef GPAC_DISABLE_VRML
    1807             :         /*signal changes in node to JS for MFFields*/
    1808       86880 :         if (field && notify_scripts && (node->sgprivate->flags & GF_NODE_HAS_BINDING) && !gf_sg_vrml_is_sf_field(field->fieldType) ) {
    1809         251 :                 sg->on_node_modified(sg, node, field, NULL);
    1810             :         }
    1811             : #endif
    1812             : 
    1813             : #ifndef GPAC_DISABLE_SVG
    1814       86880 :         if (field && node->sgprivate->interact && node->sgprivate->interact->dom_evt) {
    1815             :                 GF_DOM_Event evt;
    1816             :                 memset(&evt, 0, sizeof(GF_DOM_Event));
    1817          45 :                 evt.bubbles = 1;
    1818          45 :                 evt.type = GF_EVENT_ATTR_MODIFIED;
    1819          45 :                 evt.attr = field;
    1820          45 :                 evt.detail = field->fieldIndex;
    1821          45 :                 gf_dom_event_fire(node, &evt);
    1822             :         }
    1823             : #endif
    1824             : 
    1825             : 
    1826             :         /*internal nodes*/
    1827             : #ifndef GPAC_DISABLE_VRML
    1828       86880 :         if (gf_sg_vrml_node_changed(node, field)) return;
    1829             : #endif
    1830             : 
    1831             : #ifndef GPAC_DISABLE_SVG
    1832       61235 :         if (gf_svg_node_changed(node, field)) return;
    1833             : #endif
    1834             : 
    1835             :         /*force child dirty tag*/
    1836       61227 :         if (field && ( (field->fieldType==GF_SG_VRML_SFNODE) || (field->fieldType==GF_SG_VRML_MFNODE)) )
    1837        4383 :                 node->sgprivate->flags |= GF_SG_CHILD_DIRTY;
    1838             : 
    1839       61227 :         if (sg->NodeCallback) sg->NodeCallback(sg->userpriv, GF_SG_CALLBACK_MODIFIED, node, field);
    1840             : }
    1841             : 
    1842             : GF_EXPORT
    1843       54252 : void gf_node_changed(GF_Node *node, GF_FieldInfo *field)
    1844             : {
    1845       54252 :         gf_node_changed_internal(node, field, 1);
    1846             : 
    1847             : #ifndef GPAC_DISABLE_SVG
    1848             :         /* we should avoid dispatching a DOMSubtreeModified event on insertion of time values in begin/end fields
    1849             :            because this retriggers begin/end events and reinsertion */
    1850      108504 :         if ((field == NULL || ((field->fieldIndex != TAG_SVG_ATT_begin) && (field->fieldIndex != TAG_SVG_ATT_end))) &&
    1851       54611 :                 node->sgprivate->tag >= GF_NODE_RANGE_FIRST_SVG && node->sgprivate->tag <= GF_NODE_RANGE_LAST_SVG) {
    1852             :                 GF_DOM_Event evt;
    1853         359 :                 evt.type = GF_EVENT_TREE_MODIFIED;
    1854         359 :                 evt.bubbles = 0;
    1855         359 :                 evt.relatedNode = node;
    1856         359 :                 gf_dom_event_fire(node, &evt);
    1857             :         }
    1858             : #endif
    1859       54252 : }
    1860             : 
    1861       79750 : void gf_node_del(GF_Node *node)
    1862             : {
    1863       79750 :         if (node->sgprivate->tag==TAG_UndefinedNode) gf_node_free(node);
    1864       79137 :         else if (node->sgprivate->tag==TAG_DOMText) {
    1865             :                 GF_DOMText *t = (GF_DOMText *)node;
    1866         952 :                 if (t->textContent) gf_free(t->textContent);
    1867         952 :                 gf_sg_parent_reset(node);
    1868         952 :                 gf_node_free(node);
    1869             :         }
    1870       78185 :         else if (node->sgprivate->tag==TAG_DOMUpdates) {
    1871             :                 u32 i, count;
    1872             :                 GF_DOMUpdates *up = (GF_DOMUpdates *)node;
    1873           7 :                 if (up->data) gf_free(up->data);
    1874           7 :                 count = gf_list_count(up->updates);
    1875          23 :                 for (i=0; i<count; i++) {
    1876          16 :                         GF_Command *com = gf_list_get(up->updates, i);
    1877          16 :                         gf_sg_command_del(com);
    1878             :                 }
    1879           7 :                 gf_list_del(up->updates);
    1880           7 :                 gf_sg_parent_reset(node);
    1881           7 :                 gf_node_free(node);
    1882             :         }
    1883       78178 :         else if (node->sgprivate->tag == TAG_DOMFullNode) {
    1884             :                 GF_DOMFullNode *n = (GF_DOMFullNode *)node;
    1885             : #ifndef GPAC_DISABLE_SVG
    1886         288 :                 gf_node_delete_attributes(node);
    1887             : #endif
    1888         288 :                 if (n->name) gf_free(n->name);
    1889         288 :                 gf_sg_parent_reset(node);
    1890         288 :                 gf_node_free(node);
    1891             :         }
    1892             : #ifndef GPAC_DISABLE_VRML
    1893       77890 :         else if (node->sgprivate->tag == TAG_ProtoNode) gf_sg_proto_del_instance((GF_ProtoInstance *)node);
    1894             : #endif
    1895             : #ifndef GPAC_DISABLE_VRML
    1896       75931 :         else if (node->sgprivate->tag<=GF_NODE_RANGE_LAST_MPEG4) gf_sg_mpeg4_node_del(node);
    1897             : #ifndef GPAC_DISABLE_X3D
    1898        5581 :         else if (node->sgprivate->tag <= GF_NODE_RANGE_LAST_X3D) gf_sg_x3d_node_del(node);
    1899             : #endif
    1900             : #endif /*GPAC_DISABLE_VRML*/
    1901             : 
    1902             : #ifndef GPAC_DISABLE_SVG
    1903        5179 :         else if (node->sgprivate->tag <= GF_NODE_RANGE_LAST_SVG) gf_svg_node_del(node);
    1904             : #endif
    1905           0 :         else gf_node_free(node);
    1906       79750 : }
    1907             : 
    1908             : GF_EXPORT
    1909       30153 : u32 gf_node_get_field_count(GF_Node *node)
    1910             : {
    1911             :         assert(node);
    1912       30153 :         if (node->sgprivate->tag <= TAG_UndefinedNode) return 0;
    1913             : #ifndef GPAC_DISABLE_VRML
    1914             :         /*for both MPEG4 & X3D*/
    1915       30153 :         else if (node->sgprivate->tag <= GF_NODE_RANGE_LAST_X3D) return gf_node_get_num_fields_in_mode(node, GF_SG_FIELD_CODING_ALL);
    1916             : #endif
    1917             : #ifndef GPAC_DISABLE_SVG
    1918           0 :         else if (node->sgprivate->tag >= GF_NODE_FIRST_DOM_NODE_TAG) return gf_node_get_attribute_count(node);
    1919             : #endif
    1920             :         return 0;
    1921             : }
    1922             : 
    1923             : GF_EXPORT
    1924        8703 : const char *gf_node_get_class_name(GF_Node *node)
    1925             : {
    1926             :         assert(node && node->sgprivate->tag);
    1927        8703 :         if (node->sgprivate->tag==TAG_UndefinedNode) return "UndefinedNode";
    1928             : #ifndef GPAC_DISABLE_VRML
    1929        8703 :         else if (node->sgprivate->tag==TAG_ProtoNode) return ((GF_ProtoInstance*)node)->proto_name;
    1930        8643 :         else if (node->sgprivate->tag <= GF_NODE_RANGE_LAST_MPEG4) return gf_sg_mpeg4_node_get_class_name(node->sgprivate->tag);
    1931             : #ifndef GPAC_DISABLE_X3D
    1932        4765 :         else if (node->sgprivate->tag <= GF_NODE_RANGE_LAST_X3D) return gf_sg_x3d_node_get_class_name(node->sgprivate->tag);
    1933             : #endif
    1934             : #endif
    1935        4636 :         else if (node->sgprivate->tag==TAG_DOMText) return "DOMText";
    1936        4575 :         else if (node->sgprivate->tag==TAG_DOMFullNode) {
    1937             :                 char *xmlns;
    1938             :                 GF_DOMFullNode*full = (GF_DOMFullNode*)node;
    1939         349 :                 u32 ns = gf_sg_get_namespace_code(node->sgprivate->scenegraph, NULL);
    1940         349 :                 if (ns == full->ns) return full->name;
    1941          32 :                 xmlns = (char *) gf_sg_get_namespace_qname(node->sgprivate->scenegraph, full->ns);
    1942          32 :                 if (!xmlns) return full->name;
    1943          20 :                 sprintf(node->sgprivate->scenegraph->szNameBuffer, "%s:%s", xmlns, full->name);
    1944          20 :                 return node->sgprivate->scenegraph->szNameBuffer;
    1945             :         }
    1946             : #ifndef GPAC_DISABLE_SVG
    1947        4226 :         else return gf_xml_get_element_name(node);
    1948             : #endif
    1949             :         return "UnsupportedNode";
    1950             : }
    1951             : 
    1952             : #if 0 //unused
    1953             : u32 gf_sg_node_get_tag_by_class_name(const char *name, u32 ns)
    1954             : {
    1955             :         u32 tag = TAG_UndefinedNode;
    1956             : 
    1957             :         /* TODO: handle name spaces */
    1958             : #ifndef GPAC_DISABLE_VRML
    1959             :         tag = gf_node_mpeg4_type_by_class_name(name);
    1960             :         if (tag) return tag;
    1961             : 
    1962             : #ifndef GPAC_DISABLE_X3D
    1963             :         tag = gf_node_x3d_type_by_class_name(name);
    1964             :         if (tag) return tag;
    1965             : #endif
    1966             : 
    1967             : #endif
    1968             : 
    1969             : #ifndef GPAC_DISABLE_SVG
    1970             :         tag = gf_xml_get_element_tag(name, ns);
    1971             :         if (tag != TAG_UndefinedNode) return tag;
    1972             : #endif
    1973             : 
    1974             :         return  tag;
    1975             : }
    1976             : #endif
    1977             : 
    1978             : 
    1979             : GF_EXPORT
    1980       76995 : GF_Node *gf_node_new(GF_SceneGraph *inScene, u32 tag)
    1981             : {
    1982             :         GF_Node *node;
    1983             : //      if (!inScene) return NULL;
    1984             :         /*cannot create proto this way*/
    1985       76995 :         if (tag==TAG_ProtoNode) return NULL;
    1986       76995 :         else if (tag==TAG_UndefinedNode) node = gf_sg_new_base_node();
    1987             : #ifndef GPAC_DISABLE_VRML
    1988       76381 :         else if (tag <= GF_NODE_RANGE_LAST_MPEG4) node = gf_sg_mpeg4_node_new(tag);
    1989             : #ifndef GPAC_DISABLE_X3D
    1990        6031 :         else if (tag <= GF_NODE_RANGE_LAST_X3D) node = gf_sg_x3d_node_new(tag);
    1991             : #endif
    1992             : #endif
    1993        5629 :         else if (tag == TAG_DOMText) {
    1994             :                 GF_DOMText *n;
    1995         162 :                 GF_SAFEALLOC(n, GF_DOMText);
    1996         162 :                 if (!n) return NULL;
    1997             :                 node = (GF_Node*)n;
    1998         162 :                 gf_node_setup(node, TAG_DOMText);
    1999             :         }
    2000        5467 :         else if (tag == TAG_DOMFullNode) {
    2001             :                 GF_DOMFullNode*n;
    2002         288 :                 GF_SAFEALLOC(n, GF_DOMFullNode);
    2003         288 :                 if (!n) return NULL;
    2004             :                 node = (GF_Node*)n;
    2005         288 :                 gf_node_setup(node, TAG_DOMFullNode);
    2006             :         }
    2007             : #ifndef GPAC_DISABLE_SVG
    2008        5179 :         else if (tag <= GF_NODE_RANGE_LAST_SVG) node = (GF_Node *) gf_svg_create_node(tag);
    2009             : #endif
    2010             :         else node = NULL;
    2011             : 
    2012       76995 :         if (node) node->sgprivate->scenegraph = inScene;
    2013             : 
    2014             :         /*script is inited as soon as created since fields are dynamically added*/
    2015             : #ifndef GPAC_DISABLE_VRML
    2016       76995 :         switch (tag) {
    2017         458 :         case TAG_MPEG4_Script:
    2018             : #ifndef GPAC_DISABLE_X3D
    2019             :         case TAG_X3D_Script:
    2020             : #endif
    2021         458 :                 gf_sg_script_init(node);
    2022         458 :                 break;
    2023             :         }
    2024             : #endif
    2025             : 
    2026             :         return node;
    2027             : }
    2028             : 
    2029             : 
    2030             : GF_EXPORT
    2031      650058 : GF_Err gf_node_get_field(GF_Node *node, u32 FieldIndex, GF_FieldInfo *info)
    2032             : {
    2033             :         assert(node);
    2034             :         assert(info);
    2035             :         memset(info, 0, sizeof(GF_FieldInfo));
    2036      650058 :         info->fieldIndex = FieldIndex;
    2037             : 
    2038      650058 :         if (node->sgprivate->tag==TAG_UndefinedNode) return GF_BAD_PARAM;
    2039             : #ifndef GPAC_DISABLE_VRML
    2040      650058 :         else if (node->sgprivate->tag == TAG_ProtoNode) return gf_sg_proto_get_field(NULL, node, info);
    2041      604023 :         else if (node->sgprivate->tag == TAG_MPEG4_Script)
    2042       19167 :                 return gf_sg_script_get_field(node, info);
    2043             : #ifndef GPAC_DISABLE_X3D
    2044      584856 :         else if (node->sgprivate->tag == TAG_X3D_Script)
    2045         200 :                 return gf_sg_script_get_field(node, info);
    2046             : #endif
    2047      584656 :         else if (node->sgprivate->tag <= GF_NODE_RANGE_LAST_MPEG4) return gf_sg_mpeg4_node_get_field(node, info);
    2048             : #ifndef GPAC_DISABLE_X3D
    2049        2502 :         else if (node->sgprivate->tag <= GF_NODE_RANGE_LAST_X3D) return gf_sg_x3d_node_get_field(node, info);
    2050             : #endif
    2051             : #endif /*GPAC_DISABLE_VRML*/
    2052             : 
    2053             : #ifndef GPAC_DISABLE_SVG
    2054           7 :         else if (node->sgprivate->tag >= GF_NODE_FIRST_DOM_NODE_TAG) return gf_node_get_attribute_info(node, info);
    2055             : #endif
    2056             :         return GF_NOT_SUPPORTED;
    2057             : }
    2058             : 
    2059           0 : u32 gf_node_get_num_instances(GF_Node *node)
    2060             : {
    2061           0 :         return node ? node->sgprivate->num_instances : 0;
    2062             : }
    2063             : 
    2064        1071 : static GF_Err gf_node_get_field_by_name_enum(GF_Node *node, char *name, GF_FieldInfo *field)
    2065             : {
    2066             :         u32 i, count;
    2067             :         assert(node);
    2068        1071 :         count = gf_node_get_field_count(node);
    2069             :         memset(field, 0, sizeof(GF_FieldInfo));
    2070        9177 :         for (i=0; i<count; i++) {
    2071        8857 :                 gf_node_get_field(node, i, field);
    2072        8857 :                 if (!strcmp(field->name, name)) return GF_OK;
    2073             :         }
    2074             :         return GF_BAD_PARAM;
    2075             : }
    2076             : 
    2077             : GF_EXPORT
    2078      112957 : GF_Err gf_node_get_field_by_name(GF_Node *node, char *name, GF_FieldInfo *field)
    2079             : {
    2080             :         s32 res = -1;
    2081             : 
    2082      112957 :         if (node->sgprivate->tag==TAG_UndefinedNode) return GF_BAD_PARAM;
    2083             : #ifndef GPAC_DISABLE_VRML
    2084      112957 :         else if (node->sgprivate->tag == TAG_ProtoNode) {
    2085       24847 :                 res = gf_sg_proto_get_field_index_by_name(NULL, node, name);
    2086             :         }
    2087       88110 :         else if (node->sgprivate->tag == TAG_MPEG4_Script)
    2088        1043 :                 return gf_node_get_field_by_name_enum(node, name, field);
    2089             : #ifndef GPAC_DISABLE_X3D
    2090       87067 :         else if (node->sgprivate->tag == TAG_X3D_Script)
    2091          28 :                 return gf_node_get_field_by_name_enum(node, name, field);
    2092             : #endif
    2093       87039 :         else if (node->sgprivate->tag <= GF_NODE_RANGE_LAST_MPEG4) res = gf_sg_mpeg4_node_get_field_index_by_name(node, name);
    2094             : #ifndef GPAC_DISABLE_X3D
    2095        1503 :         else if (node->sgprivate->tag <= GF_NODE_RANGE_LAST_X3D) res = gf_sg_x3d_node_get_field_index_by_name(node, name);
    2096             : #endif
    2097             : #endif /*GPAC_DISABLE_VRML*/
    2098             : 
    2099             : #ifndef GPAC_DISABLE_SVG
    2100          55 :         else if (node->sgprivate->tag >= GF_NODE_FIRST_DOM_NODE_TAG) return gf_node_get_attribute_by_name(node, name, 0, 1, 0, field);
    2101             : #endif
    2102      111831 :         if (res==-1) return GF_BAD_PARAM;
    2103       99941 :         return gf_node_get_field(node, (u32) res, field);
    2104             : }
    2105             : 
    2106             : 
    2107             : static char log_node_name[2+16+1];
    2108           0 : const char *gf_node_get_log_name(GF_Node *anim)
    2109             : {
    2110             :         const char *name;
    2111           0 :         if (!anim) return "";
    2112           0 :         name = gf_node_get_name(anim);
    2113           0 :         if (name) return name;
    2114             :         else {
    2115             :                 sprintf(log_node_name, "%p", anim);
    2116           0 :                 return log_node_name;
    2117             :         }
    2118             : }
    2119             : 
    2120             : 
    2121             : 
    2122           2 : static GF_Err gf_node_deactivate_ex(GF_Node *node)
    2123             : {
    2124             : #ifdef GPAC_DISABLE_SVG
    2125             :         return GF_NOT_SUPPORTED;
    2126             : #else
    2127             :         GF_ChildNodeItem *item;
    2128           2 :         if (node->sgprivate->tag<GF_NODE_FIRST_DOM_NODE_TAG) return GF_BAD_PARAM;
    2129           1 :         if (! (node->sgprivate->flags & GF_NODE_IS_DEACTIVATED)) {
    2130             : 
    2131           1 :                 node->sgprivate->flags |= GF_NODE_IS_DEACTIVATED;
    2132             : 
    2133             :                 /*deactivate anmiations*/
    2134           1 :                 if (gf_svg_is_timing_tag(node->sgprivate->tag)) {
    2135             :                         SVGTimedAnimBaseElement *timed = (SVGTimedAnimBaseElement*)node;
    2136           0 :                         if (gf_list_del_item(node->sgprivate->scenegraph->smil_timed_elements, timed->timingp->runtime)>=0) {
    2137           0 :                                 if (timed->timingp->runtime->evaluate) {
    2138           0 :                                         timed->timingp->runtime->evaluate(timed->timingp->runtime, 0, SMIL_TIMING_EVAL_DEACTIVATE);
    2139             :                                 }
    2140             :                         }
    2141             :                 }
    2142             :                 /*TODO unregister all listeners*/
    2143             : 
    2144             :         }
    2145             :         /*and deactivate children*/
    2146           1 :         item = ((GF_ParentNode*)node)->children;
    2147           3 :         while (item) {
    2148           1 :                 gf_node_deactivate_ex(item->node);
    2149           1 :                 item = item->next;
    2150             :         }
    2151             :         return GF_OK;
    2152             : #endif
    2153             : }
    2154             : 
    2155           1 : GF_Err gf_node_deactivate(GF_Node *node)
    2156             : {
    2157           1 :         GF_Err e = gf_node_deactivate_ex(node);
    2158           1 :         gf_node_changed(node, NULL);
    2159           1 :         return e;
    2160             : }
    2161             : 
    2162         161 : static u32 gf_node_activate_ex(GF_Node *node)
    2163             : {
    2164             : #ifdef GPAC_DISABLE_SVG
    2165             :         return 0;
    2166             : #else
    2167             :         u32 ret = 0;
    2168             :         GF_ChildNodeItem *item;
    2169         161 :         if (node->sgprivate->tag<GF_NODE_FIRST_DOM_NODE_TAG) return 0;
    2170         100 :         if (node->sgprivate->flags & GF_NODE_IS_DEACTIVATED) {
    2171             : 
    2172           1 :                 node->sgprivate->flags &= ~GF_NODE_IS_DEACTIVATED;
    2173             :                 ret ++;
    2174             : 
    2175             :                 /*deactivate anmiations*/
    2176           1 :                 if (gf_svg_is_timing_tag(node->sgprivate->tag)) {
    2177             :                         SVGTimedAnimBaseElement *timed = (SVGTimedAnimBaseElement*)node;
    2178           0 :                         gf_list_add(node->sgprivate->scenegraph->smil_timed_elements, timed->timingp->runtime);
    2179           0 :                         node->sgprivate->flags &= ~GF_NODE_IS_DEACTIVATED;
    2180           0 :                         if (timed->timingp->runtime->evaluate) {
    2181           0 :                                 timed->timingp->runtime->evaluate(timed->timingp->runtime, 0, SMIL_TIMING_EVAL_ACTIVATE);
    2182             :                         }
    2183             :                 }
    2184             :                 /*TODO register all listeners*/
    2185             : 
    2186             :         }
    2187             :         /*and deactivate children*/
    2188         100 :         item = ((GF_ParentNode*)node)->children;
    2189         272 :         while (item) {
    2190          72 :                 ret += gf_node_activate_ex(item->node);
    2191          72 :                 item = item->next;
    2192             :         }
    2193             :         return ret;
    2194             : #endif
    2195             : }
    2196             : 
    2197          89 : GF_Err gf_node_activate(GF_Node *node)
    2198             : {
    2199          89 :         if (!node) return GF_BAD_PARAM;
    2200          89 :         if (gf_node_activate_ex(node))
    2201           1 :                 gf_node_changed(node, NULL);
    2202             :         return GF_OK;
    2203             : }
    2204             : 
    2205             : GF_EXPORT
    2206       17242 : GF_Node *gf_node_clone(GF_SceneGraph *inScene, GF_Node *orig, GF_Node *cloned_parent, char *id, Bool deep)
    2207             : {
    2208       17242 :         if (!orig) return NULL;
    2209       13515 :         if (orig->sgprivate->tag < GF_NODE_RANGE_LAST_VRML) {
    2210             : #ifndef GPAC_DISABLE_VRML
    2211             :                 /*deep clone is always true for VRML*/
    2212       13514 :                 return gf_vrml_node_clone(inScene, orig, cloned_parent, id);
    2213             : #endif
    2214           1 :         } else if (orig->sgprivate->tag == TAG_DOMUpdates) {
    2215             :                 /*TODO*/
    2216             :                 return NULL;
    2217             :         } else {
    2218             : #ifndef GPAC_DISABLE_SVG
    2219           1 :                 return gf_xml_node_clone(inScene, orig, cloned_parent, id, deep);
    2220             : #endif
    2221             :         }
    2222             :         return NULL;
    2223             : }
    2224             : 
    2225         285 : GF_NamespaceType gf_xml_get_namespace_id(char *name)
    2226             : {
    2227         285 :         if (!strcmp(name, "http://www.w3.org/XML/1998/namespace")) return GF_XMLNS_XML;
    2228         285 :         else if (!strcmp(name, "http://www.w3.org/2001/xml-events")) return GF_XMLNS_XMLEV;
    2229         269 :         else if (!strcmp(name, "http://www.w3.org/1999/xlink")) return GF_XMLNS_XLINK;
    2230         204 :         else if (!strcmp(name, "http://www.w3.org/2000/svg")) return GF_XMLNS_SVG;
    2231          64 :         else if (!strcmp(name, "urn:mpeg:mpeg4:LASeR:2005")) return GF_XMLNS_LASER;
    2232          62 :         else if (!strcmp(name, "http://www.w3.org/ns/xbl")) return GF_XMLNS_XBL;
    2233          62 :         else if (!strcmp(name, "http://gpac.io/svg-extensions")) return GF_XMLNS_SVG_GPAC_EXTENSION;
    2234          62 :         return GF_XMLNS_UNDEFINED;
    2235             : }
    2236             : 
    2237             : GF_EXPORT
    2238         285 : GF_Err gf_sg_add_namespace(GF_SceneGraph *sg, char *name, char *qname)
    2239             : {
    2240             :         u32 id;
    2241             :         GF_XMLNS *ns;
    2242         285 :         if (!name) return GF_BAD_PARAM;
    2243             : 
    2244         285 :         id = gf_xml_get_namespace_id(name);
    2245             : 
    2246         285 :         if (!sg->ns) sg->ns = gf_list_new();
    2247             : 
    2248         285 :         GF_SAFEALLOC(ns, GF_XMLNS);
    2249         285 :         if (!ns) return GF_OUT_OF_MEM;
    2250             :         
    2251         285 :         ns->xmlns_id = id ? id : gf_crc_32(name, (u32) strlen(name));
    2252         285 :         ns->name = gf_strdup(name);
    2253             : 
    2254         285 :         ns->qname = qname ? gf_strdup(qname) : NULL;
    2255         285 :         return gf_list_insert(sg->ns, ns, 0);
    2256             : }
    2257             : 
    2258         198 : GF_Err gf_sg_remove_namespace(GF_SceneGraph *sg, char *ns_name, char *q_name)
    2259             : {
    2260             :         u32 i, count;
    2261         198 :         if (!ns_name) return GF_OK;
    2262         198 :         count = sg->ns ? gf_list_count(sg->ns) : 0;
    2263         308 :         for (i=0; i<count; i++) {
    2264             :                 Bool ok=0;
    2265         308 :                 GF_XMLNS *ns = gf_list_get(sg->ns, i);
    2266         308 :                 if (!q_name && !ns->qname)
    2267             :                         ok = 1;
    2268         208 :                 else if (q_name && ns->qname && !strcmp(ns->qname, q_name) )
    2269             :                         ok = 1;
    2270             : 
    2271         198 :                 if (ok && ns->name && !strcmp(ns->name, ns_name)) {
    2272         198 :                         gf_list_rem(sg->ns, i);
    2273         198 :                         gf_free(ns->name);
    2274         198 :                         if (ns->qname) gf_free(ns->qname);
    2275         198 :                         gf_free(ns);
    2276         198 :                         return GF_OK;
    2277             :                 }
    2278             :         }
    2279             :         return GF_OK;
    2280             : }
    2281             : 
    2282        5152 : GF_NamespaceType gf_sg_get_namespace_code(GF_SceneGraph *sg, char *qname)
    2283             : {
    2284             :         u32 i, count;
    2285        5152 :         count = sg->ns ? gf_list_count(sg->ns) : 0;
    2286        8336 :         for (i=0; i<count; i++) {
    2287        7372 :                 GF_XMLNS *ns = gf_list_get(sg->ns, i);
    2288        7372 :                 if (!ns->qname && !qname)
    2289        3853 :                         return ns->xmlns_id;
    2290             : 
    2291        3519 :                 if (ns->qname && qname && !strcmp(ns->qname, qname))
    2292         335 :                         return ns->xmlns_id;
    2293             :         }
    2294         964 :         if (qname) {
    2295         240 :                 if (!strcmp(qname, "xml")) return GF_XMLNS_XML;
    2296             :                 /*we could also add the basic namespaces in case this has been forgotten ?*/
    2297             :         }
    2298         724 :         return GF_XMLNS_UNDEFINED;
    2299             : }
    2300             : 
    2301         102 : GF_NamespaceType gf_sg_get_namespace_code_from_name(GF_SceneGraph *sg, char *name)
    2302             : {
    2303             :         u32 i, count;
    2304         102 :         count = sg->ns ? gf_list_count(sg->ns) : 0;
    2305         102 :         for (i=0; i<count; i++) {
    2306         102 :                 GF_XMLNS *ns = gf_list_get(sg->ns, i);
    2307         102 :                 if (ns->name && name && !strcmp(ns->name, name))
    2308         102 :                         return ns->xmlns_id;
    2309           0 :                 if (!ns->name && !name)
    2310           0 :                         return ns->xmlns_id;
    2311             :         }
    2312             :         return GF_XMLNS_UNDEFINED;
    2313             : }
    2314             : 
    2315        1410 : const char *gf_sg_get_namespace_qname(GF_SceneGraph *sg, GF_NamespaceType xmlns_id)
    2316             : {
    2317             :         u32 i, count;
    2318        1410 :         count = sg->ns ? gf_list_count(sg->ns) : 0;
    2319        2272 :         for (i=0; i<count; i++) {
    2320        1575 :                 GF_XMLNS *ns = gf_list_get(sg->ns, i);
    2321        1575 :                 if (ns->xmlns_id == xmlns_id)
    2322         713 :                         return ns->qname;
    2323             :         }
    2324         697 :         if (xmlns_id==GF_XMLNS_XML) return "xml";
    2325         697 :         return NULL;
    2326             : }
    2327             : 
    2328             : 
    2329           1 : const char *gf_sg_get_namespace(GF_SceneGraph *sg, GF_NamespaceType xmlns_id)
    2330             : {
    2331             :         u32 i, count;
    2332           1 :         if (!sg) return NULL;
    2333           1 :         count = sg->ns ? gf_list_count(sg->ns) : 0;
    2334           1 :         for (i=0; i<count; i++) {
    2335           0 :                 GF_XMLNS *ns = gf_list_get(sg->ns, i);
    2336           0 :                 if (ns->xmlns_id == xmlns_id)
    2337           0 :                         return ns->name;
    2338             :         }
    2339             :         return NULL;
    2340             : }
    2341             : 
    2342             : 
    2343             : #if 0 //unused
    2344             : char *gf_node_dump_attribute(GF_Node *n, GF_FieldInfo *info)
    2345             : {
    2346             : #ifndef GPAC_DISABLE_SVG
    2347             :         if (gf_node_get_tag(n) >= GF_NODE_FIRST_DOM_NODE_TAG) {
    2348             :                 return gf_svg_dump_attribute(n, info);
    2349             :         }
    2350             : #endif
    2351             : 
    2352             : #ifndef GPAC_DISABLE_VRML
    2353             :         return gf_node_vrml_dump_attribute(n, info);
    2354             : #else
    2355             :         return NULL;
    2356             : #endif
    2357             : }
    2358             : #endif
    2359             : 
    2360             : 
    2361             : /*this is not a NodeReplace, thus only the given container is updated - pos is 0-based*/
    2362             : GF_EXPORT
    2363         970 : GF_Err gf_node_replace_child(GF_Node *node, GF_ChildNodeItem **container, s32 pos, GF_Node *newNode)
    2364             : {
    2365             :         GF_ChildNodeItem *child, *prev;
    2366             :         u32 tag;
    2367             :         u32 cur_pos = 0;
    2368             : 
    2369         970 :         child = *container;
    2370             :         prev = NULL;
    2371       13336 :         while (child->next) {
    2372       12347 :                 if ((pos<0) || (cur_pos!=(u32)pos)) {
    2373             :                         prev = child;
    2374             :                         child = child->next;
    2375       11396 :                         cur_pos++;
    2376       11396 :                         continue;
    2377             :                 }
    2378             :                 break;
    2379             :         }
    2380         970 :         tag = child->node->sgprivate->tag;
    2381         970 :         gf_node_unregister(child->node, node);
    2382         970 :         if (newNode) {
    2383         945 :                 child->node = newNode;
    2384             : #ifndef GPAC_DISABLE_VRML
    2385         945 :                 if (tag==TAG_MPEG4_ColorTransform)
    2386           0 :                         node->sgprivate->flags |= GF_SG_VRML_COLOR_DIRTY;
    2387             : #endif
    2388             :         } else {
    2389          25 :                 if (prev) prev->next = child->next;
    2390          25 :                 else *container = child->next;
    2391          25 :                 gf_free(child);
    2392             :         }
    2393         970 :         return GF_OK;
    2394             : }
    2395             : 
    2396             : 
    2397             : GF_EXPORT
    2398        2807 : Bool gf_node_parent_of(GF_Node *node, GF_Node *target)
    2399             : {
    2400             :         u32 i, count;
    2401             :         GF_FieldInfo info;
    2402        2807 :         if (!node) return 0;
    2403           0 :         if (node==target) return 1;
    2404             : 
    2405           0 :         if (node->sgprivate->tag>=GF_NODE_RANGE_LAST_VRML) {
    2406           0 :                 GF_ChildNodeItem *child = ((GF_ParentNode*)node)->children;
    2407           0 :                 while (child) {
    2408           0 :                         if (gf_node_parent_of(child->node, target)) return 1;
    2409           0 :                         child = child->next;
    2410             :                 }
    2411             :         } else {
    2412           0 :                 count = gf_node_get_field_count(node);
    2413           0 :                 for (i=0; i<count; i++) {
    2414           0 :                         gf_node_get_field(node, i, &info);
    2415           0 :                         if (info.fieldType==GF_SG_VRML_SFNODE) {
    2416           0 :                                 if (gf_node_parent_of(*(GF_Node **)info.far_ptr, target)) return 1;
    2417             :                         }
    2418           0 :                         else if (info.fieldType==GF_SG_VRML_MFNODE) {
    2419           0 :                                 GF_ChildNodeItem *list = *(GF_ChildNodeItem **) info.far_ptr;
    2420           0 :                                 while (list) {
    2421           0 :                                         if (gf_node_parent_of(list->node, target)) return 1;
    2422           0 :                                         list = list->next;
    2423             :                                 }
    2424             :                         }
    2425             :                 }
    2426             :         }
    2427             :         return 0;
    2428             : }
    2429             : 
    2430           2 : GF_SceneGraph *gf_sg_get_parent(GF_SceneGraph *scene)
    2431             : {
    2432           2 :         return scene ? scene->parent_scene : NULL;
    2433             : }
    2434             : 
    2435             : 
    2436             : #include <gpac/xml.h>
    2437             : 
    2438         266 : static GF_Err gf_sg_load_dom_node(GF_SceneGraph *document, GF_XMLNode *n, GF_DOMFullNode *par)
    2439             : {
    2440             :         u32 i, count;
    2441             :         GF_DOMFullAttribute *prev = NULL;
    2442             :         GF_DOMFullNode *node;
    2443             : 
    2444         266 :         if (!n) return GF_OK;
    2445         266 :         if (!par && document->RootNode) {
    2446             :                 return GF_NON_COMPLIANT_BITSTREAM;
    2447             :         }
    2448             :         /*construct text / cdata node*/
    2449         266 :         if (n->type != GF_XML_NODE_TYPE) {
    2450             :                 u32 len;
    2451             :                 GF_DOMText *txt;
    2452             :                 /*basic check, remove all empty text nodes*/
    2453         166 :                 len = (u32) strlen(n->name);
    2454         840 :                 for (i=0; i<len; i++) {
    2455         701 :                         if (!strchr(" \n\r\t", n->name[i])) break;
    2456             :                 }
    2457         166 :                 if (i==len) return GF_OK;
    2458          27 :                 txt = gf_dom_add_text_node((GF_Node *)par, gf_strdup(n->name) );
    2459          27 :                 txt->type = (n->type==GF_XML_CDATA_TYPE) ? GF_DOM_TEXT_CDATA : GF_DOM_TEXT_REGULAR;
    2460          27 :                 return GF_OK;
    2461             :         }
    2462             :         /*construct DOM node*/
    2463         100 :         node = (GF_DOMFullNode *) gf_node_new(document, TAG_DOMFullNode);
    2464         100 :         node->name = gf_strdup(n->name);
    2465         100 :         if (n->ns)
    2466           0 :                 node->ns = gf_sg_get_namespace_code(document, n->ns);
    2467             : 
    2468         100 :         count = gf_list_count(n->attributes);
    2469         378 :         for (i=0; i<count; i++) {
    2470         278 :                 GF_XMLAttribute *src_att = gf_list_get(n->attributes, i);
    2471             :                 /* special case for 'xml:id' to be parsed as an ID
    2472             :                 NOTE: we do not test for the 'id' attribute because without DTD we are not sure that it's an ID */
    2473         278 :                 if (!stricmp(src_att->name, "xml:id")) {
    2474          17 :                         u32 id = gf_sg_get_max_node_id(document) + 1;
    2475          17 :                         gf_node_set_id((GF_Node *)node, id, src_att->value);
    2476             :                 } else {
    2477             :                         GF_DOMFullAttribute *att;
    2478         261 :                         GF_SAFEALLOC(att, GF_DOMFullAttribute);
    2479         261 :                         if (!att) {
    2480           0 :                                 GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[XHR] Fail to allocate DOM attribute\n"));
    2481           0 :                                 continue;
    2482             :                         }
    2483         261 :                         att->tag = TAG_DOM_ATT_any;
    2484         261 :                         att->name = gf_strdup(src_att->name);
    2485         261 :                         att->data_type = (u16) DOM_String_datatype;
    2486         261 :                         att->data = gf_svg_create_attribute_value(att->data_type);
    2487         261 :                         *((char **)att->data) = gf_strdup(src_att->value);
    2488         261 :                         if (prev) prev->next = (GF_DOMAttribute*)att;
    2489          64 :                         else node->attributes = (GF_DOMAttribute*)att;
    2490             :                         prev = att;
    2491             :                 }
    2492             :         }
    2493         100 :         gf_node_register((GF_Node*)node, (GF_Node*)par);
    2494         100 :         if (par) {
    2495          92 :                 gf_node_list_add_child(&par->children, (GF_Node*)node);
    2496             :         } else {
    2497           8 :                 document->RootNode = (GF_Node*)node;
    2498             :         }
    2499         100 :         count = gf_list_count(n->content);
    2500         358 :         for (i=0; i<count; i++) {
    2501         258 :                 GF_XMLNode *child = gf_list_get(n->content, i);
    2502         258 :                 GF_Err e = gf_sg_load_dom_node(document, child, node);
    2503         258 :                 if (e) return e;
    2504             :         }
    2505             :         return GF_OK;
    2506             : }
    2507             : 
    2508           8 : GF_Err gf_sg_init_from_xml_node(GF_SceneGraph *document, GF_DOMXMLNODE root_node)
    2509             : {
    2510           8 :         return gf_sg_load_dom_node(document, root_node, NULL);
    2511             : }
    2512             : 

Generated by: LCOV version 1.13