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 :
|