LCOV - code coverage report
Current view: top level - scenegraph - dom_events.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 325 447 72.7 %
Date: 2021-04-29 23:48:07 Functions: 33 36 91.7 %

          Line data    Source code
       1             : /*
       2             :  *                      GPAC - Multimedia Framework C SDK
       3             :  *
       4             :  *                      Authors: Jean Le Feuvre, Cyril Concolato
       5             :  *                      Copyright (c) Telecom ParisTech 2004-2012
       6             :  *                                      All rights reserved
       7             :  *
       8             :  *  This file is part of GPAC / DOM 3 Events 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             : #include <gpac/internal/scenegraph_dev.h>
      26             : #include <gpac/xml.h>
      27             : 
      28             : #ifndef GPAC_DISABLE_SVG
      29             : 
      30             : #include <gpac/scenegraph_svg.h>
      31             : #include <gpac/events.h>
      32             : #include <gpac/nodes_svg.h>
      33             : 
      34             : static void gf_smil_handle_event(GF_Node *anim, GF_FieldInfo *info, GF_DOM_Event *evt, Bool is_end);
      35             : 
      36             : 
      37        3052 : static void gf_dom_refresh_event_filter(GF_SceneGraph *sg)
      38             : {
      39             :         GF_SceneGraph *par;
      40        3052 :         u32 prev_flags = sg->dom_evt_filter;
      41             : 
      42        3052 :         sg->dom_evt_filter = 0;
      43        3052 :         if (sg->nb_evts_mouse) sg->dom_evt_filter |= GF_DOM_EVENT_MOUSE;
      44        3052 :         if (sg->nb_evts_focus) sg->dom_evt_filter |= GF_DOM_EVENT_FOCUS;
      45        3052 :         if (sg->nb_evts_key) sg->dom_evt_filter |= GF_DOM_EVENT_KEY;
      46        3052 :         if (sg->nb_evts_ui) sg->dom_evt_filter |= GF_DOM_EVENT_UI;
      47        3052 :         if (sg->nb_evts_mutation) sg->dom_evt_filter |= GF_DOM_EVENT_MUTATION;
      48        3052 :         if (sg->nb_evts_text) sg->dom_evt_filter |= GF_DOM_EVENT_TEXT;
      49        3052 :         if (sg->nb_evts_smil) sg->dom_evt_filter |= GF_DOM_EVENT_SMIL;
      50        3052 :         if (sg->nb_evts_laser) sg->dom_evt_filter |= GF_DOM_EVENT_LASER;
      51        3052 :         if (sg->nb_evts_svg) sg->dom_evt_filter |= GF_DOM_EVENT_SVG;
      52        3052 :         if (sg->nb_evts_media) sg->dom_evt_filter |= GF_DOM_EVENT_MEDIA;
      53             : 
      54             :         /*for each graph until top, update event filter*/
      55        3052 :         par = sg->parent_scene;
      56        8372 :         while (par) {
      57        2268 :                 par->dom_evt_filter &= ~prev_flags;
      58        2268 :                 par->dom_evt_filter |= sg->dom_evt_filter;
      59        2268 :                 par = par->parent_scene;
      60             :         }
      61        3052 : }
      62             : 
      63        1526 : void gf_sg_unregister_event_type(GF_SceneGraph *sg, GF_DOMEventCategory category)
      64             : {
      65        1526 :         if (sg->nb_evts_mouse && (category & GF_DOM_EVENT_MOUSE)) sg->nb_evts_mouse--;
      66        1526 :         if (sg->nb_evts_focus && (category & GF_DOM_EVENT_FOCUS)) sg->nb_evts_focus--;
      67        1526 :         if (sg->nb_evts_key && (category & GF_DOM_EVENT_KEY)) sg->nb_evts_key--;
      68        1526 :         if (sg->nb_evts_ui && (category & GF_DOM_EVENT_UI)) sg->nb_evts_ui--;
      69        1526 :         if (sg->nb_evts_mutation && (category & GF_DOM_EVENT_MUTATION)) sg->nb_evts_mutation--;
      70        1526 :         if (sg->nb_evts_text && (category & GF_DOM_EVENT_TEXT)) sg->nb_evts_text--;
      71        1526 :         if (sg->nb_evts_smil && (category & GF_DOM_EVENT_SMIL)) sg->nb_evts_smil--;
      72        1526 :         if (sg->nb_evts_laser && (category & GF_DOM_EVENT_LASER)) sg->nb_evts_laser--;
      73        1526 :         if (sg->nb_evts_text && (category & GF_DOM_EVENT_TEXT)) sg->nb_evts_text--;
      74        1526 :         if (sg->nb_evts_svg && (category & GF_DOM_EVENT_SVG)) sg->nb_evts_svg--;
      75        1526 :         if (sg->nb_evts_media && (category & GF_DOM_EVENT_MEDIA)) sg->nb_evts_media--;
      76             : 
      77        1526 :         gf_dom_refresh_event_filter(sg);
      78        1526 : }
      79             : 
      80        1526 : void gf_sg_register_event_type(GF_SceneGraph *sg, GF_DOMEventCategory category)
      81             : {
      82        1526 :         if (category & GF_DOM_EVENT_MOUSE) sg->nb_evts_mouse++;
      83        1526 :         if (category & GF_DOM_EVENT_FOCUS) sg->nb_evts_focus++;
      84        1526 :         if (category & GF_DOM_EVENT_KEY) sg->nb_evts_key++;
      85        1526 :         if (category & GF_DOM_EVENT_UI) sg->nb_evts_ui++;
      86        1526 :         if (category & GF_DOM_EVENT_MUTATION) sg->nb_evts_mutation++;
      87        1526 :         if (category & GF_DOM_EVENT_TEXT) sg->nb_evts_text++;
      88        1526 :         if (category & GF_DOM_EVENT_SMIL) sg->nb_evts_smil++;
      89        1526 :         if (category & GF_DOM_EVENT_LASER) sg->nb_evts_laser++;
      90        1526 :         if (category & GF_DOM_EVENT_SVG) sg->nb_evts_svg++;
      91        1526 :         if (category & GF_DOM_EVENT_MEDIA) sg->nb_evts_media++;
      92             : 
      93        1526 :         gf_dom_refresh_event_filter(sg);
      94        1526 : }
      95             : 
      96             : #if 0 //unused
      97             : u32 gf_sg_get_dom_event_filter(GF_SceneGraph *sg)
      98             : {
      99             :         return sg->dom_evt_filter;
     100             : }
     101             : 
     102             : u32 gf_node_get_dom_event_filter(GF_Node *node)
     103             : {
     104             :         if (node) {
     105             :                 return node->sgprivate->scenegraph->dom_evt_filter;
     106             :         } else {
     107             :                 return 0;
     108             :         }
     109             : }
     110             : #endif
     111             : 
     112          63 : GF_Err gf_sg_listener_associate(GF_Node *listener, GF_DOMEventTarget *evt_target)
     113             : {
     114             :         GF_FieldInfo info;
     115          63 :         if (!evt_target || !listener) return GF_BAD_PARAM;
     116          63 :         if (listener->sgprivate->tag!=TAG_SVG_listener)
     117             :                 return GF_BAD_PARAM;
     118             : 
     119             :         /*only one observer per listener*/
     120          63 :         if (listener->sgprivate->UserPrivate!=NULL) return GF_NOT_SUPPORTED;
     121          63 :         listener->sgprivate->UserPrivate = evt_target;
     122             : 
     123             :         /*register with NULL parent*/
     124          63 :         gf_node_register((GF_Node *)listener, NULL);
     125             : 
     126          63 :         if (gf_node_get_attribute_by_tag((GF_Node *)listener, TAG_XMLEV_ATT_event, GF_FALSE, GF_FALSE, &info) == GF_OK) {
     127          61 :                 GF_EventType type = ((XMLEV_Event *)info.far_ptr)->type;
     128          61 :                 gf_sg_register_event_type(listener->sgprivate->scenegraph, gf_dom_event_get_category(type));
     129             :         }
     130             : 
     131          63 :         return gf_list_add(evt_target->listeners, listener);
     132             : }
     133             : 
     134             : /* Create event-related structures in the node and associate the listener with this node */
     135             : GF_EXPORT
     136          62 : GF_Err gf_node_dom_listener_add(GF_Node *node, GF_Node *listener)
     137             : {
     138          62 :         if (!node || !listener) return GF_BAD_PARAM;
     139          62 :         if (listener->sgprivate->tag!=TAG_SVG_listener)
     140             :                 return GF_BAD_PARAM;
     141             : 
     142          62 :         if (!node->sgprivate->interact) {
     143          12 :                 GF_SAFEALLOC(node->sgprivate->interact, struct _node_interactive_ext);
     144          12 :                 if (!node->sgprivate->interact)
     145             :                         return GF_OUT_OF_MEM;
     146             :         }
     147             : 
     148          62 :         if (!node->sgprivate->interact->dom_evt) {
     149          14 :                 node->sgprivate->interact->dom_evt = gf_dom_event_target_new(GF_DOM_EVENT_TARGET_NODE, node);
     150             :         }
     151          62 :         return gf_sg_listener_associate(listener, node->sgprivate->interact->dom_evt);
     152             : }
     153             : 
     154             : GF_EXPORT
     155          66 : GF_Err gf_dom_listener_del(GF_Node *listener, GF_DOMEventTarget *target)
     156             : {
     157             :         GF_FieldInfo info;
     158          66 :         if (!listener || !target) return GF_BAD_PARAM;
     159          63 :         if (gf_list_del_item(target->listeners, listener)<0) return GF_BAD_PARAM;
     160             : 
     161          63 :         if (gf_node_get_attribute_by_tag((GF_Node *)listener, TAG_XMLEV_ATT_event, GF_FALSE, GF_FALSE, &info) == GF_OK) {
     162          61 :                 GF_EventType type = ((XMLEV_Event *)info.far_ptr)->type;
     163          61 :                 gf_sg_unregister_event_type(listener->sgprivate->scenegraph, gf_dom_event_get_category(type));
     164             :         }
     165          63 :         listener->sgprivate->UserPrivate = NULL;
     166             : 
     167          63 :         gf_node_unregister((GF_Node *)listener, NULL);
     168          63 :         return GF_OK;
     169             : }
     170             : 
     171          68 : GF_Err gf_dom_event_remove_listener_from_parent(GF_DOMEventTarget *event_target, GF_Node *listener)
     172             : {
     173          68 :         if (!event_target) return GF_BAD_PARAM;
     174           0 :         if (event_target->ptr_type == GF_DOM_EVENT_TARGET_NODE) {
     175           0 :                 GF_Node *node = (GF_Node *)event_target->ptr;
     176           0 :                 node->sgprivate->UserPrivate = NULL;
     177             :         }
     178           0 :         gf_list_del_item(event_target->listeners, listener);
     179             : 
     180             : #if 0
     181             :         {
     182             :                 GF_FieldInfo info;
     183             :                 if (gf_node_get_attribute_by_tag(listener, TAG_XMLEV_ATT_event, GF_FALSE, GF_FALSE, &info) == GF_OK) {
     184             :                         GF_EventType type = ((XMLEV_Event *)info.far_ptr)->type;
     185             :                         gf_sg_unregister_event_type(listener->sgprivate->scenegraph, gf_dom_event_get_category(type));
     186             :                 }
     187             :         }
     188             : #endif
     189           0 :         return GF_OK;
     190             : }
     191             : 
     192        6117 : void gf_dom_event_remove_all_listeners(GF_DOMEventTarget *event_target)
     193             : {
     194       12297 :         while (gf_list_count(event_target->listeners)) {
     195          63 :                 GF_Node *n = (GF_Node *)gf_list_get(event_target->listeners, 0);
     196          63 :                 gf_dom_listener_del(n, event_target);
     197             :         }
     198        6117 : }
     199             : 
     200             : GF_EXPORT
     201        2071 : u32 gf_dom_listener_count(GF_Node *node)
     202             : {
     203        2071 :         if (!node || !node->sgprivate->interact || !node->sgprivate->interact->dom_evt) return 0;
     204          31 :         return gf_list_count(node->sgprivate->interact->dom_evt->listeners);
     205             : }
     206             : 
     207             : GF_EXPORT
     208           3 : GF_Node *gf_dom_listener_get(GF_Node *node, u32 i)
     209             : {
     210           3 :         if (!node || !node->sgprivate->interact || !node->sgprivate->interact->dom_evt) return 0;
     211           3 :         return (GF_Node *)gf_list_get(node->sgprivate->interact->dom_evt->listeners, i);
     212             : }
     213             : 
     214             : typedef struct
     215             : {
     216             :         GF_Node *obs;
     217             :         GF_Node *listener;
     218             : } DOMAddListener;
     219             : 
     220          37 : void gf_sg_listener_post_add(GF_Node *obs, GF_Node *listener)
     221             : {
     222             :         DOMAddListener *l;
     223          37 :         gf_mx_p(obs->sgprivate->scenegraph->dom_evt_mx);
     224          37 :         l = (DOMAddListener*)gf_malloc(sizeof(DOMAddListener));
     225          37 :         l->listener = listener;
     226          37 :         l->obs = obs;
     227          37 :         gf_list_add(obs->sgprivate->scenegraph->listeners_to_add, l);
     228          37 :         gf_mx_v(obs->sgprivate->scenegraph->dom_evt_mx);
     229          37 : }
     230             : 
     231       21358 : void gf_dom_listener_process_add(GF_SceneGraph *sg)
     232             : {
     233             :         u32 i, count;
     234       21358 :         gf_mx_p(sg->dom_evt_mx);
     235       21358 :         count = gf_list_count(sg->listeners_to_add);
     236       21395 :         for (i=0; i<count; i++) {
     237          37 :                 DOMAddListener *l = (DOMAddListener *)gf_list_get(sg->listeners_to_add, i);
     238          37 :                 gf_node_dom_listener_add(l->obs, l->listener);
     239          37 :                 gf_free(l);
     240             :         }
     241       21358 :         gf_list_reset(sg->listeners_to_add);
     242       21358 :         gf_mx_v(sg->dom_evt_mx);
     243       21358 : }
     244             : 
     245        5599 : void gf_dom_listener_reset_deferred(GF_SceneGraph *sg)
     246             : {
     247        5599 :         gf_mx_p(sg->dom_evt_mx);
     248       11198 :         while (gf_list_count(sg->listeners_to_add)) {
     249           0 :                 DOMAddListener *l = (DOMAddListener *)gf_list_get(sg->listeners_to_add, 0);
     250           0 :                 gf_list_rem(sg->listeners_to_add, 0);
     251           0 :                 gf_free(l);
     252             :         }
     253        5599 :         gf_mx_v(sg->dom_evt_mx);
     254        5599 : }
     255             : 
     256             : GF_EXPORT
     257           0 : void gf_sg_handle_dom_event(GF_Node *hdl, GF_DOM_Event *event, GF_Node *observer)
     258             : {
     259             : #ifdef GPAC_HAS_QJS
     260           0 :         if (hdl->sgprivate->scenegraph->svg_js) {
     261             :                 void svgjs_handler_execute(struct __tag_svg_script_ctx *svg_js, GF_Node *hdl, GF_DOM_Event *event, GF_Node *observer, const char *_none);
     262           0 :                 svgjs_handler_execute(hdl->sgprivate->scenegraph->svg_js, hdl, event, observer, NULL);
     263             :         }
     264             : #endif
     265           0 :         GF_LOG(GF_LOG_WARNING, GF_LOG_INTERACT, ("[DOM Events] JavaScript context not found \n"));
     266           0 : }
     267             : 
     268          96 : static GF_Node *dom_evt_get_handler(GF_Node *n)
     269             : {
     270             :         XMLRI *iri;
     271             :         GF_FieldInfo info;
     272             : 
     273          96 :         if (!n || (n->sgprivate->tag!=TAG_SVG_handler)) return n;
     274             : 
     275          96 :         if (gf_node_get_attribute_by_tag(n, TAG_XLINK_ATT_href, GF_FALSE, GF_FALSE, &info) != GF_OK) {
     276             :                 return n;
     277             :         }
     278           0 :         iri = (XMLRI *)info.far_ptr;
     279           0 :         if (!iri->target && iri->string) {
     280           0 :                 iri->target = gf_sg_find_node_by_name(n->sgprivate->scenegraph, iri->string+1);
     281             :         }
     282           0 :         return dom_evt_get_handler((GF_Node *)iri->target);
     283             : }
     284             : 
     285          96 : static void dom_event_process(GF_Node *listen, GF_DOM_Event *event, GF_Node *observer)
     286             : {
     287             :         GF_Node *hdl_node;
     288             : 
     289          96 :         switch (listen->sgprivate->tag) {
     290          96 :         case TAG_SVG_listener:
     291             :         {
     292             :                 GF_FieldInfo info;
     293          96 :                 if (gf_node_get_attribute_by_tag(listen, TAG_XMLEV_ATT_handler, GF_FALSE, GF_FALSE, &info) == GF_OK) {
     294          96 :                         XMLRI *iri = (XMLRI *)info.far_ptr;
     295             : 
     296          96 :                         if ((iri->type==XMLRI_STRING) && iri->string && !strnicmp(iri->string, "javascript:", 11)) {
     297             : #ifdef GPAC_HAS_QJS
     298             :                                 void svgjs_handler_execute(struct __tag_svg_script_ctx *svg_js, GF_Node *hdl, GF_DOM_Event *event, GF_Node *observer, const char *iri);
     299             : 
     300           0 :                                 if (listen->sgprivate->scenegraph->svg_js)
     301           0 :                                         svgjs_handler_execute(listen->sgprivate->scenegraph->svg_js, listen, event, observer, iri->string + 11);
     302             : #endif
     303           0 :                                 return;
     304             :                         }
     305          96 :                         if (!iri->target && iri->string) {
     306           0 :                                 iri->target = gf_sg_find_node_by_name(listen->sgprivate->scenegraph, iri->string+1);
     307             :                         }
     308          96 :                         hdl_node = dom_evt_get_handler((GF_Node *)iri->target);
     309             :                 } else {
     310             :                         return;
     311             :                 }
     312             :         }
     313             :         break;
     314             :         default:
     315             :                 return;
     316             :         }
     317          96 :         if (!hdl_node) return;
     318             : 
     319          96 :         GF_LOG(GF_LOG_DEBUG, GF_LOG_INTERACT, ("[DOM Events    ] Time %f - Processing event type: %s\n", gf_node_get_scene_time((GF_Node *)listen), gf_dom_event_get_name(event->type)));
     320             : 
     321          96 :         switch (hdl_node->sgprivate->tag) {
     322          96 :         case TAG_SVG_handler:
     323             :         {
     324             :                 //GF_FieldInfo info;
     325             :                 SVG_handlerElement *handler = (SVG_handlerElement *) hdl_node;
     326          96 :                 if (!handler->handle_event) return;
     327             : 
     328             :                 /*spec is not clear regarding event filtering*/
     329             : #if 0
     330             :                 if (gf_node_get_attribute_by_tag(hdl_node, TAG_XMLEV_ATT_event, GF_FALSE, GF_FALSE, &info) == GF_OK) {
     331             :                         XMLEV_Event *ev_event = (XMLEV_Event *)info.far_ptr;
     332             :                         if (ev_event->type != event->type) return;
     333             :                         handler->handle_event(hdl_node, event, observer);
     334             :                 } else {
     335             :                         handler->handle_event(hdl_node, event, observer);
     336             :                 }
     337             : #else
     338          96 :                 handler->handle_event(hdl_node, event, observer);
     339             : #endif
     340             :         }
     341          96 :         break;
     342           0 :         case TAG_LSR_conditional:
     343           0 :                 if ( ((SVG_Element*)hdl_node)->children)
     344           0 :                         gf_node_traverse(((SVG_Element*)hdl_node)->children->node, NULL);
     345             :                 break;
     346             :         case TAG_SVG_a:
     347             :         {
     348             :                 GF_DOM_Event act;
     349             :                 memset(&act, 0, sizeof(GF_DOM_Event));
     350           0 :                 act.type = GF_EVENT_ACTIVATE;
     351           0 :                 gf_dom_event_fire((GF_Node *)hdl_node, &act);
     352             :         }
     353           0 :         break;
     354             :         default:
     355             :                 return;
     356             :         }
     357             : }
     358             : 
     359             : GF_EXPORT
     360       15374 : Bool gf_sg_fire_dom_event(GF_DOMEventTarget *et, GF_DOM_Event *event, GF_SceneGraph *sg, GF_Node *n)
     361             : {
     362       15374 :         if (et) {
     363       29760 :                 if (et->ptr_type==GF_DOM_EVENT_TARGET_NODE ||
     364       14880 :                         et->ptr_type == GF_DOM_EVENT_TARGET_DOCUMENT ||
     365           0 :                         et->ptr_type == GF_DOM_EVENT_TARGET_XHR ||
     366           0 :                         et->ptr_type == GF_DOM_EVENT_TARGET_MSE_MEDIASOURCE ||
     367           0 :                         et->ptr_type == GF_DOM_EVENT_TARGET_MSE_SOURCEBUFFER ||
     368             :                         et->ptr_type == GF_DOM_EVENT_TARGET_MSE_SOURCEBUFFERLIST ) {
     369             :                         GF_Node *observer = NULL;
     370             :                         u32 i, count, post_count;
     371       14880 :                         if (et->ptr_type==GF_DOM_EVENT_TARGET_NODE) {
     372       14097 :                                 observer = (GF_Node *)et->ptr;
     373             :                         }
     374       14880 :                         count = gf_list_count(et->listeners);
     375       16118 :                         for (i=0; i<count; i++) {
     376             :                                 XMLEV_Event *listened_event;
     377        1238 :                                 GF_Node *listen = (GF_Node *)gf_list_get(et->listeners, i);
     378             : 
     379        1238 :                                 switch (listen->sgprivate->tag) {
     380        1238 :                                 case TAG_SVG_listener:
     381             :                                 {
     382             :                                         SVGAllAttributes atts;
     383        1238 :                                         gf_svg_flatten_attributes((SVG_Element*)listen, &atts);
     384        1238 :                                         listened_event = atts.event;
     385        1238 :                                         if (!listened_event) continue;
     386             : 
     387        1238 :                                         if (atts.propagate && (*atts.propagate==XMLEVENT_PROPAGATE_STOP))
     388           0 :                                                 event->event_phase |= GF_DOM_EVENT_PHASE_CANCEL;
     389        1238 :                                         if (atts.defaultAction && (*atts.defaultAction==XMLEVENT_DEFAULTACTION_CANCEL))
     390           0 :                                                 event->event_phase |= GF_DOM_EVENT_PHASE_PREVENT;
     391             :                                 }
     392             :                                 break;
     393           0 :                                 default:
     394           0 :                                         continue;
     395             :                                 }
     396        1252 :                                 if (listened_event->type <= GF_EVENT_MOUSEMOVE) event->has_ui_events=1;
     397        1238 :                                 if (listened_event->type != event->type) continue;
     398          96 :                                 if (listened_event->parameter && (listened_event->parameter != event->detail)) continue;
     399          96 :                                 event->currentTarget = et;
     400          96 :                                 event->consumed      ++;
     401             : 
     402             :                                 /*load event cannot bubble and can only be called once (on load :) ), remove it
     403             :                                 to release some resources*/
     404          96 :                                 if (event->type==GF_EVENT_LOAD) {
     405           1 :                                         dom_event_process(listen, event, observer);
     406             :                                         /*delete listener*/
     407             :                                         //gf_dom_listener_del(listen, et);
     408          95 :                                 } else if (n) {
     409             :                                         assert(n->sgprivate->num_instances);
     410             :                                         /*protect node*/
     411          10 :                                         n->sgprivate->num_instances++;
     412             :                                         /*exec event*/
     413          10 :                                         dom_event_process(listen, event, observer);
     414             :                                         /*the event has destroyed ourselves, abort propagation
     415             :                                         THIS IS NOT DOM compliant, the event should propagate on the original target+ancestors path*/
     416          10 :                                         if (n->sgprivate->num_instances==1) {
     417             :                                                 /*unprotect node event*/
     418           0 :                                                 gf_node_unregister(n, NULL);
     419           0 :                                                 return GF_FALSE;
     420             :                                         }
     421          10 :                                         n->sgprivate->num_instances--;
     422             :                                 } else {
     423          85 :                                         dom_event_process(listen, event, observer);
     424             :                                 }
     425             :                                 /*canceled*/
     426          96 :                                 if (event->event_phase & GF_DOM_EVENT_PHASE_CANCEL_ALL) {
     427           0 :                                         gf_dom_listener_process_add(sg);
     428           0 :                                         return GF_FALSE;
     429             :                                 }
     430             : 
     431             :                                 /*if listeners have been removed, update count*/
     432          96 :                                 post_count = gf_list_count(et->listeners);
     433          96 :                                 if (post_count < count) {
     434           0 :                                         s32 pos = gf_list_find(et->listeners, listen);
     435           0 :                                         if (pos>=0) i = pos;
     436             :                                         /*FIXME this is not going to work in all cases...*/
     437           0 :                                         else i--;
     438             :                                         count = post_count;
     439             :                                 }
     440             :                         }
     441             :                         /*propagation stopped*/
     442       14880 :                         if (event->event_phase & (GF_DOM_EVENT_PHASE_CANCEL|GF_DOM_EVENT_PHASE_CANCEL_ALL) ) {
     443           0 :                                 gf_dom_listener_process_add(sg);
     444           0 :                                 return GF_FALSE;
     445             :                         }
     446             :                 }
     447       14880 :                 gf_dom_listener_process_add(sg);
     448             :                 /*if the current target is a node, we can bubble*/
     449       14880 :                 return n ? GF_TRUE : GF_FALSE;
     450             :         } else {
     451             :                 /* if the node does not have a event target, probably a parent will have, so let's bubble */
     452             :                 return GF_TRUE;
     453             :         }
     454             : }
     455             : 
     456         785 : static void gf_sg_dom_event_bubble(GF_Node *node, GF_DOM_Event *event, GF_List *use_stack, u32 cur_par_idx)
     457             : {
     458             :         GF_Node *parent;
     459             : 
     460         905 :         if (!node || node->sgprivate->scenegraph->abort_bubbling) return;
     461             : 
     462             : 
     463             :         /*get the node's parent*/
     464         905 :         parent = gf_node_get_parent(node, 0);
     465             : 
     466         905 :         if (!parent) {
     467             :                 /*top of the graph, use Document*/
     468         785 :                 if (node->sgprivate->scenegraph->RootNode==node)
     469         783 :                         gf_sg_fire_dom_event(node->sgprivate->scenegraph->dom_evt, event, node->sgprivate->scenegraph, NULL);
     470             :                 return;
     471             :         }
     472         120 :         if (cur_par_idx) {
     473           0 :                 GF_Node *used_node = (GF_Node *)gf_list_get(use_stack, cur_par_idx-1);
     474             :                 /*if the node is a used one, switch to the <use> subtree*/
     475           0 :                 if (used_node==node) {
     476           0 :                         parent = (GF_Node *)gf_list_get(use_stack, cur_par_idx);
     477           0 :                         if (cur_par_idx>1) cur_par_idx-=2;
     478             :                         else cur_par_idx = 0;
     479             :                         /*if no events attached,bubble by default*/
     480           0 :                         if (parent->sgprivate->interact) {
     481           0 :                                 Bool can_bubble = gf_sg_fire_dom_event(parent->sgprivate->interact->dom_evt, event, node->sgprivate->scenegraph, parent);
     482           0 :                                 if (!can_bubble)  {
     483             :                                         return;
     484             :                                 }
     485             :                         }
     486             :                         gf_sg_dom_event_bubble(parent, event, use_stack, cur_par_idx);
     487           0 :                         return;
     488             :                 }
     489             :         }
     490             :         /*if no events attached,bubble by default*/
     491         120 :         if (parent->sgprivate->interact) {
     492             :                 Bool can_bubble;
     493           3 :                 can_bubble = gf_sg_fire_dom_event(parent->sgprivate->interact->dom_evt, event, node->sgprivate->scenegraph, parent);
     494           3 :                 if(!can_bubble) return;
     495             :         }
     496             :         gf_sg_dom_event_bubble(parent, event, use_stack, cur_par_idx);
     497             : }
     498             : 
     499             : #if 0 //unused, see below
     500             : static void gf_sg_dom_stack_parents(GF_Node *node, GF_List *stack)
     501             : {
     502             :         if (!node) return;
     503             :         if (node->sgprivate->interact && node->sgprivate->interact->dom_evt) gf_list_insert(stack, node, 0);
     504             :         gf_sg_dom_stack_parents(gf_node_get_parent(node, 0), stack);
     505             : }
     506             : #endif
     507             : 
     508             : GF_EXPORT
     509        6475 : Bool gf_dom_event_fire_ex(GF_Node *node, GF_DOM_Event *event, GF_List *use_stack)
     510             : {
     511             :         GF_SceneGraph *sg;
     512             :         GF_List *prev_use_stack;
     513             :         Bool prev_bub;
     514             :         GF_DOMEventTarget cur_target;
     515             :         u32 cur_par_idx;
     516             :         Bool can_bubble = GF_FALSE;
     517        6475 :         if (!node || !event) return GF_FALSE;
     518        6475 :         GF_LOG(GF_LOG_DEBUG, GF_LOG_INTERACT, ("[DOM Events    ] Graph %p Time %f - Firing event  %s.%s\n", gf_node_get_graph(node), gf_node_get_scene_time(node), gf_node_get_log_name(node), gf_dom_event_get_name(event->type)));
     519             : 
     520             :         /*flush any pending add_listener
     521             :         see "determine the current target's candidate event listeners" in http://www.w3.org/TR/DOM-Level-3-Events/events.html */
     522        6475 :         gf_dom_listener_process_add(node->sgprivate->scenegraph);
     523             : 
     524        6475 :         event->consumed = 0;
     525        6475 :         event->target = node;
     526        6475 :         event->target_type = GF_DOM_EVENT_TARGET_NODE;
     527        6475 :         if (node->sgprivate->interact && node->sgprivate->interact->dom_evt) {
     528        1475 :                 event->currentTarget = node->sgprivate->interact->dom_evt;
     529             :         } else {
     530        5000 :                 cur_target.ptr_type = GF_DOM_EVENT_TARGET_NODE;
     531        5000 :                 cur_target.ptr = node;
     532        5000 :                 cur_target.listeners = NULL;
     533        5000 :                 event->currentTarget = &cur_target;
     534             :         }
     535             : 
     536             :         /*capture phase - not 100% sure, the actual capture phase should be determined by the std using the DOM events
     537             :         SVGT doesn't use this phase, so we don't add it for now.*/
     538             : #if 0
     539             :         if ((0)) {
     540             :                 Bool aborted = GF_FALSE;
     541             :                 u32 i, count;
     542             :                 GF_List *parents;
     543             :                 event->event_phase = GF_DOM_EVENT_PHASE_CAPTURE;
     544             :                 parents = gf_list_new();
     545             :                 /*get all parents to top*/
     546             :                 gf_sg_dom_stack_parents(gf_node_get_parent(node, 0), parents);
     547             :                 count = gf_list_count(parents);
     548             :                 for (i=0; i<count; i++) {
     549             :                         GF_Node *n = (GF_Node *)gf_list_get(parents, i);
     550             :                         if (n->sgprivate->interact)
     551             :                                 gf_sg_fire_dom_event(n->sgprivate->interact->dom_evt, event, node->sgprivate->scenegraph, n);
     552             : 
     553             :                         /*event has been canceled*/
     554             :                         if (event->event_phase & (GF_DOM_EVENT_PHASE_CANCEL|GF_DOM_EVENT_PHASE_CANCEL_ALL) ) {
     555             :                                 aborted = GF_TRUE;
     556             :                                 break;
     557             :                         }
     558             :                 }
     559             :                 gf_list_del(parents);
     560             :                 if (aborted) {
     561             :                         event->currentTarget = NULL;
     562             :                         return GF_TRUE;
     563             :                 }
     564             :         }
     565             : #endif
     566             : 
     567             :         /*target phase*/
     568        6475 :         event->event_phase = GF_DOM_EVENT_PHASE_AT_TARGET;
     569             :         cur_par_idx = 0;
     570        6475 :         if (use_stack) {
     571          35 :                 cur_par_idx = gf_list_count(use_stack);
     572          35 :                 if (cur_par_idx) cur_par_idx--;
     573             :         }
     574             : 
     575        6475 :         sg = node->sgprivate->scenegraph;
     576             : 
     577        6475 :         prev_use_stack = sg->use_stack ;
     578        6475 :         prev_bub = sg->abort_bubbling;
     579        6475 :         sg->use_stack = use_stack;
     580        6475 :         sg->abort_bubbling = GF_FALSE;
     581             : 
     582        6475 :         if (node->sgprivate->interact) {
     583        1967 :                 can_bubble = gf_sg_fire_dom_event(node->sgprivate->interact->dom_evt, event, node->sgprivate->scenegraph, node);
     584             :         }
     585        6475 :         if ( (!node->sgprivate->interact || can_bubble) && event->bubbles) {
     586             :                 /*bubbling phase*/
     587         785 :                 event->event_phase = GF_DOM_EVENT_PHASE_BUBBLE;
     588         785 :                 gf_sg_dom_event_bubble(node, event, use_stack, cur_par_idx);
     589             :         }
     590        6475 :         sg->use_stack = prev_use_stack;
     591        6475 :         sg->abort_bubbling = prev_bub;
     592        6475 :         event->currentTarget = NULL;
     593        6475 :         return event->consumed ? GF_TRUE : GF_FALSE;
     594             : }
     595             : 
     596             : GF_EXPORT
     597        6440 : Bool gf_dom_event_fire(GF_Node *node, GF_DOM_Event *event)
     598             : {
     599        6440 :         return gf_dom_event_fire_ex(node, event, NULL);
     600             : }
     601             : 
     602          20 : GF_DOMHandler *gf_dom_listener_build_ex(GF_Node *node, u32 event_type, u32 event_parameter, GF_Node *handler, GF_Node **out_listener)
     603             : {
     604             :         SVG_Element *listener;
     605             :         GF_FieldInfo info;
     606          20 :         GF_ChildNodeItem *last = NULL;
     607             : 
     608          20 :         if (!node || !node->sgprivate || !node->sgprivate->scenegraph) {
     609           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_INTERACT, ("[DOM Events] Bad target node for listener\n"));
     610             :                 return NULL;
     611             :         }
     612          20 :         listener = (SVG_Element *) gf_node_new(node->sgprivate->scenegraph, TAG_SVG_listener);
     613             :         /*don't register the listener, this will be done when adding to the node events list*/
     614             : 
     615          20 :         if (handler) {
     616           0 :                 if (gf_node_get_attribute_by_tag(handler, TAG_XMLEV_ATT_event, GF_FALSE, GF_FALSE, &info)==GF_OK) {
     617           0 :                         event_type = ((XMLEV_Event *)info.far_ptr)->type;
     618           0 :                         event_parameter = ((XMLEV_Event *)info.far_ptr)->parameter;
     619             :                 }
     620             :         } else {
     621          20 :                 handler = gf_node_new(node->sgprivate->scenegraph, TAG_SVG_handler);
     622          20 :                 gf_node_get_attribute_by_tag(handler, TAG_XMLEV_ATT_event, GF_TRUE, GF_FALSE, &info);
     623          20 :                 ((XMLEV_Event *)info.far_ptr)->type = event_type;
     624          20 :                 ((XMLEV_Event *)info.far_ptr)->parameter = event_parameter;
     625             : 
     626             :                 /*register the handler as a child of the listener*/
     627          20 :                 gf_node_register((GF_Node *)handler, (GF_Node *) listener);
     628          20 :                 gf_node_list_add_child_last(& ((GF_ParentNode *)listener)->children, (GF_Node*)handler, &last);
     629             :         }
     630             : 
     631          20 :         gf_node_get_attribute_by_tag((GF_Node*)listener, TAG_XMLEV_ATT_event, GF_TRUE, GF_FALSE, &info);
     632          20 :         ((XMLEV_Event *)info.far_ptr)->type = event_type;
     633          20 :         ((XMLEV_Event *)info.far_ptr)->parameter = event_parameter;
     634             : 
     635          20 :         gf_node_get_attribute_by_tag((GF_Node*)listener, TAG_XMLEV_ATT_handler, GF_TRUE, GF_FALSE, &info);
     636          20 :         ((XMLRI *)info.far_ptr)->target = handler;
     637             : 
     638          20 :         gf_node_get_attribute_by_tag((GF_Node*)listener, TAG_XMLEV_ATT_target, GF_TRUE, GF_FALSE, &info);
     639          20 :         ((XMLRI *)info.far_ptr)->target = node;
     640             : 
     641          20 :         gf_node_dom_listener_add((GF_Node *) node, (GF_Node *) listener);
     642             : 
     643          20 :         if (out_listener) *out_listener = (GF_Node *) listener;
     644             : 
     645             :         /*set default handler*/
     646          20 :         ((SVG_handlerElement *) handler)->handle_event = gf_sg_handle_dom_event;
     647          20 :         return (SVG_handlerElement *) handler;
     648             : }
     649             : 
     650             : GF_EXPORT
     651          17 : GF_DOMHandler *gf_dom_listener_build(GF_Node *node, GF_EventType event_type, u32 event_parameter)
     652             : {
     653          17 :         return gf_dom_listener_build_ex(node, event_type, event_parameter, NULL, NULL);
     654             : }
     655             : 
     656           0 : static void gf_smil_handle_event(GF_Node *timed_elt, GF_FieldInfo *info, GF_DOM_Event *evt, Bool is_end)
     657             : {
     658             :         SMIL_Time *resolved, *proto;
     659           0 :         Double scene_time = gf_node_get_scene_time((GF_Node *)evt->target);
     660           0 :         GF_List *times = *(GF_List **)info->far_ptr;
     661             :         u32 found = GF_FALSE;
     662           0 :         u32 i, j, count = gf_list_count(times);
     663             : 
     664             :         /*remove all previously instantiated times that are in the past
     665             :         TODO FIXME: this is not 100% correct, a begin val in the past can be interpreted!!*/
     666           0 :         for (i=0; i<count; i++) {
     667           0 :                 proto = (SMIL_Time*)gf_list_get(times, i);
     668           0 :                 if ((proto->type == GF_SMIL_TIME_EVENT_RESOLVED) && (proto->clock<scene_time) ) {
     669           0 :                         gf_free(proto);
     670           0 :                         gf_list_rem(times, i);
     671           0 :                         i--;
     672           0 :                         count--;
     673             :                 }
     674             :         }
     675             : 
     676           0 :         for (i=0; i<count; i++) {
     677           0 :                 proto = (SMIL_Time*)gf_list_get(times, i);
     678           0 :                 if (proto->type != GF_SMIL_TIME_EVENT) continue;
     679           0 :                 if (proto->event.type != evt->type) continue;
     680           0 :                 if ((evt->type == GF_EVENT_KEYDOWN) || (evt->type == GF_EVENT_REPEAT_EVENT)) {
     681           0 :                         if (proto->event.parameter!=evt->detail) continue;
     682             :                 }
     683             :                 /*only handle event if coming from the watched element*/
     684           0 :                 if (proto->element) {
     685           0 :                         if ((evt->currentTarget->ptr_type!=GF_DOM_EVENT_TARGET_NODE) || (proto->element!= (GF_Node*)evt->currentTarget->ptr))
     686           0 :                                 continue;
     687             :                 }
     688             : 
     689             :                 /*solve*/
     690           0 :                 GF_SAFEALLOC(resolved, SMIL_Time);
     691           0 :                 if (!resolved) {
     692           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[VRML] Failed to allocate SMIL timing resolved value\n"));
     693           0 :                         continue;
     694             :                 }
     695           0 :                 resolved->type = GF_SMIL_TIME_EVENT_RESOLVED;
     696             : 
     697           0 :                 if (proto->is_absolute_event) {
     698           0 :                         resolved->clock = evt->smil_event_time + proto->clock;
     699             :                 } else {
     700           0 :                         resolved->clock = scene_time + proto->clock;
     701             :                 }
     702             : 
     703             :                 /*insert in sorted order*/
     704           0 :                 for (j=0; j<count; j++) {
     705           0 :                         proto = (SMIL_Time*)gf_list_get(times, j);
     706             : 
     707           0 :                         if ( GF_SMIL_TIME_IS_CLOCK(proto->type) ) {
     708           0 :                                 if (proto->clock > resolved->clock) break;
     709             :                         } else {
     710             :                                 break;
     711             :                         }
     712             :                 }
     713           0 :                 gf_list_insert(times, resolved, j);
     714           0 :                 if (j!=count) i++;
     715           0 :                 count++;
     716           0 :                 found++;
     717           0 :                 GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing   ] Time %f - Timed element %s - Inserting new time in %s: %f\n", gf_node_get_scene_time(timed_elt), gf_node_get_log_name(timed_elt), (is_end?"end":"begin"), resolved->clock));
     718             :         }
     719             :         /* calling indirectly gf_smil_timing_modified */
     720           0 :         if (found) gf_node_changed(timed_elt, info);
     721           0 : }
     722             : 
     723           1 : static void gf_smil_handle_event_begin(GF_Node *hdl, GF_DOM_Event *evt, GF_Node *observer)
     724             : {
     725             :         GF_FieldInfo info;
     726           1 :         SVGTimedAnimBaseElement *timed_elt = (SVGTimedAnimBaseElement *)gf_node_get_private(hdl);
     727           2 :         if (!timed_elt || !timed_elt->timingp) return;
     728             : 
     729             :         memset(&info, 0, sizeof(GF_FieldInfo));
     730           0 :         info.name = "begin";
     731           0 :         info.far_ptr = ((SVGTimedAnimBaseElement *)timed_elt)->timingp->begin;
     732           0 :         info.fieldType = SMIL_Times_datatype;
     733           0 :         gf_smil_handle_event((GF_Node *)timed_elt, &info, evt, GF_FALSE);
     734             : }
     735             : 
     736           0 : static void gf_smil_handle_event_end(GF_Node *hdl, GF_DOM_Event *evt, GF_Node *observer)
     737             : {
     738             :         GF_FieldInfo info;
     739           0 :         GF_Node *timed_elt = (GF_Node *)gf_node_get_private(hdl);
     740             :         memset(&info, 0, sizeof(GF_FieldInfo));
     741           0 :         info.name = "end";
     742           0 :         info.far_ptr = ((SVGTimedAnimBaseElement *)timed_elt)->timingp->end;
     743           0 :         info.fieldType = SMIL_Times_datatype;
     744           0 :         gf_smil_handle_event((GF_Node *)timed_elt, &info, evt, GF_TRUE);
     745           0 : }
     746             : 
     747         140 : static void gf_smil_setup_event_list(GF_Node *node, GF_List *l, Bool is_begin)
     748             : {
     749             :         GF_DOMHandler *hdl;
     750             :         u32 i, count;
     751         140 :         count = gf_list_count(l);
     752          49 :         for (i=0; i<count; i++) {
     753          49 :                 SMIL_Time *t = (SMIL_Time*)gf_list_get(l, i);
     754          49 :                 if (t->type != GF_SMIL_TIME_EVENT) continue;
     755             :                 /*not resolved yet*/
     756           3 :                 if (!t->element && t->element_id) continue;
     757           3 :                 if (t->event.type==GF_EVENT_BEGIN) {
     758           0 :                         t->event.type=GF_EVENT_BEGIN_EVENT;
     759           0 :                         t->is_absolute_event = GF_TRUE;
     760           3 :                 } else if (t->event.type==GF_EVENT_END) {
     761           2 :                         t->event.type=GF_EVENT_END_EVENT;
     762           2 :                         t->is_absolute_event = GF_TRUE;
     763           1 :                 } else if (t->event.type==GF_EVENT_REPEAT) {
     764           0 :                         t->event.type=GF_EVENT_REPEAT_EVENT;
     765           0 :                         t->is_absolute_event = GF_TRUE;
     766             :                 }
     767             : 
     768             :                 /*create a new listener*/
     769           3 :                 hdl = gf_dom_listener_build_ex(t->element, t->event.type, t->event.parameter, NULL, &t->listener);
     770             : 
     771             :                 /*register the listener so that we can handle cyclic references:
     772             :                         - If the anim node gets destroyed, the listener is removed through the SMIL_Time reference
     773             :                         - If the target gets destroyed, the listener is removed through the regular way
     774             :                 */
     775           3 :                 if (t->listener)
     776           3 :                         gf_node_register(t->listener, NULL);
     777             : 
     778           3 :                 if (hdl) {
     779           3 :                         ((SVG_handlerElement *)hdl)->handle_event = is_begin ? gf_smil_handle_event_begin : gf_smil_handle_event_end;
     780             :                 }
     781             :                 else {
     782           0 :                         continue;
     783             :                 }
     784             : 
     785             :                 //this code is broken, it introduces a cyclic ref between the parent and the handler but
     786             :                 //the listener (parent of the handler) is not inserted in the graph, so destruction of the handler
     787             :                 //will only happen if the SMIL_Time is destroyed (thus destroying the listener), but this SMIL_time
     788             :                 //will never get destroyed since the attribute owner (node) has an extra instance
     789             : #if 0
     790             :                 /*We don't want to insert the implicit listener in the DOM. However remember
     791             :                 the listener at the handler level in case the handler gets destroyed*/
     792             :                 gf_node_set_private((GF_Node *)hdl, node);
     793             :                 gf_node_register((GF_Node*)node, NULL);
     794             : #endif
     795             : 
     796             :                 /*we keep the t->element pointer in order to discard the source of identical events (begin of # elements, ...)*/
     797             :         }
     798         140 : }
     799             : 
     800          80 : void gf_smil_setup_events(GF_Node *node)
     801             : {
     802             :         GF_FieldInfo info;
     803          80 :         if (gf_node_get_attribute_by_tag(node, TAG_SVG_ATT_begin, GF_FALSE, GF_FALSE, &info) == GF_OK)
     804          60 :                 gf_smil_setup_event_list(node, * (GF_List **)info.far_ptr, GF_TRUE);
     805          80 :         if (gf_node_get_attribute_by_tag(node, TAG_SVG_ATT_end, GF_FALSE, GF_FALSE, &info) == GF_OK)
     806          80 :                 gf_smil_setup_event_list(node, * (GF_List **)info.far_ptr, GF_FALSE);
     807          80 : }
     808             : 
     809             : GF_EXPORT
     810          68 : void gf_dom_set_textContent(GF_Node *n, char *text)
     811             : {
     812             :         GF_ParentNode *par = (GF_ParentNode *)n;
     813          68 :         gf_node_unregister_children(n, par->children);
     814          68 :         par->children = NULL;
     815          68 :         if (text) gf_dom_add_text_node(n, gf_strdup( text) );
     816          68 : }
     817             : 
     818             : GF_EXPORT
     819         778 : GF_DOMText *gf_dom_add_text_node(GF_Node *parent, char *text_data)
     820             : {
     821             :         GF_DOMText *text;
     822         778 :         GF_SAFEALLOC(text, GF_DOMText);
     823         778 :         if (!text) return NULL;
     824             : 
     825         778 :         gf_node_setup((GF_Node *)text, TAG_DOMText);
     826         778 :         text->sgprivate->scenegraph = parent->sgprivate->scenegraph;
     827         778 :         text->textContent = text_data;
     828         778 :         gf_node_register((GF_Node *)text, parent);
     829         778 :         gf_node_list_add_child_last(&((GF_ParentNode *)parent)->children, (GF_Node*)text, NULL);
     830         778 :         return text;
     831             : }
     832             : 
     833          12 : GF_DOMText *gf_dom_new_text_node(GF_SceneGraph *sg)
     834             : {
     835             :         GF_DOMText *text;
     836          12 :         GF_SAFEALLOC(text, GF_DOMText);
     837          12 :         if (!text) return NULL;
     838             :         
     839          12 :         gf_node_setup((GF_Node *)text, TAG_DOMText);
     840          12 :         text->sgprivate->scenegraph = sg;
     841          12 :         return text;
     842             : }
     843             : 
     844             : GF_EXPORT
     845        2843 : char *gf_dom_flatten_textContent(GF_Node *n)
     846             : {
     847             :         u32 len = 0;
     848             :         char *res = NULL;
     849             :         GF_ChildNodeItem *list;
     850        2843 :         if (!n) return NULL;
     851             : 
     852          36 :         if ((n->sgprivate->tag==TAG_DOMText) && ((GF_DOMText*)n)->textContent) {
     853             :                 /*if ( ((GF_DOMText*)n)->type == GF_DOM_TEXT_REGULAR) */{
     854          36 :                         res = gf_strdup(((GF_DOMText*)n)->textContent);
     855          36 :                         len = (u32) strlen(res);
     856             :                 }
     857             :         }
     858             : 
     859          36 :         list = ((GF_ParentNode *)n)->children;
     860          72 :         while (list) {
     861           0 :                 char *t = gf_dom_flatten_textContent(list->node);
     862           0 :                 if (t) {
     863           0 :                         size_t sub_len = strlen(t);
     864           0 :                         res = (char *)gf_realloc(res, sizeof(char)*(len+sub_len+1));
     865           0 :                         if (!len) res[0] = 0;
     866           0 :                         len += (u32)sub_len;
     867             :                         strcat(res, t);
     868           0 :                         gf_free(t);
     869             :                 }
     870           0 :                 list = list->next;
     871             :         }
     872             :         return res;
     873             : }
     874             : 
     875           7 : GF_DOMUpdates *gf_dom_add_updates_node(GF_Node *parent)
     876             : {
     877             :         GF_DOMUpdates *text;
     878           7 :         GF_SAFEALLOC(text, GF_DOMUpdates);
     879           7 :         if (!text) return NULL;
     880             : 
     881           7 :         gf_node_setup((GF_Node *)text, TAG_DOMUpdates);
     882           7 :         text->sgprivate->scenegraph = parent->sgprivate->scenegraph;
     883           7 :         text->updates = gf_list_new();
     884           7 :         gf_node_register((GF_Node *)text, parent);
     885           7 :         gf_node_list_add_child_last(&((GF_ParentNode *)parent)->children, (GF_Node*)text, NULL);
     886           7 :         return text;
     887             : }
     888             : 
     889             : #if 0 //unused
     890             : GF_DOMUpdates *gf_dom_add_update_node(GF_Node *parent)
     891             : {
     892             :         GF_DOMUpdates *update;
     893             :         GF_SAFEALLOC(update, GF_DOMUpdates);
     894             :         if (!update) return NULL;
     895             :         
     896             :         gf_node_setup((GF_Node *)update, TAG_DOMUpdates);
     897             :         update->sgprivate->scenegraph = parent->sgprivate->scenegraph;
     898             :         update->updates = gf_list_new();
     899             :         gf_node_register((GF_Node *)update, parent);
     900             :         gf_node_list_add_child_last(&((GF_ParentNode *)parent)->children, (GF_Node*)update, NULL);
     901             :         return update;
     902             : }
     903             : #endif
     904             : 
     905         354 : void gf_dom_event_dump_listeners(GF_Node *n, FILE *f)
     906             : {
     907             :         u32             i;
     908             :         u32             count;
     909             :         GF_List *listeners = NULL;
     910             :         GF_FieldInfo info;
     911             : 
     912             :         /*re-translate dynamically created listeners/handlers */
     913         354 :         if (n && n->sgprivate && n->sgprivate->interact && n->sgprivate->interact->dom_evt) {
     914           4 :                 listeners = n->sgprivate->interact->dom_evt->listeners;
     915           4 :                 count = gf_list_count(listeners);
     916           8 :                 for (i=0; i<count; i++) {
     917             :                         SVG_handlerElement *hdl;
     918           4 :                         GF_Node *listener = (GF_Node *)gf_list_get(listeners, i);
     919             :                         /*this listener has been created for internal use*/
     920           4 :                         if (listener->sgprivate->parents) continue;
     921           2 :                         if (gf_node_get_attribute_by_tag(listener, TAG_XMLEV_ATT_handler, GF_FALSE, GF_FALSE, &info)==GF_OK) {
     922             :                                 GF_DOMText *txt;
     923           2 :                                 hdl = (SVG_handlerElement *) ((XMLRI*)info.far_ptr)->target;
     924           2 :                                 if (!hdl) continue;
     925             :                                 /*this handler was declared in the graph*/
     926           2 :                                 if (hdl->sgprivate->parents
     927           2 :                                         && (hdl->sgprivate->parents->next || (hdl->sgprivate->parents->node != listener))
     928             :                                    )
     929           2 :                                         continue;
     930             : 
     931           0 :                                 txt = hdl->children ? (GF_DOMText*)hdl->children->node : NULL;
     932           0 :                                 if (!txt || (txt->sgprivate->tag!=TAG_DOMText) || !txt->textContent) continue;
     933           0 :                                 if (gf_node_get_attribute_by_tag((GF_Node*)hdl, TAG_XMLEV_ATT_event, GF_FALSE, GF_FALSE, &info)==GF_OK) {
     934           0 :                                         gf_fprintf(f, " on%s=\"%s\"", gf_dom_event_get_name( ((XMLEV_Event*)info.far_ptr)->type), txt->textContent);
     935             :                                 }
     936             :                         }
     937             :                 }
     938             :         }
     939         354 : }
     940             : 
     941        3417 : GF_DOMEventTarget *gf_dom_event_target_new(GF_DOMEventTargetType type, void *obj)
     942             : {
     943             :         GF_DOMEventTarget *target;
     944        3417 :         GF_SAFEALLOC(target, GF_DOMEventTarget);
     945        3417 :         if (!target) return NULL;
     946        3417 :         target->ptr_type = type;
     947        3417 :         target->listeners = gf_list_new();
     948        3417 :         target->ptr = obj;
     949        3417 :         return target;
     950             : }
     951             : 
     952        3417 : void gf_dom_event_target_del(GF_DOMEventTarget *target)
     953             : {
     954             :         assert(gf_list_count(target->listeners) == 0);
     955        3417 :         gf_list_del(target->listeners);
     956        3417 :         gf_free(target);
     957        3417 : }
     958             : 
     959         513 : GF_DOMEventTarget *gf_dom_event_get_target_from_node(GF_Node *n)
     960             : {
     961             :         GF_DOMEventTarget *target = NULL;
     962             :         //GF_HTML_MediaElement *me = html_media_element_get_from_node(c, n);
     963             :         //*target = me->evt_target;
     964             : 
     965         513 :         if (!n->sgprivate->interact) {
     966         437 :                 GF_SAFEALLOC(n->sgprivate->interact, struct _node_interactive_ext);
     967         437 :                 if (!n->sgprivate->interact) return NULL;
     968             :         }
     969         513 :         if (!n->sgprivate->interact->dom_evt) {
     970         497 :                 n->sgprivate->interact->dom_evt = gf_dom_event_target_new(GF_DOM_EVENT_TARGET_NODE, n);
     971             :         }
     972         513 :         target = n->sgprivate->interact->dom_evt;
     973             : 
     974         513 :         return target;
     975             : }
     976             : 
     977             : 
     978             : 
     979             : #endif  //GPAC_DISABLE_SVG
     980             : 

Generated by: LCOV version 1.13