| // Copyright 2016 The Emscripten Authors. All rights reserved. |
| // Emscripten is available under two separate licenses, the MIT license and the |
| // University of Illinois/NCSA Open Source License. Both these licenses can be |
| // found in the LICENSE file. |
| |
| var Fetch = { |
| attr_t_offset_requestMethod: 0, |
| attr_t_offset_userData: 32, |
| attr_t_offset_onsuccess: 36, |
| attr_t_offset_onerror: 40, |
| attr_t_offset_onprogress: 44, |
| attr_t_offset_attributes: 48, |
| attr_t_offset_timeoutMSecs: 52, |
| attr_t_offset_withCredentials: 56, |
| attr_t_offset_destinationPath: 60, |
| attr_t_offset_userName: 64, |
| attr_t_offset_password: 68, |
| attr_t_offset_requestHeaders: 72, |
| attr_t_offset_overriddenMimeType: 76, |
| attr_t_offset_requestData: 80, |
| attr_t_offset_requestDataSize: 84, |
| |
| fetch_t_offset_id: 0, |
| fetch_t_offset_userData: 4, |
| fetch_t_offset_url: 8, |
| fetch_t_offset_data: 12, |
| fetch_t_offset_numBytes: 16, |
| fetch_t_offset_dataOffset: 24, |
| fetch_t_offset_totalBytes: 32, |
| fetch_t_offset_readyState: 40, |
| fetch_t_offset_status: 42, |
| fetch_t_offset_statusText: 44, |
| fetch_t_offset___proxyState: 108, |
| fetch_t_offset___attributes: 112, |
| |
| xhrs: [], |
| // The web worker that runs proxied file I/O requests. |
| worker: undefined, |
| // Specifies an instance to the IndexedDB database. The database is opened |
| // as a preload step before the Emscripten application starts. |
| dbInstance: undefined, |
| |
| setu64: function(addr, val) { |
| HEAPU32[addr >> 2] = val; |
| HEAPU32[addr + 4 >> 2] = (val / 4294967296)|0; |
| }, |
| |
| openDatabase: function(dbname, dbversion, onsuccess, onerror) { |
| try { |
| #if FETCH_DEBUG |
| console.log('fetch: indexedDB.open(dbname="' + dbname + '", dbversion="' + dbversion + '");'); |
| #endif |
| var openRequest = indexedDB.open(dbname, dbversion); |
| } catch (e) { return onerror(e); } |
| |
| openRequest.onupgradeneeded = function(event) { |
| #if FETCH_DEBUG |
| console.log('fetch: IndexedDB upgrade needed. Clearing database.'); |
| #endif |
| var db = event.target.result; |
| if (db.objectStoreNames.contains('FILES')) { |
| db.deleteObjectStore('FILES'); |
| } |
| db.createObjectStore('FILES'); |
| }; |
| openRequest.onsuccess = function(event) { onsuccess(event.target.result); }; |
| openRequest.onerror = function(error) { onerror(error); }; |
| }, |
| |
| initFetchWorker: function() { |
| var stackSize = 128*1024; |
| var stack = allocate(stackSize>>2, "i32*", ALLOC_DYNAMIC); |
| Fetch.worker.postMessage({cmd: 'init', TOTAL_MEMORY: TOTAL_MEMORY, DYNAMICTOP_PTR: DYNAMICTOP_PTR, STACKTOP: stack, STACK_MAX: stack + stackSize, queuePtr: _fetch_work_queue, buffer: HEAPU8.buffer}); |
| }, |
| |
| staticInit: function() { |
| #if USE_PTHREADS |
| var isMainThread = (typeof ENVIRONMENT_IS_FETCH_WORKER === 'undefined' && !ENVIRONMENT_IS_PTHREAD); |
| #else |
| var isMainThread = (typeof ENVIRONMENT_IS_FETCH_WORKER === 'undefined'); |
| #endif |
| |
| var onsuccess = function(db) { |
| #if FETCH_DEBUG |
| console.log('fetch: IndexedDB successfully opened.'); |
| #endif |
| Fetch.dbInstance = db; |
| |
| if (isMainThread) { |
| #if USE_PTHREADS |
| if (typeof SharedArrayBuffer !== 'undefined') Fetch.initFetchWorker(); |
| #endif |
| removeRunDependency('library_fetch_init'); |
| } |
| }; |
| var onerror = function() { |
| #if FETCH_DEBUG |
| console.error('fetch: IndexedDB open failed.'); |
| #endif |
| Fetch.dbInstance = false; |
| |
| if (isMainThread) { |
| #if USE_PTHREADS |
| if (typeof SharedArrayBuffer !== 'undefined') Fetch.initFetchWorker(); |
| #endif |
| removeRunDependency('library_fetch_init'); |
| } |
| }; |
| Fetch.openDatabase('emscripten_filesystem', 1, onsuccess, onerror); |
| |
| #if USE_PTHREADS |
| if (isMainThread) { |
| addRunDependency('library_fetch_init'); |
| |
| // Allow HTML module to configure the location where the 'worker.js' file will be loaded from, |
| // via Module.locateFile() function. If not specified, then the default URL 'worker.js' relative |
| // to the main html file is loaded. |
| var fetchJs = locateFile('{{{ FETCH_WORKER_FILE }}}'); |
| Fetch.worker = new Worker(fetchJs); |
| Fetch.worker.onmessage = function(e) { |
| out('fetch-worker sent a message: ' + e.filename + ':' + e.lineno + ': ' + e.message); |
| }; |
| Fetch.worker.onerror = function(e) { |
| err('fetch-worker sent an error! ' + e.filename + ':' + e.lineno + ': ' + e.message); |
| }; |
| } |
| #else |
| if (typeof ENVIRONMENT_IS_FETCH_WORKER === 'undefined' || !ENVIRONMENT_IS_FETCH_WORKER) addRunDependency('library_fetch_init'); |
| #endif |
| } |
| } |
| |
| function __emscripten_fetch_delete_cached_data(db, fetch, onsuccess, onerror) { |
| if (!db) { |
| #if FETCH_DEBUG |
| console.error('fetch: IndexedDB not available!'); |
| #endif |
| onerror(fetch, 0, 'IndexedDB not available!'); |
| return; |
| } |
| |
| var fetch_attr = fetch + Fetch.fetch_t_offset___attributes; |
| var path = HEAPU32[fetch_attr + Fetch.attr_t_offset_destinationPath >> 2]; |
| if (!path) path = HEAPU32[fetch + Fetch.fetch_t_offset_url >> 2]; |
| var pathStr = UTF8ToString(path); |
| |
| try { |
| var transaction = db.transaction(['FILES'], 'readwrite'); |
| var packages = transaction.objectStore('FILES'); |
| var request = packages.delete(pathStr); |
| request.onsuccess = function(event) { |
| var value = event.target.result; |
| #if FETCH_DEBUG |
| console.log('fetch: Deleted file ' + pathStr + ' from IndexedDB'); |
| #endif |
| HEAPU32[fetch + Fetch.fetch_t_offset_data >> 2] = 0; |
| Fetch.setu64(fetch + Fetch.fetch_t_offset_numBytes, 0); |
| Fetch.setu64(fetch + Fetch.fetch_t_offset_dataOffset, 0); |
| Fetch.setu64(fetch + Fetch.fetch_t_offset_dataOffset, 0); |
| HEAPU16[fetch + Fetch.fetch_t_offset_readyState >> 1] = 4; // Mimic XHR readyState 4 === 'DONE: The operation is complete' |
| HEAPU16[fetch + Fetch.fetch_t_offset_status >> 1] = 200; // Mimic XHR HTTP status code 200 "OK" |
| stringToUTF8("OK", fetch + Fetch.fetch_t_offset_statusText, 64); |
| onsuccess(fetch, 0, value); |
| }; |
| request.onerror = function(error) { |
| #if FETCH_DEBUG |
| console.error('fetch: Failed to delete file ' + pathStr + ' from IndexedDB! error: ' + error); |
| #endif |
| HEAPU16[fetch + Fetch.fetch_t_offset_readyState >> 1] = 4; // Mimic XHR readyState 4 === 'DONE: The operation is complete' |
| HEAPU16[fetch + Fetch.fetch_t_offset_status >> 1] = 404; // Mimic XHR HTTP status code 404 "Not Found" |
| stringToUTF8("Not Found", fetch + Fetch.fetch_t_offset_statusText, 64); |
| onerror(fetch, 0, error); |
| }; |
| } catch(e) { |
| #if FETCH_DEBUG |
| console.error('fetch: Failed to load file ' + pathStr + ' from IndexedDB! Got exception ' + e); |
| #endif |
| onerror(fetch, 0, e); |
| } |
| } |
| |
| function __emscripten_fetch_load_cached_data(db, fetch, onsuccess, onerror) { |
| if (!db) { |
| #if FETCH_DEBUG |
| console.error('fetch: IndexedDB not available!'); |
| #endif |
| onerror(fetch, 0, 'IndexedDB not available!'); |
| return; |
| } |
| |
| var fetch_attr = fetch + Fetch.fetch_t_offset___attributes; |
| var path = HEAPU32[fetch_attr + Fetch.attr_t_offset_destinationPath >> 2]; |
| if (!path) path = HEAPU32[fetch + Fetch.fetch_t_offset_url >> 2]; |
| var pathStr = UTF8ToString(path); |
| |
| try { |
| var transaction = db.transaction(['FILES'], 'readonly'); |
| var packages = transaction.objectStore('FILES'); |
| var getRequest = packages.get(pathStr); |
| getRequest.onsuccess = function(event) { |
| if (event.target.result) { |
| var value = event.target.result; |
| var len = value.byteLength || value.length; |
| #if FETCH_DEBUG |
| console.log('fetch: Loaded file ' + pathStr + ' from IndexedDB, length: ' + len); |
| #endif |
| |
| // The data pointer malloc()ed here has the same lifetime as the emscripten_fetch_t structure itself has, and is |
| // freed when emscripten_fetch_close() is called. |
| var ptr = _malloc(len); |
| HEAPU8.set(new Uint8Array(value), ptr); |
| HEAPU32[fetch + Fetch.fetch_t_offset_data >> 2] = ptr; |
| Fetch.setu64(fetch + Fetch.fetch_t_offset_numBytes, len); |
| Fetch.setu64(fetch + Fetch.fetch_t_offset_dataOffset, 0); |
| Fetch.setu64(fetch + Fetch.fetch_t_offset_totalBytes, len); |
| HEAPU16[fetch + Fetch.fetch_t_offset_readyState >> 1] = 4; // Mimic XHR readyState 4 === 'DONE: The operation is complete' |
| HEAPU16[fetch + Fetch.fetch_t_offset_status >> 1] = 200; // Mimic XHR HTTP status code 200 "OK" |
| stringToUTF8("OK", fetch + Fetch.fetch_t_offset_statusText, 64); |
| onsuccess(fetch, 0, value); |
| } else { |
| // Succeeded to load, but the load came back with the value of undefined, treat that as an error since we never store undefined in db. |
| #if FETCH_DEBUG |
| console.error('fetch: File ' + pathStr + ' not found in IndexedDB'); |
| #endif |
| HEAPU16[fetch + Fetch.fetch_t_offset_readyState >> 1] = 4; // Mimic XHR readyState 4 === 'DONE: The operation is complete' |
| HEAPU16[fetch + Fetch.fetch_t_offset_status >> 1] = 404; // Mimic XHR HTTP status code 404 "Not Found" |
| stringToUTF8("Not Found", fetch + Fetch.fetch_t_offset_statusText, 64); |
| onerror(fetch, 0, 'no data'); |
| } |
| }; |
| getRequest.onerror = function(error) { |
| #if FETCH_DEBUG |
| console.error('fetch: Failed to load file ' + pathStr + ' from IndexedDB!'); |
| #endif |
| HEAPU16[fetch + Fetch.fetch_t_offset_readyState >> 1] = 4; // Mimic XHR readyState 4 === 'DONE: The operation is complete' |
| HEAPU16[fetch + Fetch.fetch_t_offset_status >> 1] = 404; // Mimic XHR HTTP status code 404 "Not Found" |
| stringToUTF8("Not Found", fetch + Fetch.fetch_t_offset_statusText, 64); |
| onerror(fetch, 0, error); |
| }; |
| } catch(e) { |
| #if FETCH_DEBUG |
| console.error('fetch: Failed to load file ' + pathStr + ' from IndexedDB! Got exception ' + e); |
| #endif |
| onerror(fetch, 0, e); |
| } |
| } |
| |
| function __emscripten_fetch_cache_data(db, fetch, data, onsuccess, onerror) { |
| if (!db) { |
| #if FETCH_DEBUG |
| console.error('fetch: IndexedDB not available!'); |
| #endif |
| onerror(fetch, 0, 'IndexedDB not available!'); |
| return; |
| } |
| |
| var fetch_attr = fetch + Fetch.fetch_t_offset___attributes; |
| var destinationPath = HEAPU32[fetch_attr + Fetch.attr_t_offset_destinationPath >> 2]; |
| if (!destinationPath) destinationPath = HEAPU32[fetch + Fetch.fetch_t_offset_url >> 2]; |
| var destinationPathStr = UTF8ToString(destinationPath); |
| |
| try { |
| var transaction = db.transaction(['FILES'], 'readwrite'); |
| var packages = transaction.objectStore('FILES'); |
| var putRequest = packages.put(data, destinationPathStr); |
| putRequest.onsuccess = function(event) { |
| #if FETCH_DEBUG |
| console.log('fetch: Stored file "' + destinationPathStr + '" to IndexedDB cache.'); |
| #endif |
| HEAPU16[fetch + Fetch.fetch_t_offset_readyState >> 1] = 4; // Mimic XHR readyState 4 === 'DONE: The operation is complete' |
| HEAPU16[fetch + Fetch.fetch_t_offset_status >> 1] = 200; // Mimic XHR HTTP status code 200 "OK" |
| stringToUTF8("OK", fetch + Fetch.fetch_t_offset_statusText, 64); |
| onsuccess(fetch, 0, destinationPathStr); |
| }; |
| putRequest.onerror = function(error) { |
| #if FETCH_DEBUG |
| console.error('fetch: Failed to store file "' + destinationPathStr + '" to IndexedDB cache!'); |
| #endif |
| // Most likely we got an error if IndexedDB is unwilling to store any more data for this page. |
| // TODO: Can we identify and break down different IndexedDB-provided errors and convert those |
| // to more HTTP status codes for more information? |
| HEAPU16[fetch + Fetch.fetch_t_offset_readyState >> 1] = 4; // Mimic XHR readyState 4 === 'DONE: The operation is complete' |
| HEAPU16[fetch + Fetch.fetch_t_offset_status >> 1] = 413; // Mimic XHR HTTP status code 413 "Payload Too Large" |
| stringToUTF8("Payload Too Large", fetch + Fetch.fetch_t_offset_statusText, 64); |
| onerror(fetch, 0, error); |
| }; |
| } catch(e) { |
| #if FETCH_DEBUG |
| console.error('fetch: Failed to store file "' + destinationPathStr + '" to IndexedDB cache! Exception: ' + e); |
| #endif |
| onerror(fetch, 0, e); |
| } |
| } |
| |
| function __emscripten_fetch_xhr(fetch, onsuccess, onerror, onprogress) { |
| var url = HEAPU32[fetch + Fetch.fetch_t_offset_url >> 2]; |
| if (!url) { |
| #if FETCH_DEBUG |
| console.error('fetch: XHR failed, no URL specified!'); |
| #endif |
| onerror(fetch, 0, 'no url specified!'); |
| return; |
| } |
| var url_ = UTF8ToString(url); |
| |
| var fetch_attr = fetch + Fetch.fetch_t_offset___attributes; |
| var requestMethod = UTF8ToString(fetch_attr); |
| if (!requestMethod) requestMethod = 'GET'; |
| var userData = HEAPU32[fetch_attr + Fetch.attr_t_offset_userData >> 2]; |
| var fetchAttributes = HEAPU32[fetch_attr + Fetch.attr_t_offset_attributes >> 2]; |
| var timeoutMsecs = HEAPU32[fetch_attr + Fetch.attr_t_offset_timeoutMSecs >> 2]; |
| var withCredentials = !!HEAPU32[fetch_attr + Fetch.attr_t_offset_withCredentials >> 2]; |
| var destinationPath = HEAPU32[fetch_attr + Fetch.attr_t_offset_destinationPath >> 2]; |
| var userName = HEAPU32[fetch_attr + Fetch.attr_t_offset_userName >> 2]; |
| var password = HEAPU32[fetch_attr + Fetch.attr_t_offset_password >> 2]; |
| var requestHeaders = HEAPU32[fetch_attr + Fetch.attr_t_offset_requestHeaders >> 2]; |
| var overriddenMimeType = HEAPU32[fetch_attr + Fetch.attr_t_offset_overriddenMimeType >> 2]; |
| var dataPtr = HEAPU32[fetch_attr + Fetch.attr_t_offset_requestData >> 2]; |
| var dataLength = HEAPU32[fetch_attr + Fetch.attr_t_offset_requestDataSize >> 2]; |
| |
| var fetchAttrLoadToMemory = !!(fetchAttributes & 1/*EMSCRIPTEN_FETCH_LOAD_TO_MEMORY*/); |
| var fetchAttrStreamData = !!(fetchAttributes & 2/*EMSCRIPTEN_FETCH_STREAM_DATA*/); |
| var fetchAttrPersistFile = !!(fetchAttributes & 4/*EMSCRIPTEN_FETCH_PERSIST_FILE*/); |
| var fetchAttrAppend = !!(fetchAttributes & 8/*EMSCRIPTEN_FETCH_APPEND*/); |
| var fetchAttrReplace = !!(fetchAttributes & 16/*EMSCRIPTEN_FETCH_REPLACE*/); |
| var fetchAttrNoDownload = !!(fetchAttributes & 32/*EMSCRIPTEN_FETCH_NO_DOWNLOAD*/); |
| var fetchAttrSynchronous = !!(fetchAttributes & 64/*EMSCRIPTEN_FETCH_SYNCHRONOUS*/); |
| var fetchAttrWaitable = !!(fetchAttributes & 128/*EMSCRIPTEN_FETCH_WAITABLE*/); |
| |
| var userNameStr = userName ? UTF8ToString(userName) : undefined; |
| var passwordStr = password ? UTF8ToString(password) : undefined; |
| var overriddenMimeTypeStr = overriddenMimeType ? UTF8ToString(overriddenMimeType) : undefined; |
| |
| var xhr = new XMLHttpRequest(); |
| xhr.withCredentials = withCredentials; |
| #if FETCH_DEBUG |
| console.log('fetch: xhr.timeout: ' + xhr.timeout + ', xhr.withCredentials: ' + xhr.withCredentials); |
| console.log('fetch: xhr.open(requestMethod="' + requestMethod + '", url: "' + url_ +'", userName: ' + userNameStr + ', password: ' + passwordStr + ');'); |
| #endif |
| xhr.open(requestMethod, url_, !fetchAttrSynchronous, userNameStr, passwordStr); |
| if (!fetchAttrSynchronous) xhr.timeout = timeoutMsecs; // XHR timeout field is only accessible in async XHRs, and must be set after .open() but before .send(). |
| xhr.url_ = url_; // Save the url for debugging purposes (and for comparing to the responseURL that server side advertised) |
| xhr.responseType = fetchAttrStreamData ? 'moz-chunked-arraybuffer' : 'arraybuffer'; |
| |
| if (overriddenMimeType) { |
| #if FETCH_DEBUG |
| console.log('fetch: xhr.overrideMimeType("' + overriddenMimeTypeStr + '");'); |
| #endif |
| xhr.overrideMimeType(overriddenMimeTypeStr); |
| } |
| if (requestHeaders) { |
| for(;;) { |
| var key = HEAPU32[requestHeaders >> 2]; |
| if (!key) break; |
| var value = HEAPU32[requestHeaders + 4 >> 2]; |
| if (!value) break; |
| requestHeaders += 8; |
| var keyStr = UTF8ToString(key); |
| var valueStr = UTF8ToString(value); |
| #if FETCH_DEBUG |
| console.log('fetch: xhr.setRequestHeader("' + keyStr + '", "' + valueStr + '");'); |
| #endif |
| xhr.setRequestHeader(keyStr, valueStr); |
| } |
| } |
| Fetch.xhrs.push(xhr); |
| var id = Fetch.xhrs.length; |
| HEAPU32[fetch + Fetch.fetch_t_offset_id >> 2] = id; |
| var data = (dataPtr && dataLength) ? HEAPU8.slice(dataPtr, dataPtr + dataLength) : null; |
| // TODO: Support specifying custom headers to the request. |
| |
| xhr.onload = function(e) { |
| var len = xhr.response ? xhr.response.byteLength : 0; |
| var ptr = 0; |
| var ptrLen = 0; |
| if (fetchAttrLoadToMemory && !fetchAttrStreamData) { |
| ptrLen = len; |
| #if FETCH_DEBUG |
| console.log('fetch: allocating ' + ptrLen + ' bytes in Emscripten heap for xhr data'); |
| #endif |
| // The data pointer malloc()ed here has the same lifetime as the emscripten_fetch_t structure itself has, and is |
| // freed when emscripten_fetch_close() is called. |
| ptr = _malloc(ptrLen); |
| HEAPU8.set(new Uint8Array(xhr.response), ptr); |
| } |
| HEAPU32[fetch + Fetch.fetch_t_offset_data >> 2] = ptr; |
| Fetch.setu64(fetch + Fetch.fetch_t_offset_numBytes, ptrLen); |
| Fetch.setu64(fetch + Fetch.fetch_t_offset_dataOffset, 0); |
| if (len) { |
| // If the final XHR.onload handler receives the bytedata to compute total length, report that, |
| // otherwise don't write anything out here, which will retain the latest byte size reported in |
| // the most recent XHR.onprogress handler. |
| Fetch.setu64(fetch + Fetch.fetch_t_offset_totalBytes, len); |
| } |
| HEAPU16[fetch + Fetch.fetch_t_offset_readyState >> 1] = xhr.readyState; |
| if (xhr.readyState === 4 && xhr.status === 0) { |
| if (len > 0) xhr.status = 200; // If loading files from a source that does not give HTTP status code, assume success if we got data bytes. |
| else xhr.status = 404; // Conversely, no data bytes is 404. |
| } |
| HEAPU16[fetch + Fetch.fetch_t_offset_status >> 1] = xhr.status; |
| if (xhr.statusText) stringToUTF8(xhr.statusText, fetch + Fetch.fetch_t_offset_statusText, 64); |
| if (xhr.status >= 200 && xhr.status < 300) { |
| #if FETCH_DEBUG |
| console.log('fetch: xhr of URL "' + xhr.url_ + '" / responseURL "' + xhr.responseURL + '" succeeded with status 200'); |
| #endif |
| if (onsuccess) onsuccess(fetch, xhr, e); |
| } else { |
| #if FETCH_DEBUG |
| console.error('fetch: xhr of URL "' + xhr.url_ + '" / responseURL "' + xhr.responseURL + '" failed with status ' + xhr.status); |
| #endif |
| if (onerror) onerror(fetch, xhr, e); |
| } |
| } |
| xhr.onerror = function(e) { |
| var status = xhr.status; // XXX TODO: Overwriting xhr.status doesn't work here, so don't override anywhere else either. |
| if (xhr.readyState == 4 && status == 0) status = 404; // If no error recorded, pretend it was 404 Not Found. |
| #if FETCH_DEBUG |
| console.error('fetch: xhr of URL "' + xhr.url_ + '" / responseURL "' + xhr.responseURL + '" finished with error, readyState ' + xhr.readyState + ' and status ' + status); |
| #endif |
| HEAPU32[fetch + Fetch.fetch_t_offset_data >> 2] = 0; |
| Fetch.setu64(fetch + Fetch.fetch_t_offset_numBytes, 0); |
| Fetch.setu64(fetch + Fetch.fetch_t_offset_dataOffset, 0); |
| Fetch.setu64(fetch + Fetch.fetch_t_offset_totalBytes, 0); |
| HEAPU16[fetch + Fetch.fetch_t_offset_readyState >> 1] = xhr.readyState; |
| HEAPU16[fetch + Fetch.fetch_t_offset_status >> 1] = status; |
| if (onerror) onerror(fetch, xhr, e); |
| } |
| xhr.ontimeout = function(e) { |
| #if FETCH_DEBUG |
| console.error('fetch: xhr of URL "' + xhr.url_ + '" / responseURL "' + xhr.responseURL + '" timed out, readyState ' + xhr.readyState + ' and status ' + xhr.status); |
| #endif |
| if (onerror) onerror(fetch, xhr, e); |
| } |
| xhr.onprogress = function(e) { |
| var ptrLen = (fetchAttrLoadToMemory && fetchAttrStreamData && xhr.response) ? xhr.response.byteLength : 0; |
| var ptr = 0; |
| if (fetchAttrLoadToMemory && fetchAttrStreamData) { |
| #if FETCH_DEBUG |
| console.log('fetch: allocating ' + ptrLen + ' bytes in Emscripten heap for xhr data'); |
| #endif |
| // The data pointer malloc()ed here has the same lifetime as the emscripten_fetch_t structure itself has, and is |
| // freed when emscripten_fetch_close() is called. |
| ptr = _malloc(ptrLen); |
| HEAPU8.set(new Uint8Array(xhr.response), ptr); |
| } |
| HEAPU32[fetch + Fetch.fetch_t_offset_data >> 2] = ptr; |
| Fetch.setu64(fetch + Fetch.fetch_t_offset_numBytes, ptrLen); |
| Fetch.setu64(fetch + Fetch.fetch_t_offset_dataOffset, e.loaded - ptrLen); |
| Fetch.setu64(fetch + Fetch.fetch_t_offset_totalBytes, e.total); |
| HEAPU16[fetch + Fetch.fetch_t_offset_readyState >> 1] = xhr.readyState; |
| if (xhr.readyState >= 3 && xhr.status === 0 && e.loaded > 0) xhr.status = 200; // If loading files from a source that does not give HTTP status code, assume success if we get data bytes |
| HEAPU16[fetch + Fetch.fetch_t_offset_status >> 1] = xhr.status; |
| if (xhr.statusText) stringToUTF8(xhr.statusText, fetch + Fetch.fetch_t_offset_statusText, 64); |
| if (onprogress) onprogress(fetch, xhr, e); |
| } |
| #if FETCH_DEBUG |
| console.log('fetch: xhr.send(data=' + data + ')'); |
| #endif |
| try { |
| xhr.send(data); |
| } catch(e) { |
| #if FETCH_DEBUG |
| console.error('fetch: xhr failed with exception: ' + e); |
| #endif |
| if (onerror) onerror(fetch, xhr, e); |
| } |
| } |
| |
| function emscripten_start_fetch(fetch, successcb, errorcb, progresscb) { |
| if (typeof Module !== 'undefined') Module['noExitRuntime'] = true; // If we are the main Emscripten runtime, we should not be closing down. |
| |
| var fetch_attr = fetch + Fetch.fetch_t_offset___attributes; |
| var requestMethod = UTF8ToString(fetch_attr); |
| var onsuccess = HEAPU32[fetch_attr + Fetch.attr_t_offset_onsuccess >> 2]; |
| var onerror = HEAPU32[fetch_attr + Fetch.attr_t_offset_onerror >> 2]; |
| var onprogress = HEAPU32[fetch_attr + Fetch.attr_t_offset_onprogress >> 2]; |
| var fetchAttributes = HEAPU32[fetch_attr + Fetch.attr_t_offset_attributes >> 2]; |
| var fetchAttrLoadToMemory = !!(fetchAttributes & 1/*EMSCRIPTEN_FETCH_LOAD_TO_MEMORY*/); |
| var fetchAttrStreamData = !!(fetchAttributes & 2/*EMSCRIPTEN_FETCH_STREAM_DATA*/); |
| var fetchAttrPersistFile = !!(fetchAttributes & 4/*EMSCRIPTEN_FETCH_PERSIST_FILE*/); |
| var fetchAttrAppend = !!(fetchAttributes & 8/*EMSCRIPTEN_FETCH_APPEND*/); |
| var fetchAttrReplace = !!(fetchAttributes & 16/*EMSCRIPTEN_FETCH_REPLACE*/); |
| var fetchAttrNoDownload = !!(fetchAttributes & 32/*EMSCRIPTEN_FETCH_NO_DOWNLOAD*/); |
| |
| var reportSuccess = function(fetch, xhr, e) { |
| #if FETCH_DEBUG |
| console.log('fetch: operation success. e: ' + e); |
| #endif |
| if (onsuccess) Module['dynCall_vi'](onsuccess, fetch); |
| else if (successcb) successcb(fetch); |
| }; |
| |
| var cacheResultAndReportSuccess = function(fetch, xhr, e) { |
| #if FETCH_DEBUG |
| console.log('fetch: operation success. Caching result.. e: ' + e); |
| #endif |
| var storeSuccess = function(fetch, xhr, e) { |
| #if FETCH_DEBUG |
| console.log('fetch: IndexedDB store succeeded.'); |
| #endif |
| if (onsuccess) Module['dynCall_vi'](onsuccess, fetch); |
| else if (successcb) successcb(fetch); |
| }; |
| var storeError = function(fetch, xhr, e) { |
| #if FETCH_DEBUG |
| console.error('fetch: IndexedDB store failed.'); |
| #endif |
| if (onsuccess) Module['dynCall_vi'](onsuccess, fetch); |
| else if (successcb) successcb(fetch); |
| }; |
| __emscripten_fetch_cache_data(Fetch.dbInstance, fetch, xhr.response, storeSuccess, storeError); |
| }; |
| |
| var reportProgress = function(fetch, xhr, e) { |
| if (onprogress) Module['dynCall_vi'](onprogress, fetch); |
| else if (progresscb) progresscb(fetch); |
| }; |
| |
| var reportError = function(fetch, xhr, e) { |
| #if FETCH_DEBUG |
| console.error('fetch: operation failed: ' + e); |
| #endif |
| if (onerror) Module['dynCall_vi'](onerror, fetch); |
| else if (errorcb) errorcb(fetch); |
| }; |
| |
| var performUncachedXhr = function(fetch, xhr, e) { |
| #if FETCH_DEBUG |
| console.error('fetch: starting (uncached) XHR: ' + e); |
| #endif |
| __emscripten_fetch_xhr(fetch, reportSuccess, reportError, reportProgress); |
| }; |
| |
| var performCachedXhr = function(fetch, xhr, e) { |
| #if FETCH_DEBUG |
| console.error('fetch: starting (cached) XHR: ' + e); |
| #endif |
| __emscripten_fetch_xhr(fetch, cacheResultAndReportSuccess, reportError, reportProgress); |
| }; |
| |
| // Should we try IndexedDB first? |
| if (!fetchAttrReplace || requestMethod === 'EM_IDB_STORE' || requestMethod === 'EM_IDB_DELETE') { |
| if (!Fetch.dbInstance) { |
| #if FETCH_DEBUG |
| console.error('fetch: failed to read IndexedDB! Database is not open.'); |
| #endif |
| reportError(fetch, 0, 'IndexedDB is not open'); |
| return 0; // todo: free |
| } |
| |
| if (requestMethod === 'EM_IDB_STORE') { |
| var dataPtr = HEAPU32[fetch_attr + Fetch.attr_t_offset_requestData >> 2]; |
| var dataLength = HEAPU32[fetch_attr + Fetch.attr_t_offset_requestDataSize >> 2]; |
| var data = HEAPU8.slice(dataPtr, dataPtr + dataLength); // TODO(?): Here we perform a clone of the data, because storing shared typed arrays to IndexedDB does not seem to be allowed. |
| __emscripten_fetch_cache_data(Fetch.dbInstance, fetch, data, reportSuccess, reportError); |
| } else if (requestMethod === 'EM_IDB_DELETE') { |
| __emscripten_fetch_delete_cached_data(Fetch.dbInstance, fetch, reportSuccess, reportError); |
| } else if (fetchAttrNoDownload) { |
| __emscripten_fetch_load_cached_data(Fetch.dbInstance, fetch, reportSuccess, reportError); |
| } else if (fetchAttrPersistFile) { |
| __emscripten_fetch_load_cached_data(Fetch.dbInstance, fetch, reportSuccess, performCachedXhr); |
| } else { |
| __emscripten_fetch_load_cached_data(Fetch.dbInstance, fetch, reportSuccess, performUncachedXhr); |
| } |
| } else if (!fetchAttrNoDownload) { |
| if (fetchAttrPersistFile) { |
| __emscripten_fetch_xhr(fetch, cacheResultAndReportSuccess, reportError, reportProgress); |
| } else { |
| __emscripten_fetch_xhr(fetch, reportSuccess, reportError, reportProgress); |
| } |
| } else { |
| #if FETCH_DEBUG |
| console.error('fetch: Invalid combination of flags passed.'); |
| #endif |
| return 0; // todo: free |
| } |
| return fetch; |
| } |