Line data Source code
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 :
92 2 : void svg_mark_gc(struct __tag_svg_script_ctx *svg_js)
93 : {
94 2 : if (svg_js)
95 0 : svg_js->force_gc = 1;
96 2 : }
97 :
98 28 : void svg_free_node_binding(struct __tag_svg_script_ctx *svg_js, GF_Node *node)
99 : {
100 28 : struct _node_js_binding *js_binding = node->sgprivate->interact->js_binding;
101 56 : if (!JS_IsUndefined(js_binding->obj)) {
102 28 : JS_SetOpaque(js_binding->obj, NULL);
103 28 : JS_FreeValue(svg_js->js_ctx, js_binding->obj);
104 28 : 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 28 : gf_node_unregister(node, NULL);
108 : }
109 :
110 28 : if (svg_js->in_script)
111 0 : svg_js->force_gc = 1;
112 : else
113 28 : gf_js_call_gc(svg_js->js_ctx);
114 28 : }
115 :
116 0 : GF_Err svg_exec_script(struct __tag_svg_script_ctx *svg_js, GF_SceneGraph *sg, const char *com)
117 : {
118 0 : Bool ret = sg->svg_js->script_execute(sg, (char *)com, NULL);
119 0 : return (ret == GF_TRUE ? GF_OK : GF_BAD_PARAM);
120 : }
121 :
122 0 : 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 0 : if (svg_js->handler_execute(hdl, event, observer, (char *) iri)) {
125 : return;
126 : } else {
127 0 : 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 :
148 : static GFINLINE Bool ScriptAction(GF_SceneGraph *scene, u32 type, GF_Node *node, GF_JSAPIParam *param)
149 : {
150 4 : if (scene->script_action)
151 4 : 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 :
192 0 : static JSValue svg_nav_to_location(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
193 : {
194 : GF_JSAPIParam par;
195 0 : GF_SceneGraph *sg = JS_GetOpaque(obj, svg_globalClass.class_id);
196 0 : if ((argc!=1) || !sg)
197 0 : return JS_EXCEPTION;
198 :
199 0 : par.uri.url = (char *) JS_ToCString(c, argv[0]);
200 0 : par.uri.nb_params = 0;
201 0 : ScriptAction(sg, GF_JSAPI_OP_LOAD_URL, sg->RootNode, &par);
202 0 : JS_FreeCString(c, par.uri.url);
203 0 : return JS_UNDEFINED;
204 : }
205 :
206 : GF_Node *gf_sm_load_svg_from_string(GF_SceneGraph *sg, char *svg_str);
207 0 : 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 0 : if (!JS_IsObject(argv[1])) {
214 0 : return js_throw_err(c, GF_DOM_EXC_WRONG_DOCUMENT_ERR);
215 : }
216 :
217 : str = JS_ToCString(c, argv[0]);
218 0 : if (!str) return JS_TRUE;
219 0 : sg = dom_get_doc(c, argv[1]);
220 :
221 0 : node = gf_sm_load_svg_from_string(sg, (char *) str);
222 0 : JS_FreeCString(c, str);
223 0 : return dom_element_construct(c, node);
224 : }
225 :
226 3 : static void svg_define_udom_exception(JSContext *c, JSValue global)
227 : {
228 3 : JSValue obj = JS_NewObject(c);
229 3 : JS_SetPropertyStr(c, global, "GlobalException", obj);
230 : #define DEFCONST(_name, _code)\
231 : JS_SetPropertyStr(c, obj, _name, JS_NewInt32(c, _code));
232 :
233 3 : DEFCONST("NOT_CONNECTED_ERR", 1)
234 3 : DEFCONST("ENCODING_ERR", 2)
235 3 : DEFCONST("DENIED_ERR", 3)
236 3 : DEFCONST("UNKNOWN_ERR", 4)
237 :
238 3 : obj = JS_NewObject(c);
239 3 : JS_SetPropertyStr(c, global, "SVGException", obj);
240 3 : DEFCONST("SVG_WRONG_TYPE_ERR", 0)
241 3 : DEFCONST("SVG_INVALID_VALUE_ERR", 1)
242 3 : DEFCONST("SVG_MATRIX_NOT_INVERTABLE", 2)
243 :
244 3 : obj = JS_NewObject(c);
245 3 : JS_SetPropertyStr(c, global, "SVGSVGElement", obj);
246 3 : DEFCONST("NAV_AUTO", 1)
247 3 : DEFCONST("NAV_NEXT", 2)
248 3 : DEFCONST("NAV_PREV", 3)
249 3 : DEFCONST("NAV_UP", 4)
250 3 : DEFCONST("NAV_UP_RIGHT", 5)
251 3 : DEFCONST("NAV_RIGHT", 6)
252 3 : DEFCONST("NAV_DOWN_RIGHT", 7)
253 3 : DEFCONST("NAV_DOWN", 8)
254 3 : DEFCONST("NAV_DOWN_LEFT", 9)
255 3 : DEFCONST("NAV_LEFT", 10)
256 3 : DEFCONST("NAV_UP_LEFT", 11)
257 3 : }
258 :
259 0 : static JSValue global_getProperty(JSContext *c, JSValueConst obj, int magic)
260 : {
261 0 : GF_SceneGraph *sg = JS_GetOpaque(obj, svg_globalClass.class_id);
262 0 : if (!sg) return JS_EXCEPTION;
263 :
264 0 : switch (magic) {
265 : /*namespaceURI*/
266 0 : case 0:
267 0 : return JS_NULL;
268 : /*parent*/
269 0 : case 1:
270 0 : if (sg->parent_scene && sg->parent_scene->svg_js)
271 : return JS_DupValue(c, sg->parent_scene->svg_js->global);
272 0 : return JS_NULL;
273 0 : default:
274 0 : return JS_UNDEFINED;
275 : }
276 : }
277 :
278 : /*TODO - try to be more precise...*/
279 0 : static JSValue dom_imp_has_feature(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
280 : {
281 0 : JSValue ret = JS_FALSE;
282 :
283 0 : if (argc) {
284 : u32 len;
285 : char sep;
286 : char *fname = (char *) JS_ToCString(c, argv[0]);
287 0 : if (!fname) return JS_TRUE;
288 0 : while (strchr(" \t\n\r", fname[0])) fname++;
289 0 : len = (u32) strlen(fname);
290 0 : while (len && strchr(" \t\n\r", fname[len-1])) len--;
291 0 : sep = fname[len];
292 0 : fname[len] = 0;
293 0 : if (!stricmp(fname, "xml")) ret = JS_TRUE;
294 0 : else if (!stricmp(fname, "core")) ret = JS_TRUE;
295 0 : else if (!stricmp(fname, "traversal")) ret = JS_TRUE;
296 0 : else if (!stricmp(fname, "uievents")) ret = JS_TRUE;
297 0 : else if (!stricmp(fname, "mouseevents")) ret = JS_TRUE;
298 0 : else if (!stricmp(fname, "mutationevents")) ret = JS_TRUE;
299 0 : else if (!stricmp(fname, "events")) ret = JS_TRUE;
300 :
301 0 : fname[len] = sep;
302 0 : JS_FreeCString(c, fname);
303 : }
304 0 : return ret;
305 : }
306 :
307 0 : static GF_Node *get_corresponding_use(GF_Node *n)
308 : {
309 : u32 i, count;
310 0 : 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 0 : count = gf_list_count(n->sgprivate->scenegraph->use_stack);
314 0 : for (i=count; i>0; i-=2) {
315 0 : GF_Node *t = (GF_Node *)gf_list_get(n->sgprivate->scenegraph->use_stack, i-2);
316 0 : if (t==n) {
317 0 : GF_Node *use = (GF_Node *)gf_list_get(n->sgprivate->scenegraph->use_stack, i-1);
318 0 : GF_Node *par_use = get_corresponding_use(use);
319 0 : return par_use ? par_use : use;
320 : }
321 : }
322 : /*otherwise recursively get up the tree*/
323 0 : return get_corresponding_use(gf_node_get_parent(n, 0));
324 : }
325 :
326 0 : static JSValue svg_doc_getProperty(JSContext *c, JSValueConst obj, int magic)
327 : {
328 0 : GF_SceneGraph *sg = dom_get_doc(c, obj);
329 0 : if (!sg) return JS_EXCEPTION;
330 0 : switch (magic) {
331 0 : case 0:/*global*/
332 0 : return JS_GetGlobalObject(c);
333 : }
334 0 : return JS_UNDEFINED;
335 : }
336 :
337 0 : static JSValue svg_element_getProperty(JSContext *c, JSValueConst obj, int magic)
338 : {
339 : GF_JSAPIParam par;
340 0 : GF_Node *n = dom_get_element(c, obj);
341 0 : if (!n) return JS_TRUE;
342 :
343 0 : switch (magic) {
344 0 : case 0: /*id*/
345 : {
346 0 : const char *node_name = gf_node_get_name((GF_Node*)n);
347 0 : if (node_name) {
348 0 : return JS_NewString(c, node_name);
349 : }
350 0 : return JS_NULL;
351 : }
352 0 : case 5:/*currentScale*/
353 0 : if (n->sgprivate->tag!=TAG_SVG_svg) return JS_EXCEPTION;
354 0 : if (ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_GET_SCALE, (GF_Node *)n, &par)) {
355 0 : return JS_NewFloat64(c, FIX2FLT(par.val) );
356 : }
357 0 : return JS_EXCEPTION;
358 0 : case 6:/*currentRotate*/
359 0 : if (n->sgprivate->tag!=TAG_SVG_svg) return JS_EXCEPTION;
360 0 : if (ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_GET_ROTATION, (GF_Node *)n, &par)) {
361 0 : return JS_NewFloat64(c, FIX2FLT(par.val) );
362 : }
363 0 : return JS_EXCEPTION;
364 0 : case 7:/*currentTranslate*/
365 0 : if (n->sgprivate->tag!=TAG_SVG_svg) return JS_EXCEPTION;
366 0 : if (ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_GET_TRANSLATE, (GF_Node *)n, &par)) {
367 0 : JSValue r = JS_NewObjectClass(c, pointClass.class_id);
368 0 : pointCI *rc = (pointCI *)gf_malloc(sizeof(pointCI));
369 0 : rc->x = FIX2FLT(par.pt.x);
370 0 : rc->y = FIX2FLT(par.pt.y);
371 0 : rc->sg = n->sgprivate->scenegraph;
372 0 : JS_SetOpaque(r, rc);
373 0 : return r;
374 : }
375 0 : return JS_EXCEPTION;
376 0 : case 8:/*viewport*/
377 0 : if (n->sgprivate->tag!=TAG_SVG_svg) return JS_EXCEPTION;
378 0 : if (ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_GET_VIEWPORT, (GF_Node *)n, &par)) {
379 0 : JSValue r = JS_NewObjectClass(c, rectClass.class_id);
380 0 : rectCI *rc = (rectCI *)gf_malloc(sizeof(rectCI));
381 0 : rc->x = FIX2FLT(par.rc.x);
382 0 : rc->y = FIX2FLT(par.rc.y);
383 0 : rc->w = FIX2FLT(par.rc.width);
384 0 : rc->h = FIX2FLT(par.rc.height);
385 0 : rc->sg = n->sgprivate->scenegraph;
386 0 : JS_SetOpaque(r, rc);
387 0 : return r;
388 : }
389 0 : return JS_EXCEPTION;
390 0 : case 9:/*currentTime*/
391 0 : return JS_NewFloat64(c, gf_node_get_scene_time((GF_Node *)n) );
392 0 : case 10:/*isPaused*/
393 0 : return JS_FALSE;
394 0 : case 11:/*ownerSVGElement*/
395 : while (1) {
396 0 : GF_Node *n_par = gf_node_get_parent(n, 0);
397 0 : if (!n_par) return JS_TRUE;
398 0 : if (n_par->sgprivate->tag==TAG_SVG_svg) {
399 0 : return dom_element_construct(c, n_par);
400 : }
401 : n = n_par;
402 : }
403 : return JS_NULL;
404 0 : case 12:/*correspondingElement*/
405 : /*if we can find a corresponding element for this node, then this is an SVGElementInstance*/
406 0 : if (get_corresponding_use(n)) {
407 0 : return dom_element_construct(c, n);
408 : } else {
409 0 : return dom_element_construct(c, NULL);
410 : }
411 : break;
412 0 : case 13:/*correspondingUseElement*/
413 0 : return dom_element_construct(c, get_corresponding_use(n));
414 : default:
415 : break;
416 : }
417 0 : return JS_UNDEFINED;
418 : }
419 :
420 0 : static JSValue svg_element_setProperty(JSContext *c, JSValueConst obj, JSValueConst value, int magic)
421 : {
422 : GF_JSAPIParam par;
423 : Double d;
424 0 : GF_Node *n = dom_get_element(c, obj);
425 0 : if (!n) return JS_EXCEPTION;
426 :
427 0 : switch (magic) {
428 0 : case 0:/*id*/
429 0 : if (JSVAL_CHECK_STRING(value)) {
430 : const char *id = JS_ToCString(c, value);
431 0 : if (id) {
432 : GF_FieldInfo info;
433 0 : u32 nid = gf_node_get_id(n);
434 0 : if (!nid) nid = gf_sg_get_next_available_node_id(n->sgprivate->scenegraph);
435 0 : gf_node_set_id(n, nid, id);
436 0 : if (gf_node_get_attribute_by_tag(n, TAG_XML_ATT_id, GF_TRUE, GF_FALSE, &info)==GF_OK) {
437 0 : if (*(DOM_String *)info.far_ptr) gf_free(*(DOM_String *)info.far_ptr);
438 0 : *(DOM_String *)info.far_ptr = gf_strdup(id);
439 : }
440 0 : if (gf_node_get_attribute_by_tag(n, TAG_SVG_ATT_id, GF_TRUE, GF_FALSE, &info)==GF_OK) {
441 0 : if (*(DOM_String *)info.far_ptr) gf_free(*(DOM_String *)info.far_ptr);
442 0 : *(DOM_String *)info.far_ptr = gf_strdup(id);
443 : }
444 0 : JS_FreeCString(c, id);
445 : }
446 : }
447 0 : return JS_TRUE;
448 : /*currentScale*/
449 0 : case 5:
450 0 : if ((n->sgprivate->tag!=TAG_SVG_svg) || JS_ToFloat64(c, &d, value))
451 0 : return JS_EXCEPTION;
452 :
453 0 : par.val = FLT2FIX(d);
454 0 : if (!par.val) {
455 0 : return js_throw_err(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
456 : }
457 0 : if (ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_SET_SCALE, (GF_Node *)n, &par)) {
458 0 : return JS_TRUE;
459 : }
460 0 : return JS_FALSE;
461 : /*currentRotate*/
462 0 : case 6:
463 0 : if ((n->sgprivate->tag!=TAG_SVG_svg) || JS_ToFloat64(c, &d, value))
464 0 : return JS_EXCEPTION;
465 :
466 0 : par.val = FLT2FIX(d);
467 0 : if (ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_SET_ROTATION, (GF_Node *)n, &par)) {
468 0 : return JS_TRUE;
469 : }
470 0 : return JS_FALSE;
471 : /*currentTime*/
472 0 : case 9:
473 0 : if ((n->sgprivate->tag!=TAG_SVG_svg) || JS_ToFloat64(c, &d, value))
474 0 : return JS_EXCEPTION;
475 :
476 0 : par.time = d;
477 0 : if (ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_SET_TIME, (GF_Node *)n, &par)) {
478 0 : return JS_TRUE;
479 : }
480 0 : return JS_FALSE;
481 : default:
482 : break;
483 : }
484 0 : return JS_UNDEFINED;
485 : }
486 :
487 0 : static GF_Node *svg_udom_smil_check_instance(JSContext *c, JSValue obj)
488 : {
489 0 : GF_Node *n = dom_get_element(c, obj);
490 0 : if (!n) return NULL;
491 0 : 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 0 : return NULL;
506 : }
507 :
508 :
509 : /*TODO*/
510 0 : 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 0 : GF_Node *n = svg_udom_smil_check_instance(c, obj);
519 0 : if (!n) return JS_UNDEFINED;
520 :
521 0 : if (is_end) {
522 0 : info.far_ptr = ((SVGTimedAnimBaseElement *)n)->timingp->end;
523 : } else {
524 0 : info.far_ptr = ((SVGTimedAnimBaseElement *)n)->timingp->begin;
525 : }
526 0 : if (!info.far_ptr) {
527 0 : return JS_EXCEPTION;
528 : }
529 0 : times = *((GF_List **)info.far_ptr);
530 0 : GF_SAFEALLOC(newtime, SMIL_Time);
531 0 : if (!newtime) {
532 0 : return js_throw_err(c, GF_DOM_EXC_DATA_CLONE_ERR);
533 : }
534 0 : newtime->type = GF_SMIL_TIME_EVENT_RESOLVED;
535 :
536 0 : offset = 0;
537 0 : if (argc)
538 0 : JS_ToFloat64(c, &offset, argv[0]);
539 :
540 0 : newtime->clock = gf_node_get_scene_time(n) + offset;
541 :
542 : /*insert in sorted order*/
543 0 : count = gf_list_count(times);
544 0 : for (i=0; i<count; i++) {
545 0 : SMIL_Time*t = (SMIL_Time*)gf_list_get(times, i);
546 0 : if ( GF_SMIL_TIME_IS_CLOCK(t->type) ) {
547 0 : if (t->clock > newtime->clock) break;
548 : } else {
549 : break;
550 : }
551 : }
552 0 : gf_list_insert(times, newtime, i);
553 :
554 0 : info.fieldType = SMIL_Times_datatype;
555 0 : gf_node_changed(n, &info);
556 0 : return JS_TRUE;
557 : }
558 :
559 0 : static JSValue svg_udom_smil_begin(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
560 : {
561 0 : return svg_udom_smil_time_insert(c, obj, argc, argv, GF_FALSE);
562 : }
563 0 : static JSValue svg_udom_smil_end(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
564 : {
565 0 : return svg_udom_smil_time_insert(c, obj, argc, argv, GF_TRUE);
566 : }
567 :
568 : /*TODO*/
569 0 : static JSValue svg_udom_smil_pause(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
570 : {
571 : u32 tag;
572 0 : GF_Node *n = dom_get_element(c, obj);
573 0 : if (!n) return JS_EXCEPTION;
574 :
575 0 : tag = gf_node_get_tag(n);
576 0 : if (gf_svg_is_animation_tag(tag)) {
577 : /* pausing an animation element (set, animate ...) should pause the main time line ? */
578 0 : gf_smil_timing_pause(n);
579 0 : } else if (gf_svg_is_timing_tag(tag)) {
580 0 : ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_PAUSE_SVG, n, NULL);
581 0 : } else if ((tag==TAG_SVG_svg) && (n->sgprivate->scenegraph->RootNode==n)) {
582 0 : ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_PAUSE_SVG, n, NULL);
583 : } else {
584 0 : return JS_FALSE;
585 : }
586 0 : return JS_TRUE;
587 : }
588 : /*TODO*/
589 0 : static JSValue svg_udom_smil_resume(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
590 : {
591 : u32 tag;
592 0 : GF_Node *n = dom_get_element(c, obj);
593 0 : if (!n) return JS_EXCEPTION;
594 :
595 0 : tag = gf_node_get_tag(n);
596 0 : if (gf_svg_is_animation_tag(tag)) {
597 : /* resuming an animation element (set, animate ...) should resume the main time line ? */
598 0 : gf_smil_timing_resume(n);
599 0 : } else if (gf_svg_is_timing_tag(tag)) {
600 0 : ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_RESUME_SVG, n, NULL);
601 0 : } else if ((tag==TAG_SVG_svg) && (n->sgprivate->scenegraph->RootNode==n)) {
602 0 : ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_RESUME_SVG, n, NULL);
603 : } else {
604 0 : return JS_FALSE;
605 : }
606 0 : return JS_TRUE;
607 : }
608 :
609 0 : static JSValue svg_udom_smil_restart(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
610 : {
611 : u32 tag;
612 0 : GF_Node *n = dom_get_element(c, obj);
613 0 : if (!n) return JS_EXCEPTION;
614 :
615 0 : tag = gf_node_get_tag(n);
616 0 : if ((tag==TAG_SVG_svg) && (n->sgprivate->scenegraph->RootNode==n)) {
617 0 : ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_RESTART_SVG, n, NULL);
618 : } else {
619 0 : return JS_FALSE;
620 : }
621 0 : return JS_TRUE;
622 : }
623 :
624 0 : static JSValue svg_udom_smil_set_speed(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
625 : {
626 : u32 tag;
627 0 : Double speed = 1.0;
628 0 : GF_Node *n = dom_get_element(c, obj);
629 :
630 0 : if (!n || !argc || JS_ToFloat64(c, &speed, argv[0]) ) {
631 0 : return JS_EXCEPTION;
632 : }
633 0 : tag = gf_node_get_tag(n);
634 0 : if ((tag==TAG_SVG_svg) && (n->sgprivate->scenegraph->RootNode==n)) {
635 : GF_JSAPIParam par;
636 : memset(&par, 0, sizeof(GF_JSAPIParam));
637 0 : par.val = FLT2FIX(speed);
638 0 : ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_SET_SCENE_SPEED, n, &par);
639 : } else {
640 0 : return JS_TRUE;
641 : }
642 0 : return JS_UNDEFINED;
643 : }
644 :
645 0 : 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 0 : GF_Node *n = dom_get_element(c, obj);
653 0 : if (!n) return JS_EXCEPTION;
654 :
655 : //ns = NULL;
656 : name = NULL;
657 0 : if (! JSVAL_CHECK_STRING(argv[0]) ) return JS_TRUE;
658 0 : if (argc==2) {
659 : //ns = JS_ToCString(c, argv[0]);
660 : name = JS_ToCString(c, argv[1]);
661 0 : } else if (argc==1) {
662 : name = JS_ToCString(c, argv[0]);
663 0 : } else return JS_EXCEPTION;
664 :
665 0 : if (!name) {
666 : //JS_FreeCString(c, ns);
667 0 : return JS_EXCEPTION;
668 : }
669 0 : if (!strcmp(name, "#text")) {
670 0 : char *res = gf_dom_flatten_textContent(n);
671 0 : ret = JS_NewString(c, res);
672 0 : gf_free(res);
673 : //JS_FreeCString(c, ns);
674 0 : JS_FreeCString(c, name);
675 0 : return ret;
676 : }
677 0 : e = gf_node_get_field_by_name(n, (char *) name, &info);
678 :
679 : //JS_FreeCString(c, ns);
680 0 : JS_FreeCString(c, name);
681 :
682 0 : if (e!=GF_OK) return JS_EXCEPTION;
683 :
684 0 : switch (info.fieldType) {
685 : /* inheritable floats */
686 0 : 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 0 : attValue = gf_svg_dump_attribute(n, &info);
741 0 : ret = JS_NewString(c, attValue);
742 0 : if (attValue) gf_free(attValue);
743 0 : 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 0 : return JS_NULL;
772 : }
773 :
774 0 : 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 0 : GF_Node *n = dom_get_element(c, obj);
780 0 : if (!n) return JS_EXCEPTION;
781 :
782 0 : if ((argc!=1) || !JS_IsString(argv[0])) return JS_EXCEPTION;
783 : szName = JS_ToCString(c, argv[0]);
784 0 : if (!szName) return JS_EXCEPTION;
785 :
786 0 : e = gf_node_get_attribute_by_name(n, (char *) szName, 0, GF_TRUE, GF_TRUE, &info);
787 0 : JS_FreeCString(c, szName);
788 0 : if (e != GF_OK) return JS_EXCEPTION;
789 :
790 0 : switch (info.fieldType) {
791 : /* inheritable floats */
792 0 : case SVG_Number_datatype:
793 : case SVG_FontSize_datatype:
794 : case SVG_Length_datatype:
795 : case SVG_Coordinate_datatype:
796 : {
797 0 : SVG_Number *l = (SVG_Number *)info.far_ptr;
798 0 : if (l->type==SVG_NUMBER_AUTO || l->type==SVG_NUMBER_INHERIT) return JS_TRUE;
799 0 : return JS_NewFloat64(c, FIX2FLT(l->value));
800 : }
801 0 : case SVG_Clock_datatype:
802 0 : return JS_NewFloat64(c, *(SVG_Clock*)info.far_ptr );
803 0 : default:
804 0 : return JS_NULL;
805 : }
806 : return JS_NULL;
807 : }
808 :
809 0 : 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 0 : GF_Node *n = dom_get_element(c, obj);
815 0 : if (!n) return JS_EXCEPTION;
816 :
817 0 : if ((argc!=1) || !JS_IsString(argv[0])) return JS_EXCEPTION;
818 : szName = JS_ToCString(c, argv[0]);
819 :
820 0 : e = gf_node_get_field_by_name(n, (char *) szName, &info);
821 0 : JS_FreeCString(c, szName);
822 0 : if (e != GF_OK) return JS_EXCEPTION;
823 :
824 0 : if (info.fieldType==SVG_Transform_datatype) {
825 0 : GF_Matrix2D *mx = (GF_Matrix2D *)gf_malloc(sizeof(GF_Matrix2D));
826 0 : if (!mx) return JS_EXCEPTION;
827 0 : JSValue mO = JS_NewObjectClass(c, matrixClass.class_id);
828 0 : gf_mx2d_init(*mx);
829 0 : gf_mx2d_copy(*mx, ((SVG_Transform*)info.far_ptr)->mat);
830 :
831 0 : JS_SetOpaque(mO, mx);
832 0 : return mO;
833 : }
834 0 : return JS_NULL;
835 : }
836 :
837 2 : 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 2 : GF_Node *n = dom_get_element(c, obj);
843 2 : if (!n) return JS_EXCEPTION;
844 :
845 4 : if ((argc!=1) || !JS_IsString(argv[0])) return JS_EXCEPTION;
846 : szName = JS_ToCString(c, argv[0]);
847 :
848 2 : e = gf_node_get_field_by_name(n, (char *) szName, &info);
849 2 : JS_FreeCString(c, szName);
850 2 : if (e != GF_OK) return JS_EXCEPTION;
851 :
852 2 : if (info.fieldType==SVG_ViewBox_datatype) {
853 : JSValue newObj;
854 : rectCI *rc;
855 2 : SVG_ViewBox *v = (SVG_ViewBox *)info.far_ptr;
856 2 : GF_SAFEALLOC(rc, rectCI);
857 2 : if (!rc) {
858 0 : return js_throw_err(c, GF_DOM_EXC_DATA_CLONE_ERR);
859 : }
860 2 : newObj = JS_NewObjectClass(c, rectClass.class_id);
861 2 : rc->x = FIX2FLT(v->x);
862 2 : rc->y = FIX2FLT(v->y);
863 2 : rc->w = FIX2FLT(v->width);
864 2 : rc->h = FIX2FLT(v->height);
865 2 : JS_SetOpaque(newObj, rc);
866 2 : return newObj;
867 : }
868 0 : return JS_NULL;
869 : }
870 :
871 0 : 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 0 : GF_Node *n = dom_get_element(c, obj);
877 0 : if (!n) return JS_EXCEPTION;
878 :
879 0 : if ((argc!=1) || !JS_IsString(argv[0])) return JS_EXCEPTION;
880 : szName = JS_ToCString(c, argv[0]);
881 :
882 0 : e = gf_node_get_field_by_name(n, (char *) szName, &info);
883 0 : JS_FreeCString(c, szName);
884 0 : if (e != GF_OK) return JS_EXCEPTION;
885 :
886 0 : if (info.fieldType==SVG_PathData_datatype) {
887 : return svg_new_path_object(c, (SVG_PathData *)info.far_ptr);
888 : }
889 0 : return JS_NULL;
890 : }
891 :
892 0 : 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 0 : GF_Node *n = dom_get_element(c, obj);
901 0 : if (!n) return JS_EXCEPTION;
902 :
903 0 : if ((argc!=1) || !JS_IsString(argv[0])) return JS_EXCEPTION;
904 : szName = JS_ToCString(c, argv[0]);
905 :
906 0 : e = gf_node_get_field_by_name(n, (char *) szName, &info);
907 0 : JS_FreeCString(c, szName);
908 0 : if (e != GF_OK) return JS_EXCEPTION;
909 :
910 0 : switch (info.fieldType) {
911 0 : case SVG_Color_datatype:
912 : {
913 0 : SVG_Color *col = (SVG_Color *)info.far_ptr;
914 0 : if (col->type == SVG_COLOR_CURRENTCOLOR) return JS_UNDEFINED;
915 0 : if (col->type == SVG_COLOR_INHERIT) return JS_UNDEFINED;
916 :
917 0 : GF_SAFEALLOC(rgb, rgbCI);
918 0 : if (!rgb) {
919 0 : return js_throw_err(c, GF_DOM_EXC_DATA_CLONE_ERR);
920 : }
921 0 : newObj = JS_NewObjectClass(c, rgbClass.class_id);
922 0 : rgb->r = (u8) (255*FIX2FLT(col->red)) ;
923 0 : rgb->g = (u8) (255*FIX2FLT(col->green)) ;
924 0 : rgb->b = (u8) (255*FIX2FLT(col->blue)) ;
925 0 : JS_SetOpaque(newObj, rgb);
926 0 : return newObj;
927 : }
928 : break;
929 0 : case SVG_Paint_datatype:
930 : {
931 0 : SVG_Paint *paint = (SVG_Paint *)info.far_ptr;
932 : if ((1) || paint->type==SVG_PAINT_COLOR) {
933 0 : GF_SAFEALLOC(rgb, rgbCI);
934 0 : if (!rgb) {
935 0 : return js_throw_err(c, GF_DOM_EXC_DATA_CLONE_ERR);
936 : }
937 0 : newObj = JS_NewObjectClass(c, rgbClass.class_id);
938 0 : rgb->r = (u8) (255*FIX2FLT(paint->color.red) );
939 0 : rgb->g = (u8) (255*FIX2FLT(paint->color.green) );
940 0 : rgb->b = (u8) (255*FIX2FLT(paint->color.blue) );
941 0 : JS_SetOpaque(newObj, rgb);
942 0 : return newObj;
943 : }
944 : return JS_TRUE;
945 : }
946 : }
947 0 : return JS_NULL;
948 : }
949 :
950 0 : 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 0 : GF_Node *n = dom_get_element(c, obj);
956 0 : if (!n) return JS_EXCEPTION;
957 :
958 : val = ns = name = NULL;
959 0 : if (!JSVAL_CHECK_STRING(argv[0])) return JS_EXCEPTION;
960 0 : if (argc==3) {
961 0 : if (!JSVAL_CHECK_STRING(argv[1])) return JS_EXCEPTION;
962 0 : 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 0 : } else if (argc==2) {
967 : name = JS_ToCString(c, argv[0]);
968 : val = JS_ToCString(c, argv[1]);
969 : } else {
970 0 : return JS_EXCEPTION;
971 : }
972 0 : if (!name) {
973 0 : JS_FreeCString(c, ns);
974 0 : JS_FreeCString(c, val);
975 0 : return JS_EXCEPTION;
976 : }
977 0 : if (!strcmp(name, "#text")) {
978 0 : if (val) dom_node_set_textContent(n, (char *) val);
979 0 : JS_FreeCString(c, ns);
980 0 : JS_FreeCString(c, name);
981 0 : JS_FreeCString(c, val);
982 0 : return JS_UNDEFINED;
983 : }
984 0 : e = gf_node_get_field_by_name(n, (char *) name, &info);
985 0 : JS_FreeCString(c, ns);
986 0 : JS_FreeCString(c, name);
987 :
988 0 : if (!val || (e!=GF_OK)) {
989 0 : JS_FreeCString(c, val);
990 0 : return JS_EXCEPTION;
991 : }
992 0 : e = gf_svg_parse_attribute(n, &info, (char *) val, 0);
993 0 : JS_FreeCString(c, val);
994 :
995 0 : if (e) return js_throw_err(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
996 0 : dom_node_changed(n, GF_FALSE, &info);
997 0 : return JS_UNDEFINED;
998 : }
999 :
1000 0 : 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 0 : GF_Node *n = dom_get_element(c, obj);
1008 0 : if (!n) return JS_EXCEPTION;
1009 0 : if (argc!=2) return JS_EXCEPTION;
1010 0 : if (!JS_IsString(argv[0])) return JS_EXCEPTION;
1011 0 : if (!JS_IsNumber(argv[1]) || JS_ToFloat64(c, &d, argv[1])) return JS_EXCEPTION;
1012 :
1013 : szName = JS_ToCString(c, argv[0]);
1014 0 : e = gf_node_get_field_by_name(n, (char *) szName, &info);
1015 0 : JS_FreeCString(c, szName);
1016 0 : if (e != GF_OK) return JS_EXCEPTION;
1017 :
1018 0 : switch (info.fieldType) {
1019 : /* inheritable floats */
1020 0 : case SVG_FontSize_datatype:
1021 : case SVG_Length_datatype:
1022 : case SVG_Coordinate_datatype:
1023 : case SVG_Number_datatype:
1024 : {
1025 0 : SVG_Number *l = (SVG_Number *)info.far_ptr;
1026 0 : l->type=SVG_NUMBER_VALUE;
1027 0 : l->value = FLT2FIX(d);
1028 0 : break;
1029 : }
1030 0 : case SVG_Numbers_datatype:
1031 : case SVG_Coordinates_datatype:
1032 : {
1033 : SVG_Number *val;
1034 0 : SVG_Coordinates *l = (SVG_Coordinates *)info.far_ptr;
1035 0 : while (gf_list_count(*l)) {
1036 0 : val = (SVG_Number *)gf_list_get(*l, 0);
1037 0 : gf_list_rem(*l, 0);
1038 0 : if (val) gf_free(val);
1039 : }
1040 0 : GF_SAFEALLOC(val, SVG_Coordinate);
1041 0 : if (!val) {
1042 0 : return js_throw_err(c, GF_DOM_EXC_DATA_CLONE_ERR);
1043 : }
1044 0 : val->type=SVG_NUMBER_VALUE;
1045 0 : val->value = FLT2FIX(d);
1046 0 : gf_list_add(*l, val);
1047 0 : break;
1048 : }
1049 0 : default:
1050 0 : return JS_FALSE;
1051 : }
1052 0 : dom_node_changed(n, GF_FALSE, &info);
1053 0 : return JS_TRUE;
1054 : }
1055 :
1056 0 : 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 0 : GF_Node *n = dom_get_element(c, obj);
1064 0 : if (!n) return JS_TRUE;
1065 :
1066 0 : if (argc!=2) return JS_EXCEPTION;
1067 0 : if (!JS_IsString(argv[0])) return JS_EXCEPTION;
1068 0 : if (JS_IsNull(argv[1]) || !JS_IsObject(argv[1])) return JS_EXCEPTION;
1069 :
1070 0 : mx = JS_GetOpaque(argv[1], matrixClass.class_id);
1071 0 : if (!mx) return JS_EXCEPTION;
1072 :
1073 : szName = JS_ToCString(c, argv[0]);
1074 0 : e = gf_node_get_field_by_name(n, (char *) szName, &info);
1075 0 : JS_FreeCString(c, szName);
1076 0 : if (e != GF_OK) return JS_EXCEPTION;
1077 :
1078 0 : if (info.fieldType==SVG_Transform_datatype) {
1079 0 : gf_mx2d_copy(((SVG_Transform*)info.far_ptr)->mat, *mx);
1080 0 : dom_node_changed(n, GF_FALSE, NULL);
1081 0 : return JS_TRUE;
1082 : }
1083 0 : return JS_FALSE;
1084 : }
1085 :
1086 0 : 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 0 : GF_Node *n = dom_get_element(c, obj);
1093 0 : if (!n) return JS_EXCEPTION;
1094 :
1095 0 : if (argc!=2) return JS_EXCEPTION;
1096 0 : if (!JS_IsString(argv[0])) return JS_TRUE;
1097 0 : if (JS_IsNull(argv[1]) || !JS_IsObject(argv[1])) return JS_EXCEPTION;
1098 :
1099 0 : rc = JS_GetOpaque(argv[1], rectClass.class_id);
1100 0 : if (!rc) return JS_EXCEPTION;
1101 :
1102 : szName = JS_ToCString(c, argv[0]);
1103 0 : e = gf_node_get_field_by_name(n, (char *) szName, &info);
1104 0 : JS_FreeCString(c, szName);
1105 0 : if (e != GF_OK) return JS_EXCEPTION;
1106 :
1107 0 : if (info.fieldType==SVG_ViewBox_datatype) {
1108 0 : SVG_ViewBox *v = (SVG_ViewBox *)info.far_ptr;
1109 0 : v->x = FLT2FIX(rc->x);
1110 0 : v->y = FLT2FIX(rc->y);
1111 0 : v->width = FLT2FIX(rc->w);
1112 0 : v->height = FLT2FIX(rc->h);
1113 0 : dom_node_changed(n, GF_FALSE, NULL);
1114 0 : return JS_TRUE;
1115 : }
1116 0 : return JS_FALSE;
1117 : }
1118 :
1119 0 : 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 0 : GF_Node *n = dom_get_element(c, obj);
1126 0 : if (!n) return JS_EXCEPTION;
1127 :
1128 0 : if (argc!=2) return JS_EXCEPTION;
1129 0 : if (!JS_IsString(argv[0])) return JS_EXCEPTION;
1130 0 : if (JS_IsNull(argv[1]) || !JS_IsObject(argv[1])) return JS_EXCEPTION;
1131 0 : path = JS_GetOpaque( argv[1], pathClass.class_id);
1132 0 : if (!path) return JS_EXCEPTION;
1133 :
1134 : szName = JS_ToCString(c, argv[0]);
1135 0 : e = gf_node_get_field_by_name(n, (char *) szName, &info);
1136 0 : JS_FreeCString(c, szName);
1137 0 : 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 0 : return JS_FALSE;
1184 : }
1185 :
1186 0 : 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 0 : GF_Node *n = dom_get_element(c, obj);
1193 0 : if (!n) return JS_EXCEPTION;
1194 :
1195 0 : if (argc!=2) return JS_EXCEPTION;
1196 0 : if (!JS_IsString(argv[0])) return JS_EXCEPTION;
1197 0 : if (!JS_IsObject(argv[1])) return JS_EXCEPTION;
1198 0 : rgb = JS_GetOpaque(argv[1], rgbClass.class_id);
1199 0 : if (!rgb) return JS_EXCEPTION;
1200 :
1201 : szName = JS_ToCString(c, argv[0]);
1202 0 : e = gf_node_get_field_by_name(n, (char *) szName, &info);
1203 0 : JS_FreeCString(c, szName);
1204 0 : if (e != GF_OK) return JS_EXCEPTION;
1205 :
1206 0 : switch (info.fieldType) {
1207 0 : case SVG_Color_datatype:
1208 : {
1209 0 : SVG_Color *col = (SVG_Color *)info.far_ptr;
1210 0 : col->type = SVG_COLOR_RGBCOLOR;
1211 0 : col->red = FLT2FIX(rgb->r / 255.0f);
1212 0 : col->green = FLT2FIX(rgb->g / 255.0f);
1213 0 : col->blue = FLT2FIX(rgb->b / 255.0f);
1214 0 : dom_node_changed(n, GF_FALSE, &info);
1215 0 : return JS_TRUE;
1216 : }
1217 0 : case SVG_Paint_datatype:
1218 : {
1219 0 : SVG_Paint *paint = (SVG_Paint *)info.far_ptr;
1220 0 : paint->type = SVG_PAINT_COLOR;
1221 0 : paint->color.type = SVG_COLOR_RGBCOLOR;
1222 0 : paint->color.red = FLT2FIX(rgb->r / 255.0f);
1223 0 : paint->color.green = FLT2FIX(rgb->g / 255.0f);
1224 0 : paint->color.blue = FLT2FIX(rgb->b / 255.0f);
1225 0 : dom_node_changed(n, GF_FALSE, &info);
1226 0 : return JS_TRUE;
1227 : }
1228 : }
1229 0 : return JS_FALSE;
1230 : }
1231 :
1232 0 : static JSValue svg_get_bbox(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv, Bool get_screen)
1233 : {
1234 : GF_JSAPIParam par;
1235 0 : GF_Node *n = dom_get_element(c, obj);
1236 0 : if (!n || argc) return JS_EXCEPTION;
1237 :
1238 0 : par.bbox.is_set = GF_FALSE;
1239 0 : if (ScriptAction(n->sgprivate->scenegraph, get_screen ? GF_JSAPI_OP_GET_SCREEN_BBOX : GF_JSAPI_OP_GET_LOCAL_BBOX, (GF_Node *)n, &par) ) {
1240 0 : if (par.bbox.is_set) {
1241 0 : rectCI *rc = (rectCI *)gf_malloc(sizeof(rectCI));
1242 0 : if (!rc) return JS_EXCEPTION;
1243 0 : JSValue rO = JS_NewObjectClass(c, rectClass.class_id);
1244 0 : rc->sg = NULL;
1245 0 : rc->x = FIX2FLT(par.bbox.min_edge.x);
1246 : /*BBox is in 3D coord system style*/
1247 0 : rc->y = FIX2FLT(par.bbox.min_edge.y);
1248 0 : rc->w = FIX2FLT(par.bbox.max_edge.x - par.bbox.min_edge.x);
1249 0 : rc->h = FIX2FLT(par.bbox.max_edge.y - par.bbox.min_edge.y);
1250 0 : JS_SetOpaque(rO, rc);
1251 0 : return rO;
1252 : } else {
1253 0 : return JS_NULL;
1254 : }
1255 : return JS_TRUE;
1256 : }
1257 0 : return JS_FALSE;
1258 : }
1259 :
1260 0 : static JSValue svg_udom_get_local_bbox(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1261 : {
1262 0 : return svg_get_bbox(c, obj, argc, argv, GF_FALSE);
1263 : }
1264 0 : static JSValue svg_udom_get_screen_bbox(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1265 : {
1266 0 : return svg_get_bbox(c, obj, argc, argv, GF_TRUE);
1267 : }
1268 :
1269 0 : static JSValue svg_udom_get_screen_ctm(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1270 : {
1271 : GF_JSAPIParam par;
1272 0 : GF_Node *n = dom_get_element(c, obj);
1273 0 : if (!n || argc) return JS_EXCEPTION;
1274 :
1275 0 : if (ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_GET_TRANSFORM, (GF_Node *)n, &par)) {
1276 0 : GF_Matrix2D *mx = (GF_Matrix2D *)gf_malloc(sizeof(GF_Matrix2D));
1277 0 : if (!mx) return JS_EXCEPTION;
1278 0 : JSValue mO = JS_NewObjectClass(c, matrixClass.class_id);
1279 0 : gf_mx2d_from_mx(mx, &par.mx);
1280 0 : JS_SetOpaque(mO, mx);
1281 0 : return mO;
1282 : }
1283 0 : return JS_EXCEPTION;
1284 : }
1285 :
1286 0 : 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 0 : GF_Node *n = dom_get_element(c, obj);
1292 0 : if (!n) return JS_EXCEPTION;
1293 0 : if (argc!=6) return JS_EXCEPTION;
1294 :
1295 0 : GF_SAFEALLOC(mx, GF_Matrix2D)
1296 0 : if (!mx) return js_throw_err(c, GF_DOM_EXC_DATA_CLONE_ERR);
1297 :
1298 0 : JS_ToFloat64(c, &v, argv[0]);
1299 0 : mx->m[0] = FLT2FIX(v);
1300 0 : JS_ToFloat64(c, &v, argv[1]);
1301 0 : mx->m[3] = FLT2FIX(v);
1302 0 : JS_ToFloat64(c, &v, argv[2]);
1303 0 : mx->m[1] = FLT2FIX(v);
1304 0 : JS_ToFloat64(c, &v, argv[3]);
1305 0 : mx->m[4] = FLT2FIX(v);
1306 0 : JS_ToFloat64(c, &v, argv[4]);
1307 0 : mx->m[2] = FLT2FIX(v);
1308 0 : JS_ToFloat64(c, &v, argv[5]);
1309 0 : mx->m[5] = FLT2FIX(v);
1310 0 : mat = JS_NewObjectClass(c, matrixClass.class_id);
1311 0 : JS_SetOpaque(mat, mx);
1312 0 : return mat;
1313 : }
1314 :
1315 0 : static JSValue svg_udom_create_rect(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1316 : {
1317 : rectCI *rc;
1318 : JSValue r;
1319 0 : GF_Node *n = dom_get_element(c, obj);
1320 0 : if (!n || argc) return JS_EXCEPTION;
1321 :
1322 0 : GF_SAFEALLOC(rc, rectCI);
1323 0 : if (!rc) return js_throw_err(c, GF_DOM_EXC_DATA_CLONE_ERR);
1324 0 : r = JS_NewObjectClass(c, rectClass.class_id);
1325 0 : JS_SetOpaque(r, rc);
1326 0 : return r;
1327 : }
1328 :
1329 0 : static JSValue svg_udom_create_point(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1330 : {
1331 : pointCI *pt;
1332 : JSValue r;
1333 0 : GF_Node *n = dom_get_element(c, obj);
1334 0 : if (!n || argc) return JS_EXCEPTION;
1335 :
1336 0 : GF_SAFEALLOC(pt, pointCI);
1337 0 : if (!pt) return js_throw_err(c, GF_DOM_EXC_DATA_CLONE_ERR);
1338 0 : r = JS_NewObjectClass(c, pointClass.class_id);
1339 0 : JS_SetOpaque(r, pt);
1340 0 : return r;
1341 : }
1342 :
1343 0 : static JSValue svg_udom_create_path(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1344 : {
1345 : pathCI *path;
1346 : JSValue p;
1347 0 : GF_Node *n = dom_get_element(c, obj);
1348 0 : if (!n || argc) return JS_EXCEPTION;
1349 :
1350 0 : GF_SAFEALLOC(path, pathCI);
1351 0 : if (!path) return js_throw_err(c, GF_DOM_EXC_DATA_CLONE_ERR);
1352 0 : p = JS_NewObjectClass(c, pathClass.class_id);
1353 0 : JS_SetOpaque(p, path);
1354 0 : return p;
1355 : }
1356 :
1357 0 : static JSValue svg_udom_create_color(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1358 : {
1359 : rgbCI *col;
1360 : JSValue p;
1361 0 : GF_Node *n = dom_get_element(c, obj);
1362 0 : if (!n|| (argc!=3)) return JS_EXCEPTION;
1363 :
1364 0 : GF_SAFEALLOC(col, rgbCI);
1365 0 : if (!col) return js_throw_err(c, GF_DOM_EXC_DATA_CLONE_ERR);
1366 :
1367 0 : JS_ToInt32(c, &col->r, argv[0]);
1368 0 : JS_ToInt32(c, &col->g, argv[1]);
1369 0 : JS_ToInt32(c, &col->b, argv[2]);
1370 0 : p = JS_NewObjectClass(c, rgbClass.class_id);
1371 0 : JS_SetOpaque(p, col);
1372 0 : return p;
1373 : }
1374 :
1375 0 : 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 0 : GF_Node *n = (GF_Node *)dom_get_element(c, obj);
1381 0 : if (!n) return JS_EXCEPTION;
1382 0 : if (n->sgprivate->tag != TAG_SVG_path) return JS_EXCEPTION;
1383 :
1384 0 : gf_node_get_field_by_name(n, "d", &info);
1385 0 : if (info.fieldType == SVG_PathData_datatype) {
1386 : #if USE_GF_PATH
1387 0 : GF_Path *p = (GF_Path *)info.far_ptr;
1388 0 : GF_PathIterator *path_it = gf_path_iterator_new(p);
1389 0 : if (path_it) {
1390 0 : Fixed len = gf_path_iterator_get_length(path_it);
1391 0 : length = FIX2FLT(len);
1392 0 : gf_path_iterator_del(path_it);
1393 : }
1394 : #endif
1395 : }
1396 : return JS_NewFloat64(c, length);
1397 : }
1398 :
1399 0 : static JSValue svg_udom_move_focus(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1400 : {
1401 : GF_JSAPIParam par;
1402 0 : GF_Node *n = dom_get_element(c, obj);
1403 0 : if (!n) return JS_EXCEPTION;
1404 0 : if ((argc!=1) || !JS_IsObject(argv[0])) return JS_EXCEPTION;
1405 :
1406 0 : JS_ToInt32(c, &par.opt, argv[1]);
1407 0 : if (ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_SET_FOCUS, (GF_Node *)n, &par))
1408 0 : return JS_TRUE;
1409 0 : return JS_FALSE;
1410 : }
1411 :
1412 0 : static JSValue svg_udom_set_focus(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1413 : {
1414 : GF_JSAPIParam par;
1415 0 : GF_Node *n = dom_get_element(c, obj);
1416 0 : if (!n) return JS_EXCEPTION;
1417 0 : if ((argc!=1) || !JS_IsObject(argv[0])) return JS_EXCEPTION;
1418 :
1419 0 : par.node = dom_get_element(c, argv[0]);
1420 : /*NOT IN THE GRAPH*/
1421 0 : if (!par.node || !par.node->sgprivate->num_instances) return JS_EXCEPTION;
1422 0 : if (ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_SET_FOCUS, (GF_Node *)n, &par))
1423 0 : return JS_TRUE;
1424 0 : return JS_TRUE;
1425 : }
1426 :
1427 0 : static JSValue svg_udom_get_focus(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1428 : {
1429 : GF_JSAPIParam par;
1430 0 : GF_Node *n = dom_get_element(c, obj);
1431 0 : if (!n || argc) return JS_EXCEPTION;
1432 :
1433 0 : if (!ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_GET_FOCUS, (GF_Node *)n, &par))
1434 0 : return JS_EXCEPTION;
1435 :
1436 0 : if (par.node) {
1437 0 : return dom_element_construct(c, par.node);
1438 : }
1439 0 : return JS_NULL;
1440 : }
1441 :
1442 0 : static JSValue svg_udom_get_time(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1443 : {
1444 0 : GF_Node *n = dom_get_element(c, obj);
1445 0 : if (!n) return JS_EXCEPTION;
1446 :
1447 0 : return JS_NewFloat64(c, gf_node_get_scene_time(n) );
1448 : }
1449 :
1450 :
1451 0 : static JSValue svg_connection_create(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1452 : {
1453 0 : return js_throw_err(c, GF_DOM_EXC_NOT_SUPPORTED_ERR);
1454 : }
1455 :
1456 14 : static void baseCI_finalize(JSRuntime *rt, JSValue obj)
1457 : {
1458 : /*avoids GCC warning*/
1459 14 : void *data = JS_GetOpaque_Nocheck(obj);
1460 14 : if (data) gf_free(data);
1461 14 : }
1462 :
1463 0 : static JSValue rgb_getProperty(JSContext *c, JSValueConst obj, int magic)
1464 : {
1465 0 : rgbCI *col = (rgbCI *) JS_GetOpaque(obj, rgbClass.class_id);
1466 0 : if (!col) return JS_EXCEPTION;
1467 0 : switch (magic) {
1468 0 : case 0: return JS_NewInt32(c, col->r);
1469 0 : case 1: return JS_NewInt32(c, col->g);
1470 0 : case 2: return JS_NewInt32(c, col->b);
1471 0 : default:
1472 0 : return JS_EXCEPTION;
1473 : }
1474 : }
1475 0 : static JSValue rgb_setProperty(JSContext *c, JSValueConst obj, JSValueConst value, int magic)
1476 : {
1477 0 : rgbCI *col = (rgbCI *) JS_GetOpaque(obj, rgbClass.class_id);
1478 0 : if (!col) return JS_EXCEPTION;
1479 :
1480 0 : switch (magic) {
1481 0 : case 0: return JS_ToInt32(c, &col->r, value) ? JS_EXCEPTION : JS_TRUE;
1482 0 : case 1: return JS_ToInt32(c, &col->g, value) ? JS_EXCEPTION : JS_TRUE;
1483 0 : case 2: return JS_ToInt32(c, &col->b, value) ? JS_EXCEPTION : JS_TRUE;
1484 0 : default:
1485 0 : return JS_EXCEPTION;
1486 : }
1487 : }
1488 :
1489 4 : static JSValue rect_getProperty(JSContext *c, JSValueConst obj, int magic)
1490 : {
1491 4 : rectCI *rc = (rectCI *) JS_GetOpaque(obj, rectClass.class_id);
1492 4 : if (!rc) return JS_EXCEPTION;
1493 4 : if (rc->sg) {
1494 : GF_JSAPIParam par;
1495 0 : if (!ScriptAction(rc->sg, GF_JSAPI_OP_GET_VIEWPORT, rc->sg->RootNode, &par)) {
1496 0 : return JS_EXCEPTION;
1497 : }
1498 0 : rc->x = FIX2FLT(par.rc.x);
1499 0 : rc->y = FIX2FLT(par.rc.y);
1500 0 : rc->w = FIX2FLT(par.rc.width);
1501 0 : rc->h = FIX2FLT(par.rc.height);
1502 : }
1503 4 : switch (magic) {
1504 0 : case 0: return JS_NewFloat64(c, rc->x);
1505 0 : case 1: return JS_NewFloat64(c, rc->y);
1506 2 : case 2: return JS_NewFloat64(c, rc->w);
1507 2 : case 3: return JS_NewFloat64(c, rc->h);
1508 0 : default:
1509 0 : return JS_EXCEPTION;
1510 : }
1511 : }
1512 :
1513 0 : static JSValue rect_setProperty(JSContext *c, JSValueConst obj, JSValueConst value, int magic)
1514 : {
1515 : Double d;
1516 0 : rectCI *rc = (rectCI *) JS_GetOpaque(obj, rectClass.class_id);
1517 0 : if (!rc) return JS_EXCEPTION;
1518 0 : if (JS_ToFloat64(c, &d, value)) return JS_EXCEPTION;
1519 0 : switch (magic) {
1520 0 : case 0:
1521 0 : rc->x = (Float) d;
1522 0 : return JS_TRUE;
1523 0 : case 1:
1524 0 : rc->y = (Float) d;
1525 0 : return JS_TRUE;
1526 0 : case 2:
1527 0 : rc->w = (Float) d;
1528 0 : return JS_TRUE;
1529 0 : case 3:
1530 0 : rc->h = (Float) d;
1531 0 : return JS_TRUE;
1532 0 : default:
1533 0 : return JS_EXCEPTION;
1534 : }
1535 : }
1536 :
1537 0 : static JSValue point_getProperty(JSContext *c, JSValueConst obj, int magic)
1538 : {
1539 0 : pointCI *pt = (pointCI *) JS_GetOpaque(obj, pointClass.class_id);
1540 0 : if (!pt) return JS_EXCEPTION;
1541 :
1542 0 : if (pt->sg) {
1543 : GF_JSAPIParam par;
1544 0 : if (!ScriptAction(pt->sg, GF_JSAPI_OP_GET_TRANSLATE, pt->sg->RootNode, &par)) {
1545 0 : return JS_EXCEPTION;
1546 : }
1547 0 : pt->x = FIX2FLT(par.pt.x);
1548 0 : pt->y = FIX2FLT(par.pt.y);
1549 : }
1550 0 : switch (magic) {
1551 0 : case 0: return JS_NewFloat64(c, pt->x);
1552 0 : case 1: return JS_NewFloat64(c, pt->y);
1553 0 : default: return JS_EXCEPTION;
1554 : }
1555 : }
1556 :
1557 0 : static JSValue point_setProperty(JSContext *c, JSValueConst obj, JSValueConst value, int magic)
1558 : {
1559 0 : pointCI *pt = (pointCI *) JS_GetOpaque(obj, pointClass.class_id);
1560 0 : if (!pt) return JS_EXCEPTION;
1561 :
1562 : Double d;
1563 0 : if (JS_ToFloat64(c, &d, value)) return JS_EXCEPTION;
1564 0 : switch (magic) {
1565 0 : case 0:
1566 0 : pt->x = (Float) d;
1567 0 : break;
1568 0 : case 1:
1569 0 : pt->y = (Float) d;
1570 0 : break;
1571 0 : default:
1572 0 : return JS_EXCEPTION;
1573 : }
1574 0 : if (pt->sg) {
1575 : GF_JSAPIParam par;
1576 0 : par.pt.x = FLT2FIX(pt->x);
1577 0 : par.pt.y = FLT2FIX(pt->y);
1578 0 : ScriptAction(pt->sg, GF_JSAPI_OP_SET_TRANSLATE, pt->sg->RootNode, &par);
1579 : }
1580 0 : return JS_UNDEFINED;
1581 : }
1582 :
1583 : static JSValue svg_new_path_object(JSContext *c, SVG_PathData *d)
1584 : {
1585 : #if USE_GF_PATH
1586 0 : 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 :
1611 3 : static void pathCI_finalize(JSRuntime *rt, JSValue obj)
1612 : {
1613 3 : pathCI *p = (pathCI *) JS_GetOpaque(obj, pathClass.class_id);
1614 3 : if (p) {
1615 0 : if (p->pts) gf_free(p->pts);
1616 0 : if (p->tags) gf_free(p->tags);
1617 0 : gf_free(p);
1618 : }
1619 3 : }
1620 :
1621 0 : static JSValue path_getProperty(JSContext *c, JSValueConst obj, int magic)
1622 : {
1623 0 : pathCI *p = (pathCI *) JS_GetOpaque(obj, pathClass.class_id);
1624 0 : if (!p) return JS_EXCEPTION;
1625 0 : switch (magic) {
1626 0 : case 0: return JS_NewInt32(c, p->nb_coms);
1627 0 : default:
1628 0 : return JS_EXCEPTION;
1629 : }
1630 : }
1631 :
1632 0 : static JSValue svg_path_get_segment(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1633 : {
1634 : u32 idx;
1635 0 : pathCI *p = (pathCI *) JS_GetOpaque(obj, pathClass.class_id);
1636 0 : if (!p) return JS_EXCEPTION;
1637 0 : if ((argc!=1) || JS_ToInt32(c, &idx, argv[0])) return JS_EXCEPTION;
1638 0 : if (idx>=p->nb_coms) return JS_TRUE;
1639 0 : 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 0 : return JS_EXCEPTION;
1652 : }
1653 :
1654 0 : 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 0 : pathCI *p = (pathCI *) JS_GetOpaque(obj, pathClass.class_id);
1659 0 : if (!p) return JS_EXCEPTION;
1660 :
1661 0 : if ((argc!=2) || !JS_IsInteger(argv[0]) || !JS_IsInteger(argv[1])) return JS_EXCEPTION;
1662 0 : if (JS_ToInt32(c, &idx, argv[0])) return JS_EXCEPTION;
1663 0 : if (JS_ToInt32(c, ¶m_idx, argv[1])) return JS_EXCEPTION;
1664 0 : if (idx>=p->nb_coms) return JS_TRUE;
1665 : pt_idx = 0;
1666 0 : for (i=0; i<idx; i++) {
1667 0 : switch (p->tags[i]) {
1668 0 : case 0:
1669 0 : pt_idx++;
1670 0 : break;
1671 0 : case 1:
1672 0 : pt_idx++;
1673 0 : break;
1674 0 : case 2:
1675 0 : pt_idx+=3;
1676 0 : break;
1677 0 : case 3:
1678 0 : pt_idx+=2;
1679 0 : break;
1680 0 : case 4:
1681 0 : pt_idx+=2;
1682 0 : break;
1683 0 : case 5:
1684 0 : pt_idx+=1;
1685 0 : break;
1686 : }
1687 : }
1688 0 : switch (p->tags[idx]) {
1689 0 : case 0:
1690 : case 1:
1691 0 : if (param_idx>1) return JS_TRUE;
1692 0 : pt = &p->pts[pt_idx];
1693 0 : return JS_NewFloat64(c, param_idx ? pt->y : pt->x);
1694 0 : case 2:/* Curve To */
1695 0 : if (param_idx>5) return JS_TRUE;
1696 0 : pt = &p->pts[pt_idx + (param_idx/2) ];
1697 0 : return JS_NewFloat64(c, (param_idx%2) ? pt->y : pt->x);
1698 0 : case 3:/* Next Curve To */
1699 0 : if (param_idx>5) return JS_TRUE;
1700 0 : if (param_idx<2) {
1701 0 : pt = &p->pts[pt_idx - 1];
1702 0 : return JS_NewFloat64(c, param_idx ? pt->y : pt->x);
1703 : }
1704 0 : param_idx-=2;
1705 0 : pt = &p->pts[pt_idx + (param_idx/2)];
1706 0 : return JS_NewFloat64(c, (param_idx%2) ? pt->y : pt->x);
1707 :
1708 0 : case 4:/* Quad To */
1709 0 : if (param_idx>3) return JS_TRUE;
1710 0 : pt = &p->pts[pt_idx + (param_idx/2) ];
1711 0 : return JS_NewFloat64(c, (param_idx%2) ? pt->y : pt->x);
1712 :
1713 0 : case 5:/* Next Quad To */
1714 0 : if (param_idx>3) return JS_TRUE;
1715 0 : if (param_idx<2) {
1716 0 : pt = &p->pts[pt_idx - 1];
1717 0 : return JS_NewFloat64(c, param_idx ? pt->y : pt->x);
1718 : } else {
1719 0 : param_idx-=2;
1720 0 : pt = &p->pts[pt_idx];
1721 0 : 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 0 : return JS_EXCEPTION;
1729 : }
1730 :
1731 0 : static u32 svg_path_realloc_pts(pathCI *p, u32 nb_pts)
1732 : {
1733 : u32 i, orig_pts;
1734 : orig_pts = 0;
1735 0 : for (i=0; i<p->nb_coms; i++) {
1736 0 : switch (p->tags[i]) {
1737 0 : case 0:
1738 0 : orig_pts++;
1739 0 : break;
1740 0 : case 1:
1741 0 : orig_pts++;
1742 0 : break;
1743 0 : case 2:
1744 0 : orig_pts+=3;
1745 0 : break;
1746 0 : case 3:
1747 0 : orig_pts+=2;
1748 0 : break;
1749 0 : case 4:
1750 0 : orig_pts+=2;
1751 0 : break;
1752 0 : case 5:
1753 0 : orig_pts+=1;
1754 0 : break;
1755 : }
1756 : }
1757 0 : p->pts = (ptCI *)gf_realloc(p->pts, sizeof(ptCI)*(nb_pts+orig_pts));
1758 0 : return orig_pts;
1759 : }
1760 :
1761 0 : 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 0 : p = (pathCI *) JS_GetOpaque(obj, pathClass.class_id);
1768 0 : if (!p || (argc!=2)) return JS_EXCEPTION;
1769 :
1770 0 : if (JS_ToFloat64(c, &x, argv[0])) return JS_EXCEPTION;
1771 0 : if (JS_ToFloat64(c, &y, argv[1])) return JS_EXCEPTION;
1772 0 : nb_pts = svg_path_realloc_pts(p, 1);
1773 0 : p->pts[nb_pts].x = (Float) x;
1774 0 : p->pts[nb_pts].y = (Float) y;
1775 0 : p->tags = (u8 *)gf_realloc(p->tags, sizeof(u8)*(p->nb_coms+1) );
1776 0 : p->tags[p->nb_coms] = 0;
1777 0 : p->nb_coms++;
1778 0 : return JS_TRUE;
1779 : }
1780 :
1781 0 : 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 0 : p = (pathCI *) JS_GetOpaque(obj, pathClass.class_id);
1787 0 : if (!p || (argc!=2)) return JS_EXCEPTION;
1788 :
1789 0 : if (JS_ToFloat64(c, &x, argv[0])) return JS_EXCEPTION;
1790 0 : if (JS_ToFloat64(c, &y, argv[1])) return JS_EXCEPTION;
1791 :
1792 0 : nb_pts = svg_path_realloc_pts(p, 1);
1793 0 : p->pts[nb_pts].x = (Float) x;
1794 0 : p->pts[nb_pts].y = (Float) y;
1795 0 : p->tags = (u8 *)gf_realloc(p->tags, sizeof(u8)*(p->nb_coms+1) );
1796 0 : p->tags[p->nb_coms] = 1;
1797 0 : p->nb_coms++;
1798 0 : return JS_TRUE;
1799 : }
1800 :
1801 0 : 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 0 : p = (pathCI *) JS_GetOpaque(obj, pathClass.class_id);
1808 0 : if (!p || (argc!=4)) return JS_EXCEPTION;
1809 :
1810 0 : if (JS_ToFloat64(c, &x1, argv[0])) return JS_EXCEPTION;
1811 0 : if (JS_ToFloat64(c, &y1, argv[1])) return JS_EXCEPTION;
1812 0 : if (JS_ToFloat64(c, &x2, argv[2])) return JS_EXCEPTION;
1813 0 : if (JS_ToFloat64(c, &y2, argv[3])) return JS_EXCEPTION;
1814 0 : nb_pts = svg_path_realloc_pts(p, 2);
1815 0 : p->pts[nb_pts].x = (Float) x1;
1816 0 : p->pts[nb_pts].y = (Float) y1;
1817 0 : p->pts[nb_pts+1].x = (Float) x2;
1818 0 : p->pts[nb_pts+1].y = (Float) y2;
1819 0 : p->tags = (u8 *)gf_realloc(p->tags, sizeof(u8)*(p->nb_coms+1) );
1820 0 : p->tags[p->nb_coms] = 4;
1821 0 : p->nb_coms++;
1822 0 : return JS_TRUE;
1823 : }
1824 :
1825 0 : 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 0 : p = (pathCI *) JS_GetOpaque(obj, pathClass.class_id);
1832 0 : if (!p || (argc!=6)) return JS_EXCEPTION;
1833 :
1834 0 : if (JS_ToFloat64(c, &x1, argv[0])) return JS_EXCEPTION;
1835 0 : if (JS_ToFloat64(c, &y1, argv[1])) return JS_EXCEPTION;
1836 0 : if (JS_ToFloat64(c, &x2, argv[2])) return JS_EXCEPTION;
1837 0 : if (JS_ToFloat64(c, &y2, argv[3])) return JS_EXCEPTION;
1838 0 : if (JS_ToFloat64(c, &x, argv[4])) return JS_EXCEPTION;
1839 0 : if (JS_ToFloat64(c, &y, argv[5])) return JS_EXCEPTION;
1840 0 : nb_pts = svg_path_realloc_pts(p, 3);
1841 0 : p->pts[nb_pts].x = (Float) x1;
1842 0 : p->pts[nb_pts].y = (Float) y1;
1843 0 : p->pts[nb_pts+1].x = (Float) x2;
1844 0 : p->pts[nb_pts+1].y = (Float) y2;
1845 0 : p->pts[nb_pts+2].x = (Float) x;
1846 0 : p->pts[nb_pts+2].y = (Float) y;
1847 0 : p->tags = (u8 *)gf_realloc(p->tags, sizeof(u8)*(p->nb_coms+1) );
1848 0 : p->tags[p->nb_coms] = 2;
1849 0 : p->nb_coms++;
1850 0 : return JS_TRUE;
1851 : }
1852 :
1853 0 : static JSValue svg_path_close(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1854 : {
1855 0 : pathCI *p = (pathCI *) JS_GetOpaque(obj, pathClass.class_id);
1856 0 : if (!p) return JS_EXCEPTION;
1857 :
1858 0 : p->tags = (u8 *)gf_realloc(p->tags, sizeof(u8)*(p->nb_coms+1) );
1859 0 : p->tags[p->nb_coms] = 6;
1860 0 : p->nb_coms++;
1861 0 : return JS_TRUE;
1862 : }
1863 :
1864 :
1865 0 : static JSValue matrix_getProperty(JSContext *c, JSValueConst obj, int magic)
1866 : {
1867 0 : GF_Matrix2D *mx = (GF_Matrix2D *) JS_GetOpaque(obj, matrixClass.class_id);
1868 0 : if (!mx) return JS_EXCEPTION;
1869 :
1870 0 : switch (magic) {
1871 0 : case 0: return JS_NewFloat64(c, FIX2FLT(mx->m[0]));
1872 0 : case 1: return JS_NewFloat64(c, FIX2FLT(mx->m[3]));
1873 0 : case 2: return JS_NewFloat64(c, FIX2FLT(mx->m[1]));
1874 0 : case 3: return JS_NewFloat64(c, FIX2FLT(mx->m[4]));
1875 0 : case 4: return JS_NewFloat64(c, FIX2FLT(mx->m[2]));
1876 0 : case 5: return JS_NewFloat64(c, FIX2FLT(mx->m[5]));
1877 0 : default:
1878 0 : return JS_EXCEPTION;
1879 : }
1880 : }
1881 :
1882 0 : static JSValue matrix_setProperty(JSContext *c, JSValueConst obj, JSValueConst value, int magic)
1883 : {
1884 : Double d;
1885 0 : GF_Matrix2D *mx = (GF_Matrix2D *) JS_GetOpaque(obj, matrixClass.class_id);
1886 0 : if (!mx) return JS_EXCEPTION;
1887 0 : if (JS_ToFloat64(c, &d, value)) return JS_EXCEPTION;
1888 :
1889 0 : switch (magic) {
1890 0 : case 0:
1891 0 : mx->m[0] = FLT2FIX(d);
1892 0 : break;
1893 0 : case 1:
1894 0 : mx->m[3] = FLT2FIX(d);
1895 0 : break;
1896 0 : case 2:
1897 0 : mx->m[1] = FLT2FIX(d);
1898 0 : break;
1899 0 : case 3:
1900 0 : mx->m[4] = FLT2FIX(d);
1901 0 : break;
1902 0 : case 4:
1903 0 : mx->m[2] = FLT2FIX(d);
1904 0 : break;
1905 0 : case 5:
1906 0 : mx->m[5] = FLT2FIX(d);
1907 0 : break;
1908 : default:
1909 : break;
1910 : }
1911 0 : return JS_EXCEPTION;
1912 : }
1913 :
1914 0 : static JSValue svg_mx2d_get_component(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1915 : {
1916 : u32 comp;
1917 0 : GF_Matrix2D *mx = (GF_Matrix2D *) JS_GetOpaque(obj, matrixClass.class_id);
1918 0 : if (!mx || (argc!=1)) return JS_EXCEPTION;
1919 0 : if (JS_ToInt32(c, &comp, argv[0])) return JS_EXCEPTION;
1920 :
1921 0 : switch (comp) {
1922 0 : case 0: return JS_NewFloat64(c, FIX2FLT(mx->m[0]));
1923 0 : case 1: return JS_NewFloat64(c, FIX2FLT(mx->m[3]));
1924 0 : case 2: return JS_NewFloat64(c, FIX2FLT(mx->m[1]));
1925 0 : case 3: return JS_NewFloat64(c, FIX2FLT(mx->m[4]));
1926 0 : case 4: return JS_NewFloat64(c, FIX2FLT(mx->m[2]));
1927 0 : case 5: return JS_NewFloat64(c, FIX2FLT(mx->m[5]));
1928 : }
1929 0 : return JS_EXCEPTION;
1930 : }
1931 :
1932 0 : static JSValue svg_mx2d_multiply(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1933 : {
1934 : GF_Matrix2D *mx1, *mx2;
1935 0 : mx1 = (GF_Matrix2D *) JS_GetOpaque(obj, matrixClass.class_id);
1936 0 : if (!mx1 || (argc!=1)) return JS_EXCEPTION;
1937 0 : mx2 = (GF_Matrix2D *) JS_GetOpaque(argv[0], matrixClass.class_id);
1938 0 : if (!mx2) return JS_EXCEPTION;
1939 :
1940 0 : gf_mx2d_add_matrix(mx1, mx2);
1941 : return JS_DupValue(c, obj);
1942 : }
1943 :
1944 0 : static JSValue svg_mx2d_inverse(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1945 : {
1946 0 : GF_Matrix2D *mx = (GF_Matrix2D *) JS_GetOpaque(obj, matrixClass.class_id);
1947 0 : if (!mx) return JS_EXCEPTION;
1948 0 : gf_mx2d_inverse(mx);
1949 : return JS_DupValue(c, obj);
1950 : }
1951 :
1952 0 : static JSValue svg_mx2d_translate(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1953 : {
1954 : Double x, y;
1955 : GF_Matrix2D *mx1, mx2;
1956 0 : mx1 = (GF_Matrix2D *) JS_GetOpaque(obj, matrixClass.class_id);
1957 0 : if (!mx1 || (argc!=2)) return JS_EXCEPTION;
1958 :
1959 0 : if (JS_ToFloat64(c, &x, argv[0]) || JS_ToFloat64(c, &y, argv[1])) return JS_EXCEPTION;
1960 :
1961 0 : gf_mx2d_init(mx2);
1962 0 : mx2.m[2] = FLT2FIX(x);
1963 0 : mx2.m[5] = FLT2FIX(y);
1964 0 : gf_mx2d_pre_multiply(mx1, &mx2);
1965 : return JS_DupValue(c, obj);
1966 : }
1967 :
1968 0 : static JSValue svg_mx2d_scale(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1969 : {
1970 : Double scale;
1971 : GF_Matrix2D *mx1, mx2;
1972 0 : mx1 = (GF_Matrix2D *) JS_GetOpaque(obj, matrixClass.class_id);
1973 0 : if (!mx1 || (argc!=2)) return JS_EXCEPTION;
1974 0 : if (JS_ToFloat64(c, &scale, argv[0])) return JS_EXCEPTION;
1975 :
1976 : gf_mx2d_init(mx2);
1977 0 : mx2.m[0] = mx2.m[4] = FLT2FIX(scale);
1978 0 : gf_mx2d_pre_multiply(mx1, &mx2);
1979 : return JS_DupValue(c, obj);
1980 : }
1981 :
1982 0 : static JSValue svg_mx2d_rotate(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1983 : {
1984 : Double angle;
1985 : GF_Matrix2D *mx1, mx2;
1986 :
1987 0 : mx1 = (GF_Matrix2D *) JS_GetOpaque(obj, matrixClass.class_id);
1988 0 : if (!mx1 || (argc!=2)) return JS_EXCEPTION;
1989 0 : if (JS_ToFloat64(c, &angle, argv[0])) return JS_EXCEPTION;
1990 :
1991 0 : gf_mx2d_init(mx2);
1992 0 : gf_mx2d_add_rotation(&mx2, 0, 0, gf_mulfix(FLT2FIX(angle/180), GF_PI));
1993 0 : gf_mx2d_pre_multiply(mx1, &mx2);
1994 : return JS_DupValue(c, obj);
1995 : }
1996 :
1997 0 : JSValue svg_udom_new_rect(JSContext *c, Fixed x, Fixed y, Fixed width, Fixed height)
1998 : {
1999 0 : rectCI *rc = (rectCI *)gf_malloc(sizeof(rectCI));
2000 0 : if (!rc) return JS_EXCEPTION;
2001 0 : JSValue r = JS_NewObjectClass(c, rectClass.class_id);
2002 0 : rc->x = FIX2FLT(x);
2003 0 : rc->y = FIX2FLT(y);
2004 0 : rc->w = FIX2FLT(width);
2005 0 : rc->h = FIX2FLT(height);
2006 0 : rc->sg = NULL;
2007 0 : JS_SetOpaque(r, rc);
2008 0 : return r;
2009 : }
2010 :
2011 0 : JSValue svg_udom_new_point(JSContext *c, Fixed x, Fixed y)
2012 : {
2013 0 : pointCI *pt = (pointCI *)gf_malloc(sizeof(pointCI));
2014 0 : if (!pt) return JS_EXCEPTION;
2015 0 : JSValue p = JS_NewObjectClass(c, pointClass.class_id);
2016 0 : pt->x = FIX2FLT(x);
2017 0 : pt->y = FIX2FLT(y);
2018 0 : pt->sg = NULL;
2019 0 : JS_SetOpaque(p, pt);
2020 0 : return p;
2021 : }
2022 :
2023 : #ifdef GPAC_ENABLE_HTML5_MEDIA
2024 : void *html_get_element_class(GF_Node *n);
2025 : #endif
2026 :
2027 90 : JSClassID svg_get_element_class(GF_Node *n)
2028 : {
2029 90 : if (!n) return 0;
2030 90 : 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 90 : return svgElement.class_id;
2038 : }
2039 : return 0;
2040 : }
2041 3 : JSClassID svg_get_document_class(GF_SceneGraph *sg)
2042 : {
2043 3 : GF_Node *n = sg->RootNode;
2044 3 : if (!n) return 0;
2045 3 : if ((n->sgprivate->tag>=GF_NODE_RANGE_FIRST_SVG) && (n->sgprivate->tag<=GF_NODE_RANGE_LAST_SVG))
2046 3 : return svgDocument.class_id;
2047 : return 0;
2048 : }
2049 :
2050 37 : Bool is_svg_document_class(JSContext *c, JSValue obj)
2051 : {
2052 37 : void *ptr = JS_GetOpaque(obj, svgDocument.class_id);
2053 37 : if (ptr) return GF_TRUE;
2054 37 : return GF_FALSE;
2055 : }
2056 :
2057 37 : Bool is_svg_element_class(JSContext *c, JSValue obj)
2058 : {
2059 37 : void *ptr = JS_GetOpaque(obj, svgElement.class_id);
2060 37 : if (ptr) return GF_TRUE;
2061 37 : 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 :
2229 3 : static void svg_init_js_api(GF_SceneGraph *scene)
2230 : {
2231 3 : JSContext *c = scene->svg_js->js_ctx;
2232 3 : JSRuntime *jsrt = JS_GetRuntime(c);
2233 3 : JSValue global = JS_GetGlobalObject(scene->svg_js->js_ctx);
2234 :
2235 3 : SETUP_JSCLASS(svg_globalClass, "Window", globalFuncs, NULL, NULL, 0);
2236 3 : scene->svg_js->global = JS_NewObjectClass(c, svg_globalClass.class_id);
2237 3 : JS_SetOpaque(scene->svg_js->global, scene);
2238 3 : JS_SetPropertyStr(c, global, "Window", scene->svg_js->global);
2239 :
2240 3 : JS_SetPropertyStr(c, global, "alert", JS_NewCFunction(c, js_print, "alert", 1));
2241 :
2242 : /*initialize DOM core */
2243 3 : dom_js_load(scene, scene->svg_js->js_ctx);
2244 :
2245 3 : qjs_module_init_xhr_global(c, global);
2246 :
2247 3 : svg_define_udom_exception(scene->svg_js->js_ctx, scene->svg_js->global);
2248 3 : JSValue console = JS_NewObject(c);
2249 3 : JS_SetPropertyStr(c, console, "log", JS_NewCFunction(c, js_print, "print", 1));
2250 3 : JS_SetPropertyStr(c, global, "console", console);
2251 :
2252 3 : svgDocument.class.gc_mark = domDocument_gc_mark;
2253 3 : SETUP_JSCLASS(svgDocument, "SVGDocument", documentFuncs, NULL, dom_document_finalize, dom_js_get_document_proto(c));
2254 3 : svgElement.class.gc_mark = domElement_gc_mark;
2255 3 : SETUP_JSCLASS(svgElement, "SVGElement", svg_elementFuncs, NULL, dom_element_finalize, dom_js_get_element_proto(c));
2256 :
2257 3 : SETUP_JSCLASS(rgbClass, "SVGRGBColor", rgb_Funcs, NULL, baseCI_finalize, 0);
2258 3 : SETUP_JSCLASS(rectClass, "SVGRect", rect_Funcs, NULL, baseCI_finalize, 0);
2259 3 : SETUP_JSCLASS(pointClass, "SVGPoint", point_Funcs, NULL, baseCI_finalize, 0);
2260 3 : SETUP_JSCLASS(matrixClass, "SVGMatrix", matrix_Funcs, NULL, baseCI_finalize, 0);
2261 :
2262 3 : SETUP_JSCLASS(pathClass, "SVGPath", path_Funcs, NULL, pathCI_finalize, 0);
2263 :
2264 :
2265 3 : JS_SetPropertyStr(c, scene->svg_js->pathClass.proto, "MOVE_TO", JS_NewInt32(c, 77));
2266 3 : JS_SetPropertyStr(c, scene->svg_js->pathClass.proto, "LINE_TO", JS_NewInt32(c, 76));
2267 3 : JS_SetPropertyStr(c, scene->svg_js->pathClass.proto, "CURVE_TO", JS_NewInt32(c, 67));
2268 3 : JS_SetPropertyStr(c, scene->svg_js->pathClass.proto, "QUAD_TO", JS_NewInt32(c, 81));
2269 3 : JS_SetPropertyStr(c, scene->svg_js->pathClass.proto, "CLOSE", JS_NewInt32(c, 90));
2270 :
2271 : /*we have our own constructors*/
2272 3 : scene->get_element_class = svg_get_element_class;
2273 3 : scene->get_document_class = svg_get_document_class;
2274 :
2275 : /*create document object*/
2276 3 : dom_js_define_document(scene->svg_js->js_ctx, global, scene);
2277 : /*create event object, and remember it*/
2278 3 : scene->svg_js->event = dom_js_define_event(scene->svg_js->js_ctx);
2279 :
2280 : JS_FreeValue(c, global);
2281 3 : }
2282 :
2283 : GF_DOM_Event *dom_get_evt_private(JSValue v);
2284 :
2285 331 : JSContext *svg_script_get_context(GF_SceneGraph *sg)
2286 : {
2287 331 : return sg->svg_js ? sg->svg_js->js_ctx : NULL;
2288 : }
2289 :
2290 0 : 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 0 : char *sep = strchr(utf8_script, '(');
2297 :
2298 0 : if (!sep) {
2299 : strcpy(szFuncName, utf8_script);
2300 : strcat(szFuncName, "(evt)");
2301 : utf8_script = szFuncName;
2302 : }
2303 :
2304 0 : gf_js_lock(sg->svg_js->js_ctx, GF_TRUE);
2305 :
2306 0 : prev_event = dom_get_evt_private(sg->svg_js->event);
2307 0 : JS_SetOpaque(sg->svg_js->event, event);
2308 0 : 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 0 : 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 0 : if (JS_IsException(ret)) {
2324 0 : js_dump_error(sg->svg_js->js_ctx);
2325 : ok=GF_FALSE;
2326 : }
2327 0 : JS_FreeValue(sg->svg_js->js_ctx, ret);
2328 :
2329 0 : if (sg->svg_js->force_gc) {
2330 0 : gf_js_call_gc(sg->svg_js->js_ctx);
2331 0 : sg->svg_js->force_gc = GF_FALSE;
2332 : }
2333 0 : js_do_loop(sg->svg_js->js_ctx);
2334 0 : gf_js_lock(sg->svg_js->js_ctx, GF_FALSE);
2335 :
2336 0 : return ok;
2337 : }
2338 :
2339 : #ifdef GPAC_ENABLE_HTML5_MEDIA
2340 : void html_media_js_api_del();
2341 : #endif
2342 :
2343 3 : void gf_svg_script_context_del(GF_SVGJS *svg_js, GF_SceneGraph *scenegraph)
2344 : {
2345 3 : gf_sg_js_dom_pre_destroy(JS_GetRuntime(svg_js->js_ctx), scenegraph, NULL);
2346 3 : gf_js_delete_context(svg_js->js_ctx);
2347 3 : dom_js_unload();
2348 : #ifdef GPAC_ENABLE_HTML5_MEDIA
2349 : /* HTML */
2350 : html_media_js_api_del();
2351 : #endif
2352 :
2353 3 : gf_free(svg_js);
2354 3 : scenegraph->svg_js = NULL;
2355 :
2356 3 : }
2357 :
2358 199 : static void svg_script_predestroy(GF_Node *n, void *eff, Bool is_destroy)
2359 : {
2360 199 : if (is_destroy) {
2361 3 : GF_SVGJS *svg_js = n->sgprivate->scenegraph->svg_js;
2362 : /*unregister script from parent scene (cf base_scenegraph::sg_reset) */
2363 3 : gf_list_del_item(n->sgprivate->scenegraph->scripts, n);
2364 :
2365 3 : if (svg_js->nb_scripts) {
2366 3 : svg_js->nb_scripts--;
2367 :
2368 : /*detach this script from our object cache*/
2369 3 : gf_sg_js_dom_pre_destroy(JS_GetRuntime(svg_js->js_ctx), n->sgprivate->scenegraph, n);
2370 :
2371 3 : if (!svg_js->nb_scripts) {
2372 3 : gf_svg_script_context_del(svg_js, n->sgprivate->scenegraph);
2373 : }
2374 : }
2375 : }
2376 199 : }
2377 :
2378 3 : GF_Err JSScript_CreateSVGContext(GF_SceneGraph *sg)
2379 : {
2380 : GF_SVGJS *svg_js;
2381 :
2382 3 : if (sg->svg_js) {
2383 : /* the JS/SVG context is already created, no need to do anything */
2384 : return GF_OK;
2385 : }
2386 :
2387 3 : GF_SAFEALLOC(svg_js, GF_SVGJS);
2388 3 : if (!svg_js) {
2389 : return GF_OUT_OF_MEM;
2390 : }
2391 : /*create new ecmascript context*/
2392 3 : svg_js->js_ctx = gf_js_create_context();
2393 3 : if (!svg_js->js_ctx) {
2394 0 : gf_free(svg_js);
2395 0 : return GF_SCRIPT_ERROR;
2396 : }
2397 :
2398 3 : gf_js_lock(svg_js->js_ctx, GF_TRUE);
2399 :
2400 3 : sg->svg_js = svg_js;
2401 : /*load SVG & DOM APIs*/
2402 3 : 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 3 : svg_js->script_execute = svg_script_execute;
2411 3 : svg_js->handler_execute = svg_script_execute_handler;
2412 :
2413 3 : gf_js_lock(svg_js->js_ctx, GF_FALSE);
2414 :
2415 3 : return GF_OK;
2416 : }
2417 :
2418 0 : GF_DOMText *svg_get_text_child(GF_Node *node)
2419 : {
2420 : GF_ChildNodeItem *child;
2421 : GF_DOMText *txt;
2422 : txt = NULL;
2423 1 : child = ((SVG_Element*)node)->children;
2424 1 : if (! child) return NULL;
2425 0 : while (child) {
2426 0 : txt = (GF_DOMText*)child->node;
2427 0 : if ((txt->sgprivate->tag==TAG_DOMText) && txt->textContent) return txt;
2428 : txt = NULL;
2429 0 : child = child->next;
2430 : }
2431 : return NULL;
2432 : }
2433 :
2434 2 : 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 2 : svg_js = script->sgprivate->scenegraph->svg_js;
2446 2 : if (!strnicmp(file, "file://", 7)) file += 7;
2447 :
2448 2 : if (!gf_file_exists(file)) {
2449 : GF_JSAPIParam par;
2450 0 : GF_SceneGraph *scene = script->sgprivate->scenegraph;
2451 0 : par.uri.url = file;
2452 0 : if (scene->script_action && scene->script_action(scene->script_action_cbck, GF_JSAPI_OP_RESOLVE_XLINK, script, &par))
2453 0 : abs_url = (char *) par.uri.url;
2454 :
2455 0 : if (abs_url || !gf_file_exists(abs_url)) {
2456 0 : if (abs_url) gf_free(abs_url);
2457 0 : return GF_FALSE;
2458 : }
2459 : }
2460 :
2461 0 : e = gf_file_load_data(abs_url ? abs_url : file, &jsscript, &fsize);
2462 2 : if (abs_url) gf_free(abs_url);
2463 :
2464 2 : if (e!=GF_OK) return GF_FALSE;
2465 :
2466 : /*for handler, only load code*/
2467 2 : if (script->sgprivate->tag==TAG_SVG_handler) {
2468 0 : GF_DOMText *txt = gf_dom_add_text_node(script, jsscript);
2469 0 : txt->type = GF_DOM_TEXT_INSERTED;
2470 0 : return GF_TRUE;
2471 : }
2472 :
2473 2 : gf_js_lock(svg_js->js_ctx, GF_TRUE);
2474 :
2475 2 : if (!gf_opts_get_bool("core", "no-js-mods") && JS_DetectModule((const char *) jsscript, fsize )) {
2476 : flags = JS_EVAL_TYPE_MODULE;
2477 0 : svg_js->use_strict = GF_TRUE;
2478 : }
2479 :
2480 2 : ret = JS_Eval(svg_js->js_ctx, jsscript, sizeof(char)*fsize, file, flags);
2481 2 : if (JS_IsException(ret)) {
2482 0 : js_dump_error(svg_js->js_ctx);
2483 : success=GF_FALSE;
2484 : }
2485 2 : JS_FreeValue(svg_js->js_ctx, ret);
2486 2 : if (svg_js->force_gc) {
2487 0 : gf_js_call_gc(svg_js->js_ctx);
2488 0 : svg_js->force_gc = GF_FALSE;
2489 : }
2490 2 : gf_js_lock(svg_js->js_ctx, GF_FALSE);
2491 :
2492 2 : gf_dom_listener_process_add(script->sgprivate->scenegraph);
2493 :
2494 2 : gf_free(jsscript);
2495 2 : return success;
2496 : }
2497 :
2498 : #include <gpac/download.h>
2499 : #include <gpac/network.h>
2500 :
2501 :
2502 3 : void JSScript_LoadSVG(GF_Node *node)
2503 : {
2504 : GF_DOMText *txt;
2505 : GF_SVGJS *svg_js;
2506 : GF_FieldInfo href_info;
2507 :
2508 3 : if (!node->sgprivate->scenegraph->svg_js) {
2509 4 : if (JSScript_CreateSVGContext(node->sgprivate->scenegraph) != GF_OK) return;
2510 : }
2511 :
2512 : /*register script with parent scene (cf base_scenegraph::sg_reset) */
2513 3 : gf_list_add(node->sgprivate->scenegraph->scripts, node);
2514 :
2515 3 : svg_js = node->sgprivate->scenegraph->svg_js;
2516 3 : if (!node->sgprivate->UserCallback) {
2517 3 : svg_js->nb_scripts++;
2518 3 : node->sgprivate->UserCallback = svg_script_predestroy;
2519 : }
2520 : /*if href download the script file*/
2521 3 : 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 2 : XMLRI *xmlri = (XMLRI *)href_info.far_ptr;
2527 :
2528 : /* getting a download manager */
2529 2 : par.dnld_man = NULL;
2530 2 : ScriptAction(node->sgprivate->scenegraph, GF_JSAPI_OP_GET_DOWNLOAD_MANAGER, NULL, &par);
2531 2 : dnld_man = par.dnld_man;
2532 :
2533 :
2534 : /* resolve the uri of the script*/
2535 2 : par.uri.nb_params = 0;
2536 2 : par.uri.url = xmlri->string;
2537 2 : ScriptAction(node->sgprivate->scenegraph, GF_JSAPI_OP_RESOLVE_URI, node, &par);
2538 2 : url = (char *)par.uri.url;
2539 :
2540 : /* if the file is local, we don't need to download it */
2541 2 : if (!strstr(url, "://") || !strnicmp(url, "file://", 7)) {
2542 2 : svg_js_load_script(node, url);
2543 0 : } else if (dnld_man) {
2544 : /*fetch the remote script synchronously and load it - cf section on script processing in SVG specs*/
2545 0 : GF_DownloadSession *sess = gf_dm_sess_new(dnld_man, url, GF_NETIO_SESSION_NOT_THREADED, NULL, NULL, &e);
2546 0 : if (sess) {
2547 0 : e = gf_dm_sess_process(sess);
2548 0 : if (e==GF_OK) {
2549 0 : const char *szCache = gf_dm_sess_get_cache_name(sess);
2550 0 : if (!svg_js_load_script(node, (char *) szCache))
2551 0 : e = GF_SCRIPT_ERROR;
2552 : }
2553 0 : gf_dm_sess_del(sess);
2554 : }
2555 0 : if (e) {
2556 0 : par.info.e = e;
2557 0 : par.info.msg = "Cannot fetch script";
2558 0 : ScriptAction(node->sgprivate->scenegraph, GF_JSAPI_OP_MESSAGE, NULL, &par);
2559 : }
2560 : }
2561 2 : gf_free(url);
2562 : }
2563 : /*for scripts only, execute*/
2564 1 : 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 1 : if (!txt) return;
2571 0 : txtlen = (u32) strlen(txt->textContent);
2572 :
2573 0 : if (!gf_opts_get_bool("core", "no-js-mods") && JS_DetectModule((const char *) txt, txtlen )) {
2574 : flags = JS_EVAL_TYPE_MODULE;
2575 0 : svg_js->use_strict = GF_TRUE;
2576 : }
2577 :
2578 0 : ret = JS_Eval(svg_js->js_ctx, txt->textContent, (u32) strlen(txt->textContent), "inline_script", flags);
2579 0 : if (JS_IsException(ret)) {
2580 0 : js_dump_error(svg_js->js_ctx);
2581 : }
2582 0 : JS_FreeValue(svg_js->js_ctx, ret);
2583 0 : gf_dom_listener_process_add(node->sgprivate->scenegraph);
2584 0 : 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
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 : */
2609 0 : 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 0 : if (utf8_script) {
2622 0 : if (!node) return GF_FALSE;
2623 : hdl = NULL;
2624 : } else {
2625 0 : if (JS_IsUndefined(hdl->js_data->fun_val) && JS_IsUndefined(hdl->js_data->evt_listen_obj)) {
2626 : txt = svg_get_text_child(node);
2627 0 : 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 0 : svg_js = node->sgprivate->scenegraph->svg_js;
2634 :
2635 : #ifndef GPAC_DISABLE_LOG
2636 0 : if (gf_log_tool_level_on(GF_LOG_SCRIPT, GF_LOG_DEBUG)) {
2637 : char *content, *_content = NULL;
2638 0 : if (utf8_script) {
2639 : content = utf8_script;
2640 0 : } else if (!JS_IsUndefined(hdl->js_data->fun_val)) {
2641 0 : content = _content = (char *) JS_ToCString(svg_js->js_ctx, hdl->js_data->fun_val);
2642 0 : } else if (txt) {
2643 0 : content = txt->textContent;
2644 : } else {
2645 : content = "unknown";
2646 : }
2647 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[DOM Events ] Executing script code from handler: %s\n", content) );
2648 0 : JS_FreeCString(svg_js->js_ctx, _content);
2649 : }
2650 : #endif
2651 :
2652 0 : gf_js_lock(svg_js->js_ctx, GF_TRUE);
2653 0 : prev_event = dom_get_evt_private(svg_js->event);
2654 : /*break loops*/
2655 0 : if (prev_event && (prev_event->type==event->type) && (prev_event->target==event->target)) {
2656 0 : gf_js_lock(svg_js->js_ctx, GF_FALSE);
2657 0 : return GF_FALSE;
2658 : }
2659 0 : JS_SetOpaque(svg_js->event, event);
2660 :
2661 0 : svg_js->in_script = GF_TRUE;
2662 0 : if (svg_js->use_strict)
2663 : flags = JS_EVAL_TYPE_MODULE;
2664 :
2665 : /*if an observer is being specified, use it*/
2666 0 : 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 0 : else __this = observer ? dom_element_construct(svg_js->js_ctx, observer) : svg_js->global;
2669 :
2670 0 : if (txt && hdl && hdl->js_data && !JS_IsUndefined(hdl->js_data->fun_val)) {
2671 0 : hdl->js_data->fun_val = JS_EvalThis(svg_js->js_ctx, __this, txt->textContent, strlen(txt->textContent), "handler", flags|JS_EVAL_FLAG_COMPILE_ONLY);
2672 : }
2673 :
2674 0 : if (utf8_script) {
2675 0 : ret = JS_EvalThis(svg_js->js_ctx, __this, utf8_script, (u32) strlen(utf8_script), "inline script", flags);
2676 : }
2677 0 : else if (hdl && hdl->js_data
2678 0 : && (!JS_IsUndefined(hdl->js_data->fun_val) || !JS_IsUndefined(hdl->js_data->evt_listen_obj) )
2679 0 : ) {
2680 : JSValue evt;
2681 : JSValue argv[1];
2682 0 : evt = gf_dom_new_event(svg_js->js_ctx);
2683 0 : JS_SetOpaque(evt, event);
2684 0 : argv[0] = evt;
2685 :
2686 0 : if (!JS_IsUndefined(hdl->js_data->fun_val) ) {
2687 0 : ret = JS_Call(svg_js->js_ctx, hdl->js_data->fun_val, __this, 1, argv);
2688 : } else {
2689 0 : JSValue fun = JS_GetPropertyStr(svg_js->js_ctx, hdl->js_data->evt_listen_obj, "hanldeEvent");
2690 0 : ret = JS_Call(svg_js->js_ctx, fun, hdl->js_data->evt_listen_obj, 1, argv);
2691 0 : JS_FreeValue(svg_js->js_ctx, fun);
2692 : }
2693 0 : } else if (txt) {
2694 0 : JSValue fun = JS_GetPropertyStr(svg_js->js_ctx, svg_js->global, txt->textContent);
2695 0 : if (!JS_IsUndefined(fun)) {
2696 0 : ret = JS_NULL;
2697 0 : if (svg_script_execute(node->sgprivate->scenegraph, txt->textContent, event)) {
2698 : success = GF_FALSE;
2699 : }
2700 : }
2701 : else {
2702 0 : ret = JS_EvalThis(svg_js->js_ctx, __this, txt->textContent, (u32) strlen(txt->textContent), "internal", flags);
2703 : }
2704 0 : JS_FreeValue(svg_js->js_ctx, fun);
2705 : } else {
2706 0 : ret = JS_NULL;
2707 : }
2708 0 : if (JS_IsException(ret)) {
2709 0 : js_dump_error(svg_js->js_ctx);
2710 : success = GF_FALSE;
2711 : }
2712 0 : JS_FreeValue(svg_js->js_ctx, ret);
2713 :
2714 0 : JS_SetOpaque(svg_js->event, prev_event);
2715 0 : if (txt && hdl && hdl->js_data) hdl->js_data->fun_val = JS_UNDEFINED;
2716 :
2717 0 : while (svg_js->force_gc) {
2718 0 : svg_js->force_gc = GF_FALSE;
2719 0 : gf_js_call_gc(svg_js->js_ctx);
2720 : }
2721 0 : svg_js->in_script = GF_FALSE;
2722 :
2723 : /*check any pending exception if outer-most event*/
2724 0 : if (!prev_event) {
2725 0 : js_do_loop(svg_js->js_ctx);
2726 : }
2727 :
2728 0 : 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 0 : if (!success) {
2746 0 : 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
|