diff --git a/project/Build.xml b/project/Build.xml
index d6f0d7c857..2c518ca0e2 100755
--- a/project/Build.xml
+++ b/project/Build.xml
@@ -8,8 +8,8 @@
-
-
+
+
diff --git a/src/lime/_internal/backend/emscripten/EmscriptenFetch.hx b/src/lime/_internal/backend/emscripten/EmscriptenFetch.hx
new file mode 100644
index 0000000000..91fc89814e
--- /dev/null
+++ b/src/lime/_internal/backend/emscripten/EmscriptenFetch.hx
@@ -0,0 +1,50 @@
+package lime._internal.backend.emscripten;
+
+#if emscripten
+import cpp.Star;
+import cpp.Pointer;
+import cpp.ConstCharStar;
+
+@:include("emscripten/fetch.h")
+@:native
+extern class EmscriptenFetch {
+
+ @:native("emscripten_fetch_attr_init")
+ public static function emscripten_fetch_attr_init(attr:Pointer):Void;
+
+ @:native("emscripten_fetch")
+ public static function emscripten_fetch(attr:Pointer, url:ConstCharStar):Pointer;
+
+ @:native("emscripten_fetch_close")
+ public static function emscripten_fetch_close(fetch:Pointer):Void;
+}
+
+@:include("emscripten/fetch.h")
+@:native("emscripten_fetch_attr_t")
+@:structAccess
+@:unreflective
+extern class EmscriptenFetchAttrType {
+ public var requestMethod:ConstCharStar;
+ public var onsuccess:Star->Void;
+ public var onprogress:Star->Void;
+ public var onerror:Star->Void;
+ public var attributes:Int;
+ public var userData:Star;
+}
+
+@:include("emscripten/fetch.h")
+@:native("emscripten_fetch_t")
+@:structAccess
+extern class EmscriptenFetchType {
+ public var id:Int;
+ public var numBytes:Int;
+ public var dataOffset:Int;
+ public var totalBytes:Int;
+ public var url:ConstCharStar;
+ public var readyState:Int;
+ public var status:Int;
+ public var statusText:ConstCharStar;
+ public var data:Pointer;
+ public var userData:Star;
+}
+#end
\ No newline at end of file
diff --git a/src/lime/_internal/backend/emscripten/EmscriptenHTTPRequest.hx b/src/lime/_internal/backend/emscripten/EmscriptenHTTPRequest.hx
new file mode 100644
index 0000000000..435afeff94
--- /dev/null
+++ b/src/lime/_internal/backend/emscripten/EmscriptenHTTPRequest.hx
@@ -0,0 +1,253 @@
+package lime._internal.backend.emscripten;
+
+#if emscripten
+import haxe.io.Bytes;
+import cpp.Pointer;
+import cpp.Star;
+import lime.app.Future;
+import lime.app.Promise;
+import lime.graphics.Image;
+import lime.net.HTTPRequest._IHTTPRequest;
+import lime._internal.backend.emscripten.EmscriptenFetch.EmscriptenFetchAttrType;
+import lime._internal.backend.emscripten.EmscriptenFetch.EmscriptenFetchType;
+import lime.net.HTTPRequest._HTTPRequestErrorResponse;
+
+@:access(lime.graphics.ImageBuffer)
+@:access(lime.graphics.Image)
+class EmscriptenHTTPRequest
+{
+ private static var fetchId:Int = 0;
+ private static var activeFetches = new Map();
+
+ private var binary:Bool;
+ private var parent:_IHTTPRequest;
+ private var currentFetchId:Int = -1;
+
+ public function new()
+ {
+ }
+
+ public function cancel():Void
+ {
+ if (currentFetchId >= 0 && activeFetches.exists(currentFetchId))
+ {
+ var context = activeFetches.get(currentFetchId);
+ if (context != null && context.fetchPtr != null)
+ {
+ EmscriptenFetch.emscripten_fetch_close(context.fetchPtr);
+ }
+ activeFetches.remove(currentFetchId);
+ currentFetchId = -1;
+ }
+ }
+
+ public function init(parent:_IHTTPRequest):Void
+ {
+ this.parent = parent;
+ }
+
+ public function loadData(uri:String):Future
+ {
+ var promise = new Promise();
+ binary = true;
+ __load(uri, promise, LoadType.BINARY);
+ return promise.future;
+ }
+
+ public function loadText(uri:String):Future
+ {
+ var promise = new Promise();
+ binary = false;
+ __load(uri, promise, LoadType.TEXT);
+ return promise.future;
+ }
+
+ private function __load(uri:String, promise:Dynamic, loadType:LoadType):Void
+ {
+ var id = fetchId++;
+ currentFetchId = id;
+
+ var context:FetchContext = {
+ id: id,
+ promise: promise,
+ parent: parent,
+ loadType: loadType,
+ binary: binary,
+ fetchPtr: null
+ };
+
+ activeFetches.set(id, context);
+
+ var attr:EmscriptenFetchAttrType = untyped __cpp__("{}");
+ var attrPtr:Pointer = Pointer.addressOf(attr);
+ EmscriptenFetch.emscripten_fetch_attr_init(attrPtr);
+
+ // Set request method
+ var method = parent.method != null ? Std.string(parent.method) : "GET";
+ untyped __cpp__('strcpy({0}, {1})', attr.requestMethod, method);
+
+ // Set callbacks
+ attr.onsuccess = untyped __cpp__("onFetchSuccess");
+ attr.onprogress = untyped __cpp__("onFetchProgress");
+ attr.onerror = untyped __cpp__("onFetchError");
+
+ // Store fetch ID in userData
+ var idPointer:Star = untyped __cpp__('(int*)malloc(sizeof(int))');
+ untyped __cpp__('*{0} = {1}', idPointer, id);
+ attr.userData = cast idPointer;
+
+ // Set EMSCRIPTEN_FETCH_LOAD_TO_MEMORY
+ attr.attributes = 1;
+
+ // Start fetch
+ var fetchPtr = EmscriptenFetch.emscripten_fetch(attrPtr, uri);
+ context.fetchPtr = fetchPtr;
+ }
+
+ @:keep
+ private static function onFetchSuccess(fetchPtr:Star):Void
+ {
+ var fetch:EmscriptenFetchType = Pointer.fromStar(fetchPtr).value;
+ var idPointer:Star = cast fetch.userData;
+ var id:Int = untyped __cpp__('*(int*){0}', idPointer);
+
+ cpp.Native.free(idPointer);
+ fetch.userData = null;
+
+ if (!activeFetches.exists(id))
+ {
+ EmscriptenFetch.emscripten_fetch_close(Pointer.fromStar(fetchPtr));
+ return;
+ }
+
+ var context = activeFetches.get(id);
+ activeFetches.remove(id);
+ if(context == null)
+ trace("EmscriptenHTTPRequest.onFetchSuccess: context is null");
+
+ // Process response
+ if (context.parent.enableResponseHeaders)
+ {
+ context.parent.responseHeaders = [];
+ // Note: Emscripten Fetch API has limited header access
+ // You may need to parse headers from fetch.statusText if available
+ }
+
+ context.parent.responseStatus = fetch.status;
+
+ // Create bytes from fetch data
+ var bytes:Bytes = null;
+ if (fetch.numBytes > 0 && fetch.data != null)
+ {
+ bytes = Bytes.ofData(cast fetch.data.toUnmanagedArray(fetch.numBytes).copy());
+ }
+ else
+ {
+ bytes = Bytes.alloc(0);
+ }
+
+ EmscriptenFetch.emscripten_fetch_close(Pointer.fromStar(fetchPtr));
+
+ // Complete promise based on load type
+ if (fetch.status >= 200 && fetch.status < 400)
+ {
+ switch (context.loadType)
+ {
+ case LoadType.BINARY:
+ cast(context.promise, Promise).complete(bytes);
+
+ case LoadType.TEXT:
+ var text = bytes.getString(0, bytes.length, UTF8);
+ cast(context.promise, Promise).complete(text);
+
+ case LoadType.IMAGE:
+ // For images, we need to decode the bytes
+ var img = new Image();
+ img.__fromBytes(bytes, function(image)
+ {
+ cast(context.promise, Promise).complete(image);
+ });
+ }
+ }
+ else
+ {
+ var errorResponse:_HTTPRequestErrorResponse;
+ if(context.loadType == LoadType.BINARY) {
+ errorResponse = new _HTTPRequestErrorResponse(fetch.status, bytes);
+ cast(context.promise, Promise).error(errorResponse);
+ }else {
+ errorResponse = new _HTTPRequestErrorResponse(fetch.status, bytes.getString(0, bytes.length, UTF8));
+ cast(context.promise, Promise).error(errorResponse);
+ }
+ }
+ }
+
+ @:keep
+ private static function onFetchProgress(fetchPtr:Star):Void
+ {
+ var fetch:EmscriptenFetchType = Pointer.fromStar(fetchPtr).value;
+ var idPointer:Star = cast fetch.userData;
+ var id:Int = untyped __cpp__('*(int*){0}', idPointer);
+
+ if (activeFetches.exists(id))
+ {
+ var context = activeFetches.get(id);
+ if (fetch.totalBytes > 0)
+ {
+ if(context.loadType == LoadType.BINARY)
+ cast(context.promise, Promise).progress(fetch.dataOffset + fetch.numBytes, fetch.totalBytes);
+ else if(context.loadType == LoadType.TEXT)
+ cast(context.promise, Promise).progress(fetch.dataOffset + fetch.numBytes, fetch.totalBytes);
+ }
+ }
+ }
+
+ @:keep
+ private static function onFetchError(fetchPtr:Star):Void
+ {
+ var fetch:EmscriptenFetchType = Pointer.fromStar(fetchPtr).value;
+ var idPointer:Star = cast fetch.userData;
+ var id:Int = untyped __cpp__('*(int*){0}', idPointer);
+
+ cpp.Native.free(idPointer);
+
+ if (!activeFetches.exists(id))
+ {
+ EmscriptenFetch.emscripten_fetch_close(Pointer.fromStar(fetchPtr));
+ return;
+ }
+
+ var context = activeFetches.get(id);
+ activeFetches.remove(id);
+
+ context.parent.responseStatus = fetch.status;
+
+ EmscriptenFetch.emscripten_fetch_close(Pointer.fromStar(fetchPtr));
+
+ var errorResponse = new _HTTPRequestErrorResponse(fetch.status, null);
+ if(context.loadType == LoadType.BINARY)
+ cast(context.promise, Promise).error(errorResponse);
+ else
+ cast(context.promise, Promise).error(errorResponse);
+ }
+}
+
+@:dox(hide)
+enum LoadType
+{
+ BINARY;
+ TEXT;
+ IMAGE;
+}
+
+@:dox(hide)
+typedef FetchContext =
+{
+ var id:Int;
+ var promise:Dynamic;
+ var parent:_IHTTPRequest;
+ var loadType:LoadType;
+ var binary:Bool;
+ var fetchPtr:Pointer;
+}
+#end
\ No newline at end of file
diff --git a/src/lime/net/HTTPRequest.hx b/src/lime/net/HTTPRequest.hx
index 6627103db5..70c0ac8fe8 100644
--- a/src/lime/net/HTTPRequest.hx
+++ b/src/lime/net/HTTPRequest.hx
@@ -193,6 +193,8 @@ public function load(uri:String = null):Future
private typedef HTTPRequestBackend = lime._internal.backend.flash.FlashHTTPRequest;
#elseif (js && html5)
private typedef HTTPRequestBackend = lime._internal.backend.html5.HTML5HTTPRequest;
+#elseif emscripten
+private typedef HTTPRequestBackend = lime._internal.backend.emscripten.EmscriptenHTTPRequest;
#else
private typedef HTTPRequestBackend = lime._internal.backend.native.NativeHTTPRequest;
#end
diff --git a/templates/haxe/ManifestResources.hx b/templates/haxe/ManifestResources.hx
index 0aaab0e4be..32c5dfe329 100644
--- a/templates/haxe/ManifestResources.hx
+++ b/templates/haxe/ManifestResources.hx
@@ -55,7 +55,7 @@ import sys.FileSystem;
if (rootPath == null) {
- #if (ios || tvos || webassembly)
+ #if (ios || tvos)
rootPath = "assets/";
#elseif android
rootPath = "";
diff --git a/templates/webassembly/template/index.html b/templates/webassembly/template/index.html
index def2fa388e..33ab3e070c 100644
--- a/templates/webassembly/template/index.html
+++ b/templates/webassembly/template/index.html
@@ -55,11 +55,15 @@
Module.setStatus = function (msg) { console.log (msg); };
::if (WIN_WIDTH == 0)::::if (WIN_HEIGHT == 0)::
var container = document.getElementById ("content");
+ Module.canvas.style.width = container.clientWidth + "px";
+ Module.canvas.style.height = container.clientHeight + "px";
Module.canvas.width = container.clientWidth;
Module.canvas.height = container.clientHeight;
window.addEventListener ("resize", function (e)
{
+ Module.canvas.style.width = container.clientWidth + "px";
+ Module.canvas.style.height = container.clientHeight + "px";
Module.canvas.width = container.clientWidth;
Module.canvas.height = container.clientHeight;
}, true);::end::::end::
diff --git a/tools/platforms/WebAssemblyPlatform.hx b/tools/platforms/WebAssemblyPlatform.hx
index 53b6cc34f1..7cac7f9b86 100644
--- a/tools/platforms/WebAssemblyPlatform.hx
+++ b/tools/platforms/WebAssemblyPlatform.hx
@@ -160,9 +160,7 @@ class WebAssemblyPlatform extends PlatformTarget
}
args = args.concat([
- prefix + "ApplicationMain" + (project.debug ? "-debug" : "") + ".a",
- "-o",
- "ApplicationMain.o"
+ prefix + "ApplicationMain" + (project.debug ? "-debug" : "") + ".a"
]);
if (!project.targetFlags.exists("asmjs"))
@@ -190,27 +188,21 @@ class WebAssemblyPlatform extends PlatformTarget
}
}
- if (project.targetFlags.exists("final") || project.defines.exists("disable-exception-catching") || project.targetFlags.exists("disable-exception-catching"))
- {
- args.push("-s");
- args.push("DISABLE_EXCEPTION_CATCHING=1");
- }
- else
- {
- args.push("-gsource-map");
- args.push("-s");
- args.push("DISABLE_EXCEPTION_CATCHING=0");
- args.push("-s");
- args.push("NO_DISABLE_EXCEPTION_CATCHING=1");
- args.push("-s");
- args.push("ASSERTIONS=1");
- // args.push("-s");
- // args.push("ASSERTIONS=2");
- // args.push("-s");
- // args.push("STACK_OVERFLOW_CHECK=2");
- // args.push("-s");
- // args.push("DEMANGLE_SUPPORT=1");
- }
+ // Fix Rendering
+ args.push("-s");
+ args.push("MIN_WEBGL_VERSION=2");
+ args.push("-s");
+ args.push("MAX_WEBGL_VERSION=2");
+
+ // Fix GC
+ args.push("--Wno-limited-postlink-optimizations");
+ // https://github.com/HaxeFoundation/hxcpp/blob/767fe94d19a041147c4f65dea02c89cb206a0758/toolchain/emscripten-toolchain.xml#L29-L33
+ args.push("-s");
+ args.push("BINARYEN_EXTRA_PASSES='--spill-pointers'");
+
+ // lime.ndll requires -fwasm-exceptions
+ args.push("-s");
+ args.push("-fwasm-exceptions");
// set initial size
// args.push("-s");
@@ -219,30 +211,32 @@ class WebAssemblyPlatform extends PlatformTarget
args.push("-s");
args.push("STACK_SIZE=1MB");
+ args.push("-s");
+ args.push("FETCH=1");
+
// args.push("-s");
// args.push("SAFE_HEAP=1");
- // if (project.targetFlags.exists("final"))
- // {
- // args.push("-O3");
- // }
- // else if (!project.debug)
- // {
- // // args.push ("-s");
- // // args.push ("OUTLINING_LIMIT=70000");
- // args.push("-O2");
- // }
- // else
- // {
- // args.push("-O1");
- // }
-
- // https://github.com/HaxeFoundation/hxcpp/issues/987
- args.push("-O0");
+ if (project.targetFlags.exists("final"))
+ {
+ args.push("-O3");
+ }
+ else if (!project.debug)
+ {
+ args.push("-O2");
+ }
+ else
+ {
+ args.push("-O1");
+ }
args.push("-s");
args.push("ALLOW_MEMORY_GROWTH=1");
+ if(project.targetFlags.exists("websocket")) {
+ args.push("-lwebsocket.js");
+ }
+
if (project.targetFlags.exists("minify"))
{
// 02 enables minification
@@ -258,12 +252,6 @@ class WebAssemblyPlatform extends PlatformTarget
// args.push ("--jcache");
// args.push ("-g");
- if (FileSystem.exists(targetDirectory + "/obj/assets"))
- {
- args.push("--preload-file");
- args.push("assets");
- }
-
if (Log.verbose)
{
args.push("-v");
@@ -313,13 +301,6 @@ class WebAssemblyPlatform extends PlatformTarget
if (project.targetFlags.exists("compress"))
{
- if (FileSystem.exists(targetDirectory + "/bin/" + project.app.file + ".data"))
- {
- // var byteArray = ByteArray.readFile (targetDirectory + "/bin/" + project.app.file + ".data");
- // byteArray.compress (CompressionAlgorithm.GZIP);
- // File.saveBytes (targetDirectory + "/bin/" + project.app.file + ".data.compress", byteArray);
- }
-
// var byteArray = ByteArray.readFile (targetDirectory + "/bin/" + project.app.file + ".js");
// byteArray.compress (CompressionAlgorithm.GZIP);
// File.saveBytes (targetDirectory + "/bin/" + project.app.file + ".js.compress", byteArray);
@@ -511,6 +492,11 @@ class WebAssemblyPlatform extends PlatformTarget
System.mkdir(Path.directory(path));
AssetHelper.copyAsset(asset, path, context);
}
+ else
+ {
+ System.mkdir(Path.directory(path));
+ AssetHelper.copyAsset(asset, path, context);
+ }
}
}