@@ -25,8 +25,9 @@ const windows: Map<Usize, WebUI> = new Map();
2525let _lib : WebUILib ;
2626
2727export class WebUI {
28- #window: Usize ;
28+ #window: Usize = 0 ;
2929 #lib: WebUILib ;
30+ #isFileHandler: Boolean = false ;
3031
3132 /**
3233 * Instanciate a new WebUI window.
@@ -96,21 +97,24 @@ export class WebUI {
9697 BigInt ( this . #window) ,
9798 toCString ( content ) ,
9899 ) ;
99- if ( ! status ) {
100- throw new WebUIError ( `unable to start the browser` ) ;
101- }
102-
103- for ( let i = 0 ; i < 120 ; i ++ ) { // 30 seconds timeout
100+ if ( ! this . #isFileHandler) {
101+ // Check if window is lanched
102+ if ( ! status ) {
103+ throw new WebUIError ( `unable to start the browser` ) ;
104+ }
105+ // Wait for window connection
106+ for ( let i = 0 ; i < 120 ; i ++ ) { // 30 seconds timeout
107+ if ( ! this . isShown ) {
108+ await new Promise ( ( resolve ) => setTimeout ( resolve , 250 ) ) ;
109+ } else {
110+ break ;
111+ }
112+ }
113+ // Check if window is connected
104114 if ( ! this . isShown ) {
105- await new Promise ( ( resolve ) => setTimeout ( resolve , 250 ) ) ;
106- } else {
107- break ;
115+ throw new WebUIError ( `unable to connect to the browser` ) ;
108116 }
109117 }
110-
111- if ( ! this . isShown ) {
112- throw new WebUIError ( `unable to connect to the browser` ) ;
113- }
114118 }
115119
116120 /**
@@ -422,62 +426,73 @@ export class WebUI {
422426 }
423427
424428 /**
425- * Sets a custom handler to respond to file requests from the web browser .
429+ * Sets a custom files handler to respond to HTTP requests.
426430 *
427- * @param handler - Callback that takes an URL, and return a HTTP header + body `string` or `Uint8Array`.
431+ * @param handler - Callback that takes an URL, and return a full HTTP header
432+ * + body. (`string` or `Uint8Array`).
428433 *
429434 * @example
430- * const myWindow = new WebUI()
431435 *
432- * myWindow.setFileHandler(( myUrl: URL) => {
433- * if (myUrl.pathname === '/app.js ') return "console.log('hello from client')"
434- * if (myUrl.pathname === '/img.png') return imgBytes
435- * throw new Error(`Unknown request "${myUrl.pathname}""`)
436- * })
436+ * async function myFileHandler( myUrl: URL) {
437+ * if (myUrl.pathname === '/test ') {
438+ * return "HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\nHello";
439+ * }
440+ * };
437441 *
438- * myWindow.show(
439- * `<html>
440- * <script src="webui.js">/script>
441- * <script src="app.js"></script>
442- * <img src="img.png" />
443- * </html>`
444- * )
442+ * myWindow.setFileHandler(myFileHandler);
445443 */
446- setFileHandler ( handler : ( url : URL ) => string | Uint8Array ) {
447- // const void* (*handler)(const char *filename, int *length)
448- const cb = new Deno . UnsafeCallback (
444+ setFileHandler ( callback : ( url : URL ) => Promise < string | Uint8Array > ) {
445+
446+ // C: .show_wait_connection = false; // 0
447+ // Disable `.show()` auto waiting for window connection,
448+ // otherwise `.setFileHandler()` will be blocked.
449+ _lib . symbols . webui_set_config ( BigInt ( 0 ) , false ) ;
450+
451+ // C: .use_cookies = false; // 4
452+ // We need to disable WebUI Cookies because
453+ // user will use his own custom HTTP header
454+ // in `.setFileHandler()`.
455+ _lib . symbols . webui_set_config ( BigInt ( 4 ) , false ) ;
456+
457+ // Let `.show()` knows that the user is using `.setFileHandler()`
458+ // so no need to wait for window connection in `.show()`.
459+ this . #isFileHandler = true ;
460+
461+ // Create the callback
462+ const callbackResource = new Deno . UnsafeCallback (
449463 {
464+ // const void* (*handler)(const char *filename, int *length)
450465 parameters : [ "buffer" , "pointer" ] ,
451466 result : "pointer" ,
452- } ,
453- (
454- pointerUrl : Deno . PointerValue ,
455- pointerLength : Deno . PointerValue ,
467+ } as const ,
468+ async (
469+ param_url : Deno . PointerValue ,
470+ param_length : Deno . PointerValue ,
456471 ) => {
457472 // Get URL as string
458- const url_str :string = pointerUrl !== null ?
459- new Deno . UnsafePointerView ( pointerUrl ) . getCString ( )
473+ const url_str :string = param_url !== null ?
474+ new Deno . UnsafePointerView ( param_url ) . getCString ( )
460475 : "" ;
461476
462477 // Create URL Obj
463478 const url_obj :URL = new URL ( url_str , "http://localhost" ) ;
464479
465- // Call user callback
466- const user_response : string | Uint8Array = handler ( url_obj ) ;
480+ // Call the user callback
481+ const user_response : string | Uint8Array = await callback ( url_obj ) ;
467482
468483 // We can pass a local buffer to WebUI like this:
469484 // `return Deno.UnsafePointer.of(user_response);` However,
470485 // this may create a memory leak because WebUI cannot free
471486 // it, or cause memory corruption as Deno may free the buffer
472487 // before WebUI uses it. Therefore, the solution is to create
473- // a safe WebUI buffer through WebUI. This WebUI buffer will
488+ // a safe WebUI buffer through WebUI API . This WebUI buffer will
474489 // be automatically freed by WebUI later.
475490 const webui_buffer :Deno . PointerValue = _lib . symbols . webui_malloc ( BigInt ( user_response . length ) ) ;
476491
477492 // Copy data to C safe buffer
478493 if ( typeof user_response === "string" ) {
479494 // copy `user_response` to `webui_buffer` as String data
480- const cString = Deno . UnsafePointerView . toCString ( user_response ) ;
495+ const cString = toCString ( user_response ) ;
481496 const webui_buffer_ref = new Uint8Array ( Deno . UnsafePointerView . getArrayBuffer ( webui_buffer , cString . byteLength ) ) ;
482497 webui_buffer_ref . set ( new Uint8Array ( cString ) ) ;
483498 } else {
@@ -492,18 +507,15 @@ export class WebUI {
492507 webui_buffer ,
493508 BigInt ( user_response . length ) ,
494509 ) ;
495-
496- // Return webui-buffer to webui
497- return 0 ;
498510 } ,
499511 ) ;
500-
512+ // Pass the callback pointer to WebUI
501513 this . #lib. symbols . webui_set_file_handler (
502514 BigInt ( this . #window) ,
503- cb . pointer ,
515+ callbackResource . pointer ,
504516 ) ;
505517 }
506-
518+
507519 /**
508520 * Sets the profile name and path for the current window.
509521 * @param name - Profile name.
@@ -678,7 +690,9 @@ export class WebUI {
678690 private static init ( ) {
679691 if ( typeof _lib === 'undefined' ) {
680692 _lib = loadLib ( ) ;
681- _lib . symbols . webui_set_config ( BigInt ( 5 ) , true ) ; // Enable async calls
693+ // C: .asynchronous_response = true; // 5
694+ // Enable async calls, this is needed for `.bind()`
695+ _lib . symbols . webui_set_config ( BigInt ( 5 ) , true ) ;
682696 }
683697 }
684698
0 commit comments