Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions project/Build.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
<set name="tvos" value="1" if="appletv" />

<set name="LIME_CAIRO" value="1" />
<set name="LIME_CURL" value="1" unless="winrt" />
<set name="LIME_EFSW" value="1" if="windows || mac || linux" unless="winrt" />
<set name="LIME_CURL" value="1" unless="emscripten || winrt" />
<set name="LIME_EFSW" value="1" if="windows || mac || linux" unless="emscripten || winrt" />
<set name="LIME_JPEG" value="1" />
<!-- <set name="LIME_FAUDIO" value="1" /> -->
<set name="LIME_FREETYPE" value="1" />
Expand Down
50 changes: 50 additions & 0 deletions src/lime/_internal/backend/emscripten/EmscriptenFetch.hx
Original file line number Diff line number Diff line change
@@ -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<EmscriptenFetchAttrType>):Void;

@:native("emscripten_fetch")
public static function emscripten_fetch(attr:Pointer<EmscriptenFetchAttrType>, url:ConstCharStar):Pointer<EmscriptenFetchType>;

@:native("emscripten_fetch_close")
public static function emscripten_fetch_close(fetch:Pointer<EmscriptenFetchType>):Void;
}

@:include("emscripten/fetch.h")
@:native("emscripten_fetch_attr_t")
@:structAccess
@:unreflective
extern class EmscriptenFetchAttrType {
public var requestMethod:ConstCharStar;
public var onsuccess:Star<EmscriptenFetchType>->Void;
public var onprogress:Star<EmscriptenFetchType>->Void;
public var onerror:Star<EmscriptenFetchType>->Void;
public var attributes:Int;
public var userData:Star<cpp.Void>;
}

@: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<cpp.UInt8>;
public var userData:Star<cpp.Void>;
}
#end
253 changes: 253 additions & 0 deletions src/lime/_internal/backend/emscripten/EmscriptenHTTPRequest.hx
Original file line number Diff line number Diff line change
@@ -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<Int, FetchContext>();

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<Bytes>
{
var promise = new Promise<Bytes>();
binary = true;
__load(uri, promise, LoadType.BINARY);
return promise.future;
}

public function loadText(uri:String):Future<String>
{
var promise = new Promise<String>();
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<EmscriptenFetchAttrType> = 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<cpp.Int32> = 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<EmscriptenFetchType>):Void
{
var fetch:EmscriptenFetchType = Pointer.fromStar(fetchPtr).value;
var idPointer:Star<cpp.Int32> = 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<Bytes>).complete(bytes);

case LoadType.TEXT:
var text = bytes.getString(0, bytes.length, UTF8);
cast(context.promise, Promise<String>).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<Image>).complete(image);
});
}
}
else
{
var errorResponse:_HTTPRequestErrorResponse<Any>;
if(context.loadType == LoadType.BINARY) {
errorResponse = new _HTTPRequestErrorResponse(fetch.status, bytes);
cast(context.promise, Promise<Bytes>).error(errorResponse);
}else {
errorResponse = new _HTTPRequestErrorResponse(fetch.status, bytes.getString(0, bytes.length, UTF8));
cast(context.promise, Promise<String>).error(errorResponse);
}
}
}

@:keep
private static function onFetchProgress(fetchPtr:Star<EmscriptenFetchType>):Void
{
var fetch:EmscriptenFetchType = Pointer.fromStar(fetchPtr).value;
var idPointer:Star<cpp.Int32> = 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<Bytes>).progress(fetch.dataOffset + fetch.numBytes, fetch.totalBytes);
else if(context.loadType == LoadType.TEXT)
cast(context.promise, Promise<String>).progress(fetch.dataOffset + fetch.numBytes, fetch.totalBytes);
}
}
}

@:keep
private static function onFetchError(fetchPtr:Star<EmscriptenFetchType>):Void
{
var fetch:EmscriptenFetchType = Pointer.fromStar(fetchPtr).value;
var idPointer:Star<cpp.Int32> = 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<Bytes>).error(errorResponse);
else
cast(context.promise, Promise<String>).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<EmscriptenFetchType>;
}
#end
2 changes: 2 additions & 0 deletions src/lime/net/HTTPRequest.hx
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,8 @@ public function load(uri:String = null):Future<T>
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
Expand Down
2 changes: 1 addition & 1 deletion templates/haxe/ManifestResources.hx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ import sys.FileSystem;

if (rootPath == null) {

#if (ios || tvos || webassembly)
#if (ios || tvos)
rootPath = "assets/";
#elseif android
rootPath = "";
Expand Down
4 changes: 4 additions & 0 deletions templates/webassembly/template/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -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::
Expand Down
Loading