LCOV - code coverage report
Current view: top level - jsmods - xhr.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 417 722 57.8 %
Date: 2021-04-29 23:48:07 Functions: 31 33 93.9 %

          Line data    Source code
       1             : /*
       2             :  *                      GPAC - Multimedia Framework C SDK
       3             :  *
       4             :  *                      Authors: Jean Le Feuvre
       5             :  *                      Copyright (c) Telecom ParisTech 2007-2021
       6             :  *                      All rights reserved
       7             :  *
       8             :  *  This file is part of GPAC / JavaScript XmlHttpRequest bindings
       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             : /*
      27             :         ANY CHANGE TO THE API MUST BE REFLECTED IN THE DOCUMENTATION IN gpac/share/doc/idl/xhr.idl
      28             :         (no way to define inline JS doc with doxygen)
      29             : */
      30             : 
      31             : #include <gpac/setup.h>
      32             : 
      33             : #ifdef GPAC_HAS_QJS
      34             : 
      35             : /*base SVG type*/
      36             : #include <gpac/nodes_svg.h>
      37             : #include <gpac/nodes_mpeg4.h>
      38             : #include <gpac/nodes_x3d.h>
      39             : /*dom events*/
      40             : #include <gpac/events.h>
      41             : 
      42             : #include <gpac/download.h>
      43             : #include <gpac/network.h>
      44             : #include <gpac/options.h>
      45             : #include <gpac/xml.h>
      46             : 
      47             : 
      48             : #include <gpac/internal/scenegraph_dev.h>
      49             : 
      50             : #include "../quickjs/quickjs.h"
      51             : #include "../scenegraph/qjs_common.h"
      52             : 
      53             : typedef struct __xhr_context XMLHTTPContext;
      54             : 
      55             : /************************************************************
      56             :  *
      57             :  *      xmlHttpRequest implementation
      58             :  *
      59             :  *************************************************************/
      60             : typedef enum {
      61             :         XHR_ONABORT,
      62             :         XHR_ONERROR,
      63             :         XHR_ONLOAD,
      64             :         XHR_ONLOADEND,
      65             :         XHR_ONLOADSTART,
      66             :         XHR_ONPROGRESS,
      67             :         XHR_ONREADYSTATECHANGE,
      68             :         XHR_ONTIMEOUT,
      69             :         XHR_READYSTATE,
      70             :         XHR_RESPONSE,
      71             :         XHR_RESPONSETYPE,
      72             :         XHR_RESPONSETEXT,
      73             :         XHR_RESPONSEXML,
      74             :         XHR_STATUS,
      75             :         XHR_STATUSTEXT,
      76             :         XHR_TIMEOUT,
      77             :         XHR_UPLOAD,
      78             :         XHR_WITHCREDENTIALS,
      79             :         XHR_STATIC_UNSENT,
      80             :         XHR_STATIC_OPENED,
      81             :         XHR_STATIC_HEADERS_RECEIVED,
      82             :         XHR_STATIC_LOADING,
      83             :         XHR_STATIC_DONE,
      84             :         XHR_CACHE,
      85             : } XHR_JSProperty;
      86             : 
      87             : typedef enum {
      88             :         XHR_READYSTATE_UNSENT                   = 0,
      89             :         XHR_READYSTATE_OPENED                   = 1,
      90             :         XHR_READYSTATE_HEADERS_RECEIVED = 2,
      91             :         XHR_READYSTATE_LOADING                  = 3,
      92             :         XHR_READYSTATE_DONE                             = 4
      93             : } XHR_ReadyState;
      94             : 
      95             : typedef enum {
      96             :         XHR_RESPONSETYPE_NONE,
      97             :         XHR_RESPONSETYPE_ARRAYBUFFER,
      98             :         XHR_RESPONSETYPE_BLOB,
      99             :         XHR_RESPONSETYPE_DOCUMENT,
     100             :         XHR_RESPONSETYPE_JSON,
     101             :         XHR_RESPONSETYPE_TEXT,
     102             :         XHR_RESPONSETYPE_SAX,
     103             :         XHR_RESPONSETYPE_PUSH,
     104             : } XHR_ResponseType;
     105             : 
     106             : typedef enum {
     107             :         XHR_CACHETYPE_NORMAL,
     108             :         XHR_CACHETYPE_NONE,
     109             :         XHR_CACHETYPE_MEMORY,
     110             : } XHR_CacheType;
     111             : 
     112             : struct __xhr_context
     113             : {
     114             :         JSContext *c;
     115             :         JSValue _this;
     116             : 
     117             :         /* callback functions */
     118             :         JSValue onabort;
     119             :         JSValue onerror;
     120             :         JSValue onreadystatechange;
     121             :         JSValue onload;
     122             :         JSValue onloadstart;
     123             :         JSValue onloadend;
     124             :         JSValue onprogress;
     125             :         JSValue ontimeout;
     126             : 
     127             :         XHR_ReadyState readyState;
     128             :         Bool async;
     129             : 
     130             :         /* GPAC extension to control the caching of XHR-downloaded resources */
     131             :         XHR_CacheType  cache;
     132             : 
     133             :         /*header/header-val, terminated by NULL*/
     134             :         char **headers;
     135             :         u32 cur_header;
     136             :         char **recv_headers;
     137             : 
     138             :         char *method, *url;
     139             :         GF_DownloadSession *sess;
     140             :         char *data;
     141             :         u32 size;
     142             :         JSValue arraybuffer;
     143             :         GF_Err ret_code;
     144             :         u32 html_status;
     145             :         char *statusText;
     146             :         char *mime;
     147             :         u32 timeout;
     148             :         XHR_ResponseType responseType;
     149             :         Bool withCredentials;
     150             :         Bool isFile;
     151             : 
     152             :         GF_SAXParser *sax;
     153             :         GF_List *node_stack;
     154             : 
     155             :         GF_DOMEventTarget *event_target;
     156             : 
     157             :         /* dom graph in which the XHR is created */
     158             :         GF_SceneGraph *owning_graph;
     159             : #ifndef GPAC_DISABLE_SVG
     160             :         /* dom graph used to parse XML into */
     161             :         GF_SceneGraph *document;
     162             : #endif
     163             :         Bool js_dom_loaded;
     164             : };
     165             : 
     166             : GF_JSClass xhrClass;
     167             : 
     168             : #if 0 //unused
     169             : GF_SceneGraph *xml_http_get_scenegraph(XMLHTTPContext *ctx)
     170             : {
     171             :         return ctx->owning_graph;
     172             : }
     173             : #endif
     174             : 
     175          11 : static void xml_http_reset_recv_hdr(XMLHTTPContext *ctx)
     176             : {
     177             :         u32 nb_hdr = 0;
     178          11 :         if (ctx->recv_headers) {
     179          18 :                 while (ctx->recv_headers[nb_hdr]) {
     180          12 :                         gf_free(ctx->recv_headers[nb_hdr]);
     181          12 :                         gf_free(ctx->recv_headers[nb_hdr+1]);
     182          12 :                         nb_hdr+=2;
     183             :                 }
     184           6 :                 gf_free(ctx->recv_headers);
     185           6 :                 ctx->recv_headers = NULL;
     186             :         }
     187          11 : }
     188             : 
     189          12 : static void xml_http_append_recv_header(XMLHTTPContext *ctx, const char *hdr, const char *val)
     190             : {
     191             :         u32 nb_hdr = 0;
     192          12 :         if (ctx->recv_headers) {
     193           6 :                 while (ctx->recv_headers[nb_hdr]) nb_hdr+=2;
     194             :         }
     195          12 :         ctx->recv_headers = (char **)gf_realloc(ctx->recv_headers, sizeof(char*)*(nb_hdr+3));
     196          12 :         ctx->recv_headers[nb_hdr] = gf_strdup(hdr);
     197          12 :         ctx->recv_headers[nb_hdr+1] = gf_strdup(val ? val : "");
     198          12 :         ctx->recv_headers[nb_hdr+2] = NULL;
     199          12 : }
     200             : 
     201           6 : static void xml_http_append_send_header(XMLHTTPContext *ctx, char *hdr, char *val)
     202             : {
     203           6 :         if (!hdr) return;
     204             : 
     205           6 :         if (ctx->headers) {
     206             :                 u32 nb_hdr = 0;
     207           0 :                 while (ctx->headers && ctx->headers[nb_hdr]) {
     208           0 :                         if (stricmp(ctx->headers[nb_hdr], hdr)) {
     209           0 :                                 nb_hdr+=2;
     210           0 :                                 continue;
     211             :                         }
     212             :                         /*ignore these ones*/
     213           0 :                         if (!stricmp(hdr, "Accept-Charset")
     214           0 :                                 || !stricmp(hdr, "Accept-Encoding")
     215           0 :                                 || !stricmp(hdr, "Content-Length")
     216           0 :                                 || !stricmp(hdr, "Expect")
     217           0 :                                 || !stricmp(hdr, "Date")
     218           0 :                                 || !stricmp(hdr, "Host")
     219           0 :                                 || !stricmp(hdr, "Keep-Alive")
     220           0 :                                 || !stricmp(hdr, "Referer")
     221           0 :                                 || !stricmp(hdr, "TE")
     222           0 :                                 || !stricmp(hdr, "Trailer")
     223           0 :                                 || !stricmp(hdr, "Transfer-Encoding")
     224           0 :                                 || !stricmp(hdr, "Upgrade")
     225             :                            ) {
     226             :                                 return;
     227             :                         }
     228             : 
     229             :                         /*replace content for these ones*/
     230           0 :                         if (!stricmp(hdr, "Authorization")
     231           0 :                                 || !stricmp(hdr, "Content-Base")
     232           0 :                                 || !stricmp(hdr, "Content-Location")
     233           0 :                                 || !stricmp(hdr, "Content-MD5")
     234           0 :                                 || !stricmp(hdr, "Content-Range")
     235           0 :                                 || !stricmp(hdr, "Content-Type")
     236           0 :                                 || !stricmp(hdr, "Content-Version")
     237           0 :                                 || !stricmp(hdr, "Delta-Base")
     238           0 :                                 || !stricmp(hdr, "Depth")
     239           0 :                                 || !stricmp(hdr, "Destination")
     240           0 :                                 || !stricmp(hdr, "ETag")
     241           0 :                                 || !stricmp(hdr, "From")
     242           0 :                                 || !stricmp(hdr, "If-Modified-Since")
     243           0 :                                 || !stricmp(hdr, "If-Range")
     244           0 :                                 || !stricmp(hdr, "If-Unmodified-Since")
     245           0 :                                 || !stricmp(hdr, "Max-Forwards")
     246           0 :                                 || !stricmp(hdr, "MIME-Version")
     247           0 :                                 || !stricmp(hdr, "Overwrite")
     248           0 :                                 || !stricmp(hdr, "Proxy-Authorization")
     249           0 :                                 || !stricmp(hdr, "SOAPAction")
     250           0 :                                 || !stricmp(hdr, "Timeout") ) {
     251           0 :                                 gf_free(ctx->headers[nb_hdr+1]);
     252           0 :                                 ctx->headers[nb_hdr+1] = gf_strdup(val);
     253           0 :                                 return;
     254             :                         }
     255             :                         /*append value*/
     256             :                         else {
     257           0 :                                 char *new_val = (char *)gf_malloc(sizeof(char) * (strlen(ctx->headers[nb_hdr+1])+strlen(val)+3));
     258           0 :                                 sprintf(new_val, "%s, %s", ctx->headers[nb_hdr+1], val);
     259           0 :                                 gf_free(ctx->headers[nb_hdr+1]);
     260           0 :                                 ctx->headers[nb_hdr+1] = new_val;
     261           0 :                                 return;
     262             :                         }
     263             :                 }
     264             :         }
     265           6 :         xml_http_append_recv_header(ctx, hdr, val);
     266             : }
     267             : 
     268          15 : static void xml_http_del_data(XMLHTTPContext *ctx)
     269             : {
     270          30 :         if (!JS_IsUndefined(ctx->arraybuffer)) {
     271           4 :                 JS_DetachArrayBuffer(ctx->c, ctx->arraybuffer);
     272           4 :                 JS_FreeValue(ctx->c, ctx->arraybuffer);
     273           4 :                 ctx->arraybuffer = JS_UNDEFINED;
     274             :         }
     275          15 :         if (ctx->data) {
     276           3 :                 gf_free(ctx->data);
     277           3 :                 ctx->data = NULL;
     278             :         }
     279          15 :         ctx->size = 0;
     280          15 : }
     281             : 
     282          11 : static void xml_http_reset_partial(XMLHTTPContext *ctx)
     283             : {
     284          11 :         xml_http_reset_recv_hdr(ctx);
     285          11 :         xml_http_del_data(ctx);
     286          11 :         if (ctx->mime) {
     287           3 :                 gf_free(ctx->mime);
     288           3 :                 ctx->mime = NULL;
     289             :         }
     290          11 :         if (ctx->statusText) {
     291           0 :                 gf_free(ctx->statusText);
     292           0 :                 ctx->statusText = NULL;
     293             :         }
     294          11 :         ctx->cur_header = 0;
     295          11 :         ctx->html_status = 0;
     296          11 : }
     297             : 
     298           8 : static void xml_http_reset(XMLHTTPContext *ctx)
     299             : {
     300           8 :         if (ctx->method) {
     301           4 :                 gf_free(ctx->method);
     302           4 :                 ctx->method = NULL;
     303             :         }
     304           8 :         if (ctx->url) {
     305           4 :                 gf_free(ctx->url);
     306           4 :                 ctx->url = NULL;
     307             :         }
     308             : 
     309           8 :         xml_http_reset_partial(ctx);
     310             : 
     311           8 :         if (ctx->sess) {
     312             :                 GF_DownloadSession *tmp = ctx->sess;
     313           1 :                 ctx->sess = NULL;
     314           1 :                 gf_dm_sess_abort(tmp);
     315           1 :                 gf_dm_sess_del(tmp);
     316             :         }
     317             : 
     318           8 :         if (ctx->url) {
     319           0 :                 gf_free(ctx->url);
     320           0 :                 ctx->url = NULL;
     321             :         }
     322           8 :         if (ctx->sax) {
     323           1 :                 gf_xml_sax_del(ctx->sax);
     324           1 :                 ctx->sax = NULL;
     325             :         }
     326           8 :         if (ctx->node_stack) {
     327           1 :                 gf_list_del(ctx->node_stack);
     328           1 :                 ctx->node_stack = NULL;
     329             :         }
     330             : #ifndef GPAC_DISABLE_SVG
     331           8 :         if (ctx->document) {
     332           3 :                 if (ctx->js_dom_loaded) {
     333           2 :                         dom_js_unload();
     334           2 :                         ctx->js_dom_loaded = GF_FALSE;
     335             :                 }
     336           3 :                 gf_node_unregister(ctx->document->RootNode, NULL);
     337             : 
     338             :                 /*we're sure the graph is a "nomade" one since we initially put the refcount to 1 ourselves*/
     339           3 :                 ctx->document->reference_count--;
     340           3 :                 if (!ctx->document->reference_count) {
     341           2 :                         gf_sg_js_dom_pre_destroy(JS_GetRuntime(ctx->c), ctx->document, NULL);
     342           2 :                         gf_sg_del(ctx->document);
     343             :                 }
     344             :         }
     345           8 :         ctx->document = NULL;
     346             : #endif
     347           8 :         ctx->size = 0;
     348           8 :         ctx->async = GF_FALSE;
     349           8 :         ctx->readyState = XHR_READYSTATE_UNSENT;
     350           8 :         ctx->ret_code = GF_OK;
     351           8 : }
     352             : 
     353         415 : static void xml_http_finalize(JSRuntime *rt, JSValue obj)
     354             : {
     355         415 :         XMLHTTPContext *ctx = (XMLHTTPContext *) JS_GetOpaque(obj, xhrClass.class_id);
     356         415 :         if (!ctx) return;
     357             :         JS_FreeValueRT(rt, ctx->onabort);
     358             :         JS_FreeValueRT(rt, ctx->onerror);
     359             :         JS_FreeValueRT(rt, ctx->onload);
     360             :         JS_FreeValueRT(rt, ctx->onloadend);
     361             :         JS_FreeValueRT(rt, ctx->onloadstart);
     362             :         JS_FreeValueRT(rt, ctx->onprogress);
     363             :         JS_FreeValueRT(rt, ctx->onreadystatechange);
     364             :         JS_FreeValueRT(rt, ctx->ontimeout);
     365           4 :         xml_http_reset(ctx);
     366             : #ifndef GPAC_DISABLE_SVG
     367           4 :         if (ctx->event_target)
     368           0 :                 gf_dom_event_target_del(ctx->event_target);
     369             : #endif
     370             : 
     371           4 :         gf_free(ctx);
     372             : }
     373             : 
     374          12 : static GFINLINE GF_SceneGraph *xml_get_scenegraph(JSContext *c)
     375             : {
     376             :         GF_SceneGraph *scene;
     377          12 :         JSValue global = JS_GetGlobalObject(c);
     378          12 :         scene = (GF_SceneGraph *) JS_GetOpaque_Nocheck(global);
     379             :         JS_FreeValue(c, global);
     380          12 :         return scene;
     381             : }
     382             : 
     383             : #ifndef GPAC_DISABLE_SVG
     384           0 : void xhr_get_event_target(JSContext *c, JSValue obj, GF_SceneGraph **sg, GF_DOMEventTarget **target)
     385             : {
     386           0 :         if (c) {
     387             :                 /*XHR interface*/
     388           0 :                 XMLHTTPContext *ctx = JS_GetOpaque(obj, xhrClass.class_id);
     389           0 :                 if (!ctx) return;
     390             : 
     391           0 :                 *sg = xml_get_scenegraph(c);
     392           0 :                 *target = ctx->event_target;
     393             :         }
     394             : }
     395             : #endif
     396             : 
     397           4 : static JSValue xml_http_constructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
     398             : {
     399             :         XMLHTTPContext *p;
     400             :         JSValue obj;
     401             : 
     402           4 :         GF_SAFEALLOC(p, XMLHTTPContext);
     403           4 :         if (!p) {
     404           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[WHR] Failed to allocate XHR object\n"));
     405           0 :                 return JS_EXCEPTION;
     406             :         }
     407           4 :         obj = JS_NewObjectClass(c, xhrClass.class_id);
     408           4 :         p->c = c;
     409           4 :         p->_this = obj;
     410           4 :         p->owning_graph = xml_get_scenegraph(c);
     411             : #ifndef GPAC_DISABLE_SVG
     412           4 :         if (p->owning_graph)
     413           0 :                 p->event_target = gf_dom_event_target_new(GF_DOM_EVENT_TARGET_XHR, p);
     414             : #endif
     415             : 
     416           4 :         p->onabort = JS_NULL;
     417           4 :         p->onerror = JS_NULL;
     418           4 :         p->onreadystatechange = JS_NULL;
     419           4 :         p->onload = JS_NULL;
     420           4 :         p->onloadstart = JS_NULL;
     421           4 :         p->onloadend = JS_NULL;
     422           4 :         p->onprogress = JS_NULL;
     423           4 :         p->ontimeout = JS_NULL;
     424             : 
     425           4 :         JS_SetOpaque(obj, p);
     426           4 :         return obj;
     427             : }
     428             : 
     429          13 : static void xml_http_fire_event(XMLHTTPContext *ctx, GF_EventType evtType)
     430             : {
     431             : #ifndef GPAC_DISABLE_SVG
     432             :         GF_DOM_Event xhr_evt;
     433          13 :         if (!ctx->event_target)
     434          13 :                 return;
     435             : 
     436             :         memset(&xhr_evt, 0, sizeof(GF_DOM_Event));
     437           0 :         xhr_evt.type = evtType;
     438           0 :         xhr_evt.target = ctx->event_target->ptr;
     439           0 :         xhr_evt.target_type = ctx->event_target->ptr_type;
     440           0 :         gf_sg_fire_dom_event(ctx->event_target, &xhr_evt, ctx->owning_graph, NULL);
     441             : #endif
     442             : }
     443             : 
     444             : 
     445          10 : static void xml_http_state_change(XMLHTTPContext *ctx)
     446             : {
     447             : #ifndef GPAC_DISABLE_VRML
     448             :         GF_SceneGraph *scene;
     449             :         GF_Node *n;
     450             : #endif
     451             : 
     452          10 :         gf_js_lock(ctx->c, GF_TRUE);
     453          20 :         if (! JS_IsNull(ctx->onreadystatechange)) {
     454          10 :                 JSValue ret = JS_Call(ctx->c, ctx->onreadystatechange, ctx->_this, 0, NULL);
     455          10 :                 if (JS_IsException(ret))
     456           0 :                         js_dump_error(ctx->c);
     457          10 :                 JS_FreeValue(ctx->c, ret);
     458             :         }
     459             : 
     460          10 :         js_do_loop(ctx->c);
     461          10 :         gf_js_lock(ctx->c, GF_FALSE);
     462             : 
     463          10 :         if (! ctx->owning_graph) return;
     464             : 
     465             :         /*Flush BIFS eventOut events*/
     466             : #ifndef GPAC_DISABLE_VRML
     467           0 :         scene = (GF_SceneGraph *)JS_GetContextOpaque(ctx->c);
     468             :         /*this is a scene, we look for a node (if scene is used, this is DOM-based scripting not VRML*/
     469           0 :         if (scene->__reserved_null == 0) return;
     470           0 :         n = (GF_Node *)JS_GetContextOpaque(ctx->c);
     471           0 :         gf_js_vrml_flush_event_out(n, (GF_ScriptPriv *)n->sgprivate->UserPrivate);
     472             : #endif
     473             : }
     474             : 
     475             : 
     476           4 : static JSValue xml_http_open(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
     477             : {
     478             :         const char *val;
     479             :         GF_JSAPIParam par;
     480             :         XMLHTTPContext *ctx;
     481             :         GF_SceneGraph *scene;
     482             : 
     483           4 :         ctx = (XMLHTTPContext *) JS_GetOpaque(obj, xhrClass.class_id);
     484           4 :         if (!ctx) return JS_EXCEPTION;
     485             : 
     486             :         /*reset*/
     487           4 :         if (ctx->readyState) xml_http_reset(ctx);
     488             : 
     489           4 :         if (argc<2) return JS_EXCEPTION;
     490             :         /*method is a string*/
     491           8 :         if (!JS_CHECK_STRING(argv[0])) return JS_EXCEPTION;
     492             :         /*url is a string*/
     493           8 :         if (!JS_CHECK_STRING(argv[1])) return JS_EXCEPTION;
     494             : 
     495           4 :         xml_http_reset(ctx);
     496             :         val = JS_ToCString(c, argv[0]);
     497           4 :         if (strcmp(val, "GET") && strcmp(val, "POST") && strcmp(val, "HEAD")
     498           0 :                 && strcmp(val, "PUT") && strcmp(val, "DELETE") && strcmp(val, "OPTIONS") ) {
     499           0 :                 JS_FreeCString(c, val);
     500           0 :                 return JS_EXCEPTION;
     501             :         }
     502             : 
     503           4 :         ctx->method = gf_strdup(val);
     504           4 :         JS_FreeCString(c, val);
     505             : 
     506             :         /*concatenate URL*/
     507           4 :         scene = xml_get_scenegraph(c);
     508             : #ifndef GPAC_DISABLE_VRML
     509           4 :         while (scene && scene->pOwningProto && scene->parent_scene) scene = scene->parent_scene;
     510             : #endif
     511             : 
     512           4 :         par.uri.nb_params = 0;
     513             :         val = JS_ToCString(c, argv[1]);
     514           4 :         par.uri.url = (char *) val;
     515           4 :         ctx->url = NULL;
     516           4 :         if (scene && scene->script_action) {
     517           0 :                 scene->script_action(scene->script_action_cbck, GF_JSAPI_OP_RESOLVE_URI, scene->RootNode, &par);
     518           0 :                 ctx->url = par.uri.url;
     519             :         } else {
     520           4 :                 ctx->url = gf_strdup(val);
     521             :         }
     522           4 :         JS_FreeCString(c, val);
     523             : 
     524             :         /*async defaults to true*/
     525           4 :         ctx->async = GF_TRUE;
     526           4 :         if (argc>2) {
     527             :                 val = NULL;
     528           0 :                 ctx->async = JS_ToBool(c, argv[2]) ? GF_TRUE : GF_FALSE;
     529           0 :                 if (argc>3) {
     530           0 :                         if (!JS_CHECK_STRING(argv[3])) return JS_EXCEPTION;
     531             :                         /*TODO*/
     532           0 :                         if (argc>4) {
     533           0 :                                 if (!JS_CHECK_STRING(argv[4])) return JS_EXCEPTION;
     534             :                                 val = JS_ToCString(c, argv[4]);
     535             :                                 /*TODO*/
     536             :                         } else {
     537             :                                 val = JS_ToCString(c, argv[3]);
     538             :                         }
     539             :                 }
     540           0 :                 JS_FreeCString(c, val);
     541             :         }
     542             :         /*OPEN success*/
     543           4 :         ctx->readyState = XHR_READYSTATE_OPENED;
     544           4 :         xml_http_state_change(ctx);
     545           4 :         xml_http_fire_event(ctx, GF_EVENT_MEDIA_LOAD_START);
     546           4 :         if (JS_IsFunction(c, ctx->onloadstart) ) {
     547           0 :                 return JS_Call(ctx->c, ctx->onloadstart, ctx->_this, 0, NULL);
     548             :         }
     549           4 :         return JS_TRUE;
     550             : }
     551             : 
     552           6 : static JSValue xml_http_set_header(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
     553             : {
     554             :         const char *hdr, *val;
     555           6 :         XMLHTTPContext *ctx = (XMLHTTPContext *) JS_GetOpaque(obj, xhrClass.class_id);
     556           6 :         if (!ctx) return JS_EXCEPTION;
     557             : 
     558           6 :         if (ctx->readyState!=XHR_READYSTATE_OPENED) return JS_EXCEPTION;
     559           6 :         if (argc!=2) return JS_EXCEPTION;
     560          12 :         if (!JS_CHECK_STRING(argv[0])) return JS_EXCEPTION;
     561          12 :         if (!JS_CHECK_STRING(argv[1])) return JS_EXCEPTION;
     562             : 
     563             :         hdr = JS_ToCString(c, argv[0]);
     564             :         val = JS_ToCString(c, argv[1]);
     565           6 :         xml_http_append_send_header(ctx, (char *)hdr, (char *)val);
     566           6 :         JS_FreeCString(c, hdr);
     567           6 :         JS_FreeCString(c, val);
     568           6 :         return JS_TRUE;
     569             : }
     570             : 
     571             : #ifndef GPAC_DISABLE_SVG
     572             : 
     573          22 : static void xml_http_sax_start(void *sax_cbck, const char *node_name, const char *name_space, const GF_XMLAttribute *attributes, u32 nb_attributes)
     574             : {
     575             :         u32 i;
     576             :         GF_DOMFullAttribute *prev = NULL;
     577             :         GF_DOMFullNode *par;
     578             :         XMLHTTPContext *ctx = (XMLHTTPContext *)sax_cbck;
     579          22 :         GF_DOMFullNode *node = (GF_DOMFullNode *) gf_node_new(ctx->document, TAG_DOMFullNode);
     580             : 
     581          22 :         node->name = gf_strdup(node_name);
     582         102 :         for (i=0; i<nb_attributes; i++) {
     583             :                 /* special case for 'xml:id' to be parsed as an ID
     584             :                 NOTE: we do not test for the 'id' attribute because without DTD we are not sure that it's an ID */
     585          80 :                 if (!stricmp(attributes[i].name, "xml:id")) {
     586           0 :                         u32 id = gf_sg_get_max_node_id(ctx->document) + 1;
     587           0 :                         gf_node_set_id((GF_Node *)node, id, attributes[i].value);
     588             :                 } else {
     589             :                         GF_DOMFullAttribute *att;
     590          80 :                         GF_SAFEALLOC(att, GF_DOMFullAttribute);
     591          80 :                         if (!att) {
     592           0 :                                 GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[XHR] Fail to allocate DOM attribute\n"));
     593           0 :                                 continue;
     594             :                         }
     595          80 :                         att->tag = TAG_DOM_ATT_any;
     596          80 :                         att->name = gf_strdup(attributes[i].name);
     597          80 :                         att->data_type = (u16) DOM_String_datatype;
     598          80 :                         att->data = gf_svg_create_attribute_value(att->data_type);
     599          80 :                         *((char **)att->data) = gf_strdup(attributes[i].value);
     600          80 :                         if (prev) prev->next = (GF_DOMAttribute*)att;
     601          21 :                         else node->attributes = (GF_DOMAttribute*)att;
     602             :                         prev = att;
     603             :                 }
     604             :         }
     605          22 :         par = (GF_DOMFullNode *)gf_list_last(ctx->node_stack);
     606          22 :         gf_node_register((GF_Node*)node, (GF_Node*)par);
     607          22 :         if (par) {
     608          21 :                 gf_node_list_add_child(&par->children, (GF_Node*)node);
     609             :         } else {
     610             :                 assert(!ctx->document->RootNode);
     611           1 :                 ctx->document->RootNode = (GF_Node*)node;
     612             :         }
     613          22 :         gf_list_add(ctx->node_stack, node);
     614          22 : }
     615             : 
     616          22 : static void xml_http_sax_end(void *sax_cbck, const char *node_name, const char *name_space)
     617             : {
     618             :         XMLHTTPContext *ctx = (XMLHTTPContext *)sax_cbck;
     619          22 :         GF_DOMFullNode *par = (GF_DOMFullNode *)gf_list_last(ctx->node_stack);
     620          22 :         if (par) {
     621             :                 /*depth mismatch*/
     622          22 :                 if (strcmp(par->name, node_name)) return;
     623          22 :                 gf_list_rem_last(ctx->node_stack);
     624             :         }
     625             : }
     626          30 : static void xml_http_sax_text(void *sax_cbck, const char *content, Bool is_cdata)
     627             : {
     628             :         XMLHTTPContext *ctx = (XMLHTTPContext *)sax_cbck;
     629          30 :         GF_DOMFullNode *par = (GF_DOMFullNode *)gf_list_last(ctx->node_stack);
     630          30 :         if (par) {
     631             :                 u32 i, len;
     632             :                 GF_DOMText *txt;
     633             :                 /*basic check, remove all empty text nodes*/
     634          30 :                 len = (u32) strlen(content);
     635          55 :                 for (i=0; i<len; i++) {
     636          30 :                         if (!strchr(" \n\r\t", content[i])) break;
     637             :                 }
     638          30 :                 if (i==len) return;
     639             : 
     640           5 :                 txt = gf_dom_add_text_node((GF_Node *)par, gf_strdup(content) );
     641           5 :                 txt->type = is_cdata ? GF_DOM_TEXT_CDATA : GF_DOM_TEXT_REGULAR;
     642             :         }
     643             : }
     644             : #endif // GPAC_DISABLE_SVG
     645             : 
     646           3 : static void xml_http_terminate(XMLHTTPContext *ctx, GF_Err error)
     647             : {
     648             :         /*if we get here, destroy downloader*/
     649           3 :         if (ctx->sess) {
     650           0 :                 gf_dm_sess_del(ctx->sess);
     651           0 :                 ctx->sess = NULL;
     652             :         }
     653             : 
     654             :         /*but stay in loaded mode*/
     655           3 :         ctx->readyState = XHR_READYSTATE_DONE;
     656           3 :         xml_http_state_change(ctx);
     657           3 :         xml_http_fire_event(ctx, GF_EVENT_LOAD);
     658           3 :         xml_http_fire_event(ctx, GF_EVENT_MEDIA_LOAD_DONE);
     659           3 :         if (JS_IsFunction(ctx->c, ctx->onload)) {
     660           3 :                 JSValue rval = JS_Call(ctx->c, ctx->onload, ctx->_this, 0, NULL);
     661           3 :                 if (JS_IsException(rval)) js_dump_error(ctx->c);
     662           3 :                 JS_FreeValue(ctx->c, rval);
     663             :         }
     664           3 :         if (JS_IsFunction(ctx->c, ctx->onloadend)) {
     665           0 :                 JSValue rval = JS_Call(ctx->c, ctx->onloadend, ctx->_this, 0, NULL);
     666           0 :                 if (JS_IsException(rval)) js_dump_error(ctx->c);
     667           0 :                 JS_FreeValue(ctx->c, rval);
     668             :         }
     669           3 : }
     670             : 
     671          13 : static void xml_http_on_data(void *usr_cbk, GF_NETIO_Parameter *parameter)
     672             : {
     673             :         Bool locked = GF_FALSE;
     674             :         XMLHTTPContext *ctx = (XMLHTTPContext *)usr_cbk;
     675             : 
     676             :         /*make sure we can grab JS and the session is not destroyed*/
     677          26 :         while (ctx->sess) {
     678           0 :                 if (gf_js_try_lock(ctx->c) ) {
     679             :                         locked = GF_TRUE;
     680             :                         break;
     681             :                 }
     682           0 :                 gf_sleep(1);
     683             :         }
     684             :         /*if session not set, we've had an abort*/
     685          13 :         if (!ctx->sess && !ctx->isFile) {
     686           0 :                 if (locked)
     687           0 :                         gf_js_lock(ctx->c, GF_FALSE);
     688             :                 return;
     689             :         }
     690             : 
     691          13 :         if (!ctx->isFile) {
     692             :                 assert( locked );
     693           0 :                 gf_js_lock(ctx->c, GF_FALSE);
     694             :                 locked = GF_FALSE;
     695             :         }
     696          13 :         switch (parameter->msg_type) {
     697             :         case GF_NETIO_SETUP:
     698             :                 /*nothing to do*/
     699             :                 goto exit;
     700             :         case GF_NETIO_CONNECTED:
     701             :                 /*nothing to do*/
     702             :                 goto exit;
     703           3 :         case GF_NETIO_WAIT_FOR_REPLY:
     704             :                 /*reset send() state (data, current header) and prepare recv headers*/
     705           3 :                 xml_http_reset_partial(ctx);
     706           3 :                 ctx->readyState = XHR_READYSTATE_HEADERS_RECEIVED;
     707           3 :                 xml_http_state_change(ctx);
     708           3 :                 xml_http_fire_event(ctx, GF_EVENT_MEDIA_PROGRESS);
     709           3 :                 if (JS_IsFunction(ctx->c, ctx->onprogress) ) {
     710           0 :                         JSValue rval = JS_Call(ctx->c, ctx->onprogress, ctx->_this, 0, NULL);
     711           0 :                         if (JS_IsException(rval)) js_dump_error(ctx->c);
     712           0 :                         JS_FreeValue(ctx->c, rval);
     713             :                 }
     714             :                 goto exit;
     715             :         /*this is signaled sent AFTER headers*/
     716           0 :         case GF_NETIO_PARSE_REPLY:
     717           0 :                 ctx->html_status = parameter->reply;
     718           0 :                 if (parameter->value) {
     719           0 :                         ctx->statusText = gf_strdup(parameter->value);
     720             :                 }
     721           0 :                 ctx->readyState = XHR_READYSTATE_LOADING;
     722           0 :                 xml_http_state_change(ctx);
     723           0 :                 ctx->readyState = XHR_READYSTATE_HEADERS_RECEIVED;
     724           0 :                 xml_http_state_change(ctx);
     725           0 :                 xml_http_fire_event(ctx, GF_EVENT_MEDIA_PROGRESS);
     726           0 :                 if (JS_IsFunction(ctx->c, ctx->onprogress) ) {
     727           0 :                         JSValue rval = JS_Call(ctx->c, ctx->onprogress, ctx->_this, 0, NULL);
     728           0 :                         if (JS_IsException(rval)) js_dump_error(ctx->c);
     729           0 :                         JS_FreeValue(ctx->c, rval);
     730             :                 }
     731             :                 goto exit;
     732             : 
     733           0 :         case GF_NETIO_GET_METHOD:
     734           0 :                 parameter->name = ctx->method;
     735           0 :                 goto exit;
     736           0 :         case GF_NETIO_GET_HEADER:
     737           0 :                 if (ctx->headers && ctx->headers[2*ctx->cur_header]) {
     738           0 :                         parameter->name = ctx->headers[2*ctx->cur_header];
     739           0 :                         parameter->value = ctx->headers[2*ctx->cur_header+1];
     740           0 :                         ctx->cur_header++;
     741             :                 }
     742             :                 goto exit;
     743           0 :         case GF_NETIO_GET_CONTENT:
     744           0 :                 if (ctx->data) {
     745           0 :                         parameter->data = ctx->data;
     746           0 :                         parameter->size = ctx->size;
     747             :                 }
     748             :                 goto exit;
     749           6 :         case GF_NETIO_PARSE_HEADER:
     750           6 :                 xml_http_append_recv_header(ctx, parameter->name, parameter->value);
     751             :                 /*prepare SAX parser*/
     752           6 :                 if (ctx->responseType != XHR_RESPONSETYPE_SAX) goto exit;
     753           2 :                 if (strcmp(parameter->name, "Content-Type")) goto exit;
     754             : 
     755             : #ifndef GPAC_DISABLE_SVG
     756           1 :                 if (!strncmp(parameter->value, "application/xml", 15)
     757           0 :                         || !strncmp(parameter->value, "text/xml", 8)
     758           0 :                         || strstr(parameter->value, "+xml")
     759           0 :                         || strstr(parameter->value, "/xml")
     760             : //                      || !strncmp(parameter->value, "text/plain", 10)
     761             :                    ) {
     762             :                         assert(!ctx->sax);
     763           1 :                         ctx->sax = gf_xml_sax_new(xml_http_sax_start, xml_http_sax_end, xml_http_sax_text, ctx);
     764           1 :                         ctx->node_stack = gf_list_new();
     765           1 :                         ctx->document = gf_sg_new();
     766             :                         /*mark this doc as "nomade", and let it leave until all references to it are destroyed*/
     767           1 :                         ctx->document->reference_count = 1;
     768             :                 }
     769             : #endif
     770             : 
     771             :                 goto exit;
     772           1 :         case GF_NETIO_DATA_EXCHANGE:
     773           1 :                 if (parameter->data && parameter->size) {
     774           1 :                         if (ctx->sax) {
     775             :                                 GF_Err e;
     776           1 :                                 if (!ctx->size) e = gf_xml_sax_init(ctx->sax, (unsigned char *) parameter->data);
     777           0 :                                 else e = gf_xml_sax_parse(ctx->sax, parameter->data);
     778           1 :                                 if (e) {
     779           0 :                                         gf_xml_sax_del(ctx->sax);
     780           0 :                                         ctx->sax = NULL;
     781             :                                 }
     782             :                                 goto exit;
     783             :                         }
     784             : 
     785             :                         /*detach arraybuffer if any*/
     786           0 :                         if (!JS_IsUndefined(ctx->arraybuffer)) {
     787           0 :                                 JS_DetachArrayBuffer(ctx->c, ctx->arraybuffer);
     788           0 :                                 JS_FreeValue(ctx->c, ctx->arraybuffer);
     789           0 :                                 ctx->arraybuffer = JS_UNDEFINED;
     790             :                         }
     791             : 
     792           0 :                         if (ctx->responseType!=XHR_RESPONSETYPE_PUSH) {
     793           0 :                                 ctx->data = (char *)gf_realloc(ctx->data, sizeof(char)*(ctx->size+parameter->size+1));
     794           0 :                                 memcpy(ctx->data + ctx->size, parameter->data, sizeof(char)*parameter->size);
     795           0 :                                 ctx->size += parameter->size;
     796           0 :                                 ctx->data[ctx->size] = 0;
     797             :                         }
     798             : 
     799           0 :                         if (JS_IsFunction(ctx->c, ctx->onprogress) ) {
     800           0 :                                 JSValue prog_evt = JS_NewObject(ctx->c);
     801           0 :                                 JSValue buffer_ab=JS_UNDEFINED;
     802           0 :                                 u32 bps=0;
     803           0 :                                 u64 tot_size=0;
     804           0 :                                 gf_dm_sess_get_stats(ctx->sess, NULL, NULL, &tot_size, NULL, &bps, NULL);
     805           0 :                                 JS_SetPropertyStr(ctx->c, prog_evt, "lengthComputable", JS_NewBool(ctx->c, tot_size ? 1 : 0));
     806           0 :                                 JS_SetPropertyStr(ctx->c, prog_evt, "loaded", JS_NewInt64(ctx->c, ctx->size));
     807           0 :                                 JS_SetPropertyStr(ctx->c, prog_evt, "total", JS_NewInt64(ctx->c, tot_size));
     808           0 :                                 JS_SetPropertyStr(ctx->c, prog_evt, "bps", JS_NewInt64(ctx->c, bps*8));
     809           0 :                                 if (ctx->responseType==XHR_RESPONSETYPE_PUSH) {
     810           0 :                                         buffer_ab = JS_NewArrayBuffer(ctx->c, (u8 *) parameter->data, parameter->size, NULL, ctx, 1);
     811           0 :                                         JS_SetPropertyStr(ctx->c, prog_evt, "buffer", buffer_ab);
     812             :                                 }
     813             : 
     814           0 :                                 JSValue rval = JS_Call(ctx->c, ctx->onprogress, ctx->_this, 1, &prog_evt);
     815           0 :                                 if (JS_IsException(rval)) js_dump_error(ctx->c);
     816           0 :                                 JS_FreeValue(ctx->c, rval);
     817           0 :                                 if (ctx->responseType==XHR_RESPONSETYPE_PUSH) {
     818           0 :                                         JS_DetachArrayBuffer(ctx->c, buffer_ab);
     819             :                                 }
     820           0 :                                 JS_FreeValue(ctx->c, prog_evt);
     821             :                         }
     822             :                 }
     823             :                 goto exit;
     824             :         case GF_NETIO_DATA_TRANSFERED:
     825             :                 /* No return, go till the end of the function */
     826             :                 break;
     827             :         case GF_NETIO_DISCONNECTED:
     828             :                 goto exit;
     829           0 :         case GF_NETIO_STATE_ERROR:
     830           0 :                 ctx->ret_code = parameter->error;
     831             :                 /* No return, go till the end of the function */
     832           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[XmlHttpRequest] Download error: %s\n", gf_error_to_string(parameter->error)));
     833             :                 break;
     834           0 :         case GF_NETIO_REQUEST_SESSION:
     835             :         case GF_NETIO_CANCEL_STREAM:
     836           0 :                 parameter->error = GF_NOT_SUPPORTED;
     837           0 :                 return;
     838             :         }
     839           3 :         if (ctx->async) {
     840           3 :                 xml_http_terminate(ctx, parameter->error);
     841             :         }
     842             : 
     843          18 : exit:
     844          13 :         if (locked) {
     845           0 :                 gf_js_lock(ctx->c, GF_FALSE);
     846             :         }
     847             : }
     848             : 
     849           3 : static GF_Err xml_http_process_local(XMLHTTPContext *ctx)
     850             : {
     851             :         /* For XML Http Requests to files, we fake the processing by calling the HTTP callbacks */
     852             :         GF_NETIO_Parameter par;
     853             :         u64 fsize;
     854             :         char contentLengthHeader[256];
     855             :         FILE *responseFile;
     856             : 
     857             :         /*opera-style local host*/
     858           3 :         if (!strnicmp(ctx->url, "file://localhost", 16)) responseFile = gf_fopen(ctx->url+16, "rb");
     859             :         /*regular-style local host*/
     860           3 :         else if (!strnicmp(ctx->url, "file://", 7)) responseFile = gf_fopen(ctx->url+7, "rb");
     861             :         /* other types: e.g. "C:\" */
     862           3 :         else responseFile = gf_fopen(ctx->url, "rb");
     863             : 
     864           3 :         if (!responseFile) {
     865           0 :                 ctx->html_status = 404;
     866           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[XmlHttpRequest] cannot open local file %s\n", ctx->url));
     867           0 :                 xml_http_fire_event(ctx, GF_EVENT_ERROR);
     868           0 :                 if (JS_IsFunction(ctx->c, ctx->onerror) ) {
     869           0 :                         JSValue rval = JS_Call(ctx->c, ctx->onerror, ctx->_this, 0, NULL);
     870           0 :                         if (JS_IsException(rval)) js_dump_error(ctx->c);
     871           0 :                         JS_FreeValue(ctx->c, rval);
     872             :                 }
     873             :                 return GF_BAD_PARAM;
     874             :         }
     875           3 :         ctx->isFile = GF_TRUE;
     876             : 
     877           3 :         par.msg_type = GF_NETIO_WAIT_FOR_REPLY;
     878           3 :         xml_http_on_data(ctx, &par);
     879             : 
     880           3 :         fsize = gf_fsize(responseFile);
     881             : 
     882           3 :         ctx->html_status = 200;
     883             : 
     884           3 :         ctx->data = (char *)gf_malloc(sizeof(char)*(size_t)(fsize+1));
     885           3 :         fsize = gf_fread(ctx->data, (size_t)fsize, responseFile);
     886           3 :         gf_fclose(responseFile);
     887           3 :         ctx->data[fsize] = 0;
     888           3 :         ctx->size = (u32)fsize;
     889             : 
     890             :         memset(&par, 0, sizeof(GF_NETIO_Parameter));
     891           3 :         par.msg_type = GF_NETIO_PARSE_HEADER;
     892           3 :         par.name = "Content-Type";
     893           3 :         if (ctx->responseType == XHR_RESPONSETYPE_SAX) {
     894           1 :                 par.value = "application/xml";
     895           2 :         } else if (ctx->responseType == XHR_RESPONSETYPE_DOCUMENT) {
     896           2 :                 par.value = "application/xml";
     897             :         } else {
     898           0 :                 par.value = "application/octet-stream";
     899             :         }
     900           3 :         xml_http_on_data(ctx, &par);
     901             : 
     902             :         memset(&par, 0, sizeof(GF_NETIO_Parameter));
     903           3 :         par.msg_type = GF_NETIO_PARSE_HEADER;
     904           3 :         par.name = "Content-Length";
     905           3 :         sprintf(contentLengthHeader, "%d", ctx->size);
     906           3 :         par.value = contentLengthHeader;
     907           3 :         xml_http_on_data(ctx, &par);
     908             : 
     909             : 
     910           3 :         if (ctx->sax) {
     911             :                 memset(&par, 0, sizeof(GF_NETIO_Parameter));
     912           1 :                 par.msg_type = GF_NETIO_DATA_EXCHANGE;
     913           1 :                 par.data = ctx->data;
     914           1 :                 par.size = ctx->size;
     915           1 :                 ctx->size = 0;
     916           1 :                 xml_http_on_data(ctx, &par);
     917           1 :                 ctx->size = par.size;
     918             :         }
     919             : 
     920             :         memset(&par, 0, sizeof(GF_NETIO_Parameter));
     921           3 :         par.msg_type = GF_NETIO_DATA_TRANSFERED;
     922           3 :         xml_http_on_data(ctx, &par);
     923             : 
     924           3 :         if (!ctx->async) {
     925           0 :                 xml_http_terminate(ctx, GF_OK);
     926             :         }
     927             :         return GF_OK;
     928             : }
     929             : 
     930           4 : static JSValue xml_http_send(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
     931             : {
     932             :         GF_Err e;
     933             :         GF_JSAPIParam par;
     934             :         GF_SceneGraph *scene;
     935             :         const char *data = NULL;
     936             :         u32 data_size = 0;
     937           4 :         XMLHTTPContext *ctx = (XMLHTTPContext *) JS_GetOpaque(obj, xhrClass.class_id);
     938           4 :         if (!ctx) return JS_EXCEPTION;
     939             : 
     940           4 :         if (ctx->readyState!=XHR_READYSTATE_OPENED) return JS_EXCEPTION;
     941           4 :         if (ctx->sess) return JS_EXCEPTION;
     942             : 
     943           4 :         scene = xml_get_scenegraph(c);
     944           4 :         if (scene) {
     945           0 :                 par.dnld_man = NULL;
     946           0 :                 if (scene->script_action)
     947           0 :                         scene->script_action(scene->script_action_cbck, GF_JSAPI_OP_GET_DOWNLOAD_MANAGER, NULL, &par);
     948             :         } else {
     949           4 :                 par.dnld_man = jsf_get_download_manager(c);
     950             :         }
     951           4 :         if (!par.dnld_man) return JS_EXCEPTION;
     952             : 
     953           4 :         if (argc) {
     954           0 :                 if (JS_IsNull(argv[0])) {
     955           0 :                 } else if (JS_IsArrayBuffer(c, argv[0])) {
     956             :                         size_t asize;
     957           0 :                         data = JS_GetArrayBuffer(c, &asize, argv[0] );
     958           0 :                         if (data) data_size = (u32) asize;
     959           0 :                 } else if (JS_IsObject(argv[0])) {
     960             :                         /*NOT SUPPORTED YET, we must serialize the sg*/
     961           0 :                         return JS_EXCEPTION;
     962             :                 } else {
     963           0 :                         if (!JS_CHECK_STRING(argv[0])) return JS_EXCEPTION;
     964             :                         data = JS_ToCString(c, argv[0]);
     965           0 :                         data_size = (u32) strlen(ctx->data);
     966             :                 }
     967             :         }
     968             : 
     969             :         /*reset previous text*/
     970           4 :         xml_http_del_data(ctx);
     971           4 :         ctx->data = data ? gf_strdup(data) : NULL;
     972           4 :         ctx->size = data_size;
     973             : 
     974           4 :         JS_FreeCString(c, data);
     975             : 
     976           4 :         if (!strncmp(ctx->url, "http://", 7)) {
     977             :                 u32 flags = GF_NETIO_SESSION_NOTIFY_DATA;
     978           1 :                 if (!ctx->async)
     979             :                         flags |= GF_NETIO_SESSION_NOT_THREADED;
     980             : 
     981           1 :                 if (ctx->cache != XHR_CACHETYPE_NORMAL) {
     982           0 :                         if (ctx->cache == XHR_CACHETYPE_NONE) {
     983           0 :                                 flags |= GF_NETIO_SESSION_NOT_CACHED;
     984             :                         }
     985           0 :                         if (ctx->cache == XHR_CACHETYPE_MEMORY) {
     986           0 :                                 flags |= GF_NETIO_SESSION_MEMORY_CACHE;
     987             :                         }
     988             :                 }
     989           1 :                 ctx->sess = gf_dm_sess_new(par.dnld_man, ctx->url, flags, xml_http_on_data, ctx, &e);
     990           1 :                 if (!ctx->sess) return JS_EXCEPTION;
     991             : 
     992             :                 /*start our download (whether the session is threaded or not)*/
     993           1 :                 e = gf_dm_sess_process(ctx->sess);
     994           1 :                 if (e!=GF_OK) {
     995           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[XmlHttpRequest] Error processing %s: %s\n", ctx->url, gf_error_to_string(e) ));
     996             :                 }
     997           1 :                 if (!ctx->async) {
     998           0 :                         xml_http_terminate(ctx, e);
     999             :                 }
    1000             :         } else {
    1001           3 :                 e = xml_http_process_local(ctx);
    1002           3 :                 if (e!=GF_OK) {
    1003           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[XmlHttpRequest] Error processing %s: %s\n", ctx->url, gf_error_to_string(e) ));
    1004             :                 }
    1005             :         }
    1006             : 
    1007           4 :         return JS_TRUE;
    1008             : }
    1009             : 
    1010           0 : static JSValue xml_http_abort(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    1011             : {
    1012             :         GF_DownloadSession *sess;
    1013           0 :         XMLHTTPContext *ctx = JS_GetOpaque(obj, xhrClass.class_id);
    1014           0 :         if (!ctx) return JS_EXCEPTION;
    1015             : 
    1016           0 :         sess = ctx->sess;
    1017           0 :         ctx->sess = NULL;
    1018           0 :         if (sess) {
    1019             :                 //abort first, so that on HTTP/2 this results in RST_STREAM
    1020           0 :                 gf_dm_sess_abort(sess);
    1021           0 :                 gf_dm_sess_del(sess);
    1022             :         }
    1023             : 
    1024           0 :         xml_http_fire_event(ctx, GF_EVENT_ABORT);
    1025           0 :         xml_http_reset(ctx);
    1026           0 :         if (JS_IsFunction(c, ctx->onabort)) {
    1027           0 :                 return JS_Call(ctx->c, ctx->onabort, ctx->_this, 0, NULL);
    1028             :         }
    1029           0 :         return JS_TRUE;
    1030             : }
    1031             : 
    1032           2 : static JSValue xml_http_get_all_headers(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    1033             : {
    1034             :         u32 nb_hdr;
    1035           2 :         char *szVal = NULL;
    1036             :         JSValue res;
    1037           2 :         XMLHTTPContext *ctx = JS_GetOpaque(obj, xhrClass.class_id);
    1038           2 :         if (!ctx) return JS_EXCEPTION;
    1039             : 
    1040             :         /*must be received or loaded*/
    1041           2 :         if (ctx->readyState<XHR_READYSTATE_LOADING) return JS_EXCEPTION;
    1042             :         nb_hdr = 0;
    1043           2 :         if (ctx->recv_headers) {
    1044           6 :                 while (ctx->recv_headers[nb_hdr]) {
    1045           4 :                         if (szVal) gf_dynstrcat(&szVal, "\r\n", NULL);
    1046           4 :                         gf_dynstrcat(&szVal, ctx->recv_headers[nb_hdr], NULL);
    1047           4 :                         gf_dynstrcat(&szVal, ctx->recv_headers[nb_hdr+1], ": ");
    1048           4 :                         nb_hdr+=2;
    1049             :                 }
    1050             :         }
    1051             : 
    1052           2 :         if (!szVal) {
    1053           0 :                 return JS_NULL;
    1054             :         }
    1055           2 :         res = JS_NewString(c, szVal);
    1056           2 :         gf_free(szVal);
    1057           2 :         return res;
    1058             : }
    1059             : 
    1060           2 : static JSValue xml_http_get_header(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    1061             : {
    1062             :         u32 nb_hdr;
    1063             :         const char *hdr;
    1064           2 :         char *szVal = NULL;
    1065           2 :         XMLHTTPContext *ctx = JS_GetOpaque(obj, xhrClass.class_id);
    1066           2 :         if (!ctx) return JS_EXCEPTION;
    1067             : 
    1068           4 :         if (!JS_CHECK_STRING(argv[0])) return JS_EXCEPTION;
    1069             :         /*must be received or loaded*/
    1070           2 :         if (ctx->readyState<XHR_READYSTATE_LOADING) return JS_EXCEPTION;
    1071             :         hdr = JS_ToCString(c, argv[0]);
    1072             : 
    1073             :         nb_hdr = 0;
    1074           2 :         if (ctx->recv_headers) {
    1075           6 :                 while (ctx->recv_headers[nb_hdr]) {
    1076           4 :                         if (!strcmp(ctx->recv_headers[nb_hdr], hdr)) {
    1077           0 :                                 gf_dynstrcat(&szVal, ctx->recv_headers[nb_hdr+1], ", ");
    1078             :                         }
    1079           4 :                         nb_hdr+=2;
    1080             :                 }
    1081             :         }
    1082           2 :         JS_FreeCString(c, hdr);
    1083           2 :         if (!szVal) {
    1084           2 :                 return JS_NULL;
    1085             :         }
    1086           0 :         JSValue res = JS_NewString(c, szVal);
    1087           0 :         gf_free(szVal);
    1088           0 :         return res;
    1089             : }
    1090             : 
    1091             : #ifndef GPAC_DISABLE_SVG
    1092           2 : static GF_Err xml_http_load_dom(XMLHTTPContext *ctx)
    1093             : {
    1094             :         GF_Err e;
    1095           2 :         GF_DOMParser *parser = gf_xml_dom_new();
    1096           2 :         e = gf_xml_dom_parse_string(parser, ctx->data);
    1097           2 :         if (!e) {
    1098           2 :                 e = gf_sg_init_from_xml_node(ctx->document, gf_xml_dom_get_root(parser));
    1099             :         }
    1100           2 :         gf_xml_dom_del(parser);
    1101           2 :         return e;
    1102             : }
    1103             : #endif //GPAC_DISABLE_SVG
    1104             : 
    1105             : 
    1106           3 : static JSValue xml_http_overrideMimeType(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    1107             : {
    1108             :         const char *mime;
    1109           3 :         XMLHTTPContext *ctx = JS_GetOpaque(obj, xhrClass.class_id);
    1110           3 :         if (!ctx || !argc) return JS_EXCEPTION;
    1111             : 
    1112           6 :         if (!JS_CHECK_STRING(argv[0])) return JS_EXCEPTION;
    1113             :         mime = JS_ToCString(c, argv[0]);
    1114           3 :         if (ctx->mime) gf_free(ctx->mime);
    1115           3 :         ctx->mime = gf_strdup(mime);
    1116           3 :         JS_FreeCString(c, mime);
    1117           3 :         return JS_TRUE;
    1118             : }
    1119             : 
    1120          24 : static JSValue xml_http_getProperty(JSContext *c, JSValueConst obj, int magic)
    1121             : {
    1122          24 :         XMLHTTPContext *ctx = JS_GetOpaque(obj, xhrClass.class_id);
    1123          24 :         if (!ctx) return JS_EXCEPTION;
    1124             : 
    1125          24 :         switch (magic) {
    1126           0 :         case XHR_ONABORT: return JS_DupValue(c, ctx->onabort);
    1127           0 :         case XHR_ONERROR: return JS_DupValue(c, ctx->onerror);
    1128           0 :         case XHR_ONLOAD: return JS_DupValue(c, ctx->onload);
    1129           0 :         case XHR_ONLOADSTART: return JS_DupValue(c, ctx->onloadstart);
    1130           0 :         case XHR_ONLOADEND: return JS_DupValue(c, ctx->onloadend);
    1131           0 :         case XHR_ONPROGRESS: return JS_DupValue(c, ctx->onprogress);
    1132           0 :         case XHR_ONREADYSTATECHANGE: return JS_DupValue(c, ctx->onreadystatechange);
    1133           0 :         case XHR_ONTIMEOUT: return JS_DupValue(c, ctx->ontimeout);
    1134          17 :         case XHR_READYSTATE: return JS_NewInt32(c, ctx->readyState);
    1135           0 :         case XHR_RESPONSETEXT:
    1136           0 :                 if (ctx->readyState<XHR_READYSTATE_LOADING) return JS_NULL;
    1137           0 :                 if (ctx->data) {
    1138           0 :                         return JS_NewString(c, ctx->data);
    1139             :                 } else {
    1140           0 :                         return JS_NULL;
    1141             :                 }
    1142             : 
    1143           0 :         case XHR_RESPONSEXML:
    1144           0 :                 if (ctx->readyState<XHR_READYSTATE_LOADING) return JS_NULL;
    1145             : #ifndef GPAC_DISABLE_SVG
    1146           0 :                 if (ctx->data) {
    1147           0 :                         if (!ctx->document) {
    1148           0 :                                 ctx->document = gf_sg_new();
    1149             :                                 /*mark this doc as "nomade", and let it leave until all references to it are destroyed*/
    1150           0 :                                 ctx->document->reference_count = 1;
    1151           0 :                                 xml_http_load_dom(ctx);
    1152             :                         }
    1153           0 :                         if (!ctx->js_dom_loaded) {
    1154           0 :                                 dom_js_load(ctx->document, c);
    1155           0 :                                 ctx->js_dom_loaded = GF_TRUE;
    1156             :                         }
    1157           0 :                         return dom_document_construct_external(c, ctx->document);
    1158             :                 } else {
    1159           0 :                         return JS_NULL;
    1160             :                 }
    1161             : #else
    1162             :                 return js_throw_err_msg(c, GF_NOT_SUPPORTED, "DOM support not included in buil");
    1163             : #endif
    1164             : 
    1165           5 :         case XHR_RESPONSE:
    1166           5 :                 if (ctx->readyState<XHR_READYSTATE_LOADING) return JS_NULL;
    1167             : 
    1168           5 :                 if (ctx->data) {
    1169           5 :                         switch(ctx->responseType)
    1170             :                         {
    1171           0 :                         case XHR_RESPONSETYPE_NONE:
    1172             :                         case XHR_RESPONSETYPE_TEXT:
    1173           0 :                                 return JS_NewString(c, ctx->data);
    1174             : 
    1175           0 :                         case XHR_RESPONSETYPE_ARRAYBUFFER:
    1176           0 :                                 if (JS_IsUndefined(ctx->arraybuffer)) {
    1177           0 :                                         ctx->arraybuffer = JS_NewArrayBuffer(c, ctx->data, ctx->size, NULL, ctx, 1);
    1178             :                                 }
    1179             :                                 return JS_DupValue(c, ctx->arraybuffer);
    1180             :                                 break;
    1181             :                         case XHR_RESPONSETYPE_DOCUMENT:
    1182             : #ifndef GPAC_DISABLE_SVG
    1183             :                                 if (ctx->data) {
    1184           4 :                                         if (!ctx->document) {
    1185           2 :                                                 ctx->document = gf_sg_new();
    1186             :                                                 /*mark this doc as "nomade", and let it leave until all references to it are destroyed*/
    1187           2 :                                                 ctx->document->reference_count = 1;
    1188           2 :                                                 xml_http_load_dom(ctx);
    1189             :                                         }
    1190           4 :                                         if (!ctx->js_dom_loaded) {
    1191           2 :                                                 dom_js_load(ctx->document, c);
    1192           2 :                                                 ctx->js_dom_loaded = GF_TRUE;
    1193             :                                         }
    1194           4 :                                         return dom_document_construct_external(c, ctx->document);
    1195             :                                 }
    1196             :                                 return JS_NULL;
    1197             : #else
    1198             :                                 return js_throw_err_msg(c, GF_NOT_SUPPORTED, "DOM support not included in buil");
    1199             : #endif
    1200           0 :                         case XHR_RESPONSETYPE_JSON:
    1201           0 :                                 return JS_ParseJSON(c, ctx->data, ctx->size, "responseJSON");
    1202           0 :                         case XHR_RESPONSETYPE_PUSH:
    1203           0 :                                 return JS_EXCEPTION;
    1204             :                         default:
    1205             :                                 /*other types not supported     */
    1206             :                                 break;
    1207             :                         }
    1208           0 :                 }
    1209           1 :                 return JS_NULL;
    1210             : 
    1211           2 :         case XHR_STATUS:
    1212           2 :                 return JS_NewInt32(c, ctx->html_status);
    1213             : 
    1214           0 :         case XHR_STATUSTEXT:
    1215           0 :                 if (ctx->statusText) {
    1216           0 :                         return JS_NewString(c, ctx->statusText);
    1217             :                 }
    1218           0 :                 return JS_NULL;
    1219           0 :         case XHR_TIMEOUT:
    1220           0 :                 return JS_NewInt32(c, ctx->timeout);
    1221           0 :         case XHR_WITHCREDENTIALS:
    1222           0 :                 return ctx->withCredentials ? JS_TRUE : JS_FALSE;
    1223           0 :         case XHR_UPLOAD:
    1224             :                 /* TODO */
    1225           0 :                 return JS_EXCEPTION;
    1226           0 :         case XHR_RESPONSETYPE:
    1227           0 :                 switch (ctx->responseType) {
    1228           0 :                 case XHR_RESPONSETYPE_NONE: return JS_NewString(c, "");
    1229           0 :                 case XHR_RESPONSETYPE_ARRAYBUFFER: return JS_NewString(c, "arraybuffer");
    1230           0 :                 case XHR_RESPONSETYPE_BLOB: return JS_NewString(c, "blob");
    1231           0 :                 case XHR_RESPONSETYPE_DOCUMENT: return JS_NewString(c, "document");
    1232           0 :                 case XHR_RESPONSETYPE_SAX: return JS_NewString(c, "sax");
    1233           0 :                 case XHR_RESPONSETYPE_JSON: return JS_NewString(c, "json");
    1234           0 :                 case XHR_RESPONSETYPE_TEXT: return JS_NewString(c, "text");
    1235           0 :                 case XHR_RESPONSETYPE_PUSH: return JS_NewString(c, "push");
    1236             :                 }
    1237           0 :                 return JS_NULL;
    1238             :         case XHR_STATIC_UNSENT:
    1239             :                 return JS_NewInt32(c, XHR_READYSTATE_UNSENT);
    1240             :         case XHR_STATIC_OPENED:
    1241             :                 return JS_NewInt32(c, XHR_READYSTATE_OPENED);
    1242             :         case XHR_STATIC_HEADERS_RECEIVED:
    1243             :                 return JS_NewInt32(c, XHR_READYSTATE_HEADERS_RECEIVED);
    1244             :         case XHR_STATIC_LOADING:
    1245             :                 return JS_NewInt32(c, XHR_READYSTATE_LOADING);
    1246             :         case XHR_STATIC_DONE:
    1247             :                 return JS_NewInt32(c, XHR_READYSTATE_DONE);
    1248           0 :         case XHR_CACHE:
    1249           0 :                 switch (ctx->cache) {
    1250           0 :                 case XHR_CACHETYPE_NORMAL: return JS_NewString(c, "normal");
    1251           0 :                 case XHR_CACHETYPE_NONE:  return JS_NewString(c, "none");
    1252           0 :                 case XHR_CACHETYPE_MEMORY: return JS_NewString(c, "memory");
    1253             :                 }
    1254           0 :                 return JS_NULL;
    1255             :         default:
    1256             :                 break;
    1257             :         }
    1258           0 :         return JS_UNDEFINED;
    1259             : }
    1260             : 
    1261          17 : static JSValue xml_http_setProperty(JSContext *c, JSValueConst obj, JSValueConst value, int magic)
    1262             : {
    1263          17 :         XMLHTTPContext *ctx = JS_GetOpaque(obj, xhrClass.class_id);
    1264          17 :         if (!ctx) return JS_EXCEPTION;
    1265             : 
    1266             : #define SET_CBK(_sym) \
    1267             :                 if (JS_IsFunction(c, value) || JS_IsUndefined(value) || JS_IsNull(value)) {\
    1268             :                         JS_FreeValue(c, ctx->_sym);\
    1269             :                         ctx->_sym = JS_DupValue(c, value);\
    1270             :                         return JS_TRUE;\
    1271             :                 }\
    1272             :                 return JS_EXCEPTION;\
    1273             : 
    1274          17 :         switch (magic) {
    1275           4 :         case XHR_ONERROR:
    1276           8 :                 SET_CBK(onerror)
    1277             : 
    1278           0 :         case XHR_ONABORT:
    1279           0 :                 SET_CBK(onabort)
    1280             : 
    1281           4 :         case XHR_ONLOAD:
    1282           8 :                 SET_CBK(onload)
    1283             : 
    1284           0 :         case XHR_ONLOADSTART:
    1285           0 :                 SET_CBK(onloadstart)
    1286             : 
    1287           0 :         case XHR_ONLOADEND:
    1288           0 :                 SET_CBK(onloadend)
    1289             : 
    1290           0 :         case XHR_ONPROGRESS:
    1291           0 :                 SET_CBK(onprogress)
    1292             : 
    1293           4 :         case XHR_ONREADYSTATECHANGE:
    1294           8 :                 SET_CBK(onreadystatechange)
    1295             : 
    1296           0 :         case XHR_ONTIMEOUT:
    1297           0 :                 SET_CBK(ontimeout)
    1298             : 
    1299           0 :         case XHR_TIMEOUT:
    1300           0 :                 if (JS_ToInt32(c, &ctx->timeout, value)) return JS_EXCEPTION;
    1301           0 :                 return JS_TRUE;
    1302             : 
    1303           0 :         case XHR_WITHCREDENTIALS:
    1304           0 :                 ctx->withCredentials = JS_ToBool(c, value) ? GF_TRUE : GF_FALSE;
    1305           0 :                 return JS_TRUE;
    1306           5 :         case XHR_RESPONSETYPE:
    1307             :         {
    1308             :                 const char *str = JS_ToCString(c, value);
    1309           5 :                 if (!str || !strcmp(str, "")) {
    1310           0 :                         ctx->responseType = XHR_RESPONSETYPE_NONE;
    1311           5 :                 } else if (!strcmp(str, "arraybuffer")) {
    1312           0 :                         ctx->responseType = XHR_RESPONSETYPE_ARRAYBUFFER;
    1313           5 :                 } else if (!strcmp(str, "blob")) {
    1314           0 :                         ctx->responseType = XHR_RESPONSETYPE_BLOB;
    1315           5 :                 } else if (!strcmp(str, "document")) {
    1316           4 :                         ctx->responseType = XHR_RESPONSETYPE_DOCUMENT;
    1317           1 :                 } else if (!strcmp(str, "json")) {
    1318           0 :                         ctx->responseType = XHR_RESPONSETYPE_JSON;
    1319           1 :                 } else if (!strcmp(str, "text")) {
    1320           0 :                         ctx->responseType = XHR_RESPONSETYPE_TEXT;
    1321           1 :                 } else if (!strcmp(str, "sax")) {
    1322           1 :                         ctx->responseType = XHR_RESPONSETYPE_SAX;
    1323           0 :                 } else if (!strcmp(str, "push")) {
    1324           0 :                         ctx->responseType = XHR_RESPONSETYPE_PUSH;
    1325             :                 }
    1326           5 :                 JS_FreeCString(c, str);
    1327           5 :                 return JS_TRUE;
    1328             :         }
    1329           0 :         case XHR_CACHE:
    1330             :         {
    1331             :                 const char *str = JS_ToCString(c, value);
    1332           0 :                 if (!str) return JS_EXCEPTION;
    1333           0 :                 if (!strcmp(str, "normal")) {
    1334           0 :                         ctx->cache = XHR_CACHETYPE_NORMAL;
    1335           0 :                 } else if (!strcmp(str, "none")) {
    1336           0 :                         ctx->cache = XHR_CACHETYPE_NONE;
    1337           0 :                 } else if (!strcmp(str, "memory")) {
    1338           0 :                         ctx->cache = XHR_CACHETYPE_MEMORY;
    1339             :                 }
    1340           0 :                 JS_FreeCString(c, str);
    1341           0 :                 return JS_TRUE;
    1342             :         }
    1343             :         /*all other properties are read-only*/
    1344             :         default:
    1345             :                 break;
    1346             :         }
    1347           0 :         return JS_FALSE;
    1348             : }
    1349             : 
    1350             : static const JSCFunctionListEntry xhr_Funcs[] =
    1351             : {
    1352             :         JS_CGETSET_MAGIC_DEF("onabort", xml_http_getProperty, xml_http_setProperty, XHR_ONABORT),
    1353             :         JS_CGETSET_MAGIC_DEF("onerror", xml_http_getProperty, xml_http_setProperty, XHR_ONERROR),
    1354             :         JS_CGETSET_MAGIC_DEF("onload", xml_http_getProperty, xml_http_setProperty, XHR_ONLOAD),
    1355             :         JS_CGETSET_MAGIC_DEF("onloadend", xml_http_getProperty, xml_http_setProperty, XHR_ONLOADEND),
    1356             :         JS_CGETSET_MAGIC_DEF("onloadstart", xml_http_getProperty, xml_http_setProperty, XHR_ONLOADSTART),
    1357             :         JS_CGETSET_MAGIC_DEF("onprogress", xml_http_getProperty, xml_http_setProperty, XHR_ONPROGRESS),
    1358             :         JS_CGETSET_MAGIC_DEF("onreadystatechange", xml_http_getProperty, xml_http_setProperty, XHR_ONREADYSTATECHANGE),
    1359             :         JS_CGETSET_MAGIC_DEF("ontimeout", xml_http_getProperty, xml_http_setProperty, XHR_ONTIMEOUT),
    1360             :         JS_CGETSET_MAGIC_DEF("readyState", xml_http_getProperty, NULL, XHR_READYSTATE),
    1361             :         JS_CGETSET_MAGIC_DEF("response", xml_http_getProperty, NULL, XHR_RESPONSE),
    1362             :         JS_CGETSET_MAGIC_DEF("responseType", xml_http_getProperty, xml_http_setProperty, XHR_RESPONSETYPE),
    1363             :         JS_CGETSET_MAGIC_DEF("responseText", xml_http_getProperty, NULL, XHR_RESPONSETEXT),
    1364             :         JS_CGETSET_MAGIC_DEF("responseXML", xml_http_getProperty, NULL, XHR_RESPONSEXML),
    1365             :         JS_CGETSET_MAGIC_DEF("status", xml_http_getProperty, NULL, XHR_STATUS),
    1366             :         JS_CGETSET_MAGIC_DEF("statusText", xml_http_getProperty, NULL, XHR_STATUSTEXT),
    1367             :         JS_CGETSET_MAGIC_DEF("timeout", xml_http_getProperty, xml_http_setProperty, XHR_TIMEOUT),
    1368             :         JS_CGETSET_MAGIC_DEF("upload", xml_http_getProperty, NULL, XHR_UPLOAD),
    1369             :         JS_CGETSET_MAGIC_DEF("withCredentials", xml_http_getProperty, xml_http_setProperty, XHR_WITHCREDENTIALS),
    1370             :         JS_CGETSET_MAGIC_DEF("cache", xml_http_getProperty, xml_http_setProperty, XHR_CACHE),
    1371             : 
    1372             :         JS_CFUNC_DEF("abort", 0, xml_http_abort),
    1373             :         JS_CFUNC_DEF("getAllResponseHeaders", 0, xml_http_get_all_headers),
    1374             :         JS_CFUNC_DEF("getResponseHeader", 1, xml_http_get_header),
    1375             :         JS_CFUNC_DEF("open", 2, xml_http_open),
    1376             :         JS_CFUNC_DEF("overrideMimeType", 1, xml_http_overrideMimeType),
    1377             :         JS_CFUNC_DEF("send", 0, xml_http_send),
    1378             :         JS_CFUNC_DEF("setRequestHeader", 2, xml_http_set_header),
    1379             : 
    1380             :         /*eventTarget interface*/
    1381             :         JS_DOM3_EVENT_TARGET_INTERFACE
    1382             : };
    1383             : 
    1384      133412 : static void xml_http_gc_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func)
    1385             : {
    1386      133412 :     XMLHTTPContext *ctx = JS_GetOpaque(val, xhrClass.class_id);
    1387      133412 :     if (!ctx) return;
    1388             : 
    1389           4 :         JS_MarkValue(rt, ctx->onabort, mark_func);
    1390           4 :         JS_MarkValue(rt, ctx->onerror, mark_func);
    1391           4 :         JS_MarkValue(rt, ctx->onload, mark_func);
    1392           4 :         JS_MarkValue(rt, ctx->onloadend, mark_func);
    1393           4 :         JS_MarkValue(rt, ctx->onloadstart, mark_func);
    1394           4 :         JS_MarkValue(rt, ctx->onprogress, mark_func);
    1395           4 :         JS_MarkValue(rt, ctx->onreadystatechange, mark_func);
    1396           4 :         JS_MarkValue(rt, ctx->ontimeout, mark_func);
    1397             : }
    1398             : 
    1399         411 : static JSValue xhr_load_class(JSContext *c)
    1400             : {
    1401         411 :         if (! xhrClass.class_id) {
    1402          48 :                 JS_NewClassID(&xhrClass.class_id);
    1403          48 :                 xhrClass.class.class_name = "XMLHttpRequest";
    1404          48 :                 xhrClass.class.finalizer = xml_http_finalize;
    1405          48 :                 xhrClass.class.gc_mark = xml_http_gc_mark;
    1406          48 :                 JS_NewClass(JS_GetRuntime(c), xhrClass.class_id, &xhrClass.class);
    1407             :         }
    1408         411 :         JSValue proto = JS_NewObjectClass(c, xhrClass.class_id);
    1409         411 :         JS_SetPropertyFunctionList(c, proto, xhr_Funcs, countof(xhr_Funcs));
    1410         411 :         JS_SetClassProto(c, xhrClass.class_id, proto);
    1411         411 :         JS_SetPropertyStr(c, proto, "UNSENT", JS_NewInt32(c, XHR_STATIC_UNSENT));
    1412         411 :         JS_SetPropertyStr(c, proto, "OPENED", JS_NewInt32(c, XHR_STATIC_OPENED));
    1413         411 :         JS_SetPropertyStr(c, proto, "HEADERS_RECEIVED",       JS_NewInt32(c, XHR_STATIC_HEADERS_RECEIVED));
    1414         411 :         JS_SetPropertyStr(c, proto, "LOADING",        JS_NewInt32(c, XHR_STATIC_LOADING));
    1415         411 :         JS_SetPropertyStr(c, proto, "DONE",   JS_NewInt32(c, XHR_STATIC_DONE));
    1416             : 
    1417         411 :         return JS_NewCFunction2(c, xml_http_constructor, "XMLHttpRequest", 1, JS_CFUNC_constructor, 0);
    1418             : }
    1419             : 
    1420         407 : void qjs_module_init_xhr_global(JSContext *c, JSValue global)
    1421             : {
    1422         407 :         if (c) {
    1423         407 :                 JSValue ctor = xhr_load_class(c);
    1424         407 :                 JS_SetPropertyStr(c, global, "XMLHttpRequest", ctor);
    1425             :         }
    1426         407 : }
    1427             : 
    1428           4 : static int js_xhr_load_module(JSContext *c, JSModuleDef *m)
    1429             : {
    1430           4 :         JSValue global = JS_GetGlobalObject(c);
    1431           4 :         JSValue ctor = xhr_load_class(c);
    1432             :         JS_FreeValue(c, global);
    1433           4 :     JS_SetModuleExport(c, m, "XMLHttpRequest", ctor);
    1434           4 :         return 0;
    1435             : }
    1436          64 : void qjs_module_init_xhr(JSContext *ctx)
    1437             : {
    1438             :         JSModuleDef *m;
    1439          64 :         m = JS_NewCModule(ctx, "xhr", js_xhr_load_module);
    1440          64 :         if (!m) return;
    1441             : 
    1442          64 :         JS_AddModuleExport(ctx, m, "XMLHttpRequest");
    1443             : 
    1444             : #ifdef GPAC_ENABLE_COVERAGE
    1445          64 :         if (gf_sys_is_cov_mode()) {
    1446             :                 qjs_module_init_xhr_global(NULL, JS_TRUE);
    1447             :                 xhr_get_event_target(NULL, JS_TRUE, NULL, NULL);
    1448             :         }
    1449             : #endif
    1450             :         return;
    1451             : }
    1452             : 
    1453             : 
    1454             : #endif
    1455             : 

Generated by: LCOV version 1.13