1 /*
2  *			GPAC - Multimedia Framework C SDK
3  *
4  *			Authors: Jean Le Feuvre
5  *			Copyright (c) Telecom ParisTech 2005-2019
6  *					All rights reserved
7  *
8  *  This file is part of GPAC / Scene Graph sub-project
9  *
10  *  GPAC is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU Lesser General Public License as published by
12  *  the Free Software Foundation; either version 2, or (at your option)
13  *  any later version.
14  *
15  *  GPAC is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU Lesser General Public License for more details.
19  *
20  *  You should have received a copy of the GNU Lesser General Public
21  *  License along with this library; see the file COPYING.  If not, write to
22  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23  *
24  */
25 
26 #include <gpac/internal/scenegraph_dev.h>
27 
28 #ifndef GPAC_DISABLE_SVG
29 #include <gpac/events.h>
30 #include <gpac/nodes_svg.h>
31 
32 
33 #ifdef GPAC_HAS_QJS
34 
35 #ifdef GPAC_CONFIG_ANDROID
36 #ifndef XP_UNIX
37 #define XP_UNIX
38 #endif
39 #endif
40 
41 #include "qjs_common.h"
42 
43 #define JSVAL_CHECK_STRING(_v) (JS_IsString(_v) || JS_IsNull(_v))
44 
45 
46 /*SVG uDOM classes*/
47 GF_JSClass svgElement;
48 GF_JSClass svgDocument;
49 GF_JSClass svg_globalClass;
50 GF_JSClass connectionClass;
51 GF_JSClass rgbClass;
52 GF_JSClass rectClass;
53 GF_JSClass pointClass;
54 GF_JSClass pathClass;
55 GF_JSClass matrixClass;
56 
57 typedef struct
58 {
59 	JSValue proto;
60 	JSValue ctor;
61 } SVG_JSClass;
62 
63 
64 typedef struct __tag_svg_script_ctx
65 {
66 	Bool (*script_execute)(struct __tag_scene_graph *sg, char *utf8_script, GF_DOM_Event *event);
67 	Bool (*handler_execute)(GF_Node *n, GF_DOM_Event *event, GF_Node *observer, char *utf8_script);
68 	u32 nb_scripts;
69 	/*global script context for the scene*/
70 	JSContext *js_ctx;
71 	/*global object*/
72 	JSValue global;
73 	/*global event object - used to update the associated DOMEvent (JS private stack) when dispatching events*/
74 	JSValue event;
75 
76 	Bool in_script;
77 	Bool force_gc;
78 	Bool use_strict;
79 
80 	/*SVG uDOM classes*/
81 	SVG_JSClass svgElement;
82 	SVG_JSClass svgDocument;
83 	SVG_JSClass svg_globalClass;
84 	SVG_JSClass connectionClass;
85 	SVG_JSClass rgbClass;
86 	SVG_JSClass rectClass;
87 	SVG_JSClass pointClass;
88 	SVG_JSClass pathClass;
89 	SVG_JSClass matrixClass;
90 } GF_SVGJS;
91 
svg_mark_gc(struct __tag_svg_script_ctx * svg_js)92 void svg_mark_gc(struct __tag_svg_script_ctx *svg_js)
93 {
94 	if (svg_js)
95 		svg_js->force_gc = 1;
96 }
97 
svg_free_node_binding(struct __tag_svg_script_ctx * svg_js,GF_Node * node)98 void svg_free_node_binding(struct __tag_svg_script_ctx *svg_js, GF_Node *node)
99 {
100 	struct _node_js_binding *js_binding = node->sgprivate->interact->js_binding;
101 	if (!JS_IsUndefined(js_binding->obj)) {
102 		JS_SetOpaque(js_binding->obj, NULL);
103 		JS_FreeValue(svg_js->js_ctx, js_binding->obj);
104 		js_binding->obj = JS_UNDEFINED;
105 		//unregister after destroying JS obj since this is a recursive call and we trigger the GC, we must make sure
106 		//all JS opaque is NULL before destroying the node
107 		gf_node_unregister(node, NULL);
108 	}
109 
110 	if (svg_js->in_script)
111 		svg_js->force_gc = 1;
112 	else
113 		gf_js_call_gc(svg_js->js_ctx);
114 }
115 
svg_exec_script(struct __tag_svg_script_ctx * svg_js,GF_SceneGraph * sg,const char * com)116 GF_Err svg_exec_script(struct __tag_svg_script_ctx *svg_js, GF_SceneGraph *sg, const char *com)
117 {
118 	Bool ret = sg->svg_js->script_execute(sg, (char *)com, NULL);
119 	return (ret == GF_TRUE ? GF_OK : GF_BAD_PARAM);
120 }
121 
svgjs_handler_execute(struct __tag_svg_script_ctx * svg_js,GF_Node * hdl,GF_DOM_Event * event,GF_Node * observer,const char * iri)122 void svgjs_handler_execute(struct __tag_svg_script_ctx *svg_js, GF_Node *hdl, GF_DOM_Event *event, GF_Node *observer, const char *iri)
123 {
124 	if (svg_js->handler_execute(hdl, event, observer, (char *) iri)) {
125 		return;
126 	} else {
127 		GF_LOG(GF_LOG_WARNING, GF_LOG_INTERACT, ("[DOM Events] Error executing JavaScript event handler\n"));
128 		return;
129 	}
130 }
131 static Bool svg_script_execute_handler(GF_Node *node, GF_DOM_Event *event, GF_Node *observer, char *utf8_script);
132 
133 void dom_node_set_textContent(GF_Node *n, char *text);
134 
135 JSValue dom_node_get_sibling(JSContext *c, GF_Node *n, Bool is_prev, Bool elt_only);
136 
137 #ifdef GPAC_ENABLE_HTML5_MEDIA
138 void html_media_init_js_api(GF_SceneGraph *scene);
139 #endif
140 
141 #define _ScriptMessage(_sg, _msg) {\
142 			GF_JSAPIParam par;	\
143 			par.info.e = GF_SCRIPT_INFO;			\
144 			par.info.msg = _msg;		\
145 			_sg->script_action(_sg->script_action_cbck, GF_JSAPI_OP_MESSAGE, NULL, &par);\
146 		}
147 
ScriptAction(GF_SceneGraph * scene,u32 type,GF_Node * node,GF_JSAPIParam * param)148 static GFINLINE Bool ScriptAction(GF_SceneGraph *scene, u32 type, GF_Node *node, GF_JSAPIParam *param)
149 {
150 	if (scene->script_action)
151 		return scene->script_action(scene->script_action_cbck, type, node, param);
152 	return GF_FALSE;
153 }
154 
155 static JSValue svg_new_path_object(JSContext *c, SVG_PathData *d);
156 
157 
158 
159 /*note we are using float to avoid conversions fixed to/from JS native */
160 typedef struct
161 {
162 	u32 r, g, b;
163 } rgbCI;
164 
165 
166 typedef struct
167 {
168 	Float x, y, w, h;
169 	/*if set, this is the svg.viewport uDOM object, its values are updated upon query*/
170 	GF_SceneGraph *sg;
171 } rectCI;
172 
173 typedef struct
174 {
175 	Float x, y;
176 	/*if set, this is the svg.currentTranslate uDOM object, its values are updated upon query*/
177 	GF_SceneGraph *sg;
178 } pointCI;
179 
180 typedef struct
181 {
182 	Float x, y;
183 } ptCI;
184 
185 typedef struct
186 {
187 	u32 nb_coms;
188 	u8 *tags;
189 	ptCI *pts;
190 } pathCI;
191 
svg_nav_to_location(JSContext * c,JSValueConst obj,int argc,JSValueConst * argv)192 static JSValue svg_nav_to_location(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
193 {
194 	GF_JSAPIParam par;
195 	GF_SceneGraph *sg = JS_GetOpaque(obj, svg_globalClass.class_id);
196 	if ((argc!=1) || !sg)
197 		return JS_EXCEPTION;
198 
199 	par.uri.url = (char *) JS_ToCString(c, argv[0]);
200 	par.uri.nb_params = 0;
201 	ScriptAction(sg, GF_JSAPI_OP_LOAD_URL, sg->RootNode, &par);
202 	JS_FreeCString(c, par.uri.url);
203 	return JS_UNDEFINED;
204 }
205 
206 GF_Node *gf_sm_load_svg_from_string(GF_SceneGraph *sg, char *svg_str);
svg_parse_xml(JSContext * c,JSValueConst obj,int argc,JSValueConst * argv)207 static JSValue svg_parse_xml(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
208 {
209 	GF_SceneGraph *sg;
210 	GF_Node *node;
211 	const char *str;
212 
213 	if (!JS_IsObject(argv[1])) {
214 		return js_throw_err(c, GF_DOM_EXC_WRONG_DOCUMENT_ERR);
215 	}
216 
217 	str = JS_ToCString(c, argv[0]);
218 	if (!str) return JS_TRUE;
219 	sg = dom_get_doc(c, argv[1]);
220 
221 	node = gf_sm_load_svg_from_string(sg, (char *) str);
222 	JS_FreeCString(c, str);
223 	return dom_element_construct(c, node);
224 }
225 
svg_define_udom_exception(JSContext * c,JSValue global)226 static void svg_define_udom_exception(JSContext *c, JSValue global)
227 {
228 	JSValue obj = JS_NewObject(c);
229 	JS_SetPropertyStr(c, global, "GlobalException", obj);
230 #define DEFCONST(_name, _code)\
231 		JS_SetPropertyStr(c, obj, _name, JS_NewInt32(c, _code));
232 
233 	DEFCONST("NOT_CONNECTED_ERR", 1)
234 	DEFCONST("ENCODING_ERR", 2)
235 	DEFCONST("DENIED_ERR", 3)
236 	DEFCONST("UNKNOWN_ERR", 4)
237 
238 	obj = JS_NewObject(c);
239 	JS_SetPropertyStr(c, global, "SVGException", obj);
240 	DEFCONST("SVG_WRONG_TYPE_ERR", 0)
241 	DEFCONST("SVG_INVALID_VALUE_ERR", 1)
242 	DEFCONST("SVG_MATRIX_NOT_INVERTABLE", 2)
243 
244 	obj = JS_NewObject(c);
245 	JS_SetPropertyStr(c, global, "SVGSVGElement", obj);
246 	DEFCONST("NAV_AUTO", 1)
247 	DEFCONST("NAV_NEXT", 2)
248 	DEFCONST("NAV_PREV", 3)
249 	DEFCONST("NAV_UP", 4)
250 	DEFCONST("NAV_UP_RIGHT", 5)
251 	DEFCONST("NAV_RIGHT", 6)
252 	DEFCONST("NAV_DOWN_RIGHT", 7)
253 	DEFCONST("NAV_DOWN", 8)
254 	DEFCONST("NAV_DOWN_LEFT", 9)
255 	DEFCONST("NAV_LEFT", 10)
256 	DEFCONST("NAV_UP_LEFT", 11)
257 }
258 
global_getProperty(JSContext * c,JSValueConst obj,int magic)259 static JSValue global_getProperty(JSContext *c, JSValueConst obj, int magic)
260 {
261 	GF_SceneGraph *sg = JS_GetOpaque(obj, svg_globalClass.class_id);
262 	if (!sg) return JS_EXCEPTION;
263 
264 	switch (magic) {
265 	/*namespaceURI*/
266 	case 0:
267 		return JS_NULL;
268 	/*parent*/
269 	case 1:
270 		if (sg->parent_scene && sg->parent_scene->svg_js)
271 			return JS_DupValue(c, sg->parent_scene->svg_js->global);
272 		return JS_NULL;
273 	default:
274 		return JS_UNDEFINED;
275 	}
276 }
277 
278 /*TODO - try to be more precise...*/
dom_imp_has_feature(JSContext * c,JSValueConst obj,int argc,JSValueConst * argv)279 static JSValue dom_imp_has_feature(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
280 {
281 	JSValue ret = JS_FALSE;
282 
283 	if (argc) {
284 		u32 len;
285 		char sep;
286 		char *fname = (char *) JS_ToCString(c, argv[0]);
287 		if (!fname) return JS_TRUE;
288 		while (strchr(" \t\n\r", fname[0])) fname++;
289 		len = (u32) strlen(fname);
290 		while (len && strchr(" \t\n\r", fname[len-1])) len--;
291 		sep = fname[len];
292 		fname[len] = 0;
293 		if (!stricmp(fname, "xml")) ret = JS_TRUE;
294 		else if (!stricmp(fname, "core")) ret = JS_TRUE;
295 		else if (!stricmp(fname, "traversal")) ret = JS_TRUE;
296 		else if (!stricmp(fname, "uievents")) ret = JS_TRUE;
297 		else if (!stricmp(fname, "mouseevents")) ret = JS_TRUE;
298 		else if (!stricmp(fname, "mutationevents")) ret = JS_TRUE;
299 		else if (!stricmp(fname, "events")) ret = JS_TRUE;
300 
301 		fname[len] = sep;
302 		JS_FreeCString(c, fname);
303 	}
304 	return ret;
305 }
306 
get_corresponding_use(GF_Node * n)307 static GF_Node *get_corresponding_use(GF_Node *n)
308 {
309 	u32 i, count;
310 	if (!n || !n->sgprivate->scenegraph->use_stack) return NULL;
311 
312 	/*find current node in the use stack - if found, return the use element*/
313 	count = gf_list_count(n->sgprivate->scenegraph->use_stack);
314 	for (i=count; i>0; i-=2) {
315 		GF_Node *t = (GF_Node *)gf_list_get(n->sgprivate->scenegraph->use_stack, i-2);
316 		if (t==n) {
317 			GF_Node *use = (GF_Node *)gf_list_get(n->sgprivate->scenegraph->use_stack, i-1);
318 			GF_Node *par_use = get_corresponding_use(use);
319 			return par_use ? par_use : use;
320 		}
321 	}
322 	/*otherwise recursively get up the tree*/
323 	return get_corresponding_use(gf_node_get_parent(n, 0));
324 }
325 
svg_doc_getProperty(JSContext * c,JSValueConst obj,int magic)326 static JSValue svg_doc_getProperty(JSContext *c, JSValueConst obj, int magic)
327 {
328 	GF_SceneGraph *sg = dom_get_doc(c, obj);
329 	if (!sg) return JS_EXCEPTION;
330 	switch (magic) {
331 	case 0:/*global*/
332 		return JS_GetGlobalObject(c);
333 	}
334 	return JS_UNDEFINED;
335 }
336 
svg_element_getProperty(JSContext * c,JSValueConst obj,int magic)337 static JSValue svg_element_getProperty(JSContext *c, JSValueConst obj, int magic)
338 {
339 	GF_JSAPIParam par;
340 	GF_Node *n = dom_get_element(c, obj);
341 	if (!n) return JS_TRUE;
342 
343 	switch (magic) {
344 	case 0: /*id*/
345 	{
346 		const char *node_name = gf_node_get_name((GF_Node*)n);
347 		if (node_name) {
348 			return JS_NewString(c, node_name);
349 		}
350 		return JS_NULL;
351 	}
352 	case 5:/*currentScale*/
353 		if (n->sgprivate->tag!=TAG_SVG_svg) return JS_EXCEPTION;
354 		if (ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_GET_SCALE, (GF_Node *)n, &par)) {
355 			return JS_NewFloat64(c, FIX2FLT(par.val) );
356 		}
357 		return JS_EXCEPTION;
358 	case 6:/*currentRotate*/
359 		if (n->sgprivate->tag!=TAG_SVG_svg) return JS_EXCEPTION;
360 		if (ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_GET_ROTATION, (GF_Node *)n, &par)) {
361 			return JS_NewFloat64(c, FIX2FLT(par.val) );
362 		}
363 		return JS_EXCEPTION;
364 	case 7:/*currentTranslate*/
365 		if (n->sgprivate->tag!=TAG_SVG_svg) return JS_EXCEPTION;
366 		if (ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_GET_TRANSLATE, (GF_Node *)n, &par)) {
367 			JSValue r = JS_NewObjectClass(c, pointClass.class_id);
368 			pointCI *rc = (pointCI *)gf_malloc(sizeof(pointCI));
369 			rc->x = FIX2FLT(par.pt.x);
370 			rc->y = FIX2FLT(par.pt.y);
371 			rc->sg = n->sgprivate->scenegraph;
372 			JS_SetOpaque(r, rc);
373 			return r;
374 		}
375 		return JS_EXCEPTION;
376 	case 8:/*viewport*/
377 		if (n->sgprivate->tag!=TAG_SVG_svg) return JS_EXCEPTION;
378 		if (ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_GET_VIEWPORT, (GF_Node *)n, &par)) {
379 			JSValue r = JS_NewObjectClass(c, rectClass.class_id);
380 			rectCI *rc = (rectCI *)gf_malloc(sizeof(rectCI));
381 			rc->x = FIX2FLT(par.rc.x);
382 			rc->y = FIX2FLT(par.rc.y);
383 			rc->w = FIX2FLT(par.rc.width);
384 			rc->h = FIX2FLT(par.rc.height);
385 			rc->sg = n->sgprivate->scenegraph;
386 			JS_SetOpaque(r, rc);
387 			return r;
388 		}
389 		return JS_EXCEPTION;
390 	case 9:/*currentTime*/
391 		return JS_NewFloat64(c, gf_node_get_scene_time((GF_Node *)n) );
392 	case 10:/*isPaused*/
393 		return JS_FALSE;
394 	case 11:/*ownerSVGElement*/
395 		while (1) {
396 			GF_Node *n_par = gf_node_get_parent(n, 0);
397 			if (!n_par) return JS_TRUE;
398 			if (n_par->sgprivate->tag==TAG_SVG_svg) {
399 				return dom_element_construct(c, n_par);
400 			}
401 			n = n_par;
402 		}
403 		return JS_NULL;
404 	case 12:/*correspondingElement*/
405 		/*if we can find a corresponding element for this node, then this is an SVGElementInstance*/
406 		if (get_corresponding_use(n)) {
407 			return dom_element_construct(c, n);
408 		} else {
409 			return dom_element_construct(c, NULL);
410 		}
411 		break;
412 	case 13:/*correspondingUseElement*/
413 		return dom_element_construct(c, get_corresponding_use(n));
414 	default:
415 		break;
416 	}
417 	return JS_UNDEFINED;
418 }
419 
svg_element_setProperty(JSContext * c,JSValueConst obj,JSValueConst value,int magic)420 static JSValue svg_element_setProperty(JSContext *c, JSValueConst obj, JSValueConst value, int magic)
421 {
422 	GF_JSAPIParam par;
423 	Double d;
424 	GF_Node *n = dom_get_element(c, obj);
425 	if (!n) return JS_EXCEPTION;
426 
427 	switch (magic) {
428 	case 0:/*id*/
429 		if (JSVAL_CHECK_STRING(value)) {
430 			const char *id = JS_ToCString(c, value);
431 			if (id) {
432 				GF_FieldInfo info;
433 				u32 nid = gf_node_get_id(n);
434 				if (!nid) nid = gf_sg_get_next_available_node_id(n->sgprivate->scenegraph);
435 				gf_node_set_id(n, nid, id);
436 				if (gf_node_get_attribute_by_tag(n, TAG_XML_ATT_id, GF_TRUE, GF_FALSE, &info)==GF_OK) {
437 					if (*(DOM_String *)info.far_ptr) gf_free(*(DOM_String *)info.far_ptr);
438 					*(DOM_String *)info.far_ptr = gf_strdup(id);
439 				}
440 				if (gf_node_get_attribute_by_tag(n, TAG_SVG_ATT_id, GF_TRUE, GF_FALSE, &info)==GF_OK) {
441 					if (*(DOM_String *)info.far_ptr) gf_free(*(DOM_String *)info.far_ptr);
442 					*(DOM_String *)info.far_ptr = gf_strdup(id);
443 				}
444 				JS_FreeCString(c, id);
445 			}
446 		}
447 		return JS_TRUE;
448 		/*currentScale*/
449 	case 5:
450 		if ((n->sgprivate->tag!=TAG_SVG_svg) || JS_ToFloat64(c, &d, value))
451 			return JS_EXCEPTION;
452 
453 		par.val = FLT2FIX(d);
454 		if (!par.val) {
455 			return js_throw_err(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
456 		}
457 		if (ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_SET_SCALE, (GF_Node *)n, &par)) {
458 			return JS_TRUE;
459 		}
460 		return JS_FALSE;
461 		/*currentRotate*/
462 	case 6:
463 		if ((n->sgprivate->tag!=TAG_SVG_svg) || JS_ToFloat64(c, &d, value))
464 			return JS_EXCEPTION;
465 
466 		par.val = FLT2FIX(d);
467 		if (ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_SET_ROTATION, (GF_Node *)n, &par)) {
468 			return JS_TRUE;
469 		}
470 		return JS_FALSE;
471 		/*currentTime*/
472 	case 9:
473 		if ((n->sgprivate->tag!=TAG_SVG_svg) || JS_ToFloat64(c, &d, value))
474 			return JS_EXCEPTION;
475 
476 		par.time = d;
477 		if (ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_SET_TIME, (GF_Node *)n, &par)) {
478 			return JS_TRUE;
479 		}
480 		return JS_FALSE;
481 	default:
482 		break;
483 	}
484 	return JS_UNDEFINED;
485 }
486 
svg_udom_smil_check_instance(JSContext * c,JSValue obj)487 static GF_Node *svg_udom_smil_check_instance(JSContext *c, JSValue obj)
488 {
489 	GF_Node *n = dom_get_element(c, obj);
490 	if (!n) return NULL;
491 	switch (n->sgprivate->tag) {
492 	case TAG_SVG_animate:
493 	case TAG_SVG_animateColor:
494 	case TAG_SVG_animateMotion:
495 	case TAG_SVG_animateTransform:
496 	case TAG_SVG_animation:
497 	case TAG_SVG_audio:
498 	case TAG_SVG_video:
499 	case TAG_SVG_set:
500 	case TAG_LSR_updates:
501 	/*not sure about this one...*/
502 	case TAG_SVG_discard:
503 		return n;
504 	}
505 	return NULL;
506 }
507 
508 
509 /*TODO*/
svg_udom_smil_time_insert(JSContext * c,JSValueConst obj,int argc,JSValueConst * argv,Bool is_end)510 static JSValue svg_udom_smil_time_insert(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv, Bool is_end)
511 {
512 	GF_FieldInfo info;
513 	u32 i, count;
514 	Double offset;
515 	GF_List *times;
516 	SMIL_Time *newtime;
517 
518 	GF_Node *n = svg_udom_smil_check_instance(c, obj);
519 	if (!n) return JS_UNDEFINED;
520 
521 	if (is_end) {
522 		info.far_ptr = ((SVGTimedAnimBaseElement *)n)->timingp->end;
523 	} else {
524 		info.far_ptr = ((SVGTimedAnimBaseElement *)n)->timingp->begin;
525 	}
526 	if (!info.far_ptr) {
527 		return JS_EXCEPTION;
528 	}
529 	times = *((GF_List **)info.far_ptr);
530 	GF_SAFEALLOC(newtime, SMIL_Time);
531 	if (!newtime) {
532 		return js_throw_err(c, GF_DOM_EXC_DATA_CLONE_ERR);
533 	}
534 	newtime->type = GF_SMIL_TIME_EVENT_RESOLVED;
535 
536 	offset = 0;
537 	if (argc)
538 		JS_ToFloat64(c, &offset, argv[0]);
539 
540 	newtime->clock = gf_node_get_scene_time(n) + offset;
541 
542 	/*insert in sorted order*/
543 	count = gf_list_count(times);
544 	for (i=0; i<count; i++) {
545 		SMIL_Time*t = (SMIL_Time*)gf_list_get(times, i);
546 		if ( GF_SMIL_TIME_IS_CLOCK(t->type) ) {
547 			if (t->clock > newtime->clock) break;
548 		} else {
549 			break;
550 		}
551 	}
552 	gf_list_insert(times, newtime, i);
553 
554 	info.fieldType = SMIL_Times_datatype;
555 	gf_node_changed(n, &info);
556 	return JS_TRUE;
557 }
558 
svg_udom_smil_begin(JSContext * c,JSValueConst obj,int argc,JSValueConst * argv)559 static JSValue svg_udom_smil_begin(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
560 {
561 	return svg_udom_smil_time_insert(c, obj, argc, argv, GF_FALSE);
562 }
svg_udom_smil_end(JSContext * c,JSValueConst obj,int argc,JSValueConst * argv)563 static JSValue svg_udom_smil_end(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
564 {
565 	return svg_udom_smil_time_insert(c, obj, argc, argv, GF_TRUE);
566 }
567 
568 /*TODO*/
svg_udom_smil_pause(JSContext * c,JSValueConst obj,int argc,JSValueConst * argv)569 static JSValue svg_udom_smil_pause(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
570 {
571 	u32 tag;
572 	GF_Node *n = dom_get_element(c, obj);
573 	if (!n) return JS_EXCEPTION;
574 
575 	tag = gf_node_get_tag(n);
576 	if (gf_svg_is_animation_tag(tag)) {
577 		/* pausing an animation element (set, animate ...) should pause the main time line ? */
578 		gf_smil_timing_pause(n);
579 	} else if (gf_svg_is_timing_tag(tag)) {
580 		ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_PAUSE_SVG, n, NULL);
581 	} else if ((tag==TAG_SVG_svg) && (n->sgprivate->scenegraph->RootNode==n)) {
582 		ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_PAUSE_SVG, n, NULL);
583 	} else {
584 		return JS_FALSE;
585 	}
586 	return JS_TRUE;
587 }
588 /*TODO*/
svg_udom_smil_resume(JSContext * c,JSValueConst obj,int argc,JSValueConst * argv)589 static JSValue svg_udom_smil_resume(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
590 {
591 	u32 tag;
592 	GF_Node *n = dom_get_element(c, obj);
593 	if (!n) return JS_EXCEPTION;
594 
595 	tag = gf_node_get_tag(n);
596 	if (gf_svg_is_animation_tag(tag)) {
597 		/* resuming an animation element (set, animate ...) should resume the main time line ? */
598 		gf_smil_timing_resume(n);
599 	} else if (gf_svg_is_timing_tag(tag)) {
600 		ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_RESUME_SVG, n, NULL);
601 	} else if ((tag==TAG_SVG_svg) && (n->sgprivate->scenegraph->RootNode==n)) {
602 		ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_RESUME_SVG, n, NULL);
603 	} else {
604 		return JS_FALSE;
605 	}
606 	return JS_TRUE;
607 }
608 
svg_udom_smil_restart(JSContext * c,JSValueConst obj,int argc,JSValueConst * argv)609 static JSValue svg_udom_smil_restart(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
610 {
611 	u32 tag;
612 	GF_Node *n = dom_get_element(c, obj);
613 	if (!n) return JS_EXCEPTION;
614 
615 	tag = gf_node_get_tag(n);
616 	if ((tag==TAG_SVG_svg) && (n->sgprivate->scenegraph->RootNode==n)) {
617 		ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_RESTART_SVG, n, NULL);
618 	} else {
619 		return JS_FALSE;
620 	}
621 	return JS_TRUE;
622 }
623 
svg_udom_smil_set_speed(JSContext * c,JSValueConst obj,int argc,JSValueConst * argv)624 static JSValue svg_udom_smil_set_speed(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
625 {
626 	u32 tag;
627 	Double speed = 1.0;
628 	GF_Node *n = dom_get_element(c, obj);
629 
630 	if (!n || !argc || JS_ToFloat64(c, &speed, argv[0]) ) {
631 		return JS_EXCEPTION;
632 	}
633 	tag = gf_node_get_tag(n);
634 	if ((tag==TAG_SVG_svg) && (n->sgprivate->scenegraph->RootNode==n)) {
635 		GF_JSAPIParam par;
636 		memset(&par, 0, sizeof(GF_JSAPIParam));
637 		par.val = FLT2FIX(speed);
638 		ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_SET_SCENE_SPEED, n, &par);
639 	} else {
640 		return JS_TRUE;
641 	}
642 	return JS_UNDEFINED;
643 }
644 
svg_udom_get_trait(JSContext * c,JSValueConst obj,int argc,JSValueConst * argv)645 static JSValue svg_udom_get_trait(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
646 {
647 	char *attValue;
648 	const char *name;
649 	GF_Err e;
650 	JSValue ret;
651 	GF_FieldInfo info;
652 	GF_Node *n = dom_get_element(c, obj);
653 	if (!n) return JS_EXCEPTION;
654 
655 	//ns = NULL;
656 	name = NULL;
657 	if (! JSVAL_CHECK_STRING(argv[0]) ) return JS_TRUE;
658 	if (argc==2) {
659 		//ns = JS_ToCString(c, argv[0]);
660 		name = JS_ToCString(c, argv[1]);
661 	} else if (argc==1) {
662 		name = JS_ToCString(c, argv[0]);
663 	} else return JS_EXCEPTION;
664 
665 	if (!name) {
666 		//JS_FreeCString(c, ns);
667 		return JS_EXCEPTION;
668 	}
669 	if (!strcmp(name, "#text")) {
670 		char *res = gf_dom_flatten_textContent(n);
671 		ret = JS_NewString(c, res);
672 		gf_free(res);
673 		//JS_FreeCString(c, ns);
674 		JS_FreeCString(c, name);
675 		return ret;
676 	}
677 	e = gf_node_get_field_by_name(n, (char *) name, &info);
678 
679 	//JS_FreeCString(c, ns);
680 	JS_FreeCString(c, name);
681 
682 	if (e!=GF_OK) return JS_EXCEPTION;
683 
684 	switch (info.fieldType) {
685 	/* inheritable floats */
686 	case SVG_FontSize_datatype:
687 	case SVG_Color_datatype:
688 	case SVG_Paint_datatype:
689 	/* inheritable float and unit */
690 	case SVG_Length_datatype:
691 	case SVG_Coordinate_datatype:
692 	/*Number*/
693 	case SVG_Number_datatype:
694 
695 	/*all string traits*/
696 	case SVG_Boolean_datatype:
697 	case SVG_FillRule_datatype:
698 	case SVG_StrokeLineJoin_datatype:
699 	case SVG_StrokeLineCap_datatype:
700 	case SVG_FontStyle_datatype:
701 	case SVG_FontWeight_datatype:
702 	case SVG_FontVariant_datatype:
703 	case SVG_TextAnchor_datatype:
704 	case SVG_Display_datatype:
705 	case SVG_Visibility_datatype:
706 	case SVG_GradientUnit_datatype:
707 	case SVG_PreserveAspectRatio_datatype:
708 	case XML_Space_datatype:
709 	case XMLEV_Propagate_datatype:
710 	case XMLEV_DefaultAction_datatype:
711 	case XMLEV_Phase_datatype:
712 	case SMIL_SyncBehavior_datatype:
713 	case SMIL_SyncTolerance_datatype:
714 	case SMIL_AttributeType_datatype:
715 	case SMIL_CalcMode_datatype:
716 	case SMIL_Additive_datatype:
717 	case SMIL_Accumulate_datatype:
718 	case SMIL_Restart_datatype:
719 	case SMIL_Fill_datatype:
720 	case SVG_Overflow_datatype:
721 	case SVG_ZoomAndPan_datatype:
722 	case SVG_DisplayAlign_datatype:
723 	case SVG_TextAlign_datatype:
724 	case SVG_PointerEvents_datatype:
725 	case SVG_RenderingHint_datatype:
726 	case SVG_VectorEffect_datatype:
727 	case SVG_PlaybackOrder_datatype:
728 	case SVG_TimelineBegin_datatype:
729 	/*end of string traits*/
730 	/*DOM string traits*/
731 	case SVG_FontFamily_datatype:
732 	case XMLRI_datatype:
733 	case DOM_String_datatype:
734 	case SVG_ContentType_datatype:
735 	case SVG_LanguageID_datatype:
736 	case SVG_Focus_datatype:
737 	case SVG_ID_datatype:
738 	case SVG_GradientOffset_datatype:
739 		/*end of DOM string traits*/
740 		attValue = gf_svg_dump_attribute(n, &info);
741 		ret = JS_NewString(c, attValue);
742 		if (attValue) gf_free(attValue);
743 		return ret;
744 
745 #if 0
746 	/*SVGT 1.2 default traits*/
747 	case SMIL_KeyTimes_datatype:
748 	case SMIL_KeyPoints_datatype:
749 	case SMIL_KeySplines_datatype:
750 	case SVG_Coordinates_datatype:
751 	case SVG_StrokeDashArray_datatype:
752 	case SVG_Points_datatype:
753 	case SVG_Motion_datatype:
754 	/*end SVGT 1.2 default traits*/
755 
756 	/*unimplemented/unnkown/FIXME traits*/
757 	case SMIL_SyncTolerance_datatype:
758 	case SVG_TransformType_datatype:
759 	case SVG_TransformList_datatype:
760 	case SMIL_AnimateValue_datatype:
761 	case SMIL_AnimateValues_datatype:
762 	case SMIL_AttributeName_datatype:
763 	case SMIL_Times_datatype:
764 	case SMIL_Duration_datatype:
765 	case SMIL_RepeatCount_datatype:
766 	default:
767 		/*end unimplemented/unnkown/FIXME traits*/
768 		return JS_EXCEPTION;
769 #endif
770 	}
771 	return JS_NULL;
772 }
773 
svg_udom_get_float_trait(JSContext * c,JSValueConst obj,int argc,JSValueConst * argv)774 static JSValue svg_udom_get_float_trait(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
775 {
776 	const char *szName;
777 	GF_FieldInfo info;
778 	GF_Err e;
779 	GF_Node *n = dom_get_element(c, obj);
780 	if (!n) return JS_EXCEPTION;
781 
782 	if ((argc!=1) || !JS_IsString(argv[0])) return JS_EXCEPTION;
783 	szName = JS_ToCString(c, argv[0]);
784 	if (!szName) return JS_EXCEPTION;
785 
786 	e = gf_node_get_attribute_by_name(n, (char *) szName, 0, GF_TRUE, GF_TRUE, &info);
787 	JS_FreeCString(c, szName);
788 	if (e != GF_OK) return JS_EXCEPTION;
789 
790 	switch (info.fieldType) {
791 	/* inheritable floats */
792 	case SVG_Number_datatype:
793 	case SVG_FontSize_datatype:
794 	case SVG_Length_datatype:
795 	case SVG_Coordinate_datatype:
796 	{
797 		SVG_Number *l = (SVG_Number *)info.far_ptr;
798 		if (l->type==SVG_NUMBER_AUTO || l->type==SVG_NUMBER_INHERIT) return JS_TRUE;
799 		return JS_NewFloat64(c, FIX2FLT(l->value));
800 	}
801 	case SVG_Clock_datatype:
802 		return JS_NewFloat64(c, *(SVG_Clock*)info.far_ptr );
803 	default:
804 		return JS_NULL;
805 	}
806 	return JS_NULL;
807 }
808 
svg_udom_get_matrix_trait(JSContext * c,JSValueConst obj,int argc,JSValueConst * argv)809 static JSValue svg_udom_get_matrix_trait(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
810 {
811 	const char *szName;
812 	GF_FieldInfo info;
813 	GF_Err e;
814 	GF_Node *n = dom_get_element(c, obj);
815 	if (!n) return JS_EXCEPTION;
816 
817 	if ((argc!=1) || !JS_IsString(argv[0])) return JS_EXCEPTION;
818 	szName = JS_ToCString(c, argv[0]);
819 
820 	e = gf_node_get_field_by_name(n, (char *) szName, &info);
821 	JS_FreeCString(c, szName);
822 	if (e != GF_OK) return JS_EXCEPTION;
823 
824 	if (info.fieldType==SVG_Transform_datatype) {
825 		GF_Matrix2D *mx = (GF_Matrix2D *)gf_malloc(sizeof(GF_Matrix2D));
826 		if (!mx) return JS_EXCEPTION;
827 		JSValue mO = JS_NewObjectClass(c, matrixClass.class_id);
828 		gf_mx2d_init(*mx);
829 		gf_mx2d_copy(*mx, ((SVG_Transform*)info.far_ptr)->mat);
830 
831 		JS_SetOpaque(mO, mx);
832 		return mO;
833 	}
834 	return JS_NULL;
835 }
836 
svg_udom_get_rect_trait(JSContext * c,JSValueConst obj,int argc,JSValueConst * argv)837 static JSValue svg_udom_get_rect_trait(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
838 {
839 	const char *szName;
840 	GF_FieldInfo info;
841 	GF_Err e;
842 	GF_Node *n = dom_get_element(c, obj);
843 	if (!n) return JS_EXCEPTION;
844 
845 	if ((argc!=1) || !JS_IsString(argv[0])) return JS_EXCEPTION;
846 	szName = JS_ToCString(c, argv[0]);
847 
848 	e = gf_node_get_field_by_name(n, (char *) szName, &info);
849 	JS_FreeCString(c, szName);
850 	if (e != GF_OK) return JS_EXCEPTION;
851 
852 	if (info.fieldType==SVG_ViewBox_datatype) {
853 		JSValue newObj;
854 		rectCI *rc;
855 		SVG_ViewBox *v = (SVG_ViewBox *)info.far_ptr;
856 		GF_SAFEALLOC(rc, rectCI);
857 		if (!rc) {
858 			return js_throw_err(c, GF_DOM_EXC_DATA_CLONE_ERR);
859 		}
860 		newObj = JS_NewObjectClass(c, rectClass.class_id);
861 		rc->x = FIX2FLT(v->x);
862 		rc->y = FIX2FLT(v->y);
863 		rc->w = FIX2FLT(v->width);
864 		rc->h = FIX2FLT(v->height);
865 		JS_SetOpaque(newObj, rc);
866 		return newObj;
867 	}
868 	return JS_NULL;
869 }
870 
svg_udom_get_path_trait(JSContext * c,JSValueConst obj,int argc,JSValueConst * argv)871 static JSValue svg_udom_get_path_trait(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
872 {
873 	const char *szName;
874 	GF_FieldInfo info;
875 	GF_Err e;
876 	GF_Node *n = dom_get_element(c, obj);
877 	if (!n) return JS_EXCEPTION;
878 
879 	if ((argc!=1) || !JS_IsString(argv[0])) return JS_EXCEPTION;
880 	szName = JS_ToCString(c, argv[0]);
881 
882 	e = gf_node_get_field_by_name(n, (char *) szName, &info);
883 	JS_FreeCString(c, szName);
884 	if (e != GF_OK) return JS_EXCEPTION;
885 
886 	if (info.fieldType==SVG_PathData_datatype) {
887 		return svg_new_path_object(c, (SVG_PathData *)info.far_ptr);
888 	}
889 	return JS_NULL;
890 }
891 
svg_udom_get_rgb_color_trait(JSContext * c,JSValueConst obj,int argc,JSValueConst * argv)892 static JSValue svg_udom_get_rgb_color_trait(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
893 {
894 	const char *szName;
895 	GF_FieldInfo info;
896 	rgbCI *rgb;
897 	GF_Err e;
898 	JSValue newObj;
899 
900 	GF_Node *n = dom_get_element(c, obj);
901 	if (!n) return JS_EXCEPTION;
902 
903 	if ((argc!=1) || !JS_IsString(argv[0])) return JS_EXCEPTION;
904 	szName = JS_ToCString(c, argv[0]);
905 
906 	e = gf_node_get_field_by_name(n, (char *) szName, &info);
907 	JS_FreeCString(c, szName);
908 	if (e != GF_OK) return JS_EXCEPTION;
909 
910 	switch (info.fieldType) {
911 	case SVG_Color_datatype:
912 	{
913 		SVG_Color *col = (SVG_Color *)info.far_ptr;
914 		if (col->type == SVG_COLOR_CURRENTCOLOR) return JS_UNDEFINED;
915 		if (col->type == SVG_COLOR_INHERIT) return JS_UNDEFINED;
916 
917 		GF_SAFEALLOC(rgb, rgbCI);
918 		if (!rgb) {
919 			return js_throw_err(c, GF_DOM_EXC_DATA_CLONE_ERR);
920 		}
921 		newObj = JS_NewObjectClass(c, rgbClass.class_id);
922 		rgb->r = (u8) (255*FIX2FLT(col->red)) ;
923 		rgb->g = (u8) (255*FIX2FLT(col->green)) ;
924 		rgb->b = (u8) (255*FIX2FLT(col->blue)) ;
925 		JS_SetOpaque(newObj, rgb);
926 		return newObj;
927 	}
928 	break;
929 	case SVG_Paint_datatype:
930 	{
931 		SVG_Paint *paint = (SVG_Paint *)info.far_ptr;
932 		if ((1) || paint->type==SVG_PAINT_COLOR) {
933 			GF_SAFEALLOC(rgb, rgbCI);
934 			if (!rgb) {
935 				return js_throw_err(c, GF_DOM_EXC_DATA_CLONE_ERR);
936 			}
937 			newObj = JS_NewObjectClass(c, rgbClass.class_id);
938 			rgb->r = (u8) (255*FIX2FLT(paint->color.red) );
939 			rgb->g = (u8) (255*FIX2FLT(paint->color.green) );
940 			rgb->b = (u8) (255*FIX2FLT(paint->color.blue) );
941 			JS_SetOpaque(newObj, rgb);
942 			return newObj;
943 		}
944 		return JS_TRUE;
945 	}
946 	}
947 	return JS_NULL;
948 }
949 
svg_udom_set_trait(JSContext * c,JSValueConst obj,int argc,JSValueConst * argv)950 static JSValue svg_udom_set_trait(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
951 {
952 	const char *ns, *name, *val;
953 	GF_Err e;
954 	GF_FieldInfo info;
955 	GF_Node *n = dom_get_element(c, obj);
956 	if (!n) return JS_EXCEPTION;
957 
958 	val = ns = name = NULL;
959 	if (!JSVAL_CHECK_STRING(argv[0])) return JS_EXCEPTION;
960 	if (argc==3) {
961 		if (!JSVAL_CHECK_STRING(argv[1])) return JS_EXCEPTION;
962 		if (!JSVAL_CHECK_STRING(argv[2])) return JS_EXCEPTION;
963 		ns = JS_ToCString(c, argv[0]);
964 		name = JS_ToCString(c, argv[1]);
965 		val = JS_ToCString(c, argv[2]);
966 	} else if (argc==2) {
967 		name = JS_ToCString(c, argv[0]);
968 		val = JS_ToCString(c, argv[1]);
969 	} else {
970 		return JS_EXCEPTION;
971 	}
972 	if (!name) {
973 		JS_FreeCString(c, ns);
974 		JS_FreeCString(c, val);
975 		return JS_EXCEPTION;
976 	}
977 	if (!strcmp(name, "#text")) {
978 		if (val) dom_node_set_textContent(n, (char *) val);
979 		JS_FreeCString(c, ns);
980 		JS_FreeCString(c, name);
981 		JS_FreeCString(c, val);
982 		return JS_UNDEFINED;
983 	}
984 	e = gf_node_get_field_by_name(n, (char *) name, &info);
985 	JS_FreeCString(c, ns);
986 	JS_FreeCString(c, name);
987 
988 	if (!val || (e!=GF_OK)) {
989 		JS_FreeCString(c, val);
990 		return JS_EXCEPTION;
991 	}
992 	e = gf_svg_parse_attribute(n, &info, (char *) val, 0);
993 	JS_FreeCString(c, val);
994 
995 	if (e) return js_throw_err(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
996 	dom_node_changed(n, GF_FALSE, &info);
997 	return JS_UNDEFINED;
998 }
999 
svg_udom_set_float_trait(JSContext * c,JSValueConst obj,int argc,JSValueConst * argv)1000 static JSValue svg_udom_set_float_trait(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1001 {
1002 	GF_FieldInfo info;
1003 	Double d;
1004 	GF_Err e;
1005 	const char *szName;
1006 
1007 	GF_Node *n = dom_get_element(c, obj);
1008 	if (!n) return JS_EXCEPTION;
1009 	if (argc!=2) return JS_EXCEPTION;
1010 	if (!JS_IsString(argv[0])) return JS_EXCEPTION;
1011 	if (!JS_IsNumber(argv[1]) || JS_ToFloat64(c, &d, argv[1])) return JS_EXCEPTION;
1012 
1013 	szName = JS_ToCString(c, argv[0]);
1014 	e = gf_node_get_field_by_name(n, (char *) szName, &info);
1015 	JS_FreeCString(c, szName);
1016 	if (e != GF_OK) return JS_EXCEPTION;
1017 
1018 	switch (info.fieldType) {
1019 	/* inheritable floats */
1020 	case SVG_FontSize_datatype:
1021 	case SVG_Length_datatype:
1022 	case SVG_Coordinate_datatype:
1023 	case SVG_Number_datatype:
1024 	{
1025 		SVG_Number *l = (SVG_Number *)info.far_ptr;
1026 		l->type=SVG_NUMBER_VALUE;
1027 		l->value = FLT2FIX(d);
1028 		break;
1029 	}
1030 	case SVG_Numbers_datatype:
1031 	case SVG_Coordinates_datatype:
1032 	{
1033 		SVG_Number *val;
1034 		SVG_Coordinates *l = (SVG_Coordinates *)info.far_ptr;
1035 		while (gf_list_count(*l)) {
1036 			val = (SVG_Number *)gf_list_get(*l, 0);
1037 			gf_list_rem(*l, 0);
1038 			if (val) gf_free(val);
1039 		}
1040 		GF_SAFEALLOC(val, SVG_Coordinate);
1041 		if (!val) {
1042 			return js_throw_err(c, GF_DOM_EXC_DATA_CLONE_ERR);
1043 		}
1044 		val->type=SVG_NUMBER_VALUE;
1045 		val->value = FLT2FIX(d);
1046 		gf_list_add(*l, val);
1047 		break;
1048 	}
1049 	default:
1050 		return JS_FALSE;
1051 	}
1052 	dom_node_changed(n, GF_FALSE, &info);
1053 	return JS_TRUE;
1054 }
1055 
svg_udom_set_matrix_trait(JSContext * c,JSValueConst obj,int argc,JSValueConst * argv)1056 static JSValue svg_udom_set_matrix_trait(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1057 {
1058 	const char *szName;
1059 	GF_FieldInfo info;
1060 	GF_Matrix2D *mx;
1061 	GF_Err e;
1062 
1063 	GF_Node *n = dom_get_element(c, obj);
1064 	if (!n) return JS_TRUE;
1065 
1066 	if (argc!=2) return JS_EXCEPTION;
1067 	if (!JS_IsString(argv[0])) return JS_EXCEPTION;
1068 	if (JS_IsNull(argv[1]) || !JS_IsObject(argv[1])) return JS_EXCEPTION;
1069 
1070 	mx = JS_GetOpaque(argv[1], matrixClass.class_id);
1071 	if (!mx) return JS_EXCEPTION;
1072 
1073 	szName = JS_ToCString(c, argv[0]);
1074 	e = gf_node_get_field_by_name(n, (char *) szName, &info);
1075 	JS_FreeCString(c, szName);
1076 	if (e != GF_OK) return JS_EXCEPTION;
1077 
1078 	if (info.fieldType==SVG_Transform_datatype) {
1079 		gf_mx2d_copy(((SVG_Transform*)info.far_ptr)->mat, *mx);
1080 		dom_node_changed(n, GF_FALSE, NULL);
1081 		return JS_TRUE;
1082 	}
1083 	return JS_FALSE;
1084 }
1085 
svg_udom_set_rect_trait(JSContext * c,JSValueConst obj,int argc,JSValueConst * argv)1086 static JSValue svg_udom_set_rect_trait(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1087 {
1088 	const char *szName;
1089 	GF_FieldInfo info;
1090 	rectCI *rc;
1091 	GF_Err e;
1092 	GF_Node *n = dom_get_element(c, obj);
1093 	if (!n) return JS_EXCEPTION;
1094 
1095 	if (argc!=2) return JS_EXCEPTION;
1096 	if (!JS_IsString(argv[0])) return JS_TRUE;
1097 	if (JS_IsNull(argv[1]) || !JS_IsObject(argv[1])) return JS_EXCEPTION;
1098 
1099 	rc = JS_GetOpaque(argv[1], rectClass.class_id);
1100 	if (!rc) return JS_EXCEPTION;
1101 
1102 	szName = JS_ToCString(c, argv[0]);
1103 	e = gf_node_get_field_by_name(n, (char *) szName, &info);
1104 	JS_FreeCString(c, szName);
1105 	if (e != GF_OK) return JS_EXCEPTION;
1106 
1107 	if (info.fieldType==SVG_ViewBox_datatype) {
1108 		SVG_ViewBox *v = (SVG_ViewBox *)info.far_ptr;
1109 		v->x = FLT2FIX(rc->x);
1110 		v->y = FLT2FIX(rc->y);
1111 		v->width = FLT2FIX(rc->w);
1112 		v->height = FLT2FIX(rc->h);
1113 		dom_node_changed(n, GF_FALSE, NULL);
1114 		return JS_TRUE;
1115 	}
1116 	return JS_FALSE;
1117 }
1118 
svg_udom_set_path_trait(JSContext * c,JSValueConst obj,int argc,JSValueConst * argv)1119 static JSValue svg_udom_set_path_trait(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1120 {
1121 	pathCI *path;
1122 	GF_FieldInfo info;
1123 	const char *szName;
1124 	GF_Err e;
1125 	GF_Node *n = dom_get_element(c, obj);
1126 	if (!n) return JS_EXCEPTION;
1127 
1128 	if (argc!=2) return JS_EXCEPTION;
1129 	if (!JS_IsString(argv[0])) return JS_EXCEPTION;
1130 	if (JS_IsNull(argv[1]) || !JS_IsObject(argv[1])) return JS_EXCEPTION;
1131 	path = JS_GetOpaque( argv[1], pathClass.class_id);
1132 	if (!path) return JS_EXCEPTION;
1133 
1134 	szName = JS_ToCString(c, argv[0]);
1135 	e = gf_node_get_field_by_name(n, (char *) szName, &info);
1136 	JS_FreeCString(c, szName);
1137 	if (e != GF_OK) return JS_EXCEPTION;
1138 
1139 	if (info.fieldType==SVG_PathData_datatype) {
1140 #if USE_GF_PATH
1141 #else
1142 		u32 i;
1143 		u32 nb_pts;
1144 		SVG_PathData *d = (SVG_PathData *)info.far_ptr;
1145 		while (gf_list_count(d->commands)) {
1146 			u8 *t = gf_list_get(d->commands, 0);
1147 			gf_list_rem(d->commands, 0);
1148 			gf_free(t);
1149 		}
1150 		while (gf_list_count(d->points)) {
1151 			SVG_Point *t = gf_list_get(d->points, 0);
1152 			gf_list_rem(d->points, 0);
1153 			gf_free(t);
1154 		}
1155 		nb_pts = 0;
1156 		for (i=0; i<path->nb_coms; i++) {
1157 			u8 *t = gf_malloc(sizeof(u8));
1158 			*t = path->tags[i];
1159 			gf_list_add(d->commands, t);
1160 			switch (*t) {
1161 			case 0:
1162 			case 1:
1163 				nb_pts++;
1164 				break;
1165 			case 2:
1166 				nb_pts+=3;
1167 				break;
1168 			case 4:
1169 				nb_pts+=2;
1170 				break;
1171 			}
1172 		}
1173 		for (i=0; i<nb_pts; i++) {
1174 			SVG_Point *t = gf_malloc(sizeof(SVG_Point));
1175 			t->x = FLT2FIX(path->pts[i].x);
1176 			t->y = FLT2FIX(path->pts[i].y);
1177 			gf_list_add(d->points, t);
1178 		}
1179 		dom_node_changed(n, 0, NULL);
1180 		return JS_TRUE;
1181 #endif
1182 	}
1183 	return JS_FALSE;
1184 }
1185 
svg_udom_set_rgb_color_trait(JSContext * c,JSValueConst obj,int argc,JSValueConst * argv)1186 static JSValue svg_udom_set_rgb_color_trait(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1187 {
1188 	GF_FieldInfo info;
1189 	rgbCI *rgb;
1190 	GF_Err e;
1191 	const char *szName;
1192 	GF_Node *n = dom_get_element(c, obj);
1193 	if (!n) return JS_EXCEPTION;
1194 
1195 	if (argc!=2) return JS_EXCEPTION;
1196 	if (!JS_IsString(argv[0])) return JS_EXCEPTION;
1197 	if (!JS_IsObject(argv[1])) return JS_EXCEPTION;
1198 	rgb = JS_GetOpaque(argv[1], rgbClass.class_id);
1199 	if (!rgb) return JS_EXCEPTION;
1200 
1201 	szName = JS_ToCString(c, argv[0]);
1202 	e = gf_node_get_field_by_name(n, (char *) szName, &info);
1203 	JS_FreeCString(c, szName);
1204 	if (e != GF_OK) return JS_EXCEPTION;
1205 
1206 	switch (info.fieldType) {
1207 	case SVG_Color_datatype:
1208 	{
1209 		SVG_Color *col = (SVG_Color *)info.far_ptr;
1210 		col->type = SVG_COLOR_RGBCOLOR;
1211 		col->red = FLT2FIX(rgb->r / 255.0f);
1212 		col->green = FLT2FIX(rgb->g / 255.0f);
1213 		col->blue = FLT2FIX(rgb->b / 255.0f);
1214 		dom_node_changed(n, GF_FALSE, &info);
1215 		return JS_TRUE;
1216 	}
1217 	case SVG_Paint_datatype:
1218 	{
1219 		SVG_Paint *paint = (SVG_Paint *)info.far_ptr;
1220 		paint->type = SVG_PAINT_COLOR;
1221 		paint->color.type = SVG_COLOR_RGBCOLOR;
1222 		paint->color.red = FLT2FIX(rgb->r / 255.0f);
1223 		paint->color.green = FLT2FIX(rgb->g / 255.0f);
1224 		paint->color.blue = FLT2FIX(rgb->b / 255.0f);
1225 		dom_node_changed(n, GF_FALSE, &info);
1226 		return JS_TRUE;
1227 	}
1228 	}
1229 	return JS_FALSE;
1230 }
1231 
svg_get_bbox(JSContext * c,JSValueConst obj,int argc,JSValueConst * argv,Bool get_screen)1232 static JSValue svg_get_bbox(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv, Bool get_screen)
1233 {
1234 	GF_JSAPIParam par;
1235 	GF_Node *n = dom_get_element(c, obj);
1236 	if (!n || argc) return JS_EXCEPTION;
1237 
1238 	par.bbox.is_set = GF_FALSE;
1239 	if (ScriptAction(n->sgprivate->scenegraph, get_screen ? GF_JSAPI_OP_GET_SCREEN_BBOX : GF_JSAPI_OP_GET_LOCAL_BBOX, (GF_Node *)n, &par) ) {
1240 		if (par.bbox.is_set) {
1241 			rectCI *rc = (rectCI *)gf_malloc(sizeof(rectCI));
1242 			if (!rc) return JS_EXCEPTION;
1243 			JSValue rO = JS_NewObjectClass(c, rectClass.class_id);
1244 			rc->sg = NULL;
1245 			rc->x = FIX2FLT(par.bbox.min_edge.x);
1246 			/*BBox is in 3D coord system style*/
1247 			rc->y = FIX2FLT(par.bbox.min_edge.y);
1248 			rc->w = FIX2FLT(par.bbox.max_edge.x - par.bbox.min_edge.x);
1249 			rc->h = FIX2FLT(par.bbox.max_edge.y - par.bbox.min_edge.y);
1250 			JS_SetOpaque(rO, rc);
1251 			return rO;
1252 		} else {
1253 			return JS_NULL;
1254 		}
1255 		return JS_TRUE;
1256 	}
1257 	return JS_FALSE;
1258 }
1259 
svg_udom_get_local_bbox(JSContext * c,JSValueConst obj,int argc,JSValueConst * argv)1260 static JSValue svg_udom_get_local_bbox(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1261 {
1262 	return svg_get_bbox(c, obj, argc, argv, GF_FALSE);
1263 }
svg_udom_get_screen_bbox(JSContext * c,JSValueConst obj,int argc,JSValueConst * argv)1264 static JSValue svg_udom_get_screen_bbox(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1265 {
1266 	return svg_get_bbox(c, obj, argc, argv, GF_TRUE);
1267 }
1268 
svg_udom_get_screen_ctm(JSContext * c,JSValueConst obj,int argc,JSValueConst * argv)1269 static JSValue svg_udom_get_screen_ctm(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1270 {
1271 	GF_JSAPIParam par;
1272 	GF_Node *n = dom_get_element(c, obj);
1273 	if (!n || argc) return JS_EXCEPTION;
1274 
1275 	if (ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_GET_TRANSFORM, (GF_Node *)n, &par)) {
1276 		GF_Matrix2D *mx = (GF_Matrix2D *)gf_malloc(sizeof(GF_Matrix2D));
1277 		if (!mx) return JS_EXCEPTION;
1278 		JSValue mO = JS_NewObjectClass(c, matrixClass.class_id);
1279 		gf_mx2d_from_mx(mx, &par.mx);
1280 		JS_SetOpaque(mO, mx);
1281 		return mO;
1282 	}
1283 	return JS_EXCEPTION;
1284 }
1285 
svg_udom_create_matrix_components(JSContext * c,JSValueConst obj,int argc,JSValueConst * argv)1286 static JSValue svg_udom_create_matrix_components(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1287 {
1288 	GF_Matrix2D *mx;
1289 	JSValue mat;
1290 	Double v;
1291 	GF_Node *n = dom_get_element(c, obj);
1292 	if (!n) return JS_EXCEPTION;
1293 	if (argc!=6) return JS_EXCEPTION;
1294 
1295 	GF_SAFEALLOC(mx, GF_Matrix2D)
1296 	if (!mx) return js_throw_err(c, GF_DOM_EXC_DATA_CLONE_ERR);
1297 
1298 	JS_ToFloat64(c, &v, argv[0]);
1299 	mx->m[0] = FLT2FIX(v);
1300 	JS_ToFloat64(c, &v, argv[1]);
1301 	mx->m[3] = FLT2FIX(v);
1302 	JS_ToFloat64(c, &v, argv[2]);
1303 	mx->m[1] = FLT2FIX(v);
1304 	JS_ToFloat64(c, &v, argv[3]);
1305 	mx->m[4] = FLT2FIX(v);
1306 	JS_ToFloat64(c, &v, argv[4]);
1307 	mx->m[2] = FLT2FIX(v);
1308 	JS_ToFloat64(c, &v, argv[5]);
1309 	mx->m[5] = FLT2FIX(v);
1310 	mat = JS_NewObjectClass(c, matrixClass.class_id);
1311 	JS_SetOpaque(mat, mx);
1312 	return mat;
1313 }
1314 
svg_udom_create_rect(JSContext * c,JSValueConst obj,int argc,JSValueConst * argv)1315 static JSValue svg_udom_create_rect(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1316 {
1317 	rectCI *rc;
1318 	JSValue r;
1319 	GF_Node *n = dom_get_element(c, obj);
1320 	if (!n || argc) return JS_EXCEPTION;
1321 
1322 	GF_SAFEALLOC(rc, rectCI);
1323 	if (!rc) return js_throw_err(c, GF_DOM_EXC_DATA_CLONE_ERR);
1324 	r = JS_NewObjectClass(c, rectClass.class_id);
1325 	JS_SetOpaque(r, rc);
1326 	return r;
1327 }
1328 
svg_udom_create_point(JSContext * c,JSValueConst obj,int argc,JSValueConst * argv)1329 static JSValue svg_udom_create_point(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1330 {
1331 	pointCI *pt;
1332 	JSValue r;
1333 	GF_Node *n = dom_get_element(c, obj);
1334 	if (!n || argc) return JS_EXCEPTION;
1335 
1336 	GF_SAFEALLOC(pt, pointCI);
1337 	if (!pt) return js_throw_err(c, GF_DOM_EXC_DATA_CLONE_ERR);
1338 	r = JS_NewObjectClass(c, pointClass.class_id);
1339 	JS_SetOpaque(r, pt);
1340 	return r;
1341 }
1342 
svg_udom_create_path(JSContext * c,JSValueConst obj,int argc,JSValueConst * argv)1343 static JSValue svg_udom_create_path(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1344 {
1345 	pathCI *path;
1346 	JSValue p;
1347 	GF_Node *n = dom_get_element(c, obj);
1348 	if (!n || argc) return JS_EXCEPTION;
1349 
1350 	GF_SAFEALLOC(path, pathCI);
1351 	if (!path) return js_throw_err(c, GF_DOM_EXC_DATA_CLONE_ERR);
1352 	p = JS_NewObjectClass(c, pathClass.class_id);
1353 	JS_SetOpaque(p, path);
1354 	return p;
1355 }
1356 
svg_udom_create_color(JSContext * c,JSValueConst obj,int argc,JSValueConst * argv)1357 static JSValue svg_udom_create_color(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1358 {
1359 	rgbCI *col;
1360 	JSValue p;
1361 	GF_Node *n = dom_get_element(c, obj);
1362 	if (!n|| (argc!=3)) return JS_EXCEPTION;
1363 
1364 	GF_SAFEALLOC(col, rgbCI);
1365 	if (!col) return js_throw_err(c, GF_DOM_EXC_DATA_CLONE_ERR);
1366 
1367 	JS_ToInt32(c, &col->r, argv[0]);
1368 	JS_ToInt32(c, &col->g, argv[1]);
1369 	JS_ToInt32(c, &col->b, argv[2]);
1370 	p = JS_NewObjectClass(c, rgbClass.class_id);
1371 	JS_SetOpaque(p, col);
1372 	return p;
1373 }
1374 
svg_path_get_total_length(JSContext * c,JSValueConst obj,int argc,JSValueConst * argv)1375 static JSValue svg_path_get_total_length(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1376 {
1377 	Double length = 0;
1378 	GF_FieldInfo info;
1379 
1380 	GF_Node *n = (GF_Node *)dom_get_element(c, obj);
1381 	if (!n) return JS_EXCEPTION;
1382 	if (n->sgprivate->tag != TAG_SVG_path) return JS_EXCEPTION;
1383 
1384 	gf_node_get_field_by_name(n, "d", &info);
1385 	if (info.fieldType == SVG_PathData_datatype) {
1386 #if USE_GF_PATH
1387 		GF_Path *p = (GF_Path *)info.far_ptr;
1388 		GF_PathIterator *path_it = gf_path_iterator_new(p);
1389 		if (path_it) {
1390 			Fixed len = gf_path_iterator_get_length(path_it);
1391 			length = FIX2FLT(len);
1392 			gf_path_iterator_del(path_it);
1393 		}
1394 #endif
1395 	}
1396 	return JS_NewFloat64(c, length);
1397 }
1398 
svg_udom_move_focus(JSContext * c,JSValueConst obj,int argc,JSValueConst * argv)1399 static JSValue svg_udom_move_focus(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1400 {
1401 	GF_JSAPIParam par;
1402 	GF_Node *n = dom_get_element(c, obj);
1403 	if (!n) return JS_EXCEPTION;
1404 	if ((argc!=1) || !JS_IsObject(argv[0])) return JS_EXCEPTION;
1405 
1406 	JS_ToInt32(c, &par.opt, argv[1]);
1407 	if (ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_SET_FOCUS, (GF_Node *)n, &par))
1408 		return JS_TRUE;
1409 	return JS_FALSE;
1410 }
1411 
svg_udom_set_focus(JSContext * c,JSValueConst obj,int argc,JSValueConst * argv)1412 static JSValue svg_udom_set_focus(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1413 {
1414 	GF_JSAPIParam par;
1415 	GF_Node *n = dom_get_element(c, obj);
1416 	if (!n) return JS_EXCEPTION;
1417 	if ((argc!=1) || !JS_IsObject(argv[0])) return JS_EXCEPTION;
1418 
1419 	par.node = dom_get_element(c, argv[0]);
1420 	/*NOT IN THE GRAPH*/
1421 	if (!par.node || !par.node->sgprivate->num_instances) return JS_EXCEPTION;
1422 	if (ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_SET_FOCUS, (GF_Node *)n, &par))
1423 		return JS_TRUE;
1424 	return JS_TRUE;
1425 }
1426 
svg_udom_get_focus(JSContext * c,JSValueConst obj,int argc,JSValueConst * argv)1427 static JSValue svg_udom_get_focus(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1428 {
1429 	GF_JSAPIParam par;
1430 	GF_Node *n = dom_get_element(c, obj);
1431 	if (!n || argc) return JS_EXCEPTION;
1432 
1433 	if (!ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_GET_FOCUS, (GF_Node *)n, &par))
1434 		return JS_EXCEPTION;
1435 
1436 	if (par.node) {
1437 		return dom_element_construct(c, par.node);
1438 	}
1439 	return JS_NULL;
1440 }
1441 
svg_udom_get_time(JSContext * c,JSValueConst obj,int argc,JSValueConst * argv)1442 static JSValue svg_udom_get_time(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1443 {
1444 	GF_Node *n = dom_get_element(c, obj);
1445 	if (!n) return JS_EXCEPTION;
1446 
1447 	return JS_NewFloat64(c, gf_node_get_scene_time(n) );
1448 }
1449 
1450 
svg_connection_create(JSContext * c,JSValueConst obj,int argc,JSValueConst * argv)1451 static JSValue svg_connection_create(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1452 {
1453 	return js_throw_err(c, GF_DOM_EXC_NOT_SUPPORTED_ERR);
1454 }
1455 
baseCI_finalize(JSRuntime * rt,JSValue obj)1456 static void baseCI_finalize(JSRuntime *rt, JSValue obj)
1457 {
1458 	/*avoids GCC warning*/
1459 	void *data = JS_GetOpaque_Nocheck(obj);
1460 	if (data) gf_free(data);
1461 }
1462 
rgb_getProperty(JSContext * c,JSValueConst obj,int magic)1463 static JSValue rgb_getProperty(JSContext *c, JSValueConst obj, int magic)
1464 {
1465 	rgbCI *col = (rgbCI *) JS_GetOpaque(obj, rgbClass.class_id);
1466 	if (!col) return JS_EXCEPTION;
1467 	switch (magic) {
1468 	case 0: return JS_NewInt32(c, col->r);
1469 	case 1: return JS_NewInt32(c, col->g);
1470 	case 2: return JS_NewInt32(c, col->b);
1471 	default:
1472 		return JS_EXCEPTION;
1473 	}
1474 }
rgb_setProperty(JSContext * c,JSValueConst obj,JSValueConst value,int magic)1475 static JSValue rgb_setProperty(JSContext *c, JSValueConst obj, JSValueConst value, int magic)
1476 {
1477 	rgbCI *col = (rgbCI *) JS_GetOpaque(obj, rgbClass.class_id);
1478 	if (!col) return JS_EXCEPTION;
1479 
1480 	switch (magic) {
1481 	case 0: return JS_ToInt32(c, &col->r, value) ? JS_EXCEPTION : JS_TRUE;
1482 	case 1: return JS_ToInt32(c, &col->g, value) ? JS_EXCEPTION : JS_TRUE;
1483 	case 2: return JS_ToInt32(c, &col->b, value) ? JS_EXCEPTION : JS_TRUE;
1484 	default:
1485 		return JS_EXCEPTION;
1486 	}
1487 }
1488 
rect_getProperty(JSContext * c,JSValueConst obj,int magic)1489 static JSValue rect_getProperty(JSContext *c, JSValueConst obj, int magic)
1490 {
1491 	rectCI *rc = (rectCI *) JS_GetOpaque(obj, rectClass.class_id);
1492 	if (!rc) return JS_EXCEPTION;
1493 	if (rc->sg) {
1494 		GF_JSAPIParam par;
1495 		if (!ScriptAction(rc->sg, GF_JSAPI_OP_GET_VIEWPORT, rc->sg->RootNode, &par)) {
1496 			return JS_EXCEPTION;
1497 		}
1498 		rc->x = FIX2FLT(par.rc.x);
1499 		rc->y = FIX2FLT(par.rc.y);
1500 		rc->w = FIX2FLT(par.rc.width);
1501 		rc->h = FIX2FLT(par.rc.height);
1502 	}
1503 	switch (magic) {
1504 	case 0: return JS_NewFloat64(c, rc->x);
1505 	case 1: return JS_NewFloat64(c, rc->y);
1506 	case 2: return JS_NewFloat64(c, rc->w);
1507 	case 3: return JS_NewFloat64(c, rc->h);
1508 	default:
1509 		return JS_EXCEPTION;
1510 	}
1511 }
1512 
rect_setProperty(JSContext * c,JSValueConst obj,JSValueConst value,int magic)1513 static JSValue rect_setProperty(JSContext *c, JSValueConst obj, JSValueConst value, int magic)
1514 {
1515 	Double d;
1516 	rectCI *rc = (rectCI *) JS_GetOpaque(obj, rectClass.class_id);
1517 	if (!rc) return JS_EXCEPTION;
1518 	if (JS_ToFloat64(c, &d, value)) return JS_EXCEPTION;
1519 	switch (magic) {
1520 	case 0:
1521 		rc->x = (Float) d;
1522 		return JS_TRUE;
1523 	case 1:
1524 		rc->y = (Float) d;
1525 		return JS_TRUE;
1526 	case 2:
1527 		rc->w = (Float) d;
1528 		return JS_TRUE;
1529 	case 3:
1530 		rc->h = (Float) d;
1531 		return JS_TRUE;
1532 	default:
1533 		return JS_EXCEPTION;
1534 	}
1535 }
1536 
point_getProperty(JSContext * c,JSValueConst obj,int magic)1537 static JSValue point_getProperty(JSContext *c, JSValueConst obj, int magic)
1538 {
1539 	pointCI *pt = (pointCI *) JS_GetOpaque(obj, pointClass.class_id);
1540 	if (!pt) return JS_EXCEPTION;
1541 
1542 	if (pt->sg) {
1543 		GF_JSAPIParam par;
1544 		if (!ScriptAction(pt->sg, GF_JSAPI_OP_GET_TRANSLATE, pt->sg->RootNode, &par)) {
1545 			return JS_EXCEPTION;
1546 		}
1547 		pt->x = FIX2FLT(par.pt.x);
1548 		pt->y = FIX2FLT(par.pt.y);
1549 	}
1550 	switch (magic) {
1551 	case 0: return JS_NewFloat64(c, pt->x);
1552 	case 1: return JS_NewFloat64(c, pt->y);
1553 	default: return JS_EXCEPTION;
1554 	}
1555 }
1556 
point_setProperty(JSContext * c,JSValueConst obj,JSValueConst value,int magic)1557 static JSValue point_setProperty(JSContext *c, JSValueConst obj, JSValueConst value, int magic)
1558 {
1559 	pointCI *pt = (pointCI *) JS_GetOpaque(obj, pointClass.class_id);
1560 	if (!pt) return JS_EXCEPTION;
1561 
1562 	Double d;
1563 	if (JS_ToFloat64(c, &d, value)) return JS_EXCEPTION;
1564 	switch (magic) {
1565 	case 0:
1566 		pt->x = (Float) d;
1567 		break;
1568 	case 1:
1569 		pt->y = (Float) d;
1570 		break;
1571 	default:
1572 		return JS_EXCEPTION;
1573 	}
1574 	if (pt->sg) {
1575 		GF_JSAPIParam par;
1576 		par.pt.x = FLT2FIX(pt->x);
1577 		par.pt.y = FLT2FIX(pt->y);
1578 		ScriptAction(pt->sg, GF_JSAPI_OP_SET_TRANSLATE, pt->sg->RootNode, &par);
1579 	}
1580 	return JS_UNDEFINED;
1581 }
1582 
svg_new_path_object(JSContext * c,SVG_PathData * d)1583 static JSValue svg_new_path_object(JSContext *c, SVG_PathData *d)
1584 {
1585 #if USE_GF_PATH
1586 	return JS_NULL;
1587 #else
1588 	JSValue obj;
1589 	pathCI *p;
1590 	GF_SAFEALLOC(p, pathCI);
1591 	if (!p) return JS_EXCEPTION;
1592 	if (d) {
1593 		u32 i, count;
1594 		p->nb_coms = gf_list_count(d->commands);
1595 		p->tags = gf_malloc(sizeof(u8) * p->nb_coms);
1596 		for (i=0; i<p->nb_coms; i++) p->tags[i] = * (u8 *) gf_list_get(d->commands, i);
1597 		count = gf_list_count(d->points);
1598 		p->pts = gf_malloc(sizeof(pointCI) * count);
1599 		for (i=0; i<count; i++) {
1600 			GF_Point2D *pt = gf_list_get(d->commands, i);
1601 			p->pts[i].x = FIX2FLT(pt->x);
1602 			p->pts[i].y = FIX2FLT(pt->y);
1603 		}
1604 	}
1605 	obj = JS_NewObjectClass(c, pathClass.class_id);
1606 	JS_SetOpaque(obj, p);
1607 	return obj;
1608 #endif
1609 }
1610 
pathCI_finalize(JSRuntime * rt,JSValue obj)1611 static void pathCI_finalize(JSRuntime *rt, JSValue obj)
1612 {
1613 	pathCI *p = (pathCI *) JS_GetOpaque(obj, pathClass.class_id);
1614 	if (p) {
1615 		if (p->pts) gf_free(p->pts);
1616 		if (p->tags) gf_free(p->tags);
1617 		gf_free(p);
1618 	}
1619 }
1620 
path_getProperty(JSContext * c,JSValueConst obj,int magic)1621 static JSValue path_getProperty(JSContext *c, JSValueConst obj, int magic)
1622 {
1623 	pathCI *p = (pathCI *) JS_GetOpaque(obj, pathClass.class_id);
1624 	if (!p) return JS_EXCEPTION;
1625 	switch (magic) {
1626 	case 0: return JS_NewInt32(c, p->nb_coms);
1627 	default:
1628 		return JS_EXCEPTION;
1629 	}
1630 }
1631 
svg_path_get_segment(JSContext * c,JSValueConst obj,int argc,JSValueConst * argv)1632 static JSValue svg_path_get_segment(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1633 {
1634 	u32 idx;
1635 	pathCI *p = (pathCI *) JS_GetOpaque(obj, pathClass.class_id);
1636 	if (!p) return JS_EXCEPTION;
1637 	if ((argc!=1) || JS_ToInt32(c, &idx, argv[0])) return JS_EXCEPTION;
1638 	if (idx>=p->nb_coms) return JS_TRUE;
1639 	switch (p->tags[idx]) {
1640 	case 0: return JS_NewInt32(c, 77);/* Move To */
1641 	case 1: return JS_NewInt32(c, 76);/* Line To */
1642 	case 2:/* Curve To */
1643 	case 3:/* next Curve To */
1644 		return JS_NewInt32(c, 67);
1645 	case 4:/* Quad To */
1646 	case 5:/* next Quad To */
1647 		return JS_NewInt32(c, 81);
1648 	case 6:
1649 		return JS_NewInt32(c, 90);/* Close */
1650 	}
1651 	return JS_EXCEPTION;
1652 }
1653 
svg_path_get_segment_param(JSContext * c,JSValueConst obj,int argc,JSValueConst * argv)1654 static JSValue svg_path_get_segment_param(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1655 {
1656 	u32 i, idx, param_idx, pt_idx;
1657 	ptCI *pt;
1658 	pathCI *p = (pathCI *) JS_GetOpaque(obj, pathClass.class_id);
1659 	if (!p) return JS_EXCEPTION;
1660 
1661 	if ((argc!=2) || !JS_IsInteger(argv[0]) || !JS_IsInteger(argv[1])) return JS_EXCEPTION;
1662 	if (JS_ToInt32(c, &idx, argv[0])) return JS_EXCEPTION;
1663 	if (JS_ToInt32(c, &param_idx, argv[1])) return JS_EXCEPTION;
1664 	if (idx>=p->nb_coms) return JS_TRUE;
1665 	pt_idx = 0;
1666 	for (i=0; i<idx; i++) {
1667 		switch (p->tags[i]) {
1668 		case 0:
1669 			pt_idx++;
1670 			break;
1671 		case 1:
1672 			pt_idx++;
1673 			break;
1674 		case 2:
1675 			pt_idx+=3;
1676 			break;
1677 		case 3:
1678 			pt_idx+=2;
1679 			break;
1680 		case 4:
1681 			pt_idx+=2;
1682 			break;
1683 		case 5:
1684 			pt_idx+=1;
1685 			break;
1686 		}
1687 	}
1688 	switch (p->tags[idx]) {
1689 	case 0:
1690 	case 1:
1691 		if (param_idx>1) return JS_TRUE;
1692 		pt = &p->pts[pt_idx];
1693 		return JS_NewFloat64(c, param_idx ? pt->y : pt->x);
1694 	case 2:/* Curve To */
1695 		if (param_idx>5) return JS_TRUE;
1696 		pt = &p->pts[pt_idx + (param_idx/2) ];
1697 		return JS_NewFloat64(c, (param_idx%2) ? pt->y : pt->x);
1698 	case 3:/* Next Curve To */
1699 		if (param_idx>5) return JS_TRUE;
1700 		if (param_idx<2) {
1701 			pt = &p->pts[pt_idx - 1];
1702 			return JS_NewFloat64(c, param_idx ? pt->y : pt->x);
1703 		}
1704 		param_idx-=2;
1705 		pt = &p->pts[pt_idx + (param_idx/2)];
1706 		return JS_NewFloat64(c, (param_idx%2) ? pt->y : pt->x);
1707 
1708 	case 4:/* Quad To */
1709 		if (param_idx>3) return JS_TRUE;
1710 		pt = &p->pts[pt_idx + (param_idx/2) ];
1711 		return JS_NewFloat64(c, (param_idx%2) ? pt->y : pt->x);
1712 
1713 	case 5:/* Next Quad To */
1714 		if (param_idx>3) return JS_TRUE;
1715 		if (param_idx<2) {
1716 			pt = &p->pts[pt_idx - 1];
1717 			return JS_NewFloat64(c, param_idx ? pt->y : pt->x);
1718 		} else {
1719 			param_idx-=2;
1720 			pt = &p->pts[pt_idx];
1721 			return JS_NewFloat64(c, param_idx ? pt->y : pt->x);
1722 		}
1723 		return JS_TRUE;
1724 	/*spec is quite obscur here*/
1725 	case 6:
1726 		return JS_NewFloat64(c, 0);
1727 	}
1728 	return JS_EXCEPTION;
1729 }
1730 
svg_path_realloc_pts(pathCI * p,u32 nb_pts)1731 static u32 svg_path_realloc_pts(pathCI *p, u32 nb_pts)
1732 {
1733 	u32 i, orig_pts;
1734 	orig_pts = 0;
1735 	for (i=0; i<p->nb_coms; i++) {
1736 		switch (p->tags[i]) {
1737 		case 0:
1738 			orig_pts++;
1739 			break;
1740 		case 1:
1741 			orig_pts++;
1742 			break;
1743 		case 2:
1744 			orig_pts+=3;
1745 			break;
1746 		case 3:
1747 			orig_pts+=2;
1748 			break;
1749 		case 4:
1750 			orig_pts+=2;
1751 			break;
1752 		case 5:
1753 			orig_pts+=1;
1754 			break;
1755 		}
1756 	}
1757 	p->pts = (ptCI *)gf_realloc(p->pts, sizeof(ptCI)*(nb_pts+orig_pts));
1758 	return orig_pts;
1759 }
1760 
svg_path_move_to(JSContext * c,JSValueConst obj,int argc,JSValueConst * argv)1761 static JSValue svg_path_move_to(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1762 {
1763 	pathCI *p;
1764 	Double x, y;
1765 	u32 nb_pts;
1766 
1767 	p = (pathCI *) JS_GetOpaque(obj, pathClass.class_id);
1768 	if (!p || (argc!=2)) return JS_EXCEPTION;
1769 
1770 	if (JS_ToFloat64(c, &x, argv[0])) return JS_EXCEPTION;
1771 	if (JS_ToFloat64(c, &y, argv[1])) return JS_EXCEPTION;
1772 	nb_pts = svg_path_realloc_pts(p, 1);
1773 	p->pts[nb_pts].x = (Float) x;
1774 	p->pts[nb_pts].y = (Float) y;
1775 	p->tags = (u8 *)gf_realloc(p->tags, sizeof(u8)*(p->nb_coms+1) );
1776 	p->tags[p->nb_coms] = 0;
1777 	p->nb_coms++;
1778 	return JS_TRUE;
1779 }
1780 
svg_path_line_to(JSContext * c,JSValueConst obj,int argc,JSValueConst * argv)1781 static JSValue svg_path_line_to(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1782 {
1783 	pathCI *p;
1784 	Double x, y;
1785 	u32 nb_pts;
1786 	p = (pathCI *) JS_GetOpaque(obj, pathClass.class_id);
1787 	if (!p || (argc!=2)) return JS_EXCEPTION;
1788 
1789 	if (JS_ToFloat64(c, &x, argv[0])) return JS_EXCEPTION;
1790 	if (JS_ToFloat64(c, &y, argv[1])) return JS_EXCEPTION;
1791 
1792 	nb_pts = svg_path_realloc_pts(p, 1);
1793 	p->pts[nb_pts].x = (Float) x;
1794 	p->pts[nb_pts].y = (Float) y;
1795 	p->tags = (u8 *)gf_realloc(p->tags, sizeof(u8)*(p->nb_coms+1) );
1796 	p->tags[p->nb_coms] = 1;
1797 	p->nb_coms++;
1798 	return JS_TRUE;
1799 }
1800 
svg_path_quad_to(JSContext * c,JSValueConst obj,int argc,JSValueConst * argv)1801 static JSValue svg_path_quad_to(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1802 {
1803 	pathCI *p;
1804 	Double x1, y1, x2, y2;
1805 	u32 nb_pts;
1806 
1807 	p = (pathCI *) JS_GetOpaque(obj, pathClass.class_id);
1808 	if (!p || (argc!=4)) return JS_EXCEPTION;
1809 
1810 	if (JS_ToFloat64(c, &x1, argv[0])) return JS_EXCEPTION;
1811 	if (JS_ToFloat64(c, &y1, argv[1])) return JS_EXCEPTION;
1812 	if (JS_ToFloat64(c, &x2, argv[2])) return JS_EXCEPTION;
1813 	if (JS_ToFloat64(c, &y2, argv[3])) return JS_EXCEPTION;
1814 	nb_pts = svg_path_realloc_pts(p, 2);
1815 	p->pts[nb_pts].x = (Float) x1;
1816 	p->pts[nb_pts].y = (Float) y1;
1817 	p->pts[nb_pts+1].x = (Float) x2;
1818 	p->pts[nb_pts+1].y = (Float) y2;
1819 	p->tags = (u8 *)gf_realloc(p->tags, sizeof(u8)*(p->nb_coms+1) );
1820 	p->tags[p->nb_coms] = 4;
1821 	p->nb_coms++;
1822 	return JS_TRUE;
1823 }
1824 
svg_path_curve_to(JSContext * c,JSValueConst obj,int argc,JSValueConst * argv)1825 static JSValue svg_path_curve_to(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1826 {
1827 	pathCI *p;
1828 	Double x1, y1, x2, y2, x, y;
1829 	u32 nb_pts;
1830 
1831 	p = (pathCI *) JS_GetOpaque(obj, pathClass.class_id);
1832 	if (!p || (argc!=6)) return JS_EXCEPTION;
1833 
1834 	if (JS_ToFloat64(c, &x1, argv[0])) return JS_EXCEPTION;
1835 	if (JS_ToFloat64(c, &y1, argv[1])) return JS_EXCEPTION;
1836 	if (JS_ToFloat64(c, &x2, argv[2])) return JS_EXCEPTION;
1837 	if (JS_ToFloat64(c, &y2, argv[3])) return JS_EXCEPTION;
1838 	if (JS_ToFloat64(c, &x, argv[4])) return JS_EXCEPTION;
1839 	if (JS_ToFloat64(c, &y, argv[5])) return JS_EXCEPTION;
1840 	nb_pts = svg_path_realloc_pts(p, 3);
1841 	p->pts[nb_pts].x = (Float) x1;
1842 	p->pts[nb_pts].y = (Float) y1;
1843 	p->pts[nb_pts+1].x = (Float) x2;
1844 	p->pts[nb_pts+1].y = (Float) y2;
1845 	p->pts[nb_pts+2].x = (Float) x;
1846 	p->pts[nb_pts+2].y = (Float) y;
1847 	p->tags = (u8 *)gf_realloc(p->tags, sizeof(u8)*(p->nb_coms+1) );
1848 	p->tags[p->nb_coms] = 2;
1849 	p->nb_coms++;
1850 	return JS_TRUE;
1851 }
1852 
svg_path_close(JSContext * c,JSValueConst obj,int argc,JSValueConst * argv)1853 static JSValue svg_path_close(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1854 {
1855 	pathCI *p = (pathCI *) JS_GetOpaque(obj, pathClass.class_id);
1856 	if (!p) return JS_EXCEPTION;
1857 
1858 	p->tags = (u8 *)gf_realloc(p->tags, sizeof(u8)*(p->nb_coms+1) );
1859 	p->tags[p->nb_coms] = 6;
1860 	p->nb_coms++;
1861 	return JS_TRUE;
1862 }
1863 
1864 
matrix_getProperty(JSContext * c,JSValueConst obj,int magic)1865 static JSValue matrix_getProperty(JSContext *c, JSValueConst obj, int magic)
1866 {
1867 	GF_Matrix2D *mx = (GF_Matrix2D *) JS_GetOpaque(obj, matrixClass.class_id);
1868 	if (!mx) return JS_EXCEPTION;
1869 
1870 	switch (magic) {
1871 	case 0: return JS_NewFloat64(c, FIX2FLT(mx->m[0]));
1872 	case 1: return JS_NewFloat64(c, FIX2FLT(mx->m[3]));
1873 	case 2: return JS_NewFloat64(c, FIX2FLT(mx->m[1]));
1874 	case 3: return JS_NewFloat64(c, FIX2FLT(mx->m[4]));
1875 	case 4: return JS_NewFloat64(c, FIX2FLT(mx->m[2]));
1876 	case 5: return JS_NewFloat64(c, FIX2FLT(mx->m[5]));
1877 	default:
1878 		return JS_EXCEPTION;
1879 	}
1880 }
1881 
matrix_setProperty(JSContext * c,JSValueConst obj,JSValueConst value,int magic)1882 static JSValue matrix_setProperty(JSContext *c, JSValueConst obj, JSValueConst value, int magic)
1883 {
1884 	Double d;
1885 	GF_Matrix2D *mx = (GF_Matrix2D *) JS_GetOpaque(obj, matrixClass.class_id);
1886 	if (!mx) return JS_EXCEPTION;
1887 	if (JS_ToFloat64(c, &d, value)) return JS_EXCEPTION;
1888 
1889 	switch (magic) {
1890 	case 0:
1891 		mx->m[0] = FLT2FIX(d);
1892 		break;
1893 	case 1:
1894 		mx->m[3] = FLT2FIX(d);
1895 		break;
1896 	case 2:
1897 		mx->m[1] = FLT2FIX(d);
1898 		break;
1899 	case 3:
1900 		mx->m[4] = FLT2FIX(d);
1901 		break;
1902 	case 4:
1903 		mx->m[2] = FLT2FIX(d);
1904 		break;
1905 	case 5:
1906 		mx->m[5] = FLT2FIX(d);
1907 		break;
1908 	default:
1909 		break;
1910 	}
1911 	return JS_EXCEPTION;
1912 }
1913 
svg_mx2d_get_component(JSContext * c,JSValueConst obj,int argc,JSValueConst * argv)1914 static JSValue svg_mx2d_get_component(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1915 {
1916 	u32 comp;
1917 	GF_Matrix2D *mx = (GF_Matrix2D *) JS_GetOpaque(obj, matrixClass.class_id);
1918 	if (!mx || (argc!=1)) return JS_EXCEPTION;
1919 	if (JS_ToInt32(c, &comp, argv[0])) return JS_EXCEPTION;
1920 
1921 	switch (comp) {
1922 	case 0: return JS_NewFloat64(c, FIX2FLT(mx->m[0]));
1923 	case 1: return JS_NewFloat64(c, FIX2FLT(mx->m[3]));
1924 	case 2: return JS_NewFloat64(c, FIX2FLT(mx->m[1]));
1925 	case 3: return JS_NewFloat64(c, FIX2FLT(mx->m[4]));
1926 	case 4: return JS_NewFloat64(c, FIX2FLT(mx->m[2]));
1927 	case 5: return JS_NewFloat64(c, FIX2FLT(mx->m[5]));
1928 	}
1929 	return JS_EXCEPTION;
1930 }
1931 
svg_mx2d_multiply(JSContext * c,JSValueConst obj,int argc,JSValueConst * argv)1932 static JSValue svg_mx2d_multiply(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1933 {
1934 	GF_Matrix2D *mx1, *mx2;
1935 	mx1 = (GF_Matrix2D *) JS_GetOpaque(obj, matrixClass.class_id);
1936 	if (!mx1 || (argc!=1)) return JS_EXCEPTION;
1937 	mx2 = (GF_Matrix2D *) JS_GetOpaque(argv[0], matrixClass.class_id);
1938 	if (!mx2) return JS_EXCEPTION;
1939 
1940 	gf_mx2d_add_matrix(mx1, mx2);
1941 	return JS_DupValue(c, obj);
1942 }
1943 
svg_mx2d_inverse(JSContext * c,JSValueConst obj,int argc,JSValueConst * argv)1944 static JSValue svg_mx2d_inverse(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1945 {
1946 	GF_Matrix2D *mx = (GF_Matrix2D *) JS_GetOpaque(obj, matrixClass.class_id);
1947 	if (!mx) return JS_EXCEPTION;
1948 	gf_mx2d_inverse(mx);
1949 	return JS_DupValue(c, obj);
1950 }
1951 
svg_mx2d_translate(JSContext * c,JSValueConst obj,int argc,JSValueConst * argv)1952 static JSValue svg_mx2d_translate(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1953 {
1954 	Double x, y;
1955 	GF_Matrix2D *mx1, mx2;
1956 	mx1 = (GF_Matrix2D *) JS_GetOpaque(obj, matrixClass.class_id);
1957 	if (!mx1 || (argc!=2)) return JS_EXCEPTION;
1958 
1959 	if (JS_ToFloat64(c, &x, argv[0]) || JS_ToFloat64(c, &y, argv[1])) return JS_EXCEPTION;
1960 
1961 	gf_mx2d_init(mx2);
1962 	mx2.m[2] = FLT2FIX(x);
1963 	mx2.m[5] = FLT2FIX(y);
1964 	gf_mx2d_pre_multiply(mx1, &mx2);
1965 	return JS_DupValue(c, obj);
1966 }
1967 
svg_mx2d_scale(JSContext * c,JSValueConst obj,int argc,JSValueConst * argv)1968 static JSValue svg_mx2d_scale(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1969 {
1970 	Double scale;
1971 	GF_Matrix2D *mx1, mx2;
1972 	mx1 = (GF_Matrix2D *) JS_GetOpaque(obj, matrixClass.class_id);
1973 	if (!mx1 || (argc!=2)) return JS_EXCEPTION;
1974 	if (!JS_ToFloat64(c, &scale, argv[0])) return JS_EXCEPTION;
1975 
1976 	gf_mx2d_init(mx2);
1977 	mx2.m[0] = mx2.m[4] = FLT2FIX(scale);
1978 	gf_mx2d_pre_multiply(mx1, &mx2);
1979 	return JS_DupValue(c, obj);
1980 }
1981 
svg_mx2d_rotate(JSContext * c,JSValueConst obj,int argc,JSValueConst * argv)1982 static JSValue svg_mx2d_rotate(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1983 {
1984 	Double angle;
1985 	GF_Matrix2D *mx1, mx2;
1986 
1987 	mx1 = (GF_Matrix2D *) JS_GetOpaque(obj, matrixClass.class_id);
1988 	if (!mx1 || (argc!=2)) return JS_EXCEPTION;
1989 	if (!JS_ToFloat64(c, &angle, argv[0])) return JS_EXCEPTION;
1990 
1991 	gf_mx2d_init(mx2);
1992 	gf_mx2d_add_rotation(&mx2, 0, 0, gf_mulfix(FLT2FIX(angle/180), GF_PI));
1993 	gf_mx2d_pre_multiply(mx1, &mx2);
1994 	return JS_DupValue(c, obj);
1995 }
1996 
svg_udom_new_rect(JSContext * c,Fixed x,Fixed y,Fixed width,Fixed height)1997 JSValue svg_udom_new_rect(JSContext *c, Fixed x, Fixed y, Fixed width, Fixed height)
1998 {
1999 	rectCI *rc = (rectCI *)gf_malloc(sizeof(rectCI));
2000 	if (!rc) return JS_EXCEPTION;
2001 	JSValue r = JS_NewObjectClass(c, rectClass.class_id);
2002 	rc->x = FIX2FLT(x);
2003 	rc->y = FIX2FLT(y);
2004 	rc->w = FIX2FLT(width);
2005 	rc->h = FIX2FLT(height);
2006 	rc->sg = NULL;
2007 	JS_SetOpaque(r, rc);
2008 	return r;
2009 }
2010 
svg_udom_new_point(JSContext * c,Fixed x,Fixed y)2011 JSValue svg_udom_new_point(JSContext *c, Fixed x, Fixed y)
2012 {
2013 	pointCI *pt = (pointCI *)gf_malloc(sizeof(pointCI));
2014 	if (!pt) return JS_EXCEPTION;
2015 	JSValue p = JS_NewObjectClass(c, pointClass.class_id);
2016 	pt->x = FIX2FLT(x);
2017 	pt->y = FIX2FLT(y);
2018 	pt->sg = NULL;
2019 	JS_SetOpaque(p, pt);
2020 	return p;
2021 }
2022 
2023 #ifdef GPAC_ENABLE_HTML5_MEDIA
2024 void *html_get_element_class(GF_Node *n);
2025 #endif
2026 
svg_get_element_class(GF_Node * n)2027 JSClassID svg_get_element_class(GF_Node *n)
2028 {
2029 	if (!n) return 0;
2030 	if ((n->sgprivate->tag>=GF_NODE_RANGE_FIRST_SVG) && (n->sgprivate->tag<=GF_NODE_RANGE_LAST_SVG)) {
2031 #ifdef GPAC_ENABLE_HTML5_MEDIA
2032 		if (n->sgprivate->tag == TAG_SVG_video || n->sgprivate->tag == TAG_SVG_audio) {
2033 			assert(0);
2034 			return html_get_element_class(n);
2035 		}
2036 #endif
2037 		return svgElement.class_id;
2038 	}
2039 	return 0;
2040 }
svg_get_document_class(GF_SceneGraph * sg)2041 JSClassID svg_get_document_class(GF_SceneGraph *sg)
2042 {
2043 	GF_Node *n = sg->RootNode;
2044 	if (!n) return 0;
2045 	if ((n->sgprivate->tag>=GF_NODE_RANGE_FIRST_SVG) && (n->sgprivate->tag<=GF_NODE_RANGE_LAST_SVG))
2046 		return svgDocument.class_id;
2047 	return 0;
2048 }
2049 
is_svg_document_class(JSContext * c,JSValue obj)2050 Bool is_svg_document_class(JSContext *c, JSValue obj)
2051 {
2052 	void *ptr = JS_GetOpaque(obj, svgDocument.class_id);
2053 	if (ptr) return GF_TRUE;
2054 	return GF_FALSE;
2055 }
2056 
is_svg_element_class(JSContext * c,JSValue obj)2057 Bool is_svg_element_class(JSContext *c, JSValue obj)
2058 {
2059 	void *ptr = JS_GetOpaque(obj, svgElement.class_id);
2060 	if (ptr) return GF_TRUE;
2061 	return GF_FALSE;
2062 }
2063 
2064 #define SETUP_JSCLASS(_class, _name, _proto_funcs, _construct, _finalize, _proto_class_id) \
2065 	if (!_class.class_id) {\
2066 		JS_NewClassID(&(_class.class_id)); \
2067 		_class.class.class_name = _name; \
2068 		_class.class.finalizer = _finalize;\
2069 		JS_NewClass(jsrt, _class.class_id, &(_class.class));\
2070 	}\
2071 	scene->svg_js->_class.proto = JS_NewObjectClass(c, _proto_class_id ? _proto_class_id : _class.class_id);\
2072     	JS_SetPropertyFunctionList(c, scene->svg_js->_class.proto, _proto_funcs, countof(_proto_funcs));\
2073     	JS_SetClassProto(c, _class.class_id, scene->svg_js->_class.proto);\
2074     	if (_construct) {\
2075 		scene->svg_js->_class.ctor = JS_NewCFunction2(c, _construct, _name, 1, JS_CFUNC_constructor, 0);\
2076     		JS_SetPropertyStr(c, global, _name, scene->svg_js->_class.ctor);\
2077 	}\
2078 
2079 
2080 static const JSCFunctionListEntry globalFuncs[] =
2081 {
2082     JS_CGETSET_MAGIC_DEF("connected", global_getProperty, NULL, 0),
2083     JS_CGETSET_MAGIC_DEF("parent", global_getProperty, NULL, 1),
2084 	JS_CFUNC_DEF("createConnection", 0, svg_connection_create),
2085 	JS_CFUNC_DEF("gotoLocation", 1, svg_nav_to_location),
2086 	JS_CFUNC_DEF("alert", 0, js_print),
2087 	JS_CFUNC_DEF("print", 0, js_print),
2088 	JS_CFUNC_DEF("hasFeature", 2, dom_imp_has_feature),
2089 	JS_CFUNC_DEF("parseXML", 0, svg_parse_xml),
2090 };
2091 
2092 static const JSCFunctionListEntry documentFuncs[] =
2093 {
2094 	JS_CGETSET_MAGIC_DEF("defaultView",	svg_doc_getProperty, NULL, 0),
2095 };
2096 
2097 static const JSCFunctionListEntry svg_elementFuncs[] =
2098 {
2099 	JS_CGETSET_MAGIC_DEF("id",	svg_element_getProperty, svg_element_setProperty, 0),
2100 	/*svgSVGElement interface*/
2101 	JS_CGETSET_MAGIC_DEF("currentScale",	svg_element_getProperty, svg_element_setProperty, 5),
2102 	JS_CGETSET_MAGIC_DEF("currentRotate",	svg_element_getProperty, svg_element_setProperty, 6),
2103 	JS_CGETSET_MAGIC_DEF("currentTranslate",	svg_element_getProperty, svg_element_setProperty, 7),
2104 	JS_CGETSET_MAGIC_DEF("viewport",	svg_element_getProperty, NULL, 8),
2105 	JS_CGETSET_MAGIC_DEF("currentTime",	svg_element_getProperty, svg_element_setProperty, 9),
2106 	/*timeControl interface*/
2107 	JS_CGETSET_MAGIC_DEF("isPaused",	svg_element_getProperty, NULL, 10),
2108 	/*old SVG1.1 stuff*/
2109 	JS_CGETSET_MAGIC_DEF("ownerSVGElement",	svg_element_getProperty, NULL, 11),
2110 	/*SVGElementInstance*/
2111 	JS_CGETSET_MAGIC_DEF("correspondingElement",	svg_element_getProperty, NULL, 12),
2112 	JS_CGETSET_MAGIC_DEF("correspondingUseElement",	svg_element_getProperty, NULL, 13),
2113 
2114 	/*trait access interface*/
2115 	JS_CFUNC_DEF("getTrait", 1, svg_udom_get_trait),
2116 	JS_CFUNC_DEF("getTraitNS", 2, svg_udom_get_trait),
2117 	JS_CFUNC_DEF("getFloatTrait", 1, svg_udom_get_float_trait),
2118 	JS_CFUNC_DEF("getMatrixTrait", 1, svg_udom_get_matrix_trait),
2119 	JS_CFUNC_DEF("getRectTrait", 1, svg_udom_get_rect_trait),
2120 	JS_CFUNC_DEF("getPathTrait", 1, svg_udom_get_path_trait),
2121 	JS_CFUNC_DEF("getRGBColorTrait", 1, svg_udom_get_rgb_color_trait),
2122 	/*FALLBACK TO BASE-VALUE FOR NOW - WILL NEED EITHER DOM TREE-CLONING OR A FAKE RENDER
2123 	PASS FOR EACH PRESENTATION VALUE ACCESS*/
2124 	JS_CFUNC_DEF("getPresentationTrait", 1, svg_udom_get_trait),
2125 	JS_CFUNC_DEF("getPresentationTraitNS", 2, svg_udom_get_trait),
2126 	JS_CFUNC_DEF("getFloatPresentationTrait", 1, svg_udom_get_float_trait),
2127 	JS_CFUNC_DEF("getMatrixPresentationTrait", 1, svg_udom_get_matrix_trait),
2128 	JS_CFUNC_DEF("getRectPresentationTrait", 1, svg_udom_get_rect_trait),
2129 	JS_CFUNC_DEF("getPathPresentationTrait", 1, svg_udom_get_path_trait),
2130 	JS_CFUNC_DEF("getRGBColorPresentationTrait", 1, svg_udom_get_rgb_color_trait),
2131 	JS_CFUNC_DEF("setTrait", 2, svg_udom_set_trait),
2132 	JS_CFUNC_DEF("setTraitNS", 3, svg_udom_set_trait),
2133 	JS_CFUNC_DEF("setFloatTrait", 2, svg_udom_set_float_trait),
2134 	JS_CFUNC_DEF("setMatrixTrait", 2, svg_udom_set_matrix_trait),
2135 	JS_CFUNC_DEF("setRectTrait", 2, svg_udom_set_rect_trait),
2136 	JS_CFUNC_DEF("setPathTrait", 2, svg_udom_set_path_trait),
2137 	JS_CFUNC_DEF("setRGBColorTrait", 2, svg_udom_set_rgb_color_trait),
2138 	/*locatable interface*/
2139 	JS_CFUNC_DEF("getBBox", 0, svg_udom_get_local_bbox),
2140 	JS_CFUNC_DEF("getScreenCTM", 0, svg_udom_get_screen_ctm),
2141 	JS_CFUNC_DEF("getScreenBBox", 0, svg_udom_get_screen_bbox),
2142 	/*svgSVGElement interface*/
2143 	JS_CFUNC_DEF("createSVGMatrixComponents", 0, svg_udom_create_matrix_components),
2144 	JS_CFUNC_DEF("createSVGRect", 0, svg_udom_create_rect),
2145 	JS_CFUNC_DEF("createSVGPath", 0, svg_udom_create_path),
2146 	JS_CFUNC_DEF("createSVGRGBColor", 0, svg_udom_create_color),
2147 	JS_CFUNC_DEF("createSVGPoint", 0, svg_udom_create_point),
2148 	JS_CFUNC_DEF("moveFocus", 0, svg_udom_move_focus),
2149 	JS_CFUNC_DEF("setFocus", 0, svg_udom_set_focus),
2150 	JS_CFUNC_DEF("getCurrentFocusedObject", 0, svg_udom_get_focus),
2151 	JS_CFUNC_DEF("getCurrentTime", 0, svg_udom_get_time),
2152 
2153 	/*timeControl interface*/
2154 	JS_CFUNC_DEF("beginElementAt", 1, svg_udom_smil_begin),
2155 	JS_CFUNC_DEF("beginElement", 0, svg_udom_smil_begin),
2156 	JS_CFUNC_DEF("endElementAt", 1, svg_udom_smil_end),
2157 	JS_CFUNC_DEF("endElement", 0, svg_udom_smil_end),
2158 	JS_CFUNC_DEF("pauseElement", 0, svg_udom_smil_pause),
2159 	JS_CFUNC_DEF("resumeElement", 0, svg_udom_smil_resume),
2160 	JS_CFUNC_DEF("restartElement", 0, svg_udom_smil_restart),
2161 	JS_CFUNC_DEF("setSpeed", 0, svg_udom_smil_set_speed),
2162 	JS_CFUNC_DEF("getTotalLength", 0, svg_path_get_total_length)
2163 };
2164 
2165 /*RGBColor class*/
2166 static const JSCFunctionListEntry rgb_Funcs[] =
2167 {
2168 	JS_CGETSET_MAGIC_DEF("red", rgb_getProperty, rgb_setProperty, 0),
2169 	JS_CGETSET_MAGIC_DEF("green", rgb_getProperty, rgb_setProperty, 1),
2170 	JS_CGETSET_MAGIC_DEF("blue", rgb_getProperty, rgb_setProperty, 2),
2171 };
2172 
2173 	/*SVGRect class*/
2174 static const JSCFunctionListEntry rect_Funcs[] =
2175 {
2176 	JS_CGETSET_MAGIC_DEF("x", rect_getProperty, rect_setProperty, 0),
2177 	JS_CGETSET_MAGIC_DEF("y", rect_getProperty, rect_setProperty, 1),
2178 	JS_CGETSET_MAGIC_DEF("width", rect_getProperty, rect_setProperty, 2),
2179 	JS_CGETSET_MAGIC_DEF("height", rect_getProperty, rect_setProperty, 3),
2180 };
2181 
2182 /*SVGPoint class*/
2183 static const JSCFunctionListEntry point_Funcs[] =
2184 {
2185 	JS_CGETSET_MAGIC_DEF("x", point_getProperty, point_setProperty, 0),
2186 	JS_CGETSET_MAGIC_DEF("y", point_getProperty, point_setProperty, 1)
2187 };
2188 
2189 /*SVGMatrix class*/
2190 static const JSCFunctionListEntry matrix_Funcs[] =
2191 {
2192 	JS_CGETSET_MAGIC_DEF("a", matrix_getProperty, matrix_setProperty, 0),
2193 	JS_CGETSET_MAGIC_DEF("b", matrix_getProperty, matrix_setProperty, 1),
2194 	JS_CGETSET_MAGIC_DEF("c", matrix_getProperty, matrix_setProperty, 2),
2195 	JS_CGETSET_MAGIC_DEF("d", matrix_getProperty, matrix_setProperty, 3),
2196 	JS_CGETSET_MAGIC_DEF("e", matrix_getProperty, matrix_setProperty, 4),
2197 	JS_CGETSET_MAGIC_DEF("f", matrix_getProperty, matrix_setProperty, 5),
2198 
2199 	JS_CFUNC_DEF("getComponent", 1, svg_mx2d_get_component),
2200 	JS_CFUNC_DEF("mMultiply", 1, svg_mx2d_multiply),
2201 	JS_CFUNC_DEF("inverse", 0, svg_mx2d_inverse),
2202 	JS_CFUNC_DEF("mTranslate", 2, svg_mx2d_translate),
2203 	JS_CFUNC_DEF("mScale", 1, svg_mx2d_scale),
2204 	JS_CFUNC_DEF("mRotate", 1, svg_mx2d_rotate),
2205 };
2206 
2207 /*SVGPath class*/
2208 static const JSCFunctionListEntry path_Funcs[] =
2209 {
2210 	JS_CGETSET_MAGIC_DEF("numberOfSegments", path_getProperty, NULL, 0),
2211 	JS_CFUNC_DEF("getSegment", 1, svg_path_get_segment),
2212 	JS_CFUNC_DEF("getSegmentParam", 2, svg_path_get_segment_param),
2213 	JS_CFUNC_DEF("moveTo", 2, svg_path_move_to),
2214 	JS_CFUNC_DEF("lineTo", 2, svg_path_line_to),
2215 	JS_CFUNC_DEF("quadTo", 4, svg_path_quad_to),
2216 	JS_CFUNC_DEF("curveTo", 6, svg_path_curve_to),
2217 	JS_CFUNC_DEF("close", 0, svg_path_close),
2218 };
2219 
2220 JSClassID dom_js_get_element_proto(struct JSContext *c);
2221 JSClassID dom_js_get_document_proto(JSContext *c);
2222 
2223 /*defines a new global object "document" of type Document*/
2224 void dom_js_define_document(struct JSContext *c, JSValue global, GF_SceneGraph *doc);
2225 
2226 void domDocument_gc_mark(JSRuntime *rt, JSValueConst obj, JS_MarkFunc *mark_func);
2227 void domElement_gc_mark(JSRuntime *rt, JSValueConst obj, JS_MarkFunc *mark_func);
2228 
svg_init_js_api(GF_SceneGraph * scene)2229 static void svg_init_js_api(GF_SceneGraph *scene)
2230 {
2231 	JSContext *c = scene->svg_js->js_ctx;
2232 	JSRuntime *jsrt = JS_GetRuntime(c);
2233 	JSValue global = JS_GetGlobalObject(scene->svg_js->js_ctx);
2234 
2235 	SETUP_JSCLASS(svg_globalClass, "Window", globalFuncs, NULL, NULL, 0);
2236 	scene->svg_js->global = JS_NewObjectClass(c, svg_globalClass.class_id);
2237 	JS_SetOpaque(scene->svg_js->global, scene);
2238 	JS_SetPropertyStr(c, global, "Window", scene->svg_js->global);
2239 
2240  	JS_SetPropertyStr(c, global, "alert", JS_NewCFunction(c, js_print, "alert", 1));
2241 
2242 	/*initialize DOM core */
2243 	dom_js_load(scene, scene->svg_js->js_ctx);
2244 
2245 	qjs_module_init_xhr_global(c, global);
2246 
2247 	svg_define_udom_exception(scene->svg_js->js_ctx, scene->svg_js->global);
2248 	JSValue console = JS_NewObject(c);
2249  	JS_SetPropertyStr(c, console, "log", JS_NewCFunction(c, js_print, "print", 1));
2250 	JS_SetPropertyStr(c, global, "console", console);
2251 
2252 	svgDocument.class.gc_mark = domDocument_gc_mark;
2253 	SETUP_JSCLASS(svgDocument, "SVGDocument", documentFuncs, NULL, dom_document_finalize, dom_js_get_document_proto(c));
2254 	svgElement.class.gc_mark = domElement_gc_mark;
2255 	SETUP_JSCLASS(svgElement, "SVGElement", svg_elementFuncs, NULL, dom_element_finalize, dom_js_get_element_proto(c));
2256 
2257 	SETUP_JSCLASS(rgbClass, "SVGRGBColor", rgb_Funcs, NULL, baseCI_finalize, 0);
2258 	SETUP_JSCLASS(rectClass, "SVGRect", rect_Funcs, NULL, baseCI_finalize, 0);
2259 	SETUP_JSCLASS(pointClass, "SVGPoint", point_Funcs, NULL, baseCI_finalize, 0);
2260 	SETUP_JSCLASS(matrixClass, "SVGMatrix", matrix_Funcs, NULL, baseCI_finalize, 0);
2261 
2262 	SETUP_JSCLASS(pathClass, "SVGPath", path_Funcs, NULL, pathCI_finalize, 0);
2263 
2264 
2265 	JS_SetPropertyStr(c, scene->svg_js->pathClass.proto, "MOVE_TO", JS_NewInt32(c, 77));
2266 	JS_SetPropertyStr(c, scene->svg_js->pathClass.proto, "LINE_TO", JS_NewInt32(c, 76));
2267 	JS_SetPropertyStr(c, scene->svg_js->pathClass.proto, "CURVE_TO", JS_NewInt32(c, 67));
2268 	JS_SetPropertyStr(c, scene->svg_js->pathClass.proto, "QUAD_TO", JS_NewInt32(c, 81));
2269 	JS_SetPropertyStr(c, scene->svg_js->pathClass.proto, "CLOSE", JS_NewInt32(c, 90));
2270 
2271 	/*we have our own constructors*/
2272 	scene->get_element_class = svg_get_element_class;
2273 	scene->get_document_class = svg_get_document_class;
2274 
2275 	/*create document object*/
2276 	dom_js_define_document(scene->svg_js->js_ctx, global, scene);
2277 	/*create event object, and remember it*/
2278 	scene->svg_js->event = dom_js_define_event(scene->svg_js->js_ctx);
2279 
2280 	JS_FreeValue(c, global);
2281 }
2282 
2283 GF_DOM_Event *dom_get_evt_private(JSValue v);
2284 
svg_script_get_context(GF_SceneGraph * sg)2285 JSContext *svg_script_get_context(GF_SceneGraph *sg)
2286 {
2287 	return sg->svg_js ? sg->svg_js->js_ctx : NULL;
2288 }
2289 
svg_script_execute(GF_SceneGraph * sg,char * utf8_script,GF_DOM_Event * event)2290 Bool svg_script_execute(GF_SceneGraph *sg, char *utf8_script, GF_DOM_Event *event)
2291 {
2292 	char szFuncName[1024];
2293 	JSValue ret;
2294 	Bool ok=GF_TRUE;
2295 	GF_DOM_Event *prev_event = NULL;
2296 	char *sep = strchr(utf8_script, '(');
2297 
2298 	if (!sep) {
2299 		strcpy(szFuncName, utf8_script);
2300 		strcat(szFuncName, "(evt)");
2301 		utf8_script = szFuncName;
2302 	}
2303 
2304 	gf_js_lock(sg->svg_js->js_ctx, GF_TRUE);
2305 
2306 	prev_event = dom_get_evt_private(sg->svg_js->event);
2307 	JS_SetOpaque(sg->svg_js->event, event);
2308 	ret = JS_Eval(sg->svg_js->js_ctx, utf8_script, (u32) strlen(utf8_script), "inline script", sg->svg_js->use_strict ? JS_EVAL_TYPE_MODULE : JS_EVAL_TYPE_GLOBAL);
2309 	JS_SetOpaque(sg->svg_js->event, prev_event);
2310 
2311 #if 0
2312 	//to check, what was the purpose of this ?
2313 	if (ret==JS_FALSE) {
2314 		char *sep = strchr(utf8_script, '(');
2315 		if (sep) {
2316 			sep[0] = 0;
2317 			ret = JS_LookupProperty(sg->svg_js->js_ctx, sg->svg_js->global, utf8_script, &rval);
2318 			sep[0] = '(';
2319 		}
2320 	}
2321 #endif
2322 
2323 	if (JS_IsException(ret)) {
2324 		js_dump_error(sg->svg_js->js_ctx);
2325 		ok=GF_FALSE;
2326 	}
2327 	JS_FreeValue(sg->svg_js->js_ctx, ret);
2328 
2329 	if (sg->svg_js->force_gc) {
2330 		gf_js_call_gc(sg->svg_js->js_ctx);
2331 		sg->svg_js->force_gc = GF_FALSE;
2332 	}
2333 	js_do_loop(sg->svg_js->js_ctx);
2334 	gf_js_lock(sg->svg_js->js_ctx, GF_FALSE);
2335 
2336 	return ok;
2337 }
2338 
2339 #ifdef GPAC_ENABLE_HTML5_MEDIA
2340 void html_media_js_api_del();
2341 #endif
2342 
gf_svg_script_context_del(GF_SVGJS * svg_js,GF_SceneGraph * scenegraph)2343 void gf_svg_script_context_del(GF_SVGJS *svg_js, GF_SceneGraph *scenegraph)
2344 {
2345 	gf_sg_js_dom_pre_destroy(JS_GetRuntime(svg_js->js_ctx), scenegraph, NULL);
2346 	gf_js_delete_context(svg_js->js_ctx);
2347 	dom_js_unload();
2348 #ifdef GPAC_ENABLE_HTML5_MEDIA
2349 	/* HTML */
2350 	html_media_js_api_del();
2351 #endif
2352 
2353 	gf_free(svg_js);
2354 	scenegraph->svg_js = NULL;
2355 
2356 }
2357 
svg_script_predestroy(GF_Node * n,void * eff,Bool is_destroy)2358 static void svg_script_predestroy(GF_Node *n, void *eff, Bool is_destroy)
2359 {
2360 	if (is_destroy) {
2361 		GF_SVGJS *svg_js = n->sgprivate->scenegraph->svg_js;
2362 		/*unregister script from parent scene (cf base_scenegraph::sg_reset) */
2363 		gf_list_del_item(n->sgprivate->scenegraph->scripts, n);
2364 
2365 		if (svg_js->nb_scripts) {
2366 			svg_js->nb_scripts--;
2367 
2368 			/*detach this script from our object cache*/
2369 			gf_sg_js_dom_pre_destroy(JS_GetRuntime(svg_js->js_ctx), n->sgprivate->scenegraph, n);
2370 
2371 			if (!svg_js->nb_scripts) {
2372 				gf_svg_script_context_del(svg_js, n->sgprivate->scenegraph);
2373 			}
2374 		}
2375 	}
2376 }
2377 
JSScript_CreateSVGContext(GF_SceneGraph * sg)2378 GF_Err JSScript_CreateSVGContext(GF_SceneGraph *sg)
2379 {
2380 	GF_SVGJS *svg_js;
2381 
2382 	if (sg->svg_js) {
2383 		/* the JS/SVG context is already created, no need to do anything  */
2384 		return GF_OK;
2385 	}
2386 
2387 	GF_SAFEALLOC(svg_js, GF_SVGJS);
2388 	if (!svg_js) {
2389 		return GF_OUT_OF_MEM;
2390 	}
2391 	/*create new ecmascript context*/
2392 	svg_js->js_ctx = gf_js_create_context();
2393 	if (!svg_js->js_ctx) {
2394 		gf_free(svg_js);
2395 		return GF_SCRIPT_ERROR;
2396 	}
2397 
2398 	gf_js_lock(svg_js->js_ctx, GF_TRUE);
2399 
2400 	sg->svg_js = svg_js;
2401 	/*load SVG & DOM APIs*/
2402 	svg_init_js_api(sg);
2403 
2404 #ifdef GPAC_ENABLE_HTML5_MEDIA
2405 	/* HTML */
2406 	html_media_init_js_api(sg);
2407 #endif
2408 
2409 
2410 	svg_js->script_execute = svg_script_execute;
2411 	svg_js->handler_execute = svg_script_execute_handler;
2412 
2413 	gf_js_lock(svg_js->js_ctx, GF_FALSE);
2414 
2415 	return GF_OK;
2416 }
2417 
svg_get_text_child(GF_Node * node)2418 GF_DOMText *svg_get_text_child(GF_Node *node)
2419 {
2420 	GF_ChildNodeItem *child;
2421 	GF_DOMText *txt;
2422 	txt = NULL;
2423 	child = ((SVG_Element*)node)->children;
2424 	if (! child) return NULL;
2425 	while (child) {
2426 		txt = (GF_DOMText*)child->node;
2427 		if ((txt->sgprivate->tag==TAG_DOMText) && txt->textContent) return txt;
2428 		txt = NULL;
2429 		child = child->next;
2430 	}
2431 	return NULL;
2432 }
2433 
svg_js_load_script(GF_Node * script,char * file)2434 static Bool svg_js_load_script(GF_Node *script, char *file)
2435 {
2436 	GF_Err e;
2437 	u8 *jsscript;
2438 	u32 fsize;
2439 	Bool success = GF_TRUE;
2440 	JSValue ret;
2441 	GF_SVGJS *svg_js;
2442 	u32 flags = JS_EVAL_TYPE_GLOBAL;
2443 	char *abs_url = NULL;
2444 
2445 	svg_js = script->sgprivate->scenegraph->svg_js;
2446 	if (!strnicmp(file, "file://", 7)) file += 7;
2447 
2448 	if (!gf_file_exists(file)) {
2449 		GF_JSAPIParam par;
2450 		GF_SceneGraph *scene = script->sgprivate->scenegraph;
2451 		par.uri.url = file;
2452 		if (scene->script_action && scene->script_action(scene->script_action_cbck, GF_JSAPI_OP_RESOLVE_XLINK, script, &par))
2453 			abs_url = (char *) par.uri.url;
2454 
2455 		if (abs_url || !gf_file_exists(abs_url)) {
2456 			if (abs_url) gf_free(abs_url);
2457 			return GF_FALSE;
2458 		}
2459 	}
2460 
2461 	e = gf_file_load_data(abs_url ? abs_url : file, &jsscript, &fsize);
2462 	if (abs_url) gf_free(abs_url);
2463 
2464 	if (e!=GF_OK) return GF_FALSE;
2465 
2466 	/*for handler, only load code*/
2467 	if (script->sgprivate->tag==TAG_SVG_handler) {
2468 		GF_DOMText *txt = gf_dom_add_text_node(script, jsscript);
2469 		txt->type = GF_DOM_TEXT_INSERTED;
2470 		return GF_TRUE;
2471 	}
2472 
2473 	gf_js_lock(svg_js->js_ctx, GF_TRUE);
2474 
2475  	if (!gf_opts_get_bool("core", "no-js-mods") && JS_DetectModule((const char *) jsscript, fsize )) {
2476 		flags = JS_EVAL_TYPE_MODULE;
2477 		svg_js->use_strict = GF_TRUE;
2478 	}
2479 
2480 	ret = JS_Eval(svg_js->js_ctx, jsscript, sizeof(char)*fsize, file, flags);
2481 	if (JS_IsException(ret)) {
2482 		js_dump_error(svg_js->js_ctx);
2483 		success=GF_FALSE;
2484 	}
2485 	JS_FreeValue(svg_js->js_ctx, ret);
2486 	if (svg_js->force_gc) {
2487 		gf_js_call_gc(svg_js->js_ctx);
2488 		svg_js->force_gc = GF_FALSE;
2489 	}
2490 	gf_js_lock(svg_js->js_ctx, GF_FALSE);
2491 
2492 	gf_dom_listener_process_add(script->sgprivate->scenegraph);
2493 
2494 	gf_free(jsscript);
2495 	return success;
2496 }
2497 
2498 #include <gpac/download.h>
2499 #include <gpac/network.h>
2500 
2501 
JSScript_LoadSVG(GF_Node * node)2502 void JSScript_LoadSVG(GF_Node *node)
2503 {
2504 	GF_DOMText *txt;
2505 	GF_SVGJS *svg_js;
2506 	GF_FieldInfo href_info;
2507 
2508 	if (!node->sgprivate->scenegraph->svg_js) {
2509 		if (JSScript_CreateSVGContext(node->sgprivate->scenegraph) != GF_OK) return;
2510 	}
2511 
2512 	/*register script with parent scene (cf base_scenegraph::sg_reset) */
2513 	gf_list_add(node->sgprivate->scenegraph->scripts, node);
2514 
2515 	svg_js = node->sgprivate->scenegraph->svg_js;
2516 	if (!node->sgprivate->UserCallback) {
2517 		svg_js->nb_scripts++;
2518 		node->sgprivate->UserCallback = svg_script_predestroy;
2519 	}
2520 	/*if href download the script file*/
2521 	if (gf_node_get_attribute_by_tag(node, TAG_XLINK_ATT_href, GF_FALSE, GF_FALSE, &href_info) == GF_OK) {
2522 		GF_DownloadManager *dnld_man;
2523 		GF_JSAPIParam par;
2524 		char *url;
2525 		GF_Err e;
2526 		XMLRI *xmlri = (XMLRI *)href_info.far_ptr;
2527 
2528 		/* getting a download manager */
2529 		par.dnld_man = NULL;
2530 		ScriptAction(node->sgprivate->scenegraph, GF_JSAPI_OP_GET_DOWNLOAD_MANAGER, NULL, &par);
2531 		dnld_man = par.dnld_man;
2532 
2533 
2534 		/* resolve the uri of the script*/
2535 		par.uri.nb_params = 0;
2536 		par.uri.url = xmlri->string;
2537 		ScriptAction(node->sgprivate->scenegraph, GF_JSAPI_OP_RESOLVE_URI, node, &par);
2538 		url = (char *)par.uri.url;
2539 
2540 		/* if the file is local, we don't need to download it */
2541 		if (!strstr(url, "://") || !strnicmp(url, "file://", 7)) {
2542 			svg_js_load_script(node, url);
2543 		} else if (dnld_man) {
2544 			/*fetch the remote script synchronously and load it - cf section on script processing in SVG specs*/
2545 			GF_DownloadSession *sess = gf_dm_sess_new(dnld_man, url, GF_NETIO_SESSION_NOT_THREADED, NULL, NULL, &e);
2546 			if (sess) {
2547 				e = gf_dm_sess_process(sess);
2548 				if (e==GF_OK) {
2549 					const char *szCache = gf_dm_sess_get_cache_name(sess);
2550 					if (!svg_js_load_script(node, (char *) szCache))
2551 						e = GF_SCRIPT_ERROR;
2552 				}
2553 				gf_dm_sess_del(sess);
2554 			}
2555 			if (e) {
2556 				par.info.e = e;
2557 				par.info.msg = "Cannot fetch script";
2558 				ScriptAction(node->sgprivate->scenegraph, GF_JSAPI_OP_MESSAGE, NULL, &par);
2559 			}
2560 		}
2561 		gf_free(url);
2562 	}
2563 	/*for scripts only, execute*/
2564 	else if (node->sgprivate->tag == TAG_SVG_script) {
2565 		JSValue ret;
2566 		u32 txtlen;
2567 		u32 flags = JS_EVAL_TYPE_GLOBAL;
2568 
2569 		txt = svg_get_text_child(node);
2570 		if (!txt) return;
2571 		txtlen = (u32) strlen(txt->textContent);
2572 
2573 		if (!gf_opts_get_bool("core", "no-js-mods") && JS_DetectModule((const char *) txt, txtlen )) {
2574 			flags = JS_EVAL_TYPE_MODULE;
2575 			svg_js->use_strict = GF_TRUE;
2576 		}
2577 
2578 		ret = JS_Eval(svg_js->js_ctx, txt->textContent, (u32) strlen(txt->textContent), "inline_script", flags);
2579 		if (JS_IsException(ret)) {
2580 			js_dump_error(svg_js->js_ctx);
2581 		}
2582 		JS_FreeValue(svg_js->js_ctx, ret);
2583 		gf_dom_listener_process_add(node->sgprivate->scenegraph);
2584 		js_do_loop(svg_js->js_ctx);
2585 	}
2586 }
2587 
2588 
2589 #ifdef _DEBUG
2590 //#define DUMP_DEF_AND_ROOT
2591 #endif
2592 
2593 #ifdef DUMP_DEF_AND_ROOT
dump_root(const char * name,void * rp,void * data)2594 void dump_root(const char *name, void *rp, void *data)
2595 {
2596 	if (name[0]=='_') fprintf(stderr, "\t%s\n", name);
2597 }
2598 #endif
2599 
2600 /* Executes JavaScript code in response to an event being triggered
2601   The code to be executed (stored in a GF_DOMHandler struct) is either:
2602   - text content not yet passed to the JS engine
2603      - text contained in a node's text content not yet parsed (node)
2604 	 - text, outside of a node, obtained by some external means (XHR, ...) - utf8_script
2605   - already in the JS engine, in the form of:
2606      - an anonymous function (js_fun_val)
2607 	 - a named function (js_fun)
2608 */
svg_script_execute_handler(GF_Node * node,GF_DOM_Event * event,GF_Node * observer,char * utf8_script)2609 static Bool svg_script_execute_handler(GF_Node *node, GF_DOM_Event *event, GF_Node *observer, char *utf8_script)
2610 {
2611 	GF_DOMText *txt = NULL;
2612 	GF_SVGJS *svg_js;
2613 	JSValue __this;
2614 	JSValue ret;
2615 	u32 flags = JS_EVAL_TYPE_GLOBAL;
2616 	Bool success=GF_TRUE;
2617 	GF_DOM_Event *prev_event = NULL;
2618 	GF_DOMHandler *hdl = (GF_DOMHandler *)node;
2619 
2620 	/*LASeR hack for encoding scripts without handler - node is a listener in this case, not a handler*/
2621 	if (utf8_script) {
2622 		if (!node) return GF_FALSE;
2623 		hdl = NULL;
2624 	} else {
2625 		if (JS_IsUndefined(hdl->js_data->fun_val) && JS_IsUndefined(hdl->js_data->evt_listen_obj)) {
2626 			txt = svg_get_text_child(node);
2627 			if (!txt) return GF_FALSE;
2628 		}
2629 		/*not sure about this (cf test struct-use-205-t.svg)*/
2630 		//if (!node->sgprivate->parents) return GF_FALSE;
2631 	}
2632 
2633 	svg_js = node->sgprivate->scenegraph->svg_js;
2634 
2635 #ifndef GPAC_DISABLE_LOG
2636 	if (gf_log_tool_level_on(GF_LOG_SCRIPT, GF_LOG_DEBUG)) {
2637 		char *content, *_content = NULL;
2638 		if (utf8_script) {
2639 			content = utf8_script;
2640 		} else if (!JS_IsUndefined(hdl->js_data->fun_val)) {
2641 			content = _content = (char *) JS_ToCString(svg_js->js_ctx, hdl->js_data->fun_val);
2642 		} else if (txt) {
2643 			content = txt->textContent;
2644 		} else {
2645 			content = "unknown";
2646 		}
2647 		GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[DOM Events    ] Executing script code from handler: %s\n", content) );
2648 		JS_FreeCString(svg_js->js_ctx, _content);
2649 	}
2650 #endif
2651 
2652 	gf_js_lock(svg_js->js_ctx, GF_TRUE);
2653 	prev_event = dom_get_evt_private(svg_js->event);
2654 	/*break loops*/
2655 	if (prev_event && (prev_event->type==event->type) && (prev_event->target==event->target)) {
2656 		gf_js_lock(svg_js->js_ctx, GF_FALSE);
2657 		return GF_FALSE;
2658 	}
2659 	JS_SetOpaque(svg_js->event, event);
2660 
2661 	svg_js->in_script = GF_TRUE;
2662 	if (svg_js->use_strict)
2663 		flags = JS_EVAL_TYPE_MODULE;
2664 
2665 	/*if an observer is being specified, use it*/
2666 	if (hdl && hdl->js_data && !JS_IsUndefined(hdl->js_data->evt_listen_obj)) __this = hdl->js_data->evt_listen_obj;
2667 	/*compile the jsfun if any - 'this' is the associated observer*/
2668 	else __this = observer ? dom_element_construct(svg_js->js_ctx, observer) : svg_js->global;
2669 
2670 	if (txt && hdl && hdl->js_data && !JS_IsUndefined(hdl->js_data->fun_val)) {
2671 		hdl->js_data->fun_val = JS_EvalWithTarget(svg_js->js_ctx, __this, txt->textContent, strlen(txt->textContent), "handler", flags|JS_EVAL_FLAG_COMPILE_ONLY);
2672 	}
2673 
2674 	if (utf8_script) {
2675 		ret = JS_EvalWithTarget(svg_js->js_ctx, __this, utf8_script, (u32) strlen(utf8_script), "inline script", flags);
2676 	}
2677 	else if (hdl && hdl->js_data
2678 		&& (!JS_IsUndefined(hdl->js_data->fun_val) || !JS_IsUndefined(hdl->js_data->evt_listen_obj) )
2679 	) {
2680 		JSValue evt;
2681 		JSValue argv[1];
2682 		evt = gf_dom_new_event(svg_js->js_ctx);
2683 		JS_SetOpaque(evt, event);
2684 		argv[0] = evt;
2685 
2686 		if (!JS_IsUndefined(hdl->js_data->fun_val) ) {
2687 			ret = JS_Call(svg_js->js_ctx, hdl->js_data->fun_val, __this, 1, argv);
2688 		} else {
2689 			JSValue fun = JS_GetPropertyStr(svg_js->js_ctx, hdl->js_data->evt_listen_obj, "hanldeEvent");
2690 			ret = JS_Call(svg_js->js_ctx, fun, hdl->js_data->evt_listen_obj, 1, argv);
2691 			JS_FreeValue(svg_js->js_ctx, fun);
2692 		}
2693 	} else if (txt) {
2694 		JSValue fun = JS_GetPropertyStr(svg_js->js_ctx, svg_js->global, txt->textContent);
2695 		if (!JS_IsUndefined(fun)) {
2696 			ret = JS_NULL;
2697 			if (svg_script_execute(node->sgprivate->scenegraph, txt->textContent, event)) {
2698 				success = GF_FALSE;
2699 			}
2700 		}
2701 		else {
2702 			ret = JS_EvalWithTarget(svg_js->js_ctx, __this, txt->textContent, (u32) strlen(txt->textContent), "internal", flags);
2703 		}
2704 		JS_FreeValue(svg_js->js_ctx, fun);
2705 	} else {
2706 		ret = JS_NULL;
2707 	}
2708 	if (JS_IsException(ret)) {
2709 		js_dump_error(svg_js->js_ctx);
2710 		success = GF_FALSE;
2711 	}
2712 	JS_FreeValue(svg_js->js_ctx, ret);
2713 
2714 	JS_SetOpaque(svg_js->event, prev_event);
2715 	if (txt && hdl && hdl->js_data) hdl->js_data->fun_val = JS_UNDEFINED;
2716 
2717 	while (svg_js->force_gc) {
2718 		svg_js->force_gc = GF_FALSE;
2719 		gf_js_call_gc(svg_js->js_ctx);
2720 	}
2721 	svg_js->in_script = GF_FALSE;
2722 
2723 	/*check any pending exception if outer-most event*/
2724 	if (!prev_event) {
2725 		js_do_loop(svg_js->js_ctx);
2726 	}
2727 
2728 	gf_js_lock(svg_js->js_ctx, GF_FALSE);
2729 
2730 #ifdef DUMP_DEF_AND_ROOT
2731 	if ((event->type==GF_EVENT_CLICK) || (event->type==GF_EVENT_MOUSEOVER)) {
2732 		NodeIDedItem *reg_node;
2733 		fprintf(stderr, "Node registry\n");
2734 		reg_node = node->sgprivate->scenegraph->id_node;
2735 		while (reg_node) {
2736 			fprintf(stderr, "\t%s\n", reg_node->NodeName);
2737 			reg_node = reg_node->next;
2738 		}
2739 
2740 		fprintf(stderr, "\n\nNamed roots:\n");
2741 		JS_DumpNamedRoots(JS_GetRuntime(svg_js->js_ctx), dump_root, NULL);
2742 	}
2743 #endif
2744 
2745 	if (!success) {
2746 		GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("SVG: Invalid event handler script\n" ));
2747 		return GF_FALSE;
2748 	}
2749 	return GF_TRUE;
2750 }
2751 
2752 #endif
2753 
2754 #endif
2755