Line data Source code
1 : /*
2 : * GPAC - Multimedia Framework C SDK
3 : *
4 : * Authors: Jean Le Feuvre
5 : * Copyright (c) Telecom ParisTech 2007-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/setup.h>
27 : #include <gpac/internal/scenegraph_dev.h>
28 : #include "qjs_common.h"
29 :
30 : /*base SVG type*/
31 : #include <gpac/nodes_svg.h>
32 : /*dom events*/
33 : #include <gpac/events.h>
34 : /*dom text event*/
35 : #include <gpac/utf.h>
36 :
37 : #include <gpac/download.h>
38 : #include <gpac/network.h>
39 : #include <gpac/xml.h>
40 :
41 : #ifndef GPAC_DISABLE_SVG
42 :
43 : #ifdef GPAC_HAS_QJS
44 :
45 : #include <gpac/html5_mse.h>
46 :
47 : #ifdef GPAC_CONFIG_ANDROID
48 : #ifndef XP_UNIX
49 : #define XP_UNIX
50 : #endif /* XP_UNIX */
51 : #endif
52 :
53 : #include <gpac/html5_media.h>
54 :
55 : /************************************************************
56 : *
57 : * DOM3 core implementation
58 : *
59 : * This is NOT a full dom implementation :)
60 : *
61 : *************************************************************/
62 :
63 : GF_JSClass domDocumentClass;
64 : GF_JSClass domNodeClass;
65 : GF_JSClass domElementClass;
66 : GF_JSClass domTextClass;
67 : GF_JSClass domNodeListClass;
68 : GF_JSClass domEventClass;
69 : GF_JSClass htmlMediaElementClass;
70 :
71 :
72 : typedef struct
73 : {
74 : u32 nb_inst;
75 :
76 : GF_List *handlers;
77 : } GF_DOMRuntime;
78 :
79 : static GF_DOMRuntime *dom_rt = NULL;
80 :
81 : typedef enum {
82 : NODE_JSPROPERTY_NODENAME = -1,
83 : NODE_JSPROPERTY_NODEVALUE = -2,
84 : NODE_JSPROPERTY_NODETYPE = -3,
85 : NODE_JSPROPERTY_PARENTNODE = -4,
86 : NODE_JSPROPERTY_CHILDNODES = -5,
87 : NODE_JSPROPERTY_FIRSTCHILD = -6,
88 : NODE_JSPROPERTY_LASTCHILD = -7,
89 : NODE_JSPROPERTY_PREVIOUSSIBLING = -8,
90 : NODE_JSPROPERTY_NEXTSIBLING = -9,
91 : NODE_JSPROPERTY_ATTRIBUTES = -10,
92 : NODE_JSPROPERTY_OWNERDOCUMENT = -11,
93 : NODE_JSPROPERTY_NAMESPACEURI = -12,
94 : NODE_JSPROPERTY_PREFIX = -13,
95 : NODE_JSPROPERTY_LOCALNAME = -14,
96 : NODE_JSPROPERTY_BASEURI = -15,
97 : NODE_JSPROPERTY_TEXTCONTENT = -16,
98 : NODE_JSPROPERTY_FIRSTELEMENTCHILD = -17,
99 : NODE_JSPROPERTY_LASTELEMENTCHILD = -18,
100 : NODE_JSPROPERTY_PREVIOUSELEMENTSIBLING = -19,
101 : NODE_JSPROPERTY_NEXTELEMENTSIBLING = -20
102 : } GF_DOMNodeJSProperty;
103 :
104 : typedef enum {
105 : DOCUMENT_JSPROPERTY_DOCTYPE = -1,
106 : DOCUMENT_JSPROPERTY_IMPLEMENTATION = -2,
107 : DOCUMENT_JSPROPERTY_DOCUMENTELEMENT = -3,
108 : DOCUMENT_JSPROPERTY_INPUTENCODING = -4,
109 : DOCUMENT_JSPROPERTY_XMLENCODING = -5,
110 : DOCUMENT_JSPROPERTY_XMLSTANDALONE = -6,
111 : DOCUMENT_JSPROPERTY_XMLVERSION = -7,
112 : DOCUMENT_JSPROPERTY_STRICTERRORCHECKING = -8,
113 : DOCUMENT_JSPROPERTY_DOCUMENTURI = -9,
114 : DOCUMENT_JSPROPERTY_LOCATION = -10,
115 : DOCUMENT_JSPROPERTY_DOMCONFIG = -11,
116 : DOCUMENT_JSPROPERTY_GLOBAL = -12
117 : } GF_DOMDocumentJSProperty;
118 :
119 : typedef enum {
120 : ELEMENT_JSPROPERTY_TAGNAME = -1,
121 : ELEMENT_JSPROPERTY_SCHEMATYPEINFO = -2,
122 : } GF_DOMElementJSProperty;
123 :
124 : typedef enum {
125 : DCCI_JSPROPERTY_VALUE = -1,
126 : DCCI_JSPROPERTY_VALUETYPE = -2,
127 : DCCI_JSPROPERTY_PROPERTYTYPE = -3,
128 : DCCI_JSPROPERTY_READONLY = -4,
129 : DCCI_JSPROPERTY_DCCIMETADATAINTERFACETYPE = -5,
130 : DCCI_JSPROPERTY_DCCIMETADATAINTERFACE = -6,
131 : DCCI_JSPROPERTY_VERSION = -7,
132 : } GF_DCCIJSProperty;
133 :
134 : typedef enum {
135 : NODELIST_JSPROPERTY_LENGTH = -1,
136 : } GF_DOMNodeListJSProperty;
137 :
138 : typedef enum {
139 : EVENT_JSPROPERTY_TYPE = -1,
140 : EVENT_JSPROPERTY_TARGET = -2,
141 : EVENT_JSPROPERTY_CURRENTTARGET = -3,
142 : EVENT_JSPROPERTY_EVENTPHASE = -4,
143 : EVENT_JSPROPERTY_BUBBLES = -5,
144 : EVENT_JSPROPERTY_CANCELABLE = -6,
145 : EVENT_JSPROPERTY_TIMESTAMP = -7,
146 : EVENT_JSPROPERTY_NAMESPACEURI = -8,
147 : EVENT_JSPROPERTY_DEFAULTPREVENTED = -9,
148 : EVENT_JSPROPERTY_DETAIL = -10,
149 : EVENT_JSPROPERTY_DATA = -11,
150 : EVENT_JSPROPERTY_SCREENX = -12,
151 : EVENT_JSPROPERTY_SCREENY = -13,
152 : EVENT_JSPROPERTY_CLIENTX = -14,
153 : EVENT_JSPROPERTY_CLIENTY = -15,
154 : EVENT_JSPROPERTY_BUTTON = -16,
155 : EVENT_JSPROPERTY_RELATEDTARGET = -17,
156 : EVENT_JSPROPERTY_WHEELDELTA = -18,
157 : EVENT_JSPROPERTY_KEYIDENTIFIER = -19,
158 : EVENT_JSPROPERTY_KEYCHAR = -20,
159 : EVENT_JSPROPERTY_CHARCODE = -21,
160 : EVENT_JSPROPERTY_LENGTHCOMPUTABLE = -22,
161 : EVENT_JSPROPERTY_TYPEARG = -23,
162 : EVENT_JSPROPERTY_LOADED = -24,
163 : EVENT_JSPROPERTY_TOTAL = -25,
164 : EVENT_JSPROPERTY_BUFFER_ON = -26,
165 : EVENT_JSPROPERTY_BUFFERLEVEL = -27,
166 : EVENT_JSPROPERTY_BUFFERREMAININGTIME = -28,
167 : EVENT_JSPROPERTY_STATUS = -29,
168 : EVENT_JSPROPERTY_WIDTH = -30,
169 : EVENT_JSPROPERTY_HEIGHT = -31,
170 : EVENT_JSPROPERTY_OFFSETX = -32,
171 : EVENT_JSPROPERTY_OFFSETY = -33,
172 : EVENT_JSPROPERTY_VPWIDTH = -34,
173 : EVENT_JSPROPERTY_VPHEIGHT = -35,
174 : EVENT_JSPROPERTY_TRANSLATIONX = -36,
175 : EVENT_JSPROPERTY_TRANSLATIONY = -37,
176 : EVENT_JSPROPERTY_TYPE3D = -38,
177 : EVENT_JSPROPERTY_ERROR = -39,
178 : EVENT_JSPROPERTY_DYNAMIC_SCENE = -40,
179 : EVENT_JSPROPERTY_URL = -41,
180 : } GF_DOMEventJSProperty;
181 :
182 : typedef enum {
183 : TEXT_JSPROPERTY_DATA = -1,
184 : TEXT_JSPROPERTY_LENGTH = -2,
185 : TEXT_JSPROPERTY_ISELEMENTCONTENTWHITESPACE = -3,
186 : TEXT_JSPROPERTY_WHOLETEXT = -4,
187 : } GF_DOMTextJSProperty;
188 :
189 :
190 :
191 1 : JSValue xml_dom3_not_implemented(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
192 : {
193 1 : return js_throw_err(ctx, GF_DOM_EXC_NOT_SUPPORTED_ERR);
194 : }
195 :
196 :
197 : #define JS_DOM3_uDOM_FIRST_PROP 18
198 :
199 173 : void dom_node_changed(GF_Node *n, Bool child_modif, GF_FieldInfo *info)
200 : {
201 173 : if (info) {
202 173 : if (child_modif) {
203 0 : gf_node_dirty_set(n, GF_SG_CHILD_DIRTY, GF_FALSE);
204 : }
205 : #ifndef GPAC_DISABLE_SVG
206 : else {
207 173 : u32 flag = gf_svg_get_modification_flags((SVG_Element *)n, info);
208 173 : gf_node_dirty_set(n, flag, GF_FALSE);
209 : }
210 : #endif
211 : }
212 265 : gf_node_changed(n, info);
213 173 : }
214 :
215 409 : static void define_dom_exception(JSContext *c, JSValue global)
216 : {
217 : #define DEF_EXC(_val) \
218 : JS_SetPropertyStr(c, obj, #_val, JS_NewInt32(c, GF_DOM_EXC_##_val));
219 :
220 409 : JSValue obj = JS_NewObject(c);
221 409 : JS_SetPropertyStr(c, global, "DOMException", obj);
222 :
223 409 : DEF_EXC(INDEX_SIZE_ERR);
224 409 : DEF_EXC(DOMSTRING_SIZE_ERR);
225 409 : DEF_EXC(HIERARCHY_REQUEST_ERR);
226 409 : DEF_EXC(WRONG_DOCUMENT_ERR);
227 409 : DEF_EXC(INVALID_CHARACTER_ERR);
228 409 : DEF_EXC(NO_DATA_ALLOWED_ERR);
229 409 : DEF_EXC(NO_MODIFICATION_ALLOWED_ERR);
230 409 : DEF_EXC(NOT_FOUND_ERR);
231 409 : DEF_EXC(NOT_SUPPORTED_ERR);
232 409 : DEF_EXC(INUSE_ATTRIBUTE_ERR);
233 409 : DEF_EXC(INVALID_STATE_ERR);
234 409 : DEF_EXC(SYNTAX_ERR);
235 409 : DEF_EXC(INVALID_MODIFICATION_ERR);
236 409 : DEF_EXC(NAMESPACE_ERR);
237 409 : DEF_EXC(INVALID_ACCESS_ERR);
238 409 : DEF_EXC(VALIDATION_ERR);
239 409 : DEF_EXC(TYPE_MISMATCH_ERR);
240 409 : DEF_EXC(SECURITY_ERR);
241 409 : DEF_EXC(NETWORK_ERR);
242 409 : DEF_EXC(ABORT_ERR);
243 409 : DEF_EXC(URL_MISMATCH_ERR);
244 409 : DEF_EXC(QUOTA_EXCEEDED_ERR);
245 409 : DEF_EXC(TIMEOUT_ERR);
246 409 : DEF_EXC(INVALID_NODE_TYPE_ERR);
247 409 : DEF_EXC(DATA_CLONE_ERR);
248 409 : DEF_EXC(SECURITY_ERR);
249 : #undef DEF_EXC
250 :
251 : //JS_AliasProperty(c, global, "DOMException", "e");
252 409 : obj = JS_NewObject(c);
253 409 : JS_SetPropertyStr(c, global, "EventException", obj);
254 409 : JS_SetPropertyStr(c, obj, "UNSPECIFIED_EVENT_TYPE_ERR", JS_NewInt32(c, 0));
255 409 : JS_SetPropertyStr(c, obj, "DISPATCH_REQUEST_ERR", JS_NewInt32(c, 1));
256 409 : }
257 :
258 :
259 0 : GF_Node *dom_get_node(JSValue obj)
260 : {
261 811 : GF_Node *n = (GF_Node *) JS_GetOpaque_Nocheck(obj);
262 811 : if (n && n->sgprivate) return n;
263 0 : return NULL;
264 : }
265 :
266 2 : GF_Node *dom_get_element(JSContext *c, JSValue obj)
267 : {
268 2 : GF_Node *n = (GF_Node *) JS_GetOpaque_Nocheck(obj);
269 2 : if (!n || !n->sgprivate) return NULL;
270 2 : if (n->sgprivate->tag==TAG_DOMText) return NULL;
271 2 : return n;
272 : }
273 :
274 0 : GF_SceneGraph *dom_get_doc(JSContext *c, JSValue obj)
275 : {
276 113 : GF_SceneGraph *sg = (GF_SceneGraph *) JS_GetOpaque_Nocheck(obj);
277 113 : if (sg && !sg->__reserved_null) return sg;
278 0 : return NULL;
279 : }
280 : typedef struct _dom_js_data
281 : {
282 : JSValue document;
283 : } GF_DOMJSData;
284 :
285 3 : static void dom_js_define_document_ex(JSContext *c, JSValue global, GF_SceneGraph *doc, const char *name)
286 : {
287 : JSClassID __class = 0;
288 : JSValue obj;
289 : GF_SceneGraph *par_sg;
290 3 : if (!doc || !doc->RootNode) return;
291 :
292 3 : if (doc->reference_count)
293 0 : doc->reference_count++;
294 : //no need to register root once more, it is already registered with graph
295 : // gf_node_register(doc->RootNode, NULL);
296 : par_sg = doc;
297 6 : while (par_sg && !par_sg->get_document_class)
298 0 : par_sg = par_sg->parent_scene;
299 :
300 3 : if (par_sg && par_sg->get_document_class)
301 3 : __class = par_sg->get_document_class(doc);
302 :
303 3 : if (!__class)
304 0 : __class = domDocumentClass.class_id;
305 :
306 3 : obj = JS_NewObjectClass(c, __class);
307 3 : JS_SetOpaque(obj, doc);
308 3 : GF_SAFEALLOC(doc->js_data, GF_DOMJSData);
309 3 : if (doc->js_data)
310 3 : doc->js_data->document = JS_DupValue(c, obj);
311 :
312 3 : JS_SetPropertyStr(c, global, name, obj);
313 : }
314 :
315 3 : void dom_js_define_document(JSContext *c, JSValue global, GF_SceneGraph *doc)
316 : {
317 3 : dom_js_define_document_ex(c, global, doc, "document");
318 3 : }
319 :
320 : /* Constructs a new document based on the given context and scene graph, used for documents in XHR */
321 10 : JSValue dom_document_construct(JSContext *c, GF_SceneGraph *sg)
322 : {
323 : JSClassID __class=0;
324 : JSValue new_obj;
325 : GF_SceneGraph *par_sg = sg;
326 10 : if (!dom_rt) return JS_EXCEPTION;
327 10 : if (sg->js_data) return JS_DupValue(c, sg->js_data->document);
328 :
329 8 : if (sg->reference_count)
330 8 : sg->reference_count++;
331 : //no need to register root once more, it is already registered with graph
332 : // gf_node_register(sg->RootNode, NULL);
333 :
334 16 : while (par_sg && !par_sg->get_element_class) {
335 8 : par_sg = par_sg->parent_scene;
336 : }
337 :
338 8 : if (par_sg && par_sg->get_document_class)
339 0 : __class = par_sg->get_document_class(sg);
340 :
341 0 : if (!__class)
342 8 : __class = domDocumentClass.class_id;
343 :
344 8 : new_obj = JS_NewObjectClass(c, __class);
345 8 : JS_SetOpaque(new_obj, sg);
346 8 : GF_SAFEALLOC(sg->js_data, GF_DOMJSData);
347 8 : if (sg->js_data)
348 8 : sg->js_data->document = JS_DupValue(c, new_obj);
349 8 : return new_obj;
350 : }
351 :
352 10 : JSValue dom_document_construct_external(JSContext *c, GF_SceneGraph *sg)
353 : {
354 10 : return dom_document_construct(c, sg);
355 :
356 : }
357 :
358 138 : static JSValue dom_base_node_construct(JSContext *c, JSClassID class_id, GF_Node *n)
359 : {
360 : GF_SceneGraph *sg;
361 : JSValue new_obj;
362 138 : if (!n || !n->sgprivate->scenegraph) return JS_NULL;
363 138 : if (n->sgprivate->tag<TAG_DOMText) return JS_NULL;
364 :
365 : sg = n->sgprivate->scenegraph;
366 :
367 141 : if (n->sgprivate->interact && n->sgprivate->interact->js_binding && !JS_IsUndefined(n->sgprivate->interact->js_binding->obj) ) {
368 : return JS_DupValue(c, n->sgprivate->interact->js_binding->obj);
369 : }
370 :
371 135 : if (n->sgprivate->scenegraph->reference_count)
372 47 : n->sgprivate->scenegraph->reference_count ++;
373 :
374 135 : gf_node_register(n, NULL);
375 135 : new_obj = JS_NewObjectClass(c, class_id);
376 135 : JS_SetOpaque(new_obj, n);
377 :
378 135 : if (n->sgprivate->tag == TAG_SVG_video || n->sgprivate->tag == TAG_SVG_audio)
379 : {
380 : #ifdef GPAC_ENABLE_HTML5_MEDIA
381 : html_media_element_js_init(c, new_obj, n);
382 : #endif
383 :
384 : }
385 135 : if (!n->sgprivate->interact) {
386 135 : GF_SAFEALLOC(n->sgprivate->interact, struct _node_interactive_ext);
387 135 : if (!n->sgprivate->interact) {
388 0 : return JS_NULL;
389 : }
390 : }
391 135 : if (!n->sgprivate->interact->js_binding) {
392 135 : GF_SAFEALLOC(n->sgprivate->interact->js_binding, struct _node_js_binding);
393 135 : if (!n->sgprivate->interact->js_binding) {
394 0 : return JS_NULL;
395 : }
396 : }
397 270 : n->sgprivate->interact->js_binding->obj = JS_DupValue(c, new_obj);
398 135 : gf_list_add(sg->objects, n->sgprivate->interact->js_binding);
399 135 : return new_obj;
400 : }
401 41 : static JSValue dom_node_construct(JSContext *c, GF_Node *n)
402 : {
403 : JSClassID __class = 0;
404 : GF_SceneGraph *par_sg;
405 41 : if (!n) return JS_NULL;
406 41 : par_sg = n->sgprivate->scenegraph;
407 123 : while (par_sg && !par_sg->get_element_class)
408 41 : par_sg = par_sg->parent_scene;
409 :
410 41 : if (par_sg && par_sg->get_element_class)
411 0 : __class = par_sg->get_element_class(n);
412 :
413 0 : if (!__class)
414 41 : __class = domElementClass.class_id;
415 :
416 : /*in our implementation ONLY ELEMENTS are created, never attributes. We therefore always
417 : create Elements when asked to create a node !!*/
418 41 : return dom_base_node_construct(c, __class, n);
419 : }
420 :
421 95 : JSValue dom_element_construct(JSContext *c, GF_Node *n)
422 : {
423 : JSClassID __class = 0;
424 : GF_SceneGraph *par_sg;
425 95 : if (!n) return JS_NULL;
426 95 : par_sg = n->sgprivate->scenegraph;
427 195 : while (par_sg && !par_sg->get_element_class)
428 5 : par_sg = par_sg->parent_scene;
429 :
430 95 : if (par_sg && par_sg->get_element_class)
431 90 : __class = par_sg->get_element_class(n);
432 :
433 90 : if (!__class)
434 5 : __class = domElementClass.class_id;
435 :
436 95 : return dom_base_node_construct(c, __class, n);
437 : }
438 :
439 : static JSValue dom_text_construct(JSContext *c, GF_Node *n)
440 : {
441 2 : return dom_base_node_construct(c, domTextClass.class_id, n);
442 : }
443 :
444 123 : static void dom_unregister_node(GF_Node *n)
445 : {
446 123 : GF_SceneGraph *sg = n->sgprivate->scenegraph;
447 123 : if (!sg) return;
448 : /*!! node is being deleted !! */
449 123 : if (!n->sgprivate->num_instances) return;
450 :
451 123 : gf_node_unregister(n, NULL);
452 123 : if (sg->reference_count) {
453 78 : sg->reference_count--;
454 78 : if (!sg->reference_count)
455 7 : gf_sg_del(sg);
456 : }
457 : }
458 :
459 1 : JSValue dom_node_get_sibling(JSContext *c, GF_Node *n, Bool is_prev, Bool elt_only)
460 : {
461 : GF_Node *val;
462 : GF_ChildNodeItem *child;
463 : s32 idx, cur;
464 : GF_ParentNode *par;
465 1 : if (!n) return JS_NULL;
466 1 : par = (GF_ParentNode *)gf_node_get_parent(n, 0);
467 1 : if (!par) return JS_NULL;
468 :
469 1 : idx = gf_node_list_find_child(par->children, n);
470 1 : if (idx<0) return JS_NULL;
471 :
472 1 : if (!elt_only) {
473 1 : if (is_prev) {
474 1 : idx--;
475 1 : if (idx<0) return JS_NULL;
476 : }
477 0 : else idx++;
478 1 : return dom_node_construct(c, gf_node_list_get_child(par->children, idx) );
479 : }
480 : cur = 0;
481 : val = NULL;
482 0 : child = par->children;
483 0 : while (child) {
484 0 : if ((idx!=cur) && (child->node->sgprivate->tag!=TAG_DOMText)) {
485 : val = child->node;
486 : }
487 0 : if (is_prev) {
488 0 : if (idx<=cur)
489 : break;
490 : } else {
491 0 : if (idx>=cur) val = NULL;
492 0 : if (val && idx<cur)
493 : break;
494 : }
495 0 : child = child->next;
496 0 : cur++;
497 : }
498 0 : return dom_node_construct(c, val);
499 : }
500 :
501 :
502 :
503 : /*dom3 NodeList/NamedNodeMap*/
504 : typedef struct
505 : {
506 : /*set if the object is a childList from an existing node*/
507 : GF_ParentNode *owner;
508 : /*child list*/
509 : GF_ChildNodeItem *child;
510 : } DOMNodeList;
511 :
512 :
513 13 : static JSValue dom_nodelist_construct(JSContext *c, GF_ParentNode *n)
514 : {
515 : DOMNodeList *nl;
516 : JSValue new_obj;
517 13 : if (!n) return JS_NULL;
518 13 : GF_SAFEALLOC(nl, DOMNodeList);
519 13 : if (!nl) return JS_EXCEPTION;
520 :
521 13 : nl->owner = n;
522 13 : if (n->sgprivate->scenegraph->reference_count)
523 13 : n->sgprivate->scenegraph->reference_count++;
524 :
525 13 : gf_node_register((GF_Node*)n, NULL);
526 13 : new_obj = JS_NewObjectClass(c, domNodeListClass.class_id);
527 13 : JS_SetOpaque(new_obj, nl);
528 13 : return new_obj;
529 : }
530 :
531 436 : static void dom_nodelist_finalize(JSRuntime *rt, JSValue obj)
532 : {
533 436 : DOMNodeList *nl = JS_GetOpaque(obj, domNodeListClass.class_id);
534 436 : if (!nl) return;
535 :
536 27 : if (nl->owner) {
537 13 : dom_unregister_node((GF_Node*)nl->owner);
538 : } else {
539 : /*unregister all nodes for created lists*/
540 32 : while (nl->child) {
541 : GF_ChildNodeItem *child = nl->child;
542 18 : nl->child = child->next;
543 18 : dom_unregister_node(child->node);
544 18 : gf_free(child);
545 : }
546 : }
547 27 : gf_free(nl);
548 : }
549 :
550 39 : static JSValue dom_nodelist_item(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
551 : {
552 : GF_Node *n;
553 : u32 count;
554 : s32 idx;
555 : DOMNodeList *nl;
556 :
557 39 : nl = (DOMNodeList *) JS_GetOpaque(obj, domNodeListClass.class_id);
558 39 : if (!nl || (argc!=1) || JS_ToInt32(c, &idx, argv[0]))
559 0 : return JS_EXCEPTION;
560 :
561 39 : count = gf_node_list_get_count(nl->owner ? nl->owner->children : nl->child);
562 39 : if ((idx<0) || ((u32) idx>=count)) {
563 0 : return JS_NULL;
564 : }
565 39 : n = gf_node_list_get_child(nl->owner ? nl->owner->children : nl->child, idx);
566 39 : return dom_node_construct(c, n);
567 : }
568 :
569 83 : static JSValue dom_nodelist_getProperty(JSContext *c, JSValueConst obj, int magic)
570 : {
571 : DOMNodeList *nl;
572 : u32 count;
573 83 : nl = (DOMNodeList *) JS_GetOpaque(obj, domNodeListClass.class_id);
574 83 : if (!nl) return JS_EXCEPTION;
575 83 : count = gf_node_list_get_count(nl->owner ? nl->owner->children : nl->child);
576 :
577 83 : switch (magic) {
578 83 : case NODELIST_JSPROPERTY_LENGTH:
579 83 : return JS_NewInt32(c, count);
580 : default:
581 : break;
582 : }
583 0 : return js_throw_err(c, GF_DOM_EXC_SYNTAX_ERR);
584 : }
585 :
586 :
587 :
588 :
589 37 : static void dom_handler_remove(GF_Node *node, void *rs, Bool is_destroy)
590 : {
591 37 : if (is_destroy) {
592 : SVG_handlerElement *handler = (SVG_handlerElement *)node;
593 37 : if (handler->js_data) {
594 22 : if (handler->js_data->ctx && !JS_IsUndefined(handler->js_data->fun_val)) {
595 : /*unprotect the function*/
596 : JS_FreeValue(handler->js_data->ctx, handler->js_data->fun_val);
597 11 : gf_list_del_item(dom_rt->handlers, handler);
598 : }
599 11 : gf_free(handler->js_data);
600 11 : handler->js_data = NULL;
601 : }
602 : }
603 37 : }
604 :
605 39 : static void sg_js_get_event_target(JSContext *c, JSValue obj, GF_EventType evtType, GF_Node *vrml_node,
606 : GF_SceneGraph **sg, GF_DOMEventTarget **target, GF_Node **n)
607 : {
608 : Bool is_svg_document_class(JSContext *c, JSValue obj);
609 : Bool is_svg_element_class(JSContext *c, JSValue obj);
610 : Bool gf_mse_is_mse_object(JSContext *c, JSValue obj);
611 39 : *target = NULL;
612 39 : *sg = NULL;
613 39 : *n = NULL;
614 :
615 : #ifdef GPAC_ENABLE_HTML5_MEDIA
616 : if (gf_dom_event_get_category(evtType) == GF_DOM_EVENT_MEDIA) {
617 : void gf_html_media_get_event_target(JSContext *c, JSValue obj, GF_DOMEventTarget **target, GF_SceneGraph **sg);
618 : gf_html_media_get_event_target(c, obj, target, sg);
619 : if (*target && *sg) return;
620 : }
621 :
622 : if (gf_dom_event_get_category(evtType) == GF_DOM_EVENT_MEDIASOURCE) {
623 : void gf_mse_get_event_target(JSContext *c, JSValue obj, GF_DOMEventTarget **target, GF_SceneGraph **sg);
624 : gf_mse_get_event_target(c, obj, target, sg);
625 : if (*target && *sg) return;
626 : }
627 : #endif
628 :
629 :
630 39 : if (JS_GetOpaque(obj, domDocumentClass.class_id) || is_svg_document_class(c, obj)) {
631 : /*document interface*/
632 2 : *sg = dom_get_doc(c, obj);
633 2 : if (*sg) {
634 : #ifndef GPAC_DISABLE_SVG
635 2 : *target = (*sg)->dom_evt;
636 : #else
637 : return;
638 : #endif
639 : } else {
640 : return;
641 : }
642 37 : } else if (JS_GetOpaque(obj, domElementClass.class_id) || is_svg_element_class(c, obj) || vrml_node) {
643 : /*Element interface*/
644 37 : if (vrml_node) {
645 37 : *n = vrml_node;
646 : } else {
647 0 : *n = dom_get_element(c, obj);
648 : }
649 37 : if (*n) {
650 37 : *sg = (*n)->sgprivate->scenegraph;
651 37 : if (!(*n)->sgprivate->interact->dom_evt) {
652 7 : (*n)->sgprivate->interact->dom_evt = gf_dom_event_target_new(GF_DOM_EVENT_TARGET_NODE, *n);
653 : }
654 37 : *target = (*n)->sgprivate->interact->dom_evt;
655 : }
656 : } else {
657 : void xhr_get_event_target(JSContext *c, JSValue obj, GF_SceneGraph **sg, GF_DOMEventTarget **target);
658 :
659 0 : xhr_get_event_target(c, obj, sg, target);
660 : }
661 : }
662 :
663 39 : static GF_Err sg_js_parse_event_args(JSContext *c, JSValue obj, int argc, JSValue *argv,
664 : GF_EventType *evtType,
665 : char **callback, JSValue *funval, JSValue *evt_handler) {
666 : u32 offset = 0;
667 : const char *type = NULL;
668 : const char *inNS = NULL;
669 :
670 39 : *evtType = GF_EVENT_UNKNOWN;
671 39 : *callback = NULL;
672 39 : *funval = JS_NULL;
673 39 : if (evt_handler) *evt_handler = obj;
674 :
675 : /*NS version (4 args in DOM2, 5 in DOM3 for evt_group param)*/
676 39 : if (argc>=4) {
677 0 : if (!JS_CHECK_STRING(argv[0])) return GF_BAD_PARAM;
678 : inNS = JS_ToCString(c, argv[0]);
679 : offset = 1;
680 : }
681 :
682 78 : if (!JS_CHECK_STRING(argv[offset])) goto err_exit;
683 : type = JS_ToCString(c, argv[offset]);
684 :
685 79 : if (JS_CHECK_STRING(argv[offset+1])) {
686 : const char *str = JS_ToCString(c, argv[offset+1]);
687 1 : if (!str) goto err_exit;
688 1 : *callback = gf_strdup(str);
689 1 : JS_FreeCString(c, str);
690 38 : } else if (JS_IsFunction(c, argv[offset+1])) {
691 37 : *funval = argv[offset+1];
692 :
693 2 : } else if (JS_IsObject(argv[offset+1])) {
694 : JSValue evt_fun;
695 0 : if (evt_handler) {
696 0 : *evt_handler = JS_DupValue(c, argv[offset+1]);
697 : } else {
698 : goto err_exit;
699 : }
700 0 : evt_fun = JS_GetPropertyStr(c, *evt_handler, "handleEvent");
701 0 : if (!JS_IsFunction(c, evt_fun) ) goto err_exit;
702 0 : *funval = evt_fun;
703 : }
704 :
705 39 : *evtType = gf_dom_event_type_by_name(type);
706 39 : if (*evtType==GF_EVENT_UNKNOWN) goto err_exit;
707 :
708 39 : JS_FreeCString(c, type);
709 39 : JS_FreeCString(c, inNS);
710 39 : return GF_OK;
711 :
712 0 : err_exit:
713 0 : if (type) JS_FreeCString(c, type);
714 0 : if (inNS) JS_FreeCString(c, inNS);
715 0 : if (callback) gf_free(*callback);
716 0 : *callback = NULL;
717 0 : *evtType = GF_EVENT_UNKNOWN;
718 0 : *funval = JS_NULL;
719 0 : if (evt_handler) *evt_handler = JS_UNDEFINED;
720 : return GF_BAD_PARAM;
721 : }
722 :
723 :
724 38 : static GF_Node *create_listener(GF_SceneGraph *sg, GF_EventType evtType, GF_Node *n, GF_Node *vrml_node,
725 : JSContext *c, char *callback, JSValue funval, JSValue evt_handler)
726 : {
727 : GF_FieldInfo info;
728 : GF_Node *listener;
729 : SVG_handlerElement *handler;
730 :
731 38 : listener = gf_node_new(sg, TAG_SVG_listener);
732 : /*we don't register the listener with the parent node , it will be registered
733 : on gf_dom_listener_add*/
734 :
735 : /*!!! create the handler in the scene owning the script context !!! */
736 38 : handler = (SVG_handlerElement *) gf_node_new(sg, TAG_SVG_handler);
737 : /*we register the handler with the listener node to avoid modifying the DOM*/
738 38 : gf_node_register((GF_Node *)handler, listener);
739 38 : gf_node_list_add_child(& ((GF_ParentNode *)listener)->children, (GF_Node*)handler);
740 :
741 38 : if (!callback) {
742 37 : GF_SAFEALLOC(handler->js_data, struct js_handler_context)
743 37 : if (handler->js_data) {
744 37 : handler->js_data->fun_val = funval;
745 37 : handler->js_data->ctx = c;
746 37 : if (JS_IsFunction(c, funval)) {
747 : /*protect the function - we don't know how it was passed to us, so prevent it from being GCed*/
748 74 : handler->js_data->fun_val = JS_DupValue(c, funval);
749 37 : handler->sgprivate->UserCallback = dom_handler_remove;
750 37 : gf_list_add(dom_rt->handlers, handler);
751 : }
752 37 : handler->js_data->evt_listen_obj = evt_handler;
753 : }
754 : }
755 :
756 : /*create attributes if needed*/
757 38 : gf_node_get_attribute_by_tag(listener, TAG_XMLEV_ATT_event, GF_TRUE, GF_FALSE, &info);
758 38 : ((XMLEV_Event*)info.far_ptr)->type = evtType;
759 38 : gf_node_get_attribute_by_tag(listener, TAG_XMLEV_ATT_handler, GF_TRUE, GF_FALSE, &info);
760 38 : ((XMLRI*)info.far_ptr)->target = (GF_Node*)handler;
761 38 : if (n) {
762 37 : gf_node_get_attribute_by_tag(listener, TAG_XMLEV_ATT_target, GF_TRUE, GF_FALSE, &info);
763 37 : ((XMLRI*)info.far_ptr)->target = n;
764 : }
765 :
766 38 : gf_node_get_attribute_by_tag((GF_Node*)handler, TAG_XMLEV_ATT_event, GF_TRUE, GF_FALSE, &info);
767 38 : ((XMLEV_Event*)info.far_ptr)->type = evtType;
768 :
769 38 : if (callback) gf_dom_add_text_node((GF_Node *)handler, gf_strdup(callback));
770 :
771 : #ifndef GPAC_DISABLE_SVG
772 38 : if (handler->sgprivate->scenegraph->svg_js)
773 0 : handler->handle_event = gf_sg_handle_dom_event;
774 : #endif
775 :
776 38 : if (vrml_node) {
777 : assert(handler->js_data);
778 37 : handler->js_data->ctx = c;
779 : #ifndef GPAC_DISABLE_VRML
780 37 : if (vrml_node->sgprivate->tag <= GF_NODE_RANGE_LAST_VRML)
781 37 : handler->handle_event = gf_sg_handle_dom_event_for_vrml;
782 : #endif
783 : }
784 :
785 38 : return listener;
786 : }
787 :
788 : /*eventListeners routines used by document, element and XHR interfaces*/
789 38 : JSValue gf_sg_js_event_add_listener(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv, GF_Node *vrml_node)
790 : {
791 38 : GF_DOMEventTarget *target = NULL;
792 : GF_Node *listener = NULL;
793 38 : GF_EventType evtType = GF_EVENT_UNKNOWN;
794 38 : GF_SceneGraph *sg = NULL;
795 38 : char *callback = NULL;
796 38 : JSValue funval = JS_UNDEFINED;
797 38 : GF_Node *n = NULL;
798 38 : JSValue evt_handler = JS_UNDEFINED;
799 : GF_Err e;
800 :
801 : /* Determine the event type and handler params */
802 38 : e = sg_js_parse_event_args(c, obj, argc, argv, &evtType, &callback, &funval, &evt_handler);
803 38 : if (e != GF_OK) goto err_exit;
804 :
805 : /* First retrieve the scenegraph and the GF_DOMEventTarget object */
806 38 : sg_js_get_event_target(c, obj, evtType, vrml_node, &sg, &target, &n);
807 76 : if (!sg || !target) goto err_exit;
808 :
809 38 : listener = create_listener(sg, evtType, n, vrml_node, c, callback, funval, evt_handler);
810 38 : if (!listener) goto err_exit;
811 :
812 : /*don't add listener directly, post it and wait for event processing*/
813 38 : if (n) {
814 37 : gf_sg_listener_post_add((GF_Node *) n, listener);
815 : } else {
816 1 : gf_sg_listener_associate(listener, target);
817 : }
818 :
819 38 : err_exit:
820 38 : if (callback) gf_free(callback);
821 38 : if (e)
822 0 : return js_throw_err(c, GF_DOM_EXC_SYNTAX_ERR);
823 38 : return JS_UNDEFINED;
824 : }
825 :
826 :
827 1 : JSValue dom_event_add_listener(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
828 : {
829 1 : return gf_sg_js_event_add_listener(c, obj, argc, argv, NULL);
830 : }
831 :
832 1 : JSValue gf_sg_js_event_remove_listener(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv, GF_Node *vrml_node)
833 : {
834 : #ifndef GPAC_DISABLE_SVG
835 1 : char *callback = NULL;
836 1 : GF_EventType evtType = GF_EVENT_UNKNOWN;
837 : u32 i, count;
838 1 : GF_Node *node = NULL;
839 1 : JSValue funval = JS_UNDEFINED;
840 1 : GF_SceneGraph *sg = NULL;
841 1 : GF_DOMEventTarget *target = NULL;
842 : GF_Err e;
843 :
844 : /* Determine the event type and handler params */
845 1 : e = sg_js_parse_event_args(c, obj, argc, argv, &evtType, &callback, &funval, NULL);
846 1 : if (e != GF_OK) goto err_exit;
847 :
848 : /* First retrieve the scenegraph and the GF_DOMEventTarget object */
849 1 : sg_js_get_event_target(c, obj, evtType, vrml_node, &sg, &target, &node);
850 1 : if (!sg || !target) return JS_TRUE;
851 :
852 : /*flush all pending add_listener*/
853 1 : gf_dom_listener_process_add(sg);
854 :
855 1 : count = gf_list_count(target->listeners);
856 2 : for (i=0; i<count; i++) {
857 : GF_FieldInfo info;
858 : GF_DOMText *txt;
859 : SVG_handlerElement *hdl;
860 1 : GF_Node *el = (GF_Node *)gf_list_get(target->listeners, i);
861 :
862 1 : gf_node_get_attribute_by_tag(el, TAG_XMLEV_ATT_event, GF_FALSE, GF_FALSE, &info);
863 2 : if (!info.far_ptr) continue;
864 1 : if (((XMLEV_Event*)info.far_ptr)->type != evtType) continue;
865 :
866 1 : gf_node_get_attribute_by_tag(el, TAG_XMLEV_ATT_handler, GF_FALSE, GF_FALSE, &info);
867 1 : if (!info.far_ptr) continue;
868 1 : hdl = (SVG_handlerElement *) ((XMLRI*)info.far_ptr)->target;
869 1 : if (!hdl) continue;
870 2 : if (! JS_IsNull(funval) ) {
871 : Bool is_same = GF_FALSE;
872 :
873 : const char * f1 = JS_ToCString(c, funval);
874 : const char * f2 = JS_ToCString(c, funval);
875 0 : if (f1 && f2 && !strcmp(f1, f2)) is_same = GF_TRUE;
876 0 : JS_FreeCString(c, f1);
877 0 : JS_FreeCString(c, f2);
878 0 : if (!is_same) continue;
879 1 : } else if (hdl->children) {
880 1 : txt = (GF_DOMText *) hdl->children->node;
881 1 : if (txt->sgprivate->tag != TAG_DOMText) continue;
882 1 : if (!txt->textContent || !callback || strcmp(txt->textContent, callback)) continue;
883 : } else {
884 0 : continue;
885 : }
886 :
887 : /*this will destroy the listener and its child handler*/
888 0 : gf_dom_listener_del(el, target);
889 0 : break;
890 : }
891 : #endif
892 :
893 1 : err_exit:
894 1 : if (callback) JS_FreeCString(c, callback);
895 1 : return JS_UNDEFINED;
896 : }
897 :
898 1 : JSValue dom_event_remove_listener(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
899 : {
900 1 : return gf_sg_js_event_remove_listener(c, obj, argc, argv, NULL);
901 : }
902 :
903 : /*dom3 node*/
904 1774 : static void dom_node_finalize(JSRuntime *rt, JSValue obj)
905 : {
906 1774 : GF_Node *n = (GF_Node *) JS_GetOpaque_Nocheck(obj);
907 : /*the JS proto of the svgClass or a destroyed object*/
908 1774 : if (!n) return;
909 92 : if (!n->sgprivate) return;
910 :
911 92 : JS_SetOpaque(obj, NULL);
912 92 : if (n->sgprivate->interact)
913 92 : gf_list_del_item(n->sgprivate->scenegraph->objects, n->sgprivate->interact->js_binding);
914 92 : gf_sg_js_dom_pre_destroy(rt, n->sgprivate->scenegraph, n);
915 92 : dom_unregister_node(n);
916 : }
917 :
918 218 : static Bool check_dom_parents(JSContext *c, GF_Node *n, GF_Node *parent)
919 : {
920 218 : GF_ParentList *par = n->sgprivate->parents;
921 218 : if (n->sgprivate->scenegraph != parent->sgprivate->scenegraph) {
922 0 : js_throw_err(c, GF_DOM_EXC_WRONG_DOCUMENT_ERR);
923 0 : return GF_FALSE;
924 : }
925 349 : while (par) {
926 131 : if (par->node==parent) {
927 0 : js_throw_err(c, GF_DOM_EXC_HIERARCHY_REQUEST_ERR);
928 0 : return GF_FALSE;
929 : }
930 131 : if (!check_dom_parents(c, par->node, parent))
931 : return GF_FALSE;
932 131 : par = par->next;
933 : }
934 : return GF_TRUE;
935 : }
936 :
937 88 : static void dom_node_inserted(JSContext *c, GF_Node *n, GF_Node *parent, s32 pos)
938 : {
939 : GF_ParentNode *old_parent;
940 : Bool do_init = GF_FALSE;
941 :
942 : /*if node is already in graph, remove it from its parent*/
943 88 : old_parent = (GF_ParentNode*)gf_node_get_parent(n, 0);
944 88 : if (old_parent) {
945 : /*prevent destruction when removing node*/
946 0 : n->sgprivate->num_instances++;
947 0 : gf_node_list_del_child(&old_parent->children, n);
948 0 : gf_node_unregister(n, (GF_Node*)old_parent);
949 0 : n->sgprivate->num_instances--;
950 : } else {
951 88 : do_init = (n->sgprivate->UserCallback || n->sgprivate->UserPrivate) ? GF_FALSE : GF_TRUE;
952 :
953 : }
954 :
955 88 : if (pos<0) gf_node_list_add_child( & ((GF_ParentNode *)parent)->children, n);
956 0 : else gf_node_list_insert_child( & ((GF_ParentNode *)parent)->children, n, (u32) pos);
957 88 : gf_node_register(n, parent);
958 :
959 88 : if (do_init) {
960 : /*node is a handler, create listener*/
961 88 : if (parent && (n->sgprivate->tag==TAG_SVG_handler)) {
962 0 : gf_dom_listener_build_ex(parent, 0, 0, n, NULL);
963 : }
964 88 : gf_node_init(n);
965 :
966 :
967 : #ifndef GPAC_DISABLE_SVG
968 88 : if (n->sgprivate->interact && n->sgprivate->interact->dom_evt) {
969 : GF_DOM_Event evt;
970 : memset(&evt, 0, sizeof(GF_DOM_Event));
971 0 : evt.type = GF_EVENT_LOAD;
972 0 : gf_dom_event_fire(n, &evt);
973 : }
974 : #endif
975 : }
976 : /*node is being re-inserted, activate it in case*/
977 88 : if (!old_parent) gf_node_activate(n);
978 :
979 : dom_node_changed(parent, GF_TRUE, NULL);
980 88 : }
981 :
982 1 : static JSValue xml_node_insert_before(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
983 : {
984 : s32 idx;
985 : u32 tag;
986 : GF_Node *n, *target, *new_node;
987 : GF_ParentNode *par;
988 :
989 2 : if (!argc || !JS_IsObject(argv[0]))
990 0 : return JS_EXCEPTION;
991 :
992 : n = dom_get_node(obj);
993 : if (!n) {
994 0 : return js_throw_err(c, GF_DOM_EXC_HIERARCHY_REQUEST_ERR);
995 : }
996 :
997 : new_node = dom_get_node(argv[0]);
998 : if (!new_node)
999 0 : return js_throw_err(c, GF_DOM_EXC_SYNTAX_ERR);
1000 :
1001 : target = NULL;
1002 1 : if ((argc==2) && JS_IsObject(argv[1]) && !JS_IsNull(argv[1])) {
1003 : target = dom_get_node(argv[1]);
1004 0 : if (!target) return JS_NULL;
1005 : }
1006 1 : tag = gf_node_get_tag(n);
1007 1 : if (tag==TAG_DOMText)
1008 0 : return js_throw_err(c, GF_DOM_EXC_SYNTAX_ERR);
1009 :
1010 1 : if (!check_dom_parents(c, n, new_node))
1011 0 : return js_throw_err(c, GF_DOM_EXC_VALIDATION_ERR);
1012 : par = (GF_ParentNode*)n;
1013 : idx = -1;
1014 1 : if (target) {
1015 0 : idx = gf_node_list_find_child(par->children, target);
1016 0 : if (idx<0) {
1017 0 : return js_throw_err(c, GF_DOM_EXC_NOT_FOUND_ERR);
1018 : }
1019 : }
1020 1 : dom_node_inserted(c, new_node, n, idx);
1021 : return JS_DupValue(c, argv[0] );
1022 : }
1023 :
1024 86 : static JSValue xml_node_append_child(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1025 : {
1026 : u32 tag;
1027 : GF_Node *n, *new_node;
1028 :
1029 172 : if (!argc || !JS_IsObject(argv[0])) {
1030 0 : return JS_EXCEPTION;
1031 : }
1032 : n = dom_get_node(obj);
1033 : if (!n) {
1034 0 : return js_throw_err(c, GF_DOM_EXC_HIERARCHY_REQUEST_ERR);
1035 : }
1036 :
1037 : new_node = dom_get_node(argv[0]);
1038 : if (!new_node) {
1039 0 : return js_throw_err(c, GF_DOM_EXC_SYNTAX_ERR);
1040 : }
1041 86 : tag = gf_node_get_tag(n);
1042 86 : if (tag==TAG_DOMText) {
1043 0 : return js_throw_err(c, GF_DOM_EXC_SYNTAX_ERR);
1044 : }
1045 :
1046 86 : if (!check_dom_parents(c, n, new_node)) {
1047 0 : return js_throw_err(c, GF_DOM_EXC_VALIDATION_ERR);
1048 : }
1049 :
1050 86 : dom_node_inserted(c, new_node, n, -1);
1051 : return JS_DupValue(c, argv[0]);
1052 : }
1053 :
1054 : void svg_mark_gc(struct __tag_svg_script_ctx *svg_js);
1055 :
1056 1 : static JSValue xml_node_replace_child(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1057 : {
1058 : s32 idx;
1059 : u32 tag;
1060 : GF_Node *n, *new_node, *old_node;
1061 : GF_ParentNode *par;
1062 :
1063 3 : if ((argc!=2) || !JS_IsObject(argv[0]) || !JS_IsObject(argv[1])) return JS_EXCEPTION;
1064 : n = dom_get_node(obj);
1065 0 : if (!n) return js_throw_err(c, GF_DOM_EXC_HIERARCHY_REQUEST_ERR);
1066 :
1067 : new_node = dom_get_node(argv[0]);
1068 0 : if (!new_node) return js_throw_err(c, GF_DOM_EXC_HIERARCHY_REQUEST_ERR);
1069 : old_node = dom_get_node(argv[1]);
1070 0 : if (!old_node) return js_throw_err(c, GF_DOM_EXC_HIERARCHY_REQUEST_ERR);
1071 1 : tag = gf_node_get_tag(n);
1072 1 : if (tag==TAG_DOMText) return js_throw_err(c, GF_DOM_EXC_HIERARCHY_REQUEST_ERR);
1073 : par = (GF_ParentNode*)n;
1074 1 : idx = gf_node_list_find_child(par->children, old_node);
1075 1 : if (idx<0) return js_throw_err(c, GF_DOM_EXC_HIERARCHY_REQUEST_ERR);
1076 :
1077 1 : gf_node_list_del_child(&par->children, old_node);
1078 1 : gf_node_unregister(old_node, n);
1079 :
1080 1 : dom_node_inserted(c, new_node, n, -1);
1081 :
1082 : /*whenever we remove a node from the tree, call GC to cleanup the JS binding if any. Not doing so may screw up node IDs*/
1083 1 : svg_mark_gc(n->sgprivate->scenegraph->svg_js);
1084 : return JS_DupValue(c, argv[0]);
1085 : }
1086 :
1087 1 : static JSValue xml_node_remove_child(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1088 : {
1089 : u32 tag;
1090 : GF_Node *n, *old_node;
1091 : GF_ParentNode *par;
1092 2 : if (!argc || !JS_IsObject(argv[0])) return JS_TRUE;
1093 :
1094 : n = dom_get_node(obj);
1095 0 : if (!n) return js_throw_err(c, GF_DOM_EXC_HIERARCHY_REQUEST_ERR);
1096 :
1097 : old_node = dom_get_node(argv[0]);
1098 0 : if (!old_node) return js_throw_err(c, GF_DOM_EXC_HIERARCHY_REQUEST_ERR);
1099 1 : tag = gf_node_get_tag(n);
1100 1 : if (tag==TAG_DOMText) return js_throw_err(c, GF_DOM_EXC_HIERARCHY_REQUEST_ERR);
1101 : par = (GF_ParentNode*)n;
1102 :
1103 : /*if node is present in parent, unregister*/
1104 1 : if (gf_node_list_del_child(&par->children, old_node)) {
1105 1 : gf_node_unregister(old_node, n);
1106 : } else {
1107 0 : return js_throw_err(c, GF_DOM_EXC_NOT_FOUND_ERR);
1108 : }
1109 :
1110 : dom_node_changed(n, GF_TRUE, NULL);
1111 : /*whenever we remove a node from the tree, call GC to cleanup the JS binding if any. Not doing so may screw up node IDs*/
1112 1 : svg_mark_gc(n->sgprivate->scenegraph->svg_js);
1113 : return JS_DupValue(c, argv[0]);
1114 : }
1115 :
1116 1 : static JSValue xml_clone_node(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1117 : {
1118 : Bool deep;
1119 : GF_Node *n, *clone;
1120 :
1121 : n = dom_get_node(obj);
1122 0 : if (!n) return js_throw_err(c, GF_DOM_EXC_HIERARCHY_REQUEST_ERR);
1123 1 : deep = argc ? (JS_ToBool(c, argv[0]) ? GF_TRUE : GF_FALSE) : GF_FALSE;
1124 :
1125 1 : clone = gf_node_clone(n->sgprivate->scenegraph, n, NULL, "", deep);
1126 1 : return dom_node_construct(c, clone);
1127 : }
1128 :
1129 1 : static JSValue xml_node_has_children(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1130 : {
1131 : GF_Node *n;
1132 : n = dom_get_node(obj);
1133 0 : if (!n) return js_throw_err(c, GF_DOM_EXC_HIERARCHY_REQUEST_ERR);
1134 1 : return ((GF_ParentNode*)n)->children ? JS_TRUE : JS_FALSE;
1135 : }
1136 :
1137 1 : static JSValue xml_node_has_attributes(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1138 : {
1139 : u32 tag;
1140 : GF_Node *n;
1141 :
1142 : n = dom_get_node(obj);
1143 0 : if (!n) return js_throw_err(c, GF_DOM_EXC_HIERARCHY_REQUEST_ERR);
1144 1 : tag = gf_node_get_tag(n);
1145 1 : if (tag>=GF_NODE_FIRST_DOM_NODE_TAG) {
1146 1 : return ((GF_DOMNode*)n)->attributes ? JS_TRUE : JS_FALSE;
1147 : }
1148 : /*not supported for other node types*/
1149 0 : return JS_FALSE;
1150 : }
1151 :
1152 1 : static JSValue xml_node_is_same_node(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1153 : {
1154 : GF_Node *n, *a_node;
1155 :
1156 2 : if (!argc || !JS_IsObject(argv[0])) return JS_TRUE;
1157 : n = dom_get_node(obj);
1158 0 : if (!n) return js_throw_err(c, GF_DOM_EXC_HIERARCHY_REQUEST_ERR);
1159 :
1160 : a_node = dom_get_node(argv[0]);
1161 0 : if (!a_node) return js_throw_err(c, GF_DOM_EXC_HIERARCHY_REQUEST_ERR);
1162 1 : return (a_node==n) ? JS_TRUE : JS_FALSE;
1163 : }
1164 :
1165 1 : static const char *node_get_local_name(GF_Node *node)
1166 : {
1167 : const char *res;
1168 : GF_List *l;
1169 1 : if (!node) return NULL;
1170 1 : l = node->sgprivate->scenegraph->ns;
1171 1 : node->sgprivate->scenegraph->ns = NULL;
1172 1 : res = gf_node_get_class_name(node);
1173 1 : node->sgprivate->scenegraph->ns = l;
1174 1 : return res;
1175 : }
1176 :
1177 1 : static const char *node_lookup_namespace_by_tag(GF_Node *node, u32 tag)
1178 : {
1179 : /*browse attributes*/
1180 : GF_DOMAttribute *att;
1181 1 : if (!node) return NULL;
1182 1 : att = ((SVG_Element*)node)->attributes;
1183 7 : while (att) {
1184 6 : if (att->tag==TAG_DOM_ATT_any) {
1185 : GF_DOMFullAttribute *datt = (GF_DOMFullAttribute*)att;
1186 6 : if (datt->name && !strncmp(datt->name, "xmlns", 5)) {
1187 1 : char *xmlns = *(DOM_String *) datt->data;
1188 1 : u32 crc = gf_crc_32(xmlns, (u32) strlen(xmlns));
1189 1 : if (tag==crc)
1190 : return xmlns;
1191 1 : if (!tag && !strcmp(datt->name, "xmlns"))
1192 : return xmlns;
1193 : }
1194 : }
1195 5 : att = att->next;
1196 : }
1197 : /*browse for parent*/
1198 0 : return node_lookup_namespace_by_tag(gf_node_get_parent(node, 0), tag);
1199 : }
1200 :
1201 : #ifdef GPAC_UNUSED_FUNC
1202 : /**
1203 : * FIXME : function is not used by anybody
1204 : */
1205 : static u32 get_namespace_code_by_prefix(GF_Node *node, char *prefix)
1206 : {
1207 : /*browse attributes*/
1208 : GF_DOMAttribute *att;
1209 : if (!node) return 0;
1210 : att = ((SVG_Element*)node)->attributes;
1211 : while (att) {
1212 : if (att->tag==TAG_DOM_ATT_any) {
1213 : GF_DOMFullAttribute *datt = (GF_DOMFullAttribute*)att;
1214 : if (!prefix) {
1215 : if (!strcmp(datt->name, "xmlns")) return datt->xmlns;
1216 : } else if (!strncmp(datt->name, "xmlns:", 6)) {
1217 : if (!strcmp(datt->name+6, prefix)) return datt->xmlns;
1218 : }
1219 : }
1220 : att = att->next;
1221 : }
1222 : /*browse for parent*/
1223 : return get_namespace_code_by_prefix(gf_node_get_parent(node, 0), prefix);
1224 : }
1225 : #endif /*GPAC_UNUSED_FUNC*/
1226 :
1227 191 : static JSValue dom_node_getProperty(JSContext *c, JSValueConst obj, int magic)
1228 : {
1229 : u32 tag;
1230 : GF_Node *n;
1231 : GF_SceneGraph *sg = NULL;
1232 : GF_ParentNode *par;
1233 :
1234 : n = dom_get_node(obj);
1235 : if (!n) {
1236 : sg = dom_get_doc(c, obj);
1237 0 : if (!sg) return JS_EXCEPTION;
1238 : }
1239 191 : tag = n ? gf_node_get_tag(n) : 0;
1240 : par = (GF_ParentNode*)n;
1241 :
1242 191 : switch (magic) {
1243 139 : case NODE_JSPROPERTY_NODENAME:
1244 139 : if (sg) {
1245 0 : return JS_NewString(c, "#document");
1246 : }
1247 139 : else if (tag==TAG_DOMText) {
1248 : GF_DOMText *txt = (GF_DOMText *)n;
1249 108 : if (txt->type==GF_DOM_TEXT_CDATA) return JS_NewString(c, "#cdata-section");
1250 108 : else return JS_NewString(c, "#text");
1251 : }
1252 31 : return JS_NewString(c, gf_node_get_class_name(n) );
1253 :
1254 0 : case NODE_JSPROPERTY_NODEVALUE:
1255 0 : if (tag==TAG_DOMText) {
1256 : GF_DOMText *txt = (GF_DOMText *)n;
1257 0 : return JS_NewString(c, txt->textContent);
1258 : }
1259 0 : return JS_NULL;
1260 0 : case NODE_JSPROPERTY_NODETYPE:
1261 0 : if (sg) return JS_NewInt32(c, 9);
1262 0 : else if (tag==TAG_DOMText) {
1263 : GF_DOMText *txt = (GF_DOMText *)n;
1264 0 : if (txt->type==GF_DOM_TEXT_CDATA) return JS_NewInt32(c, 4);
1265 : else return JS_NewInt32(c, 3);
1266 : }
1267 : return JS_NewInt32(c, 1);
1268 :
1269 0 : case NODE_JSPROPERTY_PARENTNODE:
1270 0 : if (sg) {
1271 0 : return JS_NULL;
1272 : }
1273 : /*if root node of the tree, the parentNode is the document*/
1274 0 : else if (n->sgprivate->scenegraph->RootNode==n) {
1275 0 : return dom_document_construct(c, n->sgprivate->scenegraph);
1276 : } else {
1277 0 : return dom_node_construct(c, gf_node_get_parent(n, 0) );
1278 : }
1279 :
1280 13 : case NODE_JSPROPERTY_CHILDNODES:
1281 : /*NOT SUPPORTED YET*/
1282 13 : if (sg) return JS_UNDEFINED;
1283 13 : else if (tag==TAG_DOMText) return JS_NULL;
1284 13 : else return dom_nodelist_construct(c, par);
1285 :
1286 0 : case NODE_JSPROPERTY_FIRSTCHILD:
1287 0 : if (sg) return dom_node_construct(c, sg->RootNode);
1288 0 : else if (tag==TAG_DOMText) return JS_NULL;
1289 0 : else if (!par->children) return JS_NULL;
1290 0 : else return dom_node_construct(c, par->children->node);
1291 :
1292 0 : case NODE_JSPROPERTY_LASTCHILD:
1293 0 : if (sg) return dom_node_construct(c, sg->RootNode);
1294 0 : else if ((tag==TAG_DOMText) || !par->children) return JS_NULL;
1295 0 : else return dom_node_construct(c, gf_node_list_get_child(par->children, -1) );
1296 :
1297 1 : case NODE_JSPROPERTY_PREVIOUSSIBLING:
1298 : /*works for doc as well since n is NULL*/
1299 1 : return dom_node_get_sibling(c, n, GF_TRUE, GF_FALSE);
1300 :
1301 0 : case NODE_JSPROPERTY_NEXTSIBLING:
1302 0 : return dom_node_get_sibling(c, n, GF_FALSE, GF_FALSE);
1303 :
1304 0 : case NODE_JSPROPERTY_ATTRIBUTES:
1305 : /*NOT SUPPORTED YET*/
1306 0 : return JS_UNDEFINED;
1307 :
1308 0 : case NODE_JSPROPERTY_OWNERDOCUMENT:
1309 0 : if (sg) return JS_NULL;
1310 0 : else return dom_document_construct(c, n->sgprivate->scenegraph);
1311 :
1312 1 : case NODE_JSPROPERTY_NAMESPACEURI:
1313 1 : if (!sg) {
1314 : const char *xmlns;
1315 1 : tag = gf_xml_get_element_namespace(n);
1316 1 : xmlns = gf_sg_get_namespace(n->sgprivate->scenegraph, tag);
1317 1 : if (!xmlns) xmlns = node_lookup_namespace_by_tag(n, tag);
1318 1 : return xmlns ? JS_NewString(c, xmlns) : JS_NULL;
1319 : }
1320 0 : return JS_NULL;
1321 0 : case NODE_JSPROPERTY_PREFIX:
1322 0 : if (sg) tag = gf_sg_get_namespace_code(sg, NULL);
1323 0 : else tag = gf_xml_get_element_namespace(n);
1324 :
1325 0 : if (tag) {
1326 0 : char *xmlns = (char *)gf_sg_get_namespace_qname(sg ? sg : n->sgprivate->scenegraph, tag);
1327 0 : if (xmlns) return JS_NewString(c, xmlns);
1328 : }
1329 0 : return JS_NULL;
1330 1 : case NODE_JSPROPERTY_LOCALNAME:
1331 1 : if (!sg && (tag!=TAG_DOMText)) {
1332 1 : return JS_NewString(c, node_get_local_name(n) );
1333 : }
1334 0 : return JS_NULL;
1335 0 : case NODE_JSPROPERTY_BASEURI:
1336 : /*NOT SUPPORTED YET*/
1337 0 : return JS_NULL;
1338 :
1339 36 : case NODE_JSPROPERTY_TEXTCONTENT:
1340 36 : if (!sg) {
1341 36 : char *res = gf_dom_flatten_textContent(n);
1342 36 : JSValue ret = JS_NewString(c, res ? res : "");
1343 36 : if (res) gf_free(res);
1344 36 : return ret;
1345 : }
1346 0 : return JS_NewString(c, "");
1347 :
1348 0 : case NODE_JSPROPERTY_FIRSTELEMENTCHILD:
1349 0 : if (n->sgprivate->tag!=TAG_DOMText) {
1350 0 : GF_ChildNodeItem *child = ((GF_ParentNode*)n)->children;
1351 0 : while (child) {
1352 0 : if (child->node->sgprivate->tag != TAG_DOMText) {
1353 0 : return dom_element_construct(c, child->node);
1354 : }
1355 0 : child = child->next;
1356 : }
1357 : }
1358 0 : return JS_NULL;
1359 :
1360 0 : case NODE_JSPROPERTY_LASTELEMENTCHILD:
1361 0 : if (n->sgprivate->tag!=TAG_DOMText) {
1362 : GF_Node *last = NULL;
1363 0 : GF_ChildNodeItem *child = ((GF_ParentNode*)n)->children;
1364 0 : while (child) {
1365 0 : if (child->node->sgprivate->tag != TAG_DOMText) {
1366 : last = child->node;
1367 : }
1368 0 : child = child->next;
1369 : }
1370 0 : if (last) return dom_element_construct(c, last);
1371 : }
1372 0 : return JS_NULL;
1373 0 : case NODE_JSPROPERTY_PREVIOUSELEMENTSIBLING:
1374 0 : return dom_node_get_sibling(c, n, GF_TRUE, GF_TRUE);
1375 :
1376 0 : case NODE_JSPROPERTY_NEXTELEMENTSIBLING:
1377 0 : return dom_node_get_sibling(c, n, GF_FALSE, GF_TRUE);
1378 : }
1379 0 : return JS_UNDEFINED;
1380 : }
1381 :
1382 68 : void dom_node_set_textContent(GF_Node *n, char *text)
1383 : {
1384 : GF_FieldInfo info;
1385 68 : gf_dom_set_textContent(n, text);
1386 :
1387 68 : gf_node_dirty_set(n, GF_SG_CHILD_DIRTY, GF_FALSE);
1388 : memset(&info, 0, sizeof(GF_FieldInfo));
1389 68 : info.fieldIndex = (u32) -1;
1390 68 : gf_node_changed(n, &info);
1391 68 : }
1392 :
1393 68 : static JSValue dom_node_setProperty(JSContext *c, JSValueConst obj, JSValueConst value, int magic)
1394 : {
1395 : u32 tag;
1396 : GF_Node *n;
1397 :
1398 : n = dom_get_node(obj);
1399 : /*note an element - we don't support property setting on document yet*/
1400 0 : if (!n) return JS_EXCEPTION;
1401 :
1402 68 : tag = n ? gf_node_get_tag(n) : 0;
1403 68 : switch (magic) {
1404 0 : case NODE_JSPROPERTY_NODEVALUE:
1405 0 : if ((tag==TAG_DOMText) && JS_CHECK_STRING(value)) {
1406 : const char *str;
1407 : GF_DOMText *txt = (GF_DOMText *)n;
1408 0 : if (txt->textContent) gf_free(txt->textContent);
1409 : str = JS_ToCString(c, value);
1410 0 : txt->textContent = str ? gf_strdup(str) : NULL;
1411 0 : JS_FreeCString(c, str);
1412 : dom_node_changed(n, GF_TRUE, NULL);
1413 : }
1414 : /*we only support element and sg in the Node interface, no set*/
1415 0 : return JS_TRUE;
1416 0 : case NODE_JSPROPERTY_PREFIX:
1417 : /*NOT SUPPORTED YET*/
1418 0 : return JS_TRUE;
1419 68 : case NODE_JSPROPERTY_TEXTCONTENT:
1420 : {
1421 : const char *txt = JS_ToCString(c, value);
1422 68 : dom_node_set_textContent(n, (char *) txt);
1423 68 : if (txt) JS_FreeCString(c, txt);
1424 : }
1425 68 : return JS_TRUE;
1426 : }
1427 : /*not supported*/
1428 0 : return JS_TRUE;
1429 : }
1430 :
1431 :
1432 : /*dom3 document*/
1433 :
1434 : /*don't attempt to do anything with the scenegraph, it may be destroyed
1435 : fortunately a sg cannot be created like that...*/
1436 14 : void dom_document_finalize(JSRuntime *rt, JSValue obj)
1437 : {
1438 : GF_SceneGraph *sg;
1439 14 : sg = (GF_SceneGraph*) JS_GetOpaque_Nocheck(obj);
1440 : /*the JS proto of the svgClass or a destroyed object*/
1441 14 : if (!sg) return;
1442 :
1443 8 : if (sg->js_data) {
1444 8 : JS_SetOpaque(sg->js_data->document, NULL);
1445 8 : JS_FreeValueRT(rt, sg->js_data->document);
1446 8 : gf_free(sg->js_data);
1447 8 : sg->js_data = NULL;
1448 : }
1449 :
1450 : //no need to unregister root, we did not registered it in the constructor
1451 : // if (sg->RootNode) {
1452 : // gf_node_unregister(sg->RootNode, NULL);
1453 : // }
1454 :
1455 8 : if (sg->reference_count) {
1456 8 : sg->reference_count--;
1457 8 : if (!sg->reference_count)
1458 0 : gf_sg_del(sg);
1459 : }
1460 : }
1461 :
1462 7 : static JSValue dom_document_getProperty(JSContext *c, JSValueConst obj, int magic)
1463 : {
1464 : GF_SceneGraph *sg = dom_get_doc(c, obj);
1465 0 : if (!sg) return JS_EXCEPTION;
1466 :
1467 7 : switch (magic) {
1468 0 : case DOCUMENT_JSPROPERTY_IMPLEMENTATION:
1469 : { /*FIXME, this is wrong, we should have our own implementation
1470 : but at the current time we rely on the global object to provide it*/
1471 0 : JSValue global = JS_GetGlobalObject(c);
1472 0 : JSValue ret = JS_GetPropertyStr(c, global, "Window");
1473 : JS_FreeValue(c, global);
1474 0 : return ret;
1475 : }
1476 :
1477 7 : case DOCUMENT_JSPROPERTY_DOCUMENTELEMENT:
1478 7 : return dom_element_construct(c, sg->RootNode);
1479 :
1480 0 : case DOCUMENT_JSPROPERTY_GLOBAL:
1481 0 : return JS_GetGlobalObject(c);
1482 :
1483 0 : case DOCUMENT_JSPROPERTY_DOCTYPE:
1484 : case DOCUMENT_JSPROPERTY_INPUTENCODING:
1485 : case DOCUMENT_JSPROPERTY_XMLENCODING:
1486 : case DOCUMENT_JSPROPERTY_XMLSTANDALONE:
1487 : case DOCUMENT_JSPROPERTY_XMLVERSION:
1488 : case DOCUMENT_JSPROPERTY_STRICTERRORCHECKING:
1489 : case DOCUMENT_JSPROPERTY_DOCUMENTURI:
1490 : case DOCUMENT_JSPROPERTY_LOCATION:
1491 : case DOCUMENT_JSPROPERTY_DOMCONFIG:
1492 0 : return JS_NULL;
1493 : }
1494 0 : return JS_UNDEFINED;
1495 : }
1496 :
1497 1 : static JSValue dom_document_setProperty(JSContext *c, JSValueConst obj, JSValueConst value, int magic)
1498 : {
1499 : GF_SceneGraph *sg = dom_get_doc(c, obj);
1500 0 : if (!sg) return JS_EXCEPTION;
1501 1 : return js_throw_err(c, GF_DOM_EXC_NOT_SUPPORTED_ERR);
1502 : }
1503 :
1504 86 : static JSValue xml_document_create_element(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1505 : {
1506 : u32 tag, ns;
1507 86 : JSValue ret = JS_NULL;
1508 : const char *name;
1509 : const char *xmlns;
1510 : GF_SceneGraph *sg = dom_get_doc(c, obj);
1511 :
1512 172 : if (!sg || !argc || !JS_CHECK_STRING(argv[0]) )
1513 0 : return JS_EXCEPTION;
1514 :
1515 : name = NULL;
1516 : /*NS version*/
1517 : ns = 0;
1518 : xmlns = NULL;
1519 86 : if (argc==2) {
1520 0 : if (!JS_CHECK_STRING(argv[1])) return JS_EXCEPTION;
1521 : xmlns = JS_ToCString(c, argv[0]);
1522 0 : if (xmlns) ns = gf_sg_get_namespace_code_from_name(sg, (char *) xmlns);
1523 : name = JS_ToCString(c, argv[1]);
1524 : } else {
1525 : name = JS_ToCString(c, argv[0]);
1526 : }
1527 :
1528 86 : if (name) {
1529 : GF_Node *n;
1530 : /*browse all our supported DOM implementations*/
1531 86 : tag = gf_xml_get_element_tag(name, ns);
1532 86 : if (!tag) tag = TAG_DOMFullNode;
1533 86 : n = gf_node_new(sg, tag);
1534 86 : if (n && (tag == TAG_DOMFullNode)) {
1535 : GF_DOMFullNode *elt = (GF_DOMFullNode *)n;
1536 0 : elt->name = gf_strdup(name);
1537 0 : if (xmlns) {
1538 0 : gf_sg_add_namespace(sg, (char *) xmlns, NULL);
1539 0 : elt->ns = gf_sg_get_namespace_code_from_name(sg, (char *) xmlns);
1540 : }
1541 : }
1542 86 : ret = dom_element_construct(c, n);
1543 : }
1544 86 : JS_FreeCString(c, name);
1545 86 : JS_FreeCString(c, xmlns);
1546 86 : return ret;
1547 : }
1548 :
1549 2 : static JSValue xml_document_create_text(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1550 : {
1551 : GF_Node *n;
1552 : GF_SceneGraph *sg = dom_get_doc(c, obj);
1553 0 : if (!sg) return JS_EXCEPTION;
1554 :
1555 2 : n = gf_node_new(sg, TAG_DOMText);
1556 2 : if (argc) {
1557 : const char *str = JS_ToCString(c, argv[0]);
1558 1 : char *ntext = gf_strdup(str ? str : "");
1559 1 : ((GF_DOMText*)n)->textContent = ntext;
1560 1 : JS_FreeCString(c, str);
1561 : }
1562 : return dom_text_construct(c, n);
1563 : }
1564 :
1565 207 : static void xml_doc_gather_nodes(GF_ParentNode *node, char *name, DOMNodeList *nl)
1566 : {
1567 : Bool bookmark = GF_TRUE;
1568 : GF_ChildNodeItem *child;
1569 207 : if (!node) return;
1570 207 : if (name) {
1571 207 : const char *node_name = gf_node_get_class_name((GF_Node*)node);
1572 207 : if (strcmp(node_name, name)) bookmark = GF_FALSE;
1573 : }
1574 : if (bookmark) {
1575 18 : gf_node_register((GF_Node*)node, NULL);
1576 18 : if (node->sgprivate->scenegraph->reference_count)
1577 18 : node->sgprivate->scenegraph->reference_count++;
1578 18 : gf_node_list_add_child(&nl->child, (GF_Node*)node);
1579 : }
1580 207 : if (node->sgprivate->tag<GF_NODE_FIRST_PARENT_NODE_TAG) return;
1581 207 : child = node->children;
1582 607 : while (child) {
1583 193 : xml_doc_gather_nodes((GF_ParentNode*)child->node, name, nl);
1584 193 : child = child->next;
1585 : }
1586 : }
1587 :
1588 13 : static JSValue xml_document_elements_by_tag(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1589 : {
1590 : DOMNodeList *nl;
1591 : JSValue new_obj;
1592 : const char *name;
1593 : GF_SceneGraph *sg = dom_get_doc(c, obj);
1594 0 : if (!sg) return JS_EXCEPTION;
1595 :
1596 26 : if (!argc || !JS_CHECK_STRING(argv[0])) return JS_EXCEPTION;
1597 :
1598 : /*NS version - TODO*/
1599 13 : if (argc==2) {
1600 0 : if (!JS_CHECK_STRING(argv[1])) return JS_EXCEPTION;
1601 : name = JS_ToCString(c, argv[1]);
1602 : } else {
1603 : name = JS_ToCString(c, argv[0]);
1604 : }
1605 :
1606 13 : GF_SAFEALLOC(nl, DOMNodeList);
1607 13 : if (!nl) return JS_EXCEPTION;
1608 :
1609 13 : if (name && !strcmp(name, "*"))
1610 0 : xml_doc_gather_nodes((GF_ParentNode*)sg->RootNode, NULL, nl);
1611 : else
1612 13 : xml_doc_gather_nodes((GF_ParentNode*)sg->RootNode, (char *) name, nl);
1613 13 : new_obj = JS_NewObjectClass(c, domNodeListClass.class_id);
1614 13 : JS_SetOpaque(new_obj, nl);
1615 13 : JS_FreeCString(c, name);
1616 13 : return new_obj;
1617 : }
1618 :
1619 :
1620 2 : static JSValue xml_document_element_by_id(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1621 : {
1622 : NodeIDedItem *reg_node;
1623 : GF_Node *n;
1624 : const char *id;
1625 : JSValue ret;
1626 :
1627 : GF_SceneGraph *sg = dom_get_doc(c, obj);
1628 0 : if (!sg) return JS_EXCEPTION;
1629 4 : if (!argc || !JS_CHECK_STRING(argv[0])) return JS_EXCEPTION;
1630 : id = JS_ToCString(c, argv[0]);
1631 :
1632 : /*we don't use the regular gf_sg_find_node_by_name because we may have nodes defined with the
1633 : same ID and we need to locate the first one which is inserted in the tree*/
1634 : n = NULL;
1635 2 : reg_node = sg->id_node;
1636 9 : while (reg_node) {
1637 7 : if (reg_node->NodeName && !strcmp(reg_node->NodeName, id)) {
1638 2 : n = reg_node->node;
1639 : /*element is not inserted - fixme, we should check all parents*/
1640 2 : if (n && (n->sgprivate->scenegraph->RootNode!=n) && !n->sgprivate->parents) n = NULL;
1641 : else break;
1642 : }
1643 5 : reg_node = reg_node->next;
1644 : }
1645 2 : ret = dom_element_construct(c, n);
1646 2 : JS_FreeCString(c, id);
1647 2 : return ret;
1648 : }
1649 :
1650 : /*dom3 element*/
1651 88 : void dom_element_finalize(JSRuntime *rt, JSValue obj)
1652 : {
1653 88 : dom_node_finalize(rt, obj);
1654 88 : }
1655 :
1656 19 : static JSValue dom_element_getProperty(JSContext *c, JSValueConst obj, int magic)
1657 : {
1658 : GF_Node *n = dom_get_node(obj);
1659 0 : if (!n) return JS_TRUE;
1660 :
1661 19 : switch (magic) {
1662 19 : case ELEMENT_JSPROPERTY_TAGNAME:
1663 19 : return JS_NewString(c, gf_node_get_class_name(n) );
1664 :
1665 0 : case ELEMENT_JSPROPERTY_SCHEMATYPEINFO:
1666 : /*NOT SUPPORTED YET*/
1667 0 : return JS_NULL;
1668 : }
1669 0 : return JS_UNDEFINED;
1670 : }
1671 :
1672 31 : static JSValue xml_element_get_attribute(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1673 : {
1674 : const char *name;
1675 : const char *ns;
1676 31 : JSValue ret = JS_NULL;
1677 :
1678 : GF_Node *n = dom_get_node(obj);
1679 0 : if (!n) return JS_EXCEPTION;
1680 :
1681 62 : if (!argc || !JS_CHECK_STRING(argv[0]))
1682 0 : return JS_TRUE;
1683 : ns = NULL;
1684 : /*NS version*/
1685 31 : if (argc==2) {
1686 0 : if (!JS_CHECK_STRING(argv[1]))
1687 0 : return JS_TRUE;
1688 : ns = JS_ToCString(c, argv[0]);
1689 : name = JS_ToCString(c, argv[1]);
1690 : } else {
1691 : name = JS_ToCString(c, argv[0]);
1692 : }
1693 31 : if (!name) goto exit;
1694 :
1695 : /*ugly ugly hack ...*/
1696 31 : if (!strcmp(name, "id") || !strcmp(name, "xml:id") ) {
1697 6 : char *sID = (char *) gf_node_get_name(n);
1698 6 : if (sID) {
1699 0 : ret = JS_NewString(c, sID);
1700 0 : goto exit;
1701 : }
1702 : }
1703 :
1704 31 : if (n->sgprivate->tag==TAG_DOMFullNode) {
1705 : GF_DOMFullNode *node = (GF_DOMFullNode*)n;
1706 31 : GF_DOMFullAttribute *att = (GF_DOMFullAttribute*)node->attributes;
1707 98 : while (att) {
1708 55 : if ((att->tag==TAG_DOM_ATT_any) && !strcmp(att->name, name)) {
1709 19 : ret = JS_NewString(c, *(char**)att->data );
1710 19 : goto exit;
1711 : }
1712 36 : att = (GF_DOMFullAttribute *) att->next;
1713 : }
1714 : }
1715 0 : else if (n->sgprivate->tag==TAG_DOMText) {
1716 :
1717 0 : } else if (n->sgprivate->tag<=GF_NODE_RANGE_LAST_SVG) {
1718 : GF_FieldInfo info;
1719 : u32 ns_code = 0;
1720 0 : if (ns) {
1721 0 : ns_code = gf_sg_get_namespace_code_from_name(n->sgprivate->scenegraph, (char *) ns);
1722 0 : if (!ns_code) ns_code = gf_crc_32(ns, (u32) strlen(ns));
1723 : }
1724 : else {
1725 0 : ns_code = gf_xml_get_element_namespace(n);
1726 : }
1727 :
1728 0 : if (gf_node_get_attribute_by_name(n, (char *) name, ns_code, GF_FALSE, GF_FALSE, &info)==GF_OK) {
1729 0 : char *szAtt = gf_svg_dump_attribute(n, &info);
1730 0 : ret = JS_NewString(c, szAtt);
1731 0 : if (szAtt) gf_free(szAtt);
1732 0 : goto exit;
1733 : }
1734 : }
1735 12 : ret = JS_NewString(c, "");
1736 31 : exit:
1737 31 : JS_FreeCString(c, name);
1738 31 : JS_FreeCString(c, ns);
1739 31 : return ret;
1740 : }
1741 :
1742 1 : static JSValue xml_element_has_attribute(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1743 : {
1744 : const char *name;
1745 : const char *ns;
1746 1 : JSValue ret = JS_NULL;
1747 :
1748 : GF_Node *n = dom_get_node(obj);
1749 0 : if (!n) return JS_EXCEPTION;
1750 :
1751 2 : if (!argc || !JS_CHECK_STRING(argv[0])) return JS_EXCEPTION;
1752 : ns = NULL;
1753 : /*NS version*/
1754 1 : if (argc==2) {
1755 0 : if (!JS_CHECK_STRING(argv[1])) return JS_EXCEPTION;
1756 : ns = JS_ToCString(c, argv[0]);
1757 : name = JS_ToCString(c, argv[1]);
1758 : } else {
1759 : name = JS_ToCString(c, argv[0]);
1760 : }
1761 1 : if (!name) goto exit;
1762 :
1763 1 : if (n->sgprivate->tag==TAG_DOMFullNode) {
1764 : GF_DOMFullNode *node = (GF_DOMFullNode*)n;
1765 1 : GF_DOMFullAttribute *att = (GF_DOMFullAttribute*)node->attributes;
1766 2 : while (att) {
1767 1 : if ((att->tag==TAG_DOM_ATT_any) && !strcmp(att->name, name)) {
1768 1 : ret = JS_TRUE;
1769 1 : goto exit;
1770 : }
1771 0 : att = (GF_DOMFullAttribute *) att->next;
1772 : }
1773 : }
1774 0 : else if (n->sgprivate->tag==TAG_DOMText) {
1775 : }
1776 0 : else if (n->sgprivate->tag<=GF_NODE_RANGE_LAST_SVG) {
1777 : GF_FieldInfo info;
1778 : u32 ns_code = 0;
1779 0 : if (ns) ns_code = gf_sg_get_namespace_code_from_name(n->sgprivate->scenegraph, (char *) ns);
1780 0 : else ns_code = gf_sg_get_namespace_code(n->sgprivate->scenegraph, NULL);
1781 :
1782 0 : if (gf_node_get_attribute_by_name(n, (char *) name, ns_code, GF_FALSE, GF_FALSE, &info)==GF_OK) {
1783 0 : ret = JS_TRUE;
1784 0 : goto exit;
1785 : }
1786 : }
1787 0 : ret = JS_FALSE;
1788 1 : exit:
1789 1 : JS_FreeCString(c, name);
1790 1 : JS_FreeCString(c, ns);
1791 1 : return ret;
1792 : }
1793 :
1794 :
1795 :
1796 1 : static JSValue xml_element_remove_attribute(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
1797 : {
1798 : u32 tag;
1799 : GF_DOMFullNode *node;
1800 : GF_DOMFullAttribute *prev, *att;
1801 : const char *name, *ns;
1802 :
1803 : GF_Node *n = dom_get_node(obj);
1804 0 : if (!n) return JS_EXCEPTION;
1805 :
1806 2 : if (!argc || !JS_CHECK_STRING(argv[0])) return JS_EXCEPTION;
1807 : ns = NULL;
1808 : /*NS version*/
1809 1 : if (argc==2) {
1810 0 : if (!JS_CHECK_STRING(argv[1])) return JS_EXCEPTION;
1811 : ns = JS_ToCString(c, argv[0]);
1812 : name = JS_ToCString(c, argv[1]);
1813 : } else {
1814 : name = JS_ToCString(c, argv[0]);
1815 : }
1816 1 : if (!name) goto exit;
1817 :
1818 : tag = TAG_DOM_ATT_any;
1819 : node = (GF_DOMFullNode*)n;
1820 : prev = NULL;
1821 1 : att = (GF_DOMFullAttribute*)node->attributes;
1822 :
1823 1 : if (n->sgprivate->tag==TAG_DOMFullNode) tag = TAG_DOM_ATT_any;
1824 0 : else if (n->sgprivate->tag==TAG_DOMText) {
1825 : goto exit;
1826 0 : } else if (n->sgprivate->tag<=GF_NODE_RANGE_LAST_SVG) {
1827 : u32 ns_code = 0;
1828 0 : if (ns) ns_code = gf_sg_get_namespace_code_from_name(n->sgprivate->scenegraph, (char *) ns);
1829 0 : else ns_code = gf_sg_get_namespace_code(n->sgprivate->scenegraph, NULL);
1830 :
1831 0 : tag = gf_xml_get_attribute_tag(n, (char *)name, ns_code);
1832 : }
1833 :
1834 1 : while (att) {
1835 1 : if ((att->tag==TAG_DOM_ATT_any) && !strcmp(att->name, name)) {
1836 : DOM_String *s;
1837 1 : if (prev) prev->next = att->next;
1838 1 : else node->attributes = att->next;
1839 1 : s = att->data;
1840 1 : if (*s) gf_free(*s);
1841 1 : gf_free(s);
1842 1 : gf_free(att->name);
1843 1 : gf_free(att);
1844 : dom_node_changed(n, GF_FALSE, NULL);
1845 : goto exit;
1846 0 : } else if (tag==att->tag) {
1847 0 : if (prev) prev->next = att->next;
1848 0 : else node->attributes = att->next;
1849 0 : gf_svg_delete_attribute_value(att->data_type, att->data, n->sgprivate->scenegraph);
1850 0 : gf_free(att);
1851 : dom_node_changed(n, GF_FALSE, NULL);
1852 : goto exit;
1853 : }
1854 : prev = att;
1855 0 : att = (GF_DOMFullAttribute *) att->next;
1856 : }
1857 0 : exit:
1858 1 : JS_FreeCString(c, name);
1859 1 : JS_FreeCString(c, ns);
1860 1 : return JS_TRUE;
1861 : }
1862 :
1863 1 : static void gf_dom_add_handler_listener(GF_Node *n, u32 evtType, char *handlerCode)
1864 : {
1865 : /*check if we're modifying an existing listener*/
1866 : SVG_handlerElement *handler;
1867 1 : u32 i, count = gf_dom_listener_count(n);
1868 1 : for (i=0; i<count; i++) {
1869 : GF_FieldInfo info;
1870 : GF_DOMText *text;
1871 0 : GF_Node *listen = gf_dom_listener_get(n, i);
1872 :
1873 0 : gf_node_get_attribute_by_tag(listen, TAG_XMLEV_ATT_event, GF_FALSE, GF_FALSE, &info);
1874 0 : if (!info.far_ptr || (((XMLEV_Event*)info.far_ptr)->type != evtType)) continue;
1875 :
1876 : /* found a listener for this event, override the handler
1877 : TODO: FIX this, there may be a listener/handler already set with JS, why overriding ? */
1878 0 : gf_node_get_attribute_by_tag(listen, TAG_XMLEV_ATT_handler, GF_FALSE, GF_FALSE, &info);
1879 : assert(info.far_ptr);
1880 0 : handler = (SVG_handlerElement *) ((XMLRI*)info.far_ptr)->target;
1881 0 : text = (GF_DOMText*)handler->children->node;
1882 0 : if (text->sgprivate->tag==TAG_DOMText) {
1883 0 : if (text->textContent) gf_free(text->textContent);
1884 0 : text->textContent = gf_strdup(handlerCode);
1885 : }
1886 0 : return;
1887 : }
1888 : /*nope, create a listener*/
1889 1 : handler = gf_dom_listener_build(n, evtType, 0);
1890 1 : gf_dom_add_text_node((GF_Node*)handler, gf_strdup(handlerCode));
1891 1 : return;
1892 : }
1893 :
1894 1 : static void gf_dom_full_set_attribute(GF_DOMFullNode *node, char *attribute_name, char *attribute_content)
1895 : {
1896 : GF_DOMFullAttribute *prev = NULL;
1897 1 : GF_DOMFullAttribute *att = (GF_DOMFullAttribute*)node->attributes;
1898 2 : while (att) {
1899 1 : if ((att->tag==TAG_DOM_ATT_any) && !strcmp(att->name, attribute_name)) {
1900 : DOM_String *s;
1901 : assert(att->data_type == DOM_String_datatype);
1902 : assert(att->data);
1903 1 : s = (DOM_String *) att->data;
1904 1 : if ( *s ) gf_free( *s);
1905 1 : *s = gf_strdup(attribute_content);
1906 : dom_node_changed((GF_Node *)node, GF_FALSE, NULL);
1907 : return;
1908 : }
1909 : prev = att;
1910 0 : att = (GF_DOMFullAttribute *) att->next;
1911 : }
1912 : /*create new att*/
1913 0 : GF_SAFEALLOC(att, GF_DOMFullAttribute);
1914 0 : if (!att) {
1915 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[DOMJS] Failed to allocate DOM attribute\n"));
1916 : return;
1917 : }
1918 0 : att->name = gf_strdup(attribute_name);
1919 0 : att->data_type = (u16) DOM_String_datatype;
1920 0 : att->data = gf_svg_create_attribute_value(att->data_type);
1921 0 : *((char **)att->data) = gf_strdup(attribute_content);
1922 :
1923 0 : if (prev) prev->next = (GF_DOMAttribute*) att;
1924 0 : else node->attributes = (GF_DOMAttribute*) att;
1925 : return;
1926 : }
1927 :
1928 173 : void gf_svg_set_attributeNS(GF_Node *n, u32 ns_code, char *name, char *val)
1929 : {
1930 : GF_FieldInfo info;
1931 : u32 anim_value_type = 0;
1932 :
1933 173 : if (!strcmp(name, "attributeName")) {
1934 0 : if (gf_node_get_attribute_by_tag(n, TAG_SVG_ATT_attributeName, GF_FALSE, GF_FALSE, &info) == GF_OK) {
1935 0 : SMIL_AttributeName *attname = (SMIL_AttributeName *)info.far_ptr;
1936 :
1937 : /*parse the attribute name even if the target is not found, because a namespace could be specified and
1938 : only valid for the current node*/
1939 0 : if (!attname->type) {
1940 : char *sep;
1941 0 : char *a_name = attname->name;
1942 0 : sep = strchr(a_name, ':');
1943 0 : if (sep) {
1944 0 : sep[0] = 0;
1945 0 : attname->type = gf_sg_get_namespace_code(n->sgprivate->scenegraph, a_name);
1946 0 : sep[0] = ':';
1947 0 : a_name = gf_strdup(sep+1);
1948 0 : gf_free(attname->name);
1949 0 : attname->name = a_name;
1950 : }
1951 : }
1952 : }
1953 : }
1954 :
1955 173 : if ((n->sgprivate->tag == TAG_SVG_animateTransform) && (strstr(name, "from") || strstr(name, "to")) ) {
1956 0 : if (gf_node_get_attribute_by_tag((GF_Node *)n, TAG_SVG_ATT_transform_type, GF_TRUE, GF_FALSE, &info) != GF_OK) {
1957 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_SCRIPT, ("Cannot retrieve attribute 'type' from animateTransform\n"));
1958 173 : return;
1959 : }
1960 :
1961 0 : switch(*(SVG_TransformType *) info.far_ptr) {
1962 : case SVG_TRANSFORM_TRANSLATE:
1963 : anim_value_type = SVG_Transform_Translate_datatype;
1964 : break;
1965 0 : case SVG_TRANSFORM_SCALE:
1966 : anim_value_type = SVG_Transform_Scale_datatype;
1967 0 : break;
1968 0 : case SVG_TRANSFORM_ROTATE:
1969 : anim_value_type = SVG_Transform_Rotate_datatype;
1970 0 : break;
1971 0 : case SVG_TRANSFORM_SKEWX:
1972 : anim_value_type = SVG_Transform_SkewX_datatype;
1973 0 : break;
1974 0 : case SVG_TRANSFORM_SKEWY:
1975 : anim_value_type = SVG_Transform_SkewY_datatype;
1976 0 : break;
1977 0 : case SVG_TRANSFORM_MATRIX:
1978 : anim_value_type = SVG_Transform_datatype;
1979 0 : break;
1980 : default:
1981 : return;
1982 : }
1983 173 : }
1984 :
1985 173 : if (gf_node_get_attribute_by_name(n, name, ns_code, GF_TRUE, GF_TRUE, &info)==GF_OK) {
1986 : GF_Err e;
1987 173 : if (!strcmp(name, "from") || !strcmp(name, "to") || !strcmp(name, "values") ) {
1988 : GF_FieldInfo attType;
1989 : SMIL_AttributeName *attname;
1990 0 : if (gf_node_get_attribute_by_tag((GF_Node *)n, TAG_SVG_ATT_attributeName, GF_FALSE, GF_FALSE, &attType) != GF_OK) {
1991 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_SCRIPT, ("Cannot retrieve attribute 'attributeName'\n"));
1992 0 : return;
1993 : }
1994 :
1995 0 : attname = (SMIL_AttributeName *)attType.far_ptr;
1996 0 : if (!attname->type && attname->name) {
1997 0 : GF_Node *anim_target = gf_smil_anim_get_target(n);
1998 0 : if (anim_target) {
1999 0 : gf_node_get_attribute_by_name((GF_Node *)anim_target, attname->name, attname->type, GF_FALSE, GF_FALSE, &attType);
2000 0 : attname->type = attType.fieldType;
2001 : } else {
2002 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[DOM] Cannot find target of the animation to parse attribute %s\n", attname->name));
2003 : }
2004 : }
2005 :
2006 0 : anim_value_type = attname->type;
2007 : }
2008 173 : e = gf_svg_parse_attribute(n, &info, val, anim_value_type);
2009 173 : if (e != GF_OK) {
2010 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[DOM] Error parsing attribute\n"));
2011 : }
2012 :
2013 173 : if (info.fieldType==SVG_ID_datatype) {
2014 0 : char *idname = *(SVG_String*)info.far_ptr;
2015 0 : gf_svg_parse_element_id(n, idname, GF_FALSE);
2016 : }
2017 173 : if (info.fieldType==XMLRI_datatype) {
2018 0 : gf_node_dirty_set(n, GF_SG_SVG_XLINK_HREF_DIRTY, GF_FALSE);
2019 : }
2020 173 : dom_node_changed(n, GF_FALSE, &info);
2021 173 : return;
2022 : }
2023 : }
2024 :
2025 173 : void gf_svg_set_attribute(GF_Node *n, char * ns, char *name, char *val)
2026 : {
2027 : u32 ns_code = 0;
2028 173 : if (ns) {
2029 0 : ns_code = gf_sg_get_namespace_code_from_name(n->sgprivate->scenegraph, ns);
2030 : } else {
2031 173 : ns_code = gf_xml_get_element_namespace(n);
2032 : }
2033 173 : gf_svg_set_attributeNS(n, ns_code, name, val);
2034 173 : }
2035 :
2036 175 : static JSValue xml_element_set_attribute(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
2037 : {
2038 : u32 idx;
2039 : const char *name, *_val, *val, *ns;
2040 : char szVal[100];
2041 :
2042 : GF_Node *n = dom_get_node(obj);
2043 0 : if (!n) return JS_EXCEPTION;
2044 175 : if ((argc < 2)) return JS_EXCEPTION;
2045 :
2046 350 : if (!JS_CHECK_STRING(argv[0]))
2047 0 : return JS_EXCEPTION;
2048 :
2049 : idx = 1;
2050 : name = _val = val = NULL;
2051 : ns = NULL;
2052 : /*NS version*/
2053 175 : if (argc==3) {
2054 : char *sep;
2055 0 : if (!JS_CHECK_STRING(argv[1]))
2056 0 : return JS_EXCEPTION;
2057 : ns = JS_ToCString(c, argv[0]);
2058 0 : gf_sg_add_namespace(n->sgprivate->scenegraph, (char *) ns, NULL);
2059 : name = JS_ToCString(c, argv[1]);
2060 : idx = 2;
2061 :
2062 0 : sep = strchr(name, ':');
2063 0 : if (sep) name = sep+1;
2064 :
2065 : } else {
2066 : name = JS_ToCString(c, argv[0]);
2067 : }
2068 :
2069 : val = NULL;
2070 350 : if (JS_CHECK_STRING(argv[idx])) {
2071 78 : val = _val = JS_ToCString(c, argv[idx]);
2072 97 : } else if (JS_IsBool(argv[idx])) {
2073 0 : sprintf(szVal, "%s", JS_ToBool(c, argv[idx]) ? "true" : "false");
2074 : val = szVal;
2075 97 : } else if (JS_IsNumber(argv[idx])) {
2076 : Double d;
2077 97 : JS_ToFloat64(c, &d, argv[idx]);
2078 97 : sprintf(szVal, "%g", d);
2079 : val = szVal;
2080 0 : } else if (JS_IsInteger(argv[idx])) {
2081 : u32 i;
2082 0 : JS_ToInt32(c, &i, argv[idx]);
2083 0 : sprintf(szVal, "%d", i);
2084 : val = szVal;
2085 : } else {
2086 : goto exit;
2087 : }
2088 175 : if (!name || !val)
2089 : goto exit;
2090 :
2091 :
2092 : /* For on* attribute (e.g. onclick), we create a couple listener/handler elements on setting the attribute */
2093 175 : if ((name[0]=='o') && (name[1]=='n')) {
2094 1 : u32 evtType = gf_dom_event_type_by_name(name + 2);
2095 1 : if (evtType != GF_EVENT_UNKNOWN) {
2096 1 : gf_dom_add_handler_listener(n, evtType, (char *) val);
2097 1 : goto exit;
2098 : }
2099 : }
2100 :
2101 174 : if (n->sgprivate->tag==TAG_DOMFullNode) {
2102 1 : gf_dom_full_set_attribute((GF_DOMFullNode*)n, (char *) name, (char *) val);
2103 1 : goto exit;
2104 : }
2105 :
2106 173 : if (n->sgprivate->tag==TAG_DOMText) {
2107 : goto exit;
2108 : }
2109 :
2110 173 : if (n->sgprivate->tag<=GF_NODE_RANGE_LAST_SVG) {
2111 173 : gf_svg_set_attribute(n, (char *) ns, (char *) name, (char *) val);
2112 : }
2113 175 : exit:
2114 175 : JS_FreeCString(c, name);
2115 175 : JS_FreeCString(c, ns);
2116 175 : JS_FreeCString(c, _val);
2117 175 : return JS_TRUE;
2118 : }
2119 :
2120 :
2121 1 : static JSValue xml_element_elements_by_tag(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
2122 : {
2123 : DOMNodeList *nl;
2124 : JSValue new_obj;
2125 : const char *name;
2126 :
2127 : GF_Node *n = dom_get_node(obj);
2128 0 : if (!n) return JS_EXCEPTION;
2129 :
2130 2 : if (!argc || !JS_CHECK_STRING(argv[0])) return JS_EXCEPTION;
2131 :
2132 : /*NS version*/
2133 1 : if (argc==2) {
2134 0 : if (!JS_CHECK_STRING(argv[1])) return JS_EXCEPTION;
2135 : name = JS_ToCString(c, argv[1]);
2136 : } else {
2137 : name = JS_ToCString(c, argv[0]);
2138 : }
2139 1 : GF_SAFEALLOC(nl, DOMNodeList);
2140 1 : if (!nl) return JS_EXCEPTION;
2141 :
2142 1 : if (name && !strcmp(name, "*")) {
2143 0 : JS_FreeCString(c, name);
2144 : name = NULL;
2145 : }
2146 1 : xml_doc_gather_nodes((GF_ParentNode*)n, (char *)name, nl);
2147 1 : new_obj = JS_NewObjectClass(c, domNodeListClass.class_id);
2148 1 : JS_SetOpaque(new_obj, nl);
2149 1 : JS_FreeCString(c, name);
2150 1 : return new_obj;
2151 : }
2152 :
2153 1 : static JSValue xml_element_set_id(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
2154 : {
2155 : u32 node_id;
2156 : const char *name;
2157 : Bool is_id;
2158 :
2159 : GF_Node *n = dom_get_node(obj);
2160 0 : if (!n) return JS_EXCEPTION;
2161 :
2162 2 : if ((argc<2) || !JS_CHECK_STRING(argv[0])) return JS_EXCEPTION;
2163 :
2164 : /*NS version*/
2165 1 : if (argc==3) {
2166 0 : if (!JS_CHECK_STRING(argv[1])) return JS_EXCEPTION;
2167 : name = JS_ToCString(c, argv[1]);
2168 0 : is_id = JS_ToBool(c, argv[2]) ? GF_TRUE : GF_FALSE;
2169 : } else {
2170 : name = JS_ToCString(c, argv[0]);
2171 1 : is_id = JS_ToBool(c, argv[1]) ? GF_TRUE : GF_FALSE;
2172 : }
2173 1 : gf_node_get_name_and_id(n, &node_id);
2174 1 : if (node_id && is_id) {
2175 : /*we only support ONE ID per node*/
2176 0 : JS_FreeCString(c, name);
2177 0 : return JS_EXCEPTION;
2178 : }
2179 1 : if (is_id) {
2180 0 : if (!name) return JS_EXCEPTION;
2181 0 : gf_node_set_id(n, gf_sg_get_max_node_id(n->sgprivate->scenegraph) + 1, gf_strdup(name) );
2182 1 : } else if (node_id) {
2183 1 : gf_node_remove_id(n);
2184 : }
2185 1 : JS_FreeCString(c, name);
2186 1 : return JS_TRUE;
2187 : }
2188 235 : static void gather_text(GF_ParentNode *n, char **out_str)
2189 : {
2190 235 : if (n->sgprivate->tag==TAG_DOMText) {
2191 : GF_DOMText *dom_text = (GF_DOMText *)n;
2192 122 : if (dom_text->textContent) gf_dynstrcat(out_str, dom_text->textContent, NULL);
2193 : } else {
2194 113 : GF_ChildNodeItem *child = ((GF_ParentNode *) n)->children;
2195 331 : while (child) {
2196 105 : gather_text((GF_ParentNode *)child->node, out_str);
2197 105 : child = child->next;
2198 : }
2199 : }
2200 235 : }
2201 122 : static JSValue xml_element_to_string(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
2202 : {
2203 : JSValue ret;
2204 122 : char *out_str = NULL;
2205 : GF_Node *n = dom_get_node(obj);
2206 0 : if (!n) return JS_EXCEPTION;
2207 :
2208 : GF_ChildNodeItem *child;
2209 122 : child = ((GF_ParentNode *) n)->children;
2210 374 : while (child) {
2211 130 : gather_text((GF_ParentNode *)child->node, &out_str);
2212 130 : child = child->next;
2213 : }
2214 122 : if (!out_str) {
2215 35 : const char *node_name = gf_node_get_class_name(n);
2216 35 : if (node_name)
2217 35 : return JS_NewString(c, node_name);
2218 0 : return JS_NULL;
2219 : }
2220 87 : ret = JS_NewString(c, out_str);
2221 87 : gf_free(out_str);
2222 87 : return ret;
2223 : }
2224 :
2225 : /*dom3 character/text/comment*/
2226 1 : static JSValue dom_text_getProperty(JSContext *c, JSValueConst obj, int magic)
2227 : {
2228 : GF_DOMText *txt = (GF_DOMText*)dom_get_node(obj);
2229 1 : if (!txt || (txt->sgprivate->tag != TAG_DOMText)) return JS_EXCEPTION;
2230 :
2231 1 : switch (magic) {
2232 0 : case TEXT_JSPROPERTY_DATA:
2233 0 : if (txt->textContent) return JS_NewString(c, txt->textContent);
2234 0 : else return JS_NewString(c, "");
2235 :
2236 1 : case TEXT_JSPROPERTY_LENGTH:
2237 1 : return JS_NewInt32(c, txt->textContent ? (u32) strlen(txt->textContent) : 0);
2238 :
2239 0 : case TEXT_JSPROPERTY_ISELEMENTCONTENTWHITESPACE:
2240 0 : return JS_FALSE;
2241 :
2242 0 : case TEXT_JSPROPERTY_WHOLETEXT:
2243 : /*FIXME - this is wrong we should serialize adjacent text strings as well*/
2244 0 : if (txt->textContent) return JS_NewString(c, txt->textContent);
2245 0 : else return JS_NewString(c, "");
2246 : }
2247 0 : return JS_UNDEFINED;
2248 : }
2249 :
2250 1 : static JSValue dom_text_setProperty(JSContext *c, JSValueConst obj, JSValueConst value, int magic)
2251 : {
2252 : GF_DOMText *txt = (GF_DOMText*)dom_get_node(obj);
2253 1 : if (!txt || (txt->sgprivate->tag != TAG_DOMText)) return JS_EXCEPTION;
2254 :
2255 1 : switch (magic) {
2256 1 : case TEXT_JSPROPERTY_DATA:
2257 1 : if (txt->textContent) gf_free(txt->textContent);
2258 1 : txt->textContent = NULL;
2259 1 : if (JS_CHECK_STRING(value)) {
2260 : const char *str = JS_ToCString(c, value);
2261 1 : txt->textContent = gf_strdup(str ? str : "");
2262 1 : JS_FreeCString(c, str);
2263 : }
2264 : dom_node_changed((GF_Node*)txt, GF_FALSE, NULL);
2265 1 : return JS_TRUE;
2266 : /*the rest is read-only*/
2267 : }
2268 0 : return JS_UNDEFINED;
2269 : }
2270 :
2271 0 : static JSValue event_stop_propagation(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
2272 : {
2273 0 : GF_DOM_Event *evt = JS_GetOpaque(obj, domEventClass.class_id);
2274 0 : if (!evt) return JS_EXCEPTION;
2275 0 : evt->event_phase |= GF_DOM_EVENT_PHASE_CANCEL;
2276 0 : return JS_TRUE;
2277 : }
2278 0 : static JSValue event_stop_immediate_propagation(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
2279 : {
2280 0 : GF_DOM_Event *evt = JS_GetOpaque(obj, domEventClass.class_id);
2281 0 : if (!evt) return JS_EXCEPTION;
2282 0 : evt->event_phase |= GF_DOM_EVENT_PHASE_CANCEL_ALL;
2283 0 : return JS_TRUE;
2284 : }
2285 0 : static JSValue event_prevent_default(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
2286 : {
2287 0 : GF_DOM_Event *evt = JS_GetOpaque(obj, domEventClass.class_id);
2288 0 : if (!evt) return JS_EXCEPTION;
2289 0 : evt->event_phase |= GF_DOM_EVENT_PHASE_PREVENT;
2290 0 : return JS_TRUE;
2291 : }
2292 :
2293 398 : static JSValue event_getProperty(JSContext *c, JSValueConst obj, int magic)
2294 : {
2295 398 : GF_DOM_Event *evt = JS_GetOpaque(obj, domEventClass.class_id);
2296 398 : if (evt==NULL) return JS_TRUE;
2297 :
2298 398 : switch (magic) {
2299 0 : case EVENT_JSPROPERTY_TYPE:
2300 0 : return JS_NewString(c, gf_dom_event_get_name(evt->type) );
2301 :
2302 0 : case EVENT_JSPROPERTY_TARGET:
2303 0 : if (evt->is_vrml) return JS_TRUE;
2304 0 : switch (evt->target_type) {
2305 0 : case GF_DOM_EVENT_TARGET_NODE:
2306 0 : return dom_element_construct(c, (GF_Node*) evt->target);
2307 :
2308 0 : case GF_DOM_EVENT_TARGET_DOCUMENT:
2309 0 : return dom_document_construct(c, (GF_SceneGraph *) evt->target);
2310 :
2311 : #ifdef GPAC_ENABLE_HTML5_MEDIA
2312 : case GF_DOM_EVENT_TARGET_MSE_MEDIASOURCE:
2313 : return OBJECT_TO_JSValue(((GF_HTML_MediaSource *)evt->target)->_this);
2314 : case GF_DOM_EVENT_TARGET_MSE_SOURCEBUFFER:
2315 : return OBJECT_TO_JSValue(((GF_HTML_SourceBuffer *)evt->target)->_this);
2316 : case GF_DOM_EVENT_TARGET_MSE_SOURCEBUFFERLIST:
2317 : return OBJECT_TO_JSValue(((GF_HTML_SourceBufferList *)evt->target)->_this);
2318 : #endif
2319 : default:
2320 : break;
2321 : }
2322 0 : return JS_TRUE;
2323 0 : case EVENT_JSPROPERTY_CURRENTTARGET:
2324 0 : if (evt->is_vrml) return JS_NULL;
2325 0 : switch (evt->currentTarget->ptr_type) {
2326 0 : case GF_DOM_EVENT_TARGET_NODE:
2327 0 : return dom_element_construct(c, (GF_Node*) evt->currentTarget->ptr);
2328 0 : case GF_DOM_EVENT_TARGET_DOCUMENT:
2329 0 : return dom_document_construct(c, (GF_SceneGraph *) evt->currentTarget->ptr);
2330 : #ifdef GPAC_ENABLE_HTML5_MEDIA
2331 : case GF_DOM_EVENT_TARGET_MSE_MEDIASOURCE:
2332 : return OBJECT_TO_JSValue(((GF_HTML_MediaSource *)evt->target)->_this);
2333 : case GF_DOM_EVENT_TARGET_MSE_SOURCEBUFFER:
2334 : return OBJECT_TO_JSValue(((GF_HTML_SourceBuffer *)evt->target)->_this);
2335 : break;
2336 : case GF_DOM_EVENT_TARGET_MSE_SOURCEBUFFERLIST:
2337 : return OBJECT_TO_JSValue(((GF_HTML_SourceBufferList *)evt->target)->_this);
2338 : #endif
2339 : default:
2340 : break;
2341 : }
2342 0 : return JS_TRUE;
2343 0 : case EVENT_JSPROPERTY_EVENTPHASE:
2344 0 : return JS_NewInt32(c, (evt->event_phase & 0x3) );
2345 :
2346 0 : case EVENT_JSPROPERTY_BUBBLES:
2347 0 : return evt->bubbles ? JS_TRUE : JS_FALSE;
2348 :
2349 0 : case EVENT_JSPROPERTY_CANCELABLE:
2350 0 : return evt->cancelable ? JS_TRUE : JS_FALSE;
2351 0 : case EVENT_JSPROPERTY_NAMESPACEURI:
2352 0 : return JS_NULL;
2353 :
2354 0 : case EVENT_JSPROPERTY_TIMESTAMP:
2355 0 : return JS_NULL;
2356 :
2357 0 : case EVENT_JSPROPERTY_DEFAULTPREVENTED:
2358 0 : return (evt->event_phase & GF_DOM_EVENT_PHASE_PREVENT) ? JS_TRUE : JS_FALSE;
2359 :
2360 0 : case EVENT_JSPROPERTY_DETAIL:
2361 0 : return JS_NewInt32(c, evt->detail);
2362 :
2363 0 : case EVENT_JSPROPERTY_DATA:
2364 : {
2365 : u32 len;
2366 : s16 txt[2];
2367 : const u16 *srcp;
2368 : char szData[5];
2369 0 : txt[0] = evt->detail;
2370 0 : txt[1] = 0;
2371 0 : srcp = (const u16 *) txt;
2372 0 : len = (u32) gf_utf8_wcstombs(szData, 5, &srcp);
2373 0 : szData[len] = 0;
2374 0 : return JS_NewString(c, szData);
2375 : }
2376 :
2377 0 : case EVENT_JSPROPERTY_SCREENX:
2378 0 : return JS_NewInt32(c, evt->screenX);
2379 0 : case EVENT_JSPROPERTY_SCREENY:
2380 0 : return JS_NewInt32(c, evt->screenY);
2381 0 : case EVENT_JSPROPERTY_CLIENTX:
2382 0 : return JS_NewInt32(c, evt->clientX);
2383 0 : case EVENT_JSPROPERTY_CLIENTY:
2384 0 : return JS_NewInt32(c, evt->clientY);
2385 0 : case EVENT_JSPROPERTY_BUTTON:
2386 0 : return JS_NewInt32(c, evt->button);
2387 0 : case EVENT_JSPROPERTY_RELATEDTARGET:
2388 0 : if (evt->is_vrml) return JS_NULL;
2389 0 : return dom_element_construct(c, evt->relatedTarget);
2390 0 : case EVENT_JSPROPERTY_WHEELDELTA:
2391 0 : return JS_NewInt32(c, FIX2INT(evt->new_scale) );
2392 0 : case EVENT_JSPROPERTY_KEYIDENTIFIER:
2393 0 : return JS_NewString(c, gf_dom_get_key_name(evt->detail) );
2394 : /*Mozilla keyChar, charCode: wrap up to same value*/
2395 0 : case EVENT_JSPROPERTY_KEYCHAR:
2396 : case EVENT_JSPROPERTY_CHARCODE:
2397 0 : return JS_NewInt32(c, evt->detail);
2398 1 : case EVENT_JSPROPERTY_LOADED:
2399 1 : return JS_NewInt64(c, evt->media_event.loaded_size);
2400 86 : case EVENT_JSPROPERTY_TOTAL:
2401 86 : return JS_NewInt64(c, evt->media_event.total_size);
2402 85 : case EVENT_JSPROPERTY_BUFFER_ON:
2403 85 : return evt->media_event.bufferValid ? JS_TRUE : JS_FALSE;
2404 90 : case EVENT_JSPROPERTY_BUFFERLEVEL:
2405 90 : return JS_NewInt32(c, evt->media_event.level);
2406 84 : case EVENT_JSPROPERTY_BUFFERREMAININGTIME:
2407 84 : return JS_NewFloat64(c, evt->media_event.remaining_time);
2408 0 : case EVENT_JSPROPERTY_STATUS:
2409 0 : return JS_NewInt32(c, evt->media_event.status);
2410 :
2411 : /*VRML ones*/
2412 27 : case EVENT_JSPROPERTY_WIDTH:
2413 27 : return JS_NewFloat64(c, FIX2FLT(evt->screen_rect.width) );
2414 21 : case EVENT_JSPROPERTY_HEIGHT:
2415 21 : return JS_NewFloat64(c, FIX2FLT(evt->screen_rect.height) );
2416 0 : case EVENT_JSPROPERTY_OFFSETX:
2417 0 : return JS_NewFloat64(c, FIX2FLT(evt->screen_rect.x) );
2418 0 : case EVENT_JSPROPERTY_OFFSETY:
2419 0 : return JS_NewFloat64(c, FIX2FLT(evt->screen_rect.y) );
2420 0 : case EVENT_JSPROPERTY_VPWIDTH:
2421 0 : return JS_NewFloat64(c, FIX2FLT(evt->prev_translate.x) );
2422 0 : case EVENT_JSPROPERTY_VPHEIGHT:
2423 0 : return JS_NewFloat64(c, FIX2FLT(evt->prev_translate.y) );
2424 0 : case EVENT_JSPROPERTY_TRANSLATIONX:
2425 0 : return JS_NewFloat64(c, FIX2FLT(evt->new_translate.x) );
2426 0 : case EVENT_JSPROPERTY_TRANSLATIONY:
2427 0 : return JS_NewFloat64(c, FIX2FLT(evt->new_translate.y) );
2428 1 : case EVENT_JSPROPERTY_TYPE3D:
2429 1 : return JS_NewInt32(c, evt->detail);
2430 2 : case EVENT_JSPROPERTY_ERROR:
2431 2 : return JS_NewInt32(c, evt->error_state);
2432 1 : case EVENT_JSPROPERTY_DYNAMIC_SCENE:
2433 1 : return JS_NewInt32(c, evt->key_flags);
2434 0 : case EVENT_JSPROPERTY_URL:
2435 0 : return JS_NewString(c, evt->addon_url ? evt->addon_url : "");
2436 :
2437 : default:
2438 : break;
2439 : }
2440 0 : return JS_UNDEFINED;
2441 : }
2442 :
2443 :
2444 : #define SETUP_JSCLASS(_class, _name, _proto_funcs, _construct, _finalize, _proto_class_id) \
2445 : if (! _class.class_id) {\
2446 : JS_NewClassID(&(_class.class_id)); \
2447 : _class.class.class_name = _name; \
2448 : _class.class.finalizer = _finalize;\
2449 : JS_NewClass(jsrt, _class.class_id, &(_class.class));\
2450 : }\
2451 : proto = JS_NewObjectClass(c, _proto_class_id ? _proto_class_id : _class.class_id);\
2452 : JS_SetPropertyFunctionList(c, proto, _proto_funcs, countof(_proto_funcs));\
2453 : JS_SetClassProto(c, _class.class_id, proto);\
2454 : if (_construct) {\
2455 : JSValue ctor = JS_NewCFunction2(c, _construct, _name, 1, JS_CFUNC_constructor, 0);\
2456 : JS_SetPropertyStr(c, global, _name, ctor);\
2457 : }\
2458 :
2459 :
2460 : /*SVGMatrix class*/
2461 : static const JSCFunctionListEntry node_Funcs[] =
2462 : {
2463 : JS_CGETSET_MAGIC_DEF("nodeName", dom_node_getProperty, NULL, NODE_JSPROPERTY_NODENAME),
2464 : JS_CGETSET_MAGIC_DEF("nodeValue", dom_node_getProperty, dom_node_setProperty, NODE_JSPROPERTY_NODEVALUE),
2465 :
2466 : JS_CGETSET_MAGIC_DEF("nodeType", dom_node_getProperty, NULL, NODE_JSPROPERTY_NODETYPE),
2467 : JS_CGETSET_MAGIC_DEF("parentNode", dom_node_getProperty, NULL, NODE_JSPROPERTY_PARENTNODE),
2468 : JS_CGETSET_MAGIC_DEF("childNodes", dom_node_getProperty, NULL, NODE_JSPROPERTY_CHILDNODES),
2469 : JS_CGETSET_MAGIC_DEF("firstChild", dom_node_getProperty, NULL, NODE_JSPROPERTY_FIRSTCHILD),
2470 : JS_CGETSET_MAGIC_DEF("lastChild", dom_node_getProperty, NULL, NODE_JSPROPERTY_LASTCHILD),
2471 : JS_CGETSET_MAGIC_DEF("previousSibling", dom_node_getProperty, NULL, NODE_JSPROPERTY_PREVIOUSSIBLING),
2472 : JS_CGETSET_MAGIC_DEF("nextSibling", dom_node_getProperty, NULL, NODE_JSPROPERTY_NEXTSIBLING),
2473 : JS_CGETSET_MAGIC_DEF("attributes", dom_node_getProperty, NULL, NODE_JSPROPERTY_ATTRIBUTES),
2474 : JS_CGETSET_MAGIC_DEF("ownerDocument", dom_node_getProperty, NULL, NODE_JSPROPERTY_OWNERDOCUMENT),
2475 : JS_CGETSET_MAGIC_DEF("namespaceURI", dom_node_getProperty, NULL, NODE_JSPROPERTY_NAMESPACEURI),
2476 : JS_CGETSET_MAGIC_DEF("prefix", dom_node_getProperty, dom_node_setProperty, NODE_JSPROPERTY_PREFIX),
2477 : JS_CGETSET_MAGIC_DEF("localName", dom_node_getProperty, NULL, NODE_JSPROPERTY_LOCALNAME),
2478 : JS_CGETSET_MAGIC_DEF("baseURI", dom_node_getProperty, NULL, NODE_JSPROPERTY_BASEURI),
2479 : JS_CGETSET_MAGIC_DEF("textContent", dom_node_getProperty, dom_node_setProperty, NODE_JSPROPERTY_TEXTCONTENT),
2480 : /*elementTraversal interface*/
2481 : JS_CGETSET_MAGIC_DEF("firstElementChild", dom_node_getProperty, NULL, NODE_JSPROPERTY_FIRSTELEMENTCHILD),
2482 : JS_CGETSET_MAGIC_DEF("lastElementChild", dom_node_getProperty, NULL, NODE_JSPROPERTY_LASTELEMENTCHILD),
2483 : JS_CGETSET_MAGIC_DEF("previousElementSibling", dom_node_getProperty, NULL, NODE_JSPROPERTY_PREVIOUSELEMENTSIBLING),
2484 : JS_CGETSET_MAGIC_DEF("nextElementSibling", dom_node_getProperty, NULL, NODE_JSPROPERTY_NEXTELEMENTSIBLING),
2485 :
2486 : JS_CFUNC_DEF("insertBefore", 2, xml_node_insert_before),
2487 : JS_CFUNC_DEF("replaceChild", 2, xml_node_replace_child),
2488 : JS_CFUNC_DEF("removeChild", 1, xml_node_remove_child),
2489 : JS_CFUNC_DEF("appendChild", 1, xml_node_append_child),
2490 : JS_CFUNC_DEF("hasChildNodes", 0, xml_node_has_children),
2491 : JS_CFUNC_DEF("cloneNode", 1, xml_clone_node),
2492 : JS_CFUNC_DEF("normalize", 0, xml_dom3_not_implemented),
2493 : JS_CFUNC_DEF("isSupported", 2, xml_dom3_not_implemented),
2494 : JS_CFUNC_DEF("hasAttributes", 0, xml_node_has_attributes),
2495 : JS_CFUNC_DEF("compareDocumentPosition", 1, xml_dom3_not_implemented),
2496 : JS_CFUNC_DEF("isSameNode", 1, xml_node_is_same_node),
2497 : JS_CFUNC_DEF("lookupPrefix", 1, xml_dom3_not_implemented),
2498 : JS_CFUNC_DEF("isDefaultNamespace", 1, xml_dom3_not_implemented),
2499 : JS_CFUNC_DEF("lookupNamespaceURI", 1, xml_dom3_not_implemented),
2500 : /*we don't support full node compare*/
2501 : JS_CFUNC_DEF("isEqualNode", 1, xml_node_is_same_node),
2502 : JS_CFUNC_DEF("getFeature", 2, xml_dom3_not_implemented),
2503 : JS_CFUNC_DEF("setUserData", 3, xml_dom3_not_implemented),
2504 : JS_CFUNC_DEF("getUserData", 1, xml_dom3_not_implemented),
2505 : };
2506 :
2507 :
2508 : static const JSCFunctionListEntry document_Funcs[] =
2509 : {
2510 : JS_CGETSET_MAGIC_DEF("doctype", dom_document_getProperty, NULL, DOCUMENT_JSPROPERTY_DOCTYPE),
2511 : JS_CGETSET_MAGIC_DEF("implementation", dom_document_getProperty, NULL, DOCUMENT_JSPROPERTY_IMPLEMENTATION),
2512 : JS_CGETSET_MAGIC_DEF("documentElement", dom_document_getProperty, NULL, DOCUMENT_JSPROPERTY_DOCUMENTELEMENT),
2513 : JS_CGETSET_MAGIC_DEF("inputEncoding", dom_document_getProperty, NULL, DOCUMENT_JSPROPERTY_INPUTENCODING),
2514 : JS_CGETSET_MAGIC_DEF("xmlEncoding", dom_document_getProperty, NULL, DOCUMENT_JSPROPERTY_XMLENCODING),
2515 : JS_CGETSET_MAGIC_DEF("xmlStandalone", dom_document_getProperty, dom_document_setProperty, DOCUMENT_JSPROPERTY_XMLSTANDALONE),
2516 : JS_CGETSET_MAGIC_DEF("xmlVersion", dom_document_getProperty, dom_document_setProperty, DOCUMENT_JSPROPERTY_XMLVERSION),
2517 : JS_CGETSET_MAGIC_DEF("strictErrorChecking", dom_document_getProperty, dom_document_setProperty, DOCUMENT_JSPROPERTY_STRICTERRORCHECKING),
2518 : JS_CGETSET_MAGIC_DEF("documentURI", dom_document_getProperty, dom_document_setProperty, DOCUMENT_JSPROPERTY_DOCUMENTURI),
2519 : JS_CGETSET_MAGIC_DEF("location", dom_document_getProperty, dom_document_setProperty, DOCUMENT_JSPROPERTY_LOCATION),
2520 : JS_CGETSET_MAGIC_DEF("domConfig", dom_document_getProperty, dom_document_setProperty, DOCUMENT_JSPROPERTY_DOMCONFIG),
2521 : JS_CGETSET_MAGIC_DEF("global", dom_document_getProperty, dom_document_setProperty, DOCUMENT_JSPROPERTY_GLOBAL),
2522 :
2523 : JS_CFUNC_DEF("createElement", 1, xml_document_create_element),
2524 : JS_CFUNC_DEF("createDocumentFragment", 0, xml_dom3_not_implemented),
2525 : JS_CFUNC_DEF("createTextNode", 1, xml_document_create_text),
2526 : JS_CFUNC_DEF("createComment", 1, xml_dom3_not_implemented),
2527 : JS_CFUNC_DEF("createCDATASection", 1, xml_dom3_not_implemented),
2528 : JS_CFUNC_DEF("createProcessingInstruction", 2, xml_dom3_not_implemented),
2529 : JS_CFUNC_DEF("createAttribute", 1, xml_dom3_not_implemented),
2530 : JS_CFUNC_DEF("createEntityReference", 1, xml_dom3_not_implemented),
2531 : JS_CFUNC_DEF("getElementsByTagName", 1, xml_document_elements_by_tag),
2532 : JS_CFUNC_DEF("importNode", 2, xml_dom3_not_implemented),
2533 : JS_CFUNC_DEF("createElementNS", 2, xml_document_create_element),
2534 : JS_CFUNC_DEF("createAttributeNS", 2, xml_dom3_not_implemented),
2535 : JS_CFUNC_DEF("getElementsByTagNameNS", 2, xml_document_elements_by_tag),
2536 : JS_CFUNC_DEF("getElementById", 1, xml_document_element_by_id),
2537 : JS_CFUNC_DEF("adoptNode", 1, xml_dom3_not_implemented),
2538 : JS_CFUNC_DEF("normalizeDocument", 0, xml_dom3_not_implemented),
2539 : JS_CFUNC_DEF("renameNode", 3, xml_dom3_not_implemented),
2540 : /*eventTarget interface*/
2541 : JS_DOM3_EVENT_TARGET_INTERFACE
2542 : /*DocumentEvent interface*/
2543 : JS_CFUNC_DEF("createEvent", 1, xml_dom3_not_implemented),
2544 : };
2545 :
2546 : static const JSCFunctionListEntry element_Funcs[] =
2547 : {
2548 : JS_CGETSET_MAGIC_DEF("tagName", dom_element_getProperty, NULL, ELEMENT_JSPROPERTY_TAGNAME),
2549 : JS_CGETSET_MAGIC_DEF("schemaTypeInfo", dom_element_getProperty, NULL, ELEMENT_JSPROPERTY_SCHEMATYPEINFO),
2550 :
2551 : JS_CFUNC_DEF("getAttribute", 1, xml_element_get_attribute),
2552 : JS_CFUNC_DEF("setAttribute", 2, xml_element_set_attribute),
2553 : JS_CFUNC_DEF("removeAttribute", 1, xml_element_remove_attribute),
2554 : JS_CFUNC_DEF("getAttributeNS", 2, xml_element_get_attribute),
2555 : JS_CFUNC_DEF("setAttributeNS", 3, xml_element_set_attribute),
2556 : JS_CFUNC_DEF("removeAttributeNS", 2, xml_element_remove_attribute),
2557 : JS_CFUNC_DEF("hasAttribute", 1, xml_element_has_attribute),
2558 : JS_CFUNC_DEF("hasAttributeNS", 2, xml_element_has_attribute),
2559 : JS_CFUNC_DEF("getElementsByTagName", 1, xml_element_elements_by_tag),
2560 : JS_CFUNC_DEF("getElementsByTagNameNS", 2, xml_element_elements_by_tag),
2561 : JS_CFUNC_DEF("setIdAttribute", 2, xml_element_set_id),
2562 : JS_CFUNC_DEF("setIdAttributeNS", 3, xml_element_set_id),
2563 : JS_CFUNC_DEF("toString", 0, xml_element_to_string),
2564 : JS_CFUNC_DEF("getAttributeNode", 1, xml_dom3_not_implemented),
2565 : JS_CFUNC_DEF("setAttributeNode", 1, xml_dom3_not_implemented),
2566 : JS_CFUNC_DEF("removeAttributeNode", 1, xml_dom3_not_implemented),
2567 : JS_CFUNC_DEF("getAttributeNodeNS", 2, xml_dom3_not_implemented),
2568 : JS_CFUNC_DEF("setAttributeNodeNS", 1, xml_dom3_not_implemented),
2569 : JS_CFUNC_DEF("setIdAttributeNode", 2, xml_dom3_not_implemented)
2570 : };
2571 :
2572 : static const JSCFunctionListEntry text_Funcs[] =
2573 : {
2574 : JS_CGETSET_MAGIC_DEF("data", dom_text_getProperty, dom_text_setProperty, TEXT_JSPROPERTY_DATA),
2575 : JS_CGETSET_MAGIC_DEF("length", dom_text_getProperty, NULL, TEXT_JSPROPERTY_LENGTH),
2576 : JS_CGETSET_MAGIC_DEF("isElementContentWhitespace", dom_text_getProperty, NULL, TEXT_JSPROPERTY_ISELEMENTCONTENTWHITESPACE),
2577 : JS_CGETSET_MAGIC_DEF("wholeText", dom_text_getProperty, NULL, TEXT_JSPROPERTY_WHOLETEXT),
2578 : #if 0
2579 : JS_CFUNC_DEF("substringData", 2, xml_dom3_not_implemented),
2580 : JS_CFUNC_DEF("appendData", 1, xml_dom3_not_implemented),
2581 : JS_CFUNC_DEF("insertData", 2, xml_dom3_not_implemented),
2582 : JS_CFUNC_DEF("deleteData", 2, xml_dom3_not_implemented),
2583 : JS_CFUNC_DEF("replaceData", 3, xml_dom3_not_implemented),
2584 : JS_CFUNC_DEF("splitText", 1, xml_dom3_not_implemented),
2585 : JS_CFUNC_DEF("replaceWholeText", 1, xml_dom3_not_implemented),
2586 : #endif
2587 : };
2588 :
2589 : static const JSCFunctionListEntry event_Funcs[] =
2590 : {
2591 : JS_CGETSET_MAGIC_DEF("type", event_getProperty, NULL, EVENT_JSPROPERTY_TYPE),
2592 : JS_CGETSET_MAGIC_DEF("target", event_getProperty, NULL, EVENT_JSPROPERTY_TARGET),
2593 : JS_CGETSET_MAGIC_DEF("currentTarget", event_getProperty, NULL, EVENT_JSPROPERTY_CURRENTTARGET),
2594 : JS_CGETSET_MAGIC_DEF("eventPhase", event_getProperty, NULL, EVENT_JSPROPERTY_EVENTPHASE),
2595 : JS_CGETSET_MAGIC_DEF("bubbles", event_getProperty, NULL, EVENT_JSPROPERTY_BUBBLES),
2596 : JS_CGETSET_MAGIC_DEF("cancelable", event_getProperty, NULL, EVENT_JSPROPERTY_CANCELABLE),
2597 : JS_CGETSET_MAGIC_DEF("timeStamp", event_getProperty, NULL, EVENT_JSPROPERTY_TIMESTAMP),
2598 : JS_CGETSET_MAGIC_DEF("namespaceURI", event_getProperty, NULL, EVENT_JSPROPERTY_NAMESPACEURI),
2599 : JS_CGETSET_MAGIC_DEF("defaultPrevented", event_getProperty, NULL, EVENT_JSPROPERTY_DEFAULTPREVENTED),
2600 : /*UIEvent*/
2601 : JS_CGETSET_MAGIC_DEF("detail", event_getProperty, NULL, EVENT_JSPROPERTY_DETAIL),
2602 : /*text, connectionEvent*/
2603 : JS_CGETSET_MAGIC_DEF("data", event_getProperty, NULL, EVENT_JSPROPERTY_DATA),
2604 : /*MouseEvent*/
2605 : JS_CGETSET_MAGIC_DEF("screenX", event_getProperty, NULL, EVENT_JSPROPERTY_SCREENX),
2606 : JS_CGETSET_MAGIC_DEF("screenY", event_getProperty, NULL, EVENT_JSPROPERTY_SCREENY),
2607 : JS_CGETSET_MAGIC_DEF("clientX", event_getProperty, NULL, EVENT_JSPROPERTY_CLIENTX),
2608 : JS_CGETSET_MAGIC_DEF("clientY", event_getProperty, NULL, EVENT_JSPROPERTY_CLIENTY),
2609 : JS_CGETSET_MAGIC_DEF("button", event_getProperty, NULL, EVENT_JSPROPERTY_BUTTON),
2610 : JS_CGETSET_MAGIC_DEF("relatedTarget", event_getProperty, NULL, EVENT_JSPROPERTY_RELATEDTARGET),
2611 : /*wheelEvent*/
2612 : JS_CGETSET_MAGIC_DEF("wheelDelta", event_getProperty, NULL, EVENT_JSPROPERTY_WHEELDELTA),
2613 : /*keyboard*/
2614 : JS_CGETSET_MAGIC_DEF("keyIdentifier", event_getProperty, NULL, EVENT_JSPROPERTY_KEYIDENTIFIER),
2615 : JS_CGETSET_MAGIC_DEF("keyChar", event_getProperty, NULL, EVENT_JSPROPERTY_KEYCHAR),
2616 : JS_CGETSET_MAGIC_DEF("charCode", event_getProperty, NULL, EVENT_JSPROPERTY_CHARCODE),
2617 : /*progress*/
2618 : JS_CGETSET_MAGIC_DEF("lengthComputable", event_getProperty, NULL, EVENT_JSPROPERTY_LENGTHCOMPUTABLE),
2619 : JS_CGETSET_MAGIC_DEF("typeArg", event_getProperty, NULL, EVENT_JSPROPERTY_TYPEARG),
2620 : JS_CGETSET_MAGIC_DEF("loaded", event_getProperty, NULL, EVENT_JSPROPERTY_LOADED),
2621 : JS_CGETSET_MAGIC_DEF("total", event_getProperty, NULL, EVENT_JSPROPERTY_TOTAL),
2622 : JS_CGETSET_MAGIC_DEF("buffering", event_getProperty, NULL, EVENT_JSPROPERTY_BUFFER_ON),
2623 : JS_CGETSET_MAGIC_DEF("bufferLevel", event_getProperty, NULL, EVENT_JSPROPERTY_BUFFERLEVEL),
2624 : JS_CGETSET_MAGIC_DEF("bufferRemainingTime", event_getProperty, NULL, EVENT_JSPROPERTY_BUFFERREMAININGTIME),
2625 : JS_CGETSET_MAGIC_DEF("status", event_getProperty, NULL, EVENT_JSPROPERTY_STATUS),
2626 : /*used by vrml*/
2627 : JS_CGETSET_MAGIC_DEF("width", event_getProperty, NULL, EVENT_JSPROPERTY_WIDTH),
2628 : JS_CGETSET_MAGIC_DEF("height", event_getProperty, NULL, EVENT_JSPROPERTY_HEIGHT),
2629 : JS_CGETSET_MAGIC_DEF("offset_x", event_getProperty, NULL, EVENT_JSPROPERTY_OFFSETX),
2630 : JS_CGETSET_MAGIC_DEF("offset_y", event_getProperty, NULL, EVENT_JSPROPERTY_OFFSETY),
2631 : JS_CGETSET_MAGIC_DEF("vp_width", event_getProperty, NULL, EVENT_JSPROPERTY_VPWIDTH),
2632 : JS_CGETSET_MAGIC_DEF("vp_height", event_getProperty, NULL, EVENT_JSPROPERTY_VPHEIGHT),
2633 : JS_CGETSET_MAGIC_DEF("translation_x", event_getProperty, NULL, EVENT_JSPROPERTY_TRANSLATIONX),
2634 : JS_CGETSET_MAGIC_DEF("translation_y", event_getProperty, NULL, EVENT_JSPROPERTY_TRANSLATIONY),
2635 : JS_CGETSET_MAGIC_DEF("type3d", event_getProperty, NULL, EVENT_JSPROPERTY_TYPE3D),
2636 : JS_CGETSET_MAGIC_DEF("error", event_getProperty, NULL, EVENT_JSPROPERTY_ERROR),
2637 : JS_CGETSET_MAGIC_DEF("dynamic_scene", event_getProperty, NULL, EVENT_JSPROPERTY_DYNAMIC_SCENE),
2638 : JS_CGETSET_MAGIC_DEF("url", event_getProperty, NULL, EVENT_JSPROPERTY_URL),
2639 :
2640 : JS_CFUNC_DEF("stopPropagation", 0, event_stop_propagation),
2641 : JS_CFUNC_DEF("stopImmediatePropagation", 0, event_stop_immediate_propagation),
2642 : JS_CFUNC_DEF("preventDefault", 0, event_prevent_default),
2643 : #if 0
2644 : JS_CFUNC_DEF("initEvent", 3, event_prevent_default),
2645 : JS_CFUNC_DEF("initEventNS", 4, event_prevent_default),
2646 : #endif
2647 : };
2648 :
2649 : static const JSCFunctionListEntry nodeList_Funcs[] =
2650 : {
2651 : JS_CGETSET_MAGIC_DEF("length", dom_nodelist_getProperty, NULL, NODELIST_JSPROPERTY_LENGTH),
2652 : JS_CFUNC_DEF("item", 1, dom_nodelist_item),
2653 : };
2654 :
2655 :
2656 152 : void domDocument_gc_mark(JSRuntime *rt, JSValueConst obj, JS_MarkFunc *mark_func)
2657 : {
2658 : GF_SceneGraph *sg;
2659 152 : sg = (GF_SceneGraph*) JS_GetOpaque_Nocheck(obj);
2660 : /*the JS proto of the svgClass or a destroyed object*/
2661 152 : if (!sg || !sg->js_data) return;
2662 144 : if (!JS_IsUndefined(sg->js_data->document))
2663 72 : JS_MarkValue(rt, sg->js_data->document, mark_func);
2664 : }
2665 533940 : void domElement_gc_mark(JSRuntime *rt, JSValueConst obj, JS_MarkFunc *mark_func)
2666 : {
2667 533940 : GF_Node *n = (GF_Node *) JS_GetOpaque_Nocheck(obj);
2668 : /*the JS proto of the svgClass or a destroyed object*/
2669 533940 : if (!n) return;
2670 240 : if (!n->sgprivate || !n->sgprivate->interact || !n->sgprivate->interact->js_binding) return;
2671 :
2672 480 : if (!JS_IsUndefined(n->sgprivate->interact->js_binding->obj))
2673 240 : JS_MarkValue(rt, n->sgprivate->interact->js_binding->obj, mark_func);
2674 : }
2675 :
2676 409 : void dom_js_load(GF_SceneGraph *scene, JSContext *c)
2677 : {
2678 : JSValue proto;
2679 409 : JSValue global = JS_GetGlobalObject(c);
2680 409 : JSRuntime *jsrt = JS_GetRuntime(c);
2681 :
2682 409 : if (!dom_rt) {
2683 46 : GF_SAFEALLOC(dom_rt, GF_DOMRuntime);
2684 46 : if (!dom_rt) {
2685 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[DOMJS] Failed to allocate DOM runtime\n"));
2686 0 : return;
2687 : }
2688 46 : dom_rt->handlers = gf_list_new();
2689 46 : GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[DOMCore] dom run-time allocated\n"));
2690 : }
2691 409 : dom_rt->nb_inst++;
2692 :
2693 409 : define_dom_exception(c, global);
2694 :
2695 409 : domNodeClass.class.gc_mark = domElement_gc_mark;
2696 409 : SETUP_JSCLASS(domNodeClass, "Node", node_Funcs, NULL, dom_node_finalize, 0);
2697 409 : JS_SetPropertyStr(c, proto, "ELEMENT_NODE", JS_NewInt32(c, 1) );
2698 409 : JS_SetPropertyStr(c, proto, "TEXT_NODE", JS_NewInt32(c, 3));
2699 409 : JS_SetPropertyStr(c, proto, "CDATA_SECTION_NODE", JS_NewInt32(c, 4));
2700 409 : JS_SetPropertyStr(c, proto, "DOCUMENT_NODE", JS_NewInt32(c, 9));
2701 :
2702 409 : domDocumentClass.class.gc_mark = domDocument_gc_mark;
2703 409 : SETUP_JSCLASS(domDocumentClass, "Document", document_Funcs, NULL, dom_document_finalize, domNodeClass.class_id);
2704 409 : domElementClass.class.gc_mark = domElement_gc_mark;
2705 409 : SETUP_JSCLASS(domElementClass, "Element", element_Funcs, NULL, dom_node_finalize, domNodeClass.class_id);
2706 409 : domTextClass.class.gc_mark = domElement_gc_mark;
2707 409 : SETUP_JSCLASS(domTextClass, "Text", text_Funcs, NULL, dom_node_finalize, domNodeClass.class_id);
2708 :
2709 409 : SETUP_JSCLASS(domEventClass, "Event", event_Funcs, NULL, NULL, 0);
2710 409 : JS_SetPropertyStr(c, proto, "CAPTURING_PHASE", JS_NewInt32(c, 1));
2711 409 : JS_SetPropertyStr(c, proto, "AT_TARGET", JS_NewInt32(c, 2));
2712 409 : JS_SetPropertyStr(c, proto, "BUBBLING_PHASE", JS_NewInt32(c, 3));
2713 409 : JS_SetPropertyStr(c, proto, "DOM_KEY_LOCATION_STANDARD ", JS_NewInt32(c, 0));
2714 409 : JS_SetPropertyStr(c, proto, "DOM_KEY_LOCATION_LEFT", JS_NewInt32(c, 1));
2715 409 : JS_SetPropertyStr(c, proto, "DOM_KEY_LOCATION_RIGHT", JS_NewInt32(c, 2));
2716 409 : JS_SetPropertyStr(c, proto, "DOM_KEY_LOCATION_NUMPAD", JS_NewInt32(c, 3));
2717 :
2718 :
2719 409 : SETUP_JSCLASS(domNodeListClass, "NodeList", nodeList_Funcs, NULL, dom_nodelist_finalize, 0);
2720 :
2721 : JS_FreeValue(c, global);
2722 : }
2723 :
2724 : #ifdef GPAC_ENABLE_HTML5_MEDIA
2725 : void html_media_element_js_finalize(JSContext *c, GF_Node *n);
2726 : #endif
2727 :
2728 : GF_EXPORT
2729 504 : void gf_sg_js_dom_pre_destroy(JSRuntime *rt, GF_SceneGraph *sg, GF_Node *n)
2730 : {
2731 : u32 i, count;
2732 504 : if (n) {
2733 187 : if (n->sgprivate->interact && n->sgprivate->interact->js_binding && !JS_IsUndefined(n->sgprivate->interact->js_binding->obj) ) {
2734 92 : JS_SetOpaque(n->sgprivate->interact->js_binding->obj, NULL);
2735 92 : if (gf_list_del_item(sg->objects, n->sgprivate->interact->js_binding)>=0) {
2736 0 : JS_SetOpaque(n->sgprivate->interact->js_binding->obj, NULL);
2737 0 : JS_FreeValueRT(rt, n->sgprivate->interact->js_binding->obj);
2738 : }
2739 92 : n->sgprivate->interact->js_binding->obj = JS_UNDEFINED;
2740 : }
2741 : return;
2742 : }
2743 :
2744 : /*force cleanup of scripts/handlers not destroyed - this usually happens when a script/handler node has been created in a script
2745 : but not inserted in the graph*/
2746 424 : while (gf_list_count(sg->objects)) {
2747 : GF_Node *js_node;
2748 15 : struct _node_js_binding *js_bind = (struct _node_js_binding *)gf_list_get(sg->objects, 0);
2749 : js_node = dom_get_node(js_bind->obj);
2750 : if (js_node) {
2751 : if (js_node->sgprivate->tag == TAG_SVG_video || js_node->sgprivate->tag == TAG_SVG_audio) {
2752 : #ifdef GPAC_ENABLE_HTML5_MEDIA
2753 : html_media_element_js_finalize(c, js_node);
2754 : #endif
2755 : }
2756 15 : JS_SetOpaque(js_bind->obj, NULL);
2757 15 : JS_FreeValueRT(rt, js_node->sgprivate->interact->js_binding->obj);
2758 15 : js_node->sgprivate->interact->js_binding->obj=JS_UNDEFINED;
2759 15 : gf_node_unregister(js_node, NULL);
2760 : }
2761 15 : gf_list_rem(sg->objects, 0);
2762 : }
2763 :
2764 409 : count = dom_rt ? gf_list_count(dom_rt->handlers) : 0;
2765 26 : for (i=0; i<count; i++) {
2766 26 : SVG_handlerElement *handler = (SVG_handlerElement *)gf_list_get(dom_rt->handlers, i);
2767 26 : if (!handler->js_data) continue;
2768 :
2769 : /*if same context and same document, discard handler*/
2770 26 : if (!sg || (handler->sgprivate->scenegraph==sg)) {
2771 : /*unprotect the function*/
2772 : JS_FreeValueRT(rt, handler->js_data->fun_val);
2773 26 : gf_free(handler->js_data);
2774 26 : handler->js_data = NULL;
2775 26 : gf_list_rem(dom_rt->handlers, i);
2776 26 : i--;
2777 26 : count--;
2778 : }
2779 : }
2780 409 : if (sg->js_data) {
2781 6 : if (!JS_IsUndefined(sg->js_data->document)) {
2782 : //detach sg from object and free it
2783 3 : JS_SetOpaque(sg->js_data->document, NULL);
2784 3 : JS_FreeValueRT(rt, sg->js_data->document);
2785 : }
2786 3 : gf_free(sg->js_data);
2787 3 : sg->js_data = NULL;
2788 : }
2789 : }
2790 :
2791 409 : void dom_js_unload()
2792 : {
2793 409 : if (!dom_rt) return;
2794 409 : dom_rt->nb_inst--;
2795 409 : if (!dom_rt->nb_inst) {
2796 46 : gf_list_del(dom_rt->handlers);
2797 46 : gf_free(dom_rt);
2798 46 : dom_rt = NULL;
2799 : }
2800 : }
2801 :
2802 407 : JSValue dom_js_define_event(JSContext *c)
2803 : {
2804 407 : JSValue global = JS_GetGlobalObject(c);
2805 :
2806 407 : JSValue obj = JS_NewObjectClass(c, domEventClass.class_id);
2807 407 : JS_SetPropertyStr(c, global, "evt", obj);
2808 : // JS_DefinePropertyStr(c, global, "event", obj);
2809 : JS_FreeValue(c, global);
2810 407 : return obj;
2811 : }
2812 0 : GF_DOM_Event *dom_get_evt_private(JSValue v)
2813 : {
2814 0 : return JS_GetOpaque(v, domEventClass.class_id);
2815 : }
2816 :
2817 94 : JSValue gf_dom_new_event(JSContext *c)
2818 : {
2819 94 : return JS_NewObjectClass(c, domEventClass.class_id);
2820 : }
2821 :
2822 6 : JSClassID dom_js_get_element_proto(JSContext *c)
2823 : {
2824 6 : return domElementClass.class_id;
2825 : }
2826 6 : JSClassID dom_js_get_document_proto(JSContext *c)
2827 : {
2828 6 : return domDocumentClass.class_id;
2829 : }
2830 : #endif /*GPAC_HAS_QJS*/
2831 :
2832 :
2833 :
2834 : #if 0 //unused
2835 : /*! parses the given XML document and returns a scene graph composed of GF_DOMFullNode
2836 : \param src the source file
2837 : \param scene set to the new scene graph
2838 : \return error if any
2839 : */
2840 : GF_Err gf_sg_new_from_xml_doc(const char *src, GF_SceneGraph **scene)
2841 : {
2842 : #ifdef GPAC_HAS_QJS
2843 : GF_Err e;
2844 : XMLHTTPContext *ctx;
2845 : GF_SceneGraph *sg;
2846 :
2847 : GF_SAFEALLOC(ctx, XMLHTTPContext);
2848 : if (!ctx) return GF_OUT_OF_MEM;
2849 :
2850 : ctx->sax = gf_xml_sax_new(xml_http_sax_start, xml_http_sax_end, xml_http_sax_text, ctx);
2851 : ctx->node_stack = gf_list_new();
2852 : sg = gf_sg_new();
2853 : ctx->document = sg;
2854 : e = gf_xml_sax_parse_file(ctx->sax, src, NULL);
2855 : gf_xml_sax_del(ctx->sax);
2856 : gf_list_del(ctx->node_stack);
2857 : gf_free(ctx);
2858 :
2859 : *scene = NULL;
2860 : if (e<0) {
2861 : gf_sg_del(sg);
2862 : return e;
2863 : }
2864 : *scene = sg;
2865 : return GF_OK;
2866 : #else
2867 : return GF_NOT_SUPPORTED;
2868 : #endif
2869 : }
2870 : #endif
2871 :
2872 :
2873 :
2874 : #if 0 //unused
2875 :
2876 : typedef struct
2877 : {
2878 : GF_SAXParser *sax;
2879 : GF_List *node_stack;
2880 : /*dom graph*/
2881 : GF_SceneGraph *document;
2882 : } XMLReloadContext;
2883 :
2884 : static void xml_reload_node_start(void *sax_cbck, const char *node_name, const char *name_space, const GF_XMLAttribute *attributes, u32 nb_attributes)
2885 : {
2886 : GF_DOM_Event evt;
2887 : Bool is_root = GF_FALSE;
2888 : Bool modified = GF_FALSE;
2889 : Bool atts_modified = GF_FALSE;
2890 : Bool new_node = GF_FALSE;
2891 : u32 i;
2892 : GF_DOMFullAttribute *prev = NULL;
2893 : GF_DOMFullNode *par = NULL;
2894 : XMLReloadContext *ctx = (XMLReloadContext *)sax_cbck;
2895 : GF_DOMFullNode *node;
2896 :
2897 : node = (GF_DOMFullNode *)gf_list_last(ctx->node_stack);
2898 : if (!node) {
2899 : is_root = GF_TRUE;
2900 : node = (GF_DOMFullNode *) ctx->document->RootNode;
2901 : }
2902 :
2903 : if (is_root) {
2904 : Bool same_node = GF_TRUE;
2905 : if (strcmp(node->name, node_name)) same_node = GF_FALSE;
2906 : if (node->ns && name_space && strcmp(gf_sg_get_namespace(node->sgprivate->scenegraph, node->ns), name_space)) same_node = GF_FALSE;
2907 :
2908 : if (!same_node) {
2909 : if (node->name) gf_free(node->name);
2910 : node->name = gf_strdup(node_name);
2911 : node->ns = 0;
2912 : if (name_space) {
2913 : gf_sg_add_namespace(node->sgprivate->scenegraph, (char *) name_space, NULL);
2914 : node->ns = gf_sg_get_namespace_code_from_name(node->sgprivate->scenegraph, (char*)name_space);
2915 : }
2916 : modified = GF_TRUE;
2917 : }
2918 : } else {
2919 : GF_ChildNodeItem *child = node->children;
2920 : node = NULL;
2921 : /*locate the node in the children*/
2922 : while (child) {
2923 : Bool same_node = GF_TRUE;
2924 : node = (GF_DOMFullNode*)child->node;
2925 : if (strcmp(node->name, node_name)) same_node = GF_FALSE;
2926 : if (node->ns && name_space && strcmp(gf_sg_get_namespace(node->sgprivate->scenegraph, node->ns), name_space)) same_node = GF_FALSE;
2927 : if (same_node) {
2928 : break;
2929 : }
2930 : node = NULL;
2931 : child = child->next;
2932 : }
2933 : if (!node) {
2934 : modified = GF_TRUE;
2935 :
2936 : /*create the new node*/
2937 : node = (GF_DOMFullNode *) gf_node_new(ctx->document, TAG_DOMFullNode);
2938 : node->name = gf_strdup(node_name);
2939 : if (name_space) {
2940 : gf_sg_add_namespace(node->sgprivate->scenegraph, (char *)name_space, NULL);
2941 : node->ns = gf_sg_get_namespace_code(node->sgprivate->scenegraph, (char *)name_space);
2942 : }
2943 :
2944 : par = (GF_DOMFullNode *)gf_list_last(ctx->node_stack);
2945 : gf_node_register((GF_Node*)node, (GF_Node*)par);
2946 : gf_node_list_add_child(&par->children, (GF_Node*)node);
2947 : new_node = GF_TRUE;
2948 : }
2949 : }
2950 : gf_list_add(ctx->node_stack, node);
2951 :
2952 : if (!modified) {
2953 : u32 count = 0;
2954 : GF_DOMFullAttribute *att = (GF_DOMFullAttribute *)node->attributes;
2955 : while (att) {
2956 : Bool found = GF_FALSE;
2957 : for (i=0; i<nb_attributes; i++) {
2958 : if (!stricmp(attributes[i].name, "xml:id")) {
2959 : const char *id = gf_node_get_name((GF_Node*)node);
2960 : if (!id || strcmp(id, attributes[i].value))
2961 : modified = GF_TRUE;
2962 : } else if (!strcmp(att->name, attributes[i].name)) {
2963 : found = GF_TRUE;
2964 : if (strcmp((const char *)att->data, attributes[i].value)) {
2965 : atts_modified = GF_TRUE;
2966 : gf_free(att->data);
2967 : att->data = gf_strdup(attributes[i].value);
2968 : }
2969 : }
2970 : }
2971 : if (!found) {
2972 : modified = GF_TRUE;
2973 : break;
2974 : }
2975 : count++;
2976 : att = (GF_DOMFullAttribute *)att->next;
2977 : }
2978 : if (count != nb_attributes)
2979 : modified = GF_TRUE;
2980 : }
2981 :
2982 :
2983 : if (modified) {
2984 : GF_DOMFullAttribute *tmp, *att;
2985 : att = (GF_DOMFullAttribute *)node->attributes;
2986 : while (att) {
2987 : if (att->name) gf_free(att->name);
2988 : if (att->data) gf_free(att->data);
2989 : tmp = att;
2990 : att = (GF_DOMFullAttribute *)att->next;
2991 : gf_free(tmp);
2992 : }
2993 :
2994 : /*parse all atts*/
2995 : for (i=0; i<nb_attributes; i++) {
2996 : if (!stricmp(attributes[i].name, "xml:id")) {
2997 : u32 id = gf_sg_get_max_node_id(ctx->document) + 1;
2998 : gf_node_set_id((GF_Node *)node, id, attributes[i].value);
2999 : } else {
3000 : GF_DOMFullAttribute *att;
3001 : GF_SAFEALLOC(att, GF_DOMFullAttribute);
3002 : if (!att) {
3003 : GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[DOMJS] Failed to allocate DOM attribute\n"));
3004 : continue;
3005 : }
3006 : att->tag = TAG_DOM_ATT_any;
3007 : att->name = gf_strdup(attributes[i].name);
3008 : att->data = gf_strdup(attributes[i].value);
3009 : if (prev) prev->next = (GF_DOMAttribute*)att;
3010 : else node->attributes = (GF_DOMAttribute*)att;
3011 : prev = att;
3012 : }
3013 : }
3014 : atts_modified = GF_TRUE;
3015 : }
3016 :
3017 : if (atts_modified || new_node) {
3018 : memset(&evt, 0, sizeof(GF_DOM_Event));
3019 : evt.type = new_node ? GF_EVENT_NODE_INSERTED : GF_EVENT_ATTR_MODIFIED;
3020 : evt.bubbles = 1;
3021 : evt.relatedNode = (GF_Node*) ( (evt.type == GF_EVENT_NODE_INSERTED) ? par : node);
3022 : gf_dom_event_fire((GF_Node*)node, &evt);
3023 : }
3024 : }
3025 :
3026 : static void xml_reload_node_end(void *sax_cbck, const char *node_name, const char *name_space)
3027 : {
3028 : XMLReloadContext *ctx = (XMLReloadContext *)sax_cbck;
3029 : GF_DOMFullNode *par = (GF_DOMFullNode *)gf_list_last(ctx->node_stack);
3030 : if (par) {
3031 : /*depth mismatch*/
3032 : if (strcmp(par->name, node_name)) return;
3033 : gf_list_rem_last(ctx->node_stack);
3034 : }
3035 : }
3036 : static void xml_reload_text_content(void *sax_cbck, const char *content, Bool is_cdata)
3037 : {
3038 : GF_DOM_Event evt;
3039 : u32 i, len;
3040 : GF_ChildNodeItem *child;
3041 : GF_DOMText *txt = NULL;
3042 : XMLReloadContext *ctx = (XMLReloadContext *)sax_cbck;
3043 : GF_DOMFullNode *par = (GF_DOMFullNode *)gf_list_last(ctx->node_stack);
3044 : if (!par) return;
3045 :
3046 : /*basic check, remove all empty text nodes*/
3047 : len = (u32) strlen(content);
3048 : for (i=0; i<len; i++) {
3049 : if (!strchr(" \n\r\t", content[i])) break;
3050 : }
3051 : if (i==len) return;
3052 :
3053 : child = par->children;
3054 : while (child) {
3055 : if (child->node->sgprivate->tag == TAG_DOMText) {
3056 : txt = (GF_DOMText *)child->node;
3057 : if (!strcmp(txt->textContent, content) && ((txt->type==GF_DOM_TEXT_REGULAR) || is_cdata))
3058 : return;
3059 : if (txt->textContent) gf_free(txt->textContent);
3060 : txt->textContent = gf_strdup(content);
3061 : txt->type = is_cdata ? GF_DOM_TEXT_CDATA : GF_DOM_TEXT_REGULAR;
3062 : break;
3063 : }
3064 : child = child->next;
3065 : }
3066 : if (!txt) {
3067 : txt = gf_dom_add_text_node((GF_Node *)par, gf_strdup(content) );
3068 : txt->type = is_cdata ? GF_DOM_TEXT_CDATA : GF_DOM_TEXT_REGULAR;
3069 :
3070 : memset(&evt, 0, sizeof(GF_DOM_Event));
3071 : evt.type = GF_EVENT_NODE_INSERTED;
3072 : evt.bubbles = 1;
3073 : evt.relatedNode = (GF_Node*) par;
3074 : gf_dom_event_fire((GF_Node*)txt, &evt);
3075 : }
3076 :
3077 : memset(&evt, 0, sizeof(GF_DOM_Event));
3078 : evt.type = GF_EVENT_CHAR_DATA_MODIFIED;
3079 : evt.bubbles = 1;
3080 : evt.relatedNode = (GF_Node*)par;
3081 : gf_dom_event_fire((GF_Node*)par, &evt);
3082 :
3083 : memset(&evt, 0, sizeof(GF_DOM_Event));
3084 : evt.type = GF_EVENT_DCCI_PROP_CHANGE;
3085 : evt.bubbles = 1;
3086 : evt.relatedNode = (GF_Node*)par;
3087 : gf_dom_event_fire((GF_Node*)par, &evt);
3088 : }
3089 :
3090 : GF_Err gf_sg_reload_xml_doc(const char *src, GF_SceneGraph *scene)
3091 : {
3092 : GF_Err e;
3093 : XMLReloadContext ctx;
3094 :
3095 : if (!src || !scene) return GF_BAD_PARAM;
3096 : memset(&ctx, 0, sizeof(XMLReloadContext));
3097 : ctx.document = scene;
3098 : ctx.node_stack = gf_list_new();
3099 :
3100 : ctx.sax = gf_xml_sax_new(xml_reload_node_start, xml_reload_node_end, xml_reload_text_content, &ctx);
3101 : e = gf_xml_sax_parse_file(ctx.sax, src, NULL);
3102 : gf_list_del(ctx.node_stack);
3103 : gf_xml_sax_del(ctx.sax);
3104 : if (e<0) return e;
3105 : return GF_OK;
3106 : }
3107 : #endif
3108 :
3109 :
3110 : #endif /*GPAC_DISABLE_SVG*/
|