Bool gf_sg_fire_dom_event(GF_DOMEventTarget *et, GF_DOM_Event *event, GF_SceneGraph *sg, GF_Node *n)
{
	if (et) {
		if (et->ptr_type==GF_DOM_EVENT_TARGET_NODE ||
		        et->ptr_type == GF_DOM_EVENT_TARGET_DOCUMENT ||
		        et->ptr_type == GF_DOM_EVENT_TARGET_XHR ||
		        et->ptr_type == GF_DOM_EVENT_TARGET_MSE_MEDIASOURCE ||
		        et->ptr_type == GF_DOM_EVENT_TARGET_MSE_SOURCEBUFFER ||
		        et->ptr_type == GF_DOM_EVENT_TARGET_MSE_SOURCEBUFFERLIST ) {
			GF_Node *observer = NULL;
			u32 i, count, post_count;
			if (et->ptr_type==GF_DOM_EVENT_TARGET_NODE) {
				observer = (GF_Node *)et->ptr;
			}
			count = gf_list_count(et->listeners);
			for (i=0; i<count; i++) {
				XMLEV_Event *listened_event;
				GF_Node *listen = (GF_Node *)gf_list_get(et->listeners, i);

				switch (listen->sgprivate->tag) {
				case TAG_SVG_listener:
				{
					SVGAllAttributes atts;
					gf_svg_flatten_attributes((SVG_Element*)listen, &atts);
					listened_event = atts.event;
					if (!listened_event) continue;

					if (atts.propagate && (*atts.propagate==XMLEVENT_PROPAGATE_STOP))
						event->event_phase |= GF_DOM_EVENT_PHASE_CANCEL;
					if (atts.defaultAction && (*atts.defaultAction==XMLEVENT_DEFAULTACTION_CANCEL))
						event->event_phase |= GF_DOM_EVENT_PHASE_PREVENT;
				}
				break;
				default:
					continue;
				}
				if (listened_event->type <= GF_EVENT_MOUSEMOVE) event->has_ui_events=1;
				if (listened_event->type != event->type) continue;
				if (listened_event->parameter && (listened_event->parameter != event->detail)) continue;
				event->currentTarget = et;
				event->consumed	++;

				/*load event cannot bubble and can only be called once (on load :) ), remove it
				to release some resources*/
				if (event->type==GF_EVENT_LOAD) {
					dom_event_process(listen, event, observer);
					/*delete listener*/
					//gf_dom_listener_del(listen, et);
				} else if (n) {
					assert(n->sgprivate->num_instances);
					/*protect node*/
					n->sgprivate->num_instances++;
					/*exec event*/
					dom_event_process(listen, event, observer);
					/*the event has destroyed ourselves, abort propagation
					THIS IS NOT DOM compliant, the event should propagate on the original target+ancestors path*/
					if (n->sgprivate->num_instances==1) {
						/*unprotect node event*/
						gf_node_unregister(n, NULL);
						return GF_FALSE;
					}
					n->sgprivate->num_instances--;
				} else {
					dom_event_process(listen, event, observer);
				}
				/*canceled*/
				if (event->event_phase & GF_DOM_EVENT_PHASE_CANCEL_ALL) {
					gf_dom_listener_process_add(sg);
					return GF_FALSE;
				}

				/*if listeners have been removed, update count*/
				post_count = gf_list_count(et->listeners);
				if (post_count < count) {
					s32 pos = gf_list_find(et->listeners, listen);
					if (pos>=0) i = pos;
					/*FIXME this is not going to work in all cases...*/
					else i--;
					count = post_count;
				}
			}
			/*propagation stopped*/
			if (event->event_phase & (GF_DOM_EVENT_PHASE_CANCEL|GF_DOM_EVENT_PHASE_CANCEL_ALL) ) {
				gf_dom_listener_process_add(sg);
				return GF_FALSE;
			}
		}
		gf_dom_listener_process_add(sg);
		/*if the current target is a node, we can bubble*/
		return n ? GF_TRUE : GF_FALSE;
	} else {
		/* if the node does not have a event target, probably a parent will have, so let's bubble */
		return GF_TRUE;
	}
}
Bool gf_sg_fire_dom_event(GF_DOMEventTarget *et, GF_DOM_Event *event, GF_SceneGraph *sg, GF_Node *n)
{
	if (et) {
		if (et->ptr_type==GF_DOM_EVENT_TARGET_NODE ||
		        et->ptr_type == GF_DOM_EVENT_TARGET_DOCUMENT ||
		        et->ptr_type == GF_DOM_EVENT_TARGET_XHR ||
		        et->ptr_type == GF_DOM_EVENT_TARGET_MSE_MEDIASOURCE ||
		        et->ptr_type == GF_DOM_EVENT_TARGET_MSE_SOURCEBUFFER ||
		        et->ptr_type == GF_DOM_EVENT_TARGET_MSE_SOURCEBUFFERLIST ) {
			GF_Node *observer = NULL;
			u32 i, count, post_count;
			if (et->ptr_type==GF_DOM_EVENT_TARGET_NODE) {
				observer = (GF_Node *)et->ptr;
			}
			count = gf_list_count(et->listeners);
			for (i=0; i<count; i++) {
				XMLEV_Event *listened_event;
				GF_Node *listen = (GF_Node *)gf_list_get(et->listeners, i);

				switch (listen->sgprivate->tag) {
				case TAG_SVG_listener:
				{
					SVGAllAttributes atts;
					gf_svg_flatten_attributes((SVG_Element*)listen, &atts);
					listened_event = atts.event;
					if (!listened_event) continue;

					if (atts.propagate && (*atts.propagate==XMLEVENT_PROPAGATE_STOP))
						event->event_phase |= GF_DOM_EVENT_PHASE_CANCEL;
					if (atts.defaultAction && (*atts.defaultAction==XMLEVENT_DEFAULTACTION_CANCEL))
						event->event_phase |= GF_DOM_EVENT_PHASE_PREVENT;
				}
				break;
				default:
					continue;
				}
				if (listened_event->type <= GF_EVENT_MOUSEMOVE) event->has_ui_events=1;
				if (listened_event->type != event->type) continue;
				if (listened_event->parameter && (listened_event->parameter != event->detail)) continue;
				event->currentTarget = et;
				event->consumed	++;

				 
				if (event->type==GF_EVENT_LOAD) {
					dom_event_process(listen, event, observer);
					 
					 
				} else if (n) {
					assert(n->sgprivate->num_instances);
					 
					n->sgprivate->num_instances++;
					 
					dom_event_process(listen, event, observer);
					 
					if (n->sgprivate->num_instances==1) {
						 
						gf_node_unregister(n, NULL);
						return GF_FALSE;
					}
					n->sgprivate->num_instances--;
				} else {
					dom_event_process(listen, event, observer);
				}
				 
				if (event->event_phase & GF_DOM_EVENT_PHASE_CANCEL_ALL) {
					gf_dom_listener_process_add(sg);
					return GF_FALSE;
				}

				 
				post_count = gf_list_count(et->listeners);
				if (post_count < count) {
					s32 pos = gf_list_find(et->listeners, listen);
					if (pos>=0) i = pos;
					 
					else i--;
					count = post_count;
				}
			}
			 
			if (event->event_phase & (GF_DOM_EVENT_PHASE_CANCEL|GF_DOM_EVENT_PHASE_CANCEL_ALL) ) {
				gf_dom_listener_process_add(sg);
				return GF_FALSE;
			}
		}
		gf_dom_listener_process_add(sg);
		 
		return n ? GF_TRUE : GF_FALSE;
	} else {
		 
		return GF_TRUE;
	}
}