Repository: iced-rs/iced (language: rust)

Issue:
Running `trunk serve` on examples results in blank web pages
### Is your issue REALLY a bug?

- [X] My issue is indeed a bug!
- [X] I am not crazy! I will not fill out this form just to ask a question or request a feature. Pinky promise.

### Is there an existing issue for this?

- [X] I have searched the existing issues.

### Is this issue related to iced?

- [X] My hardware is compatible and my graphics drivers are up-to-date.

### What happened?

When I run `cargo r` on the examples, a window is created and it works as expected. When I run `trunk serve` on the examples which are also supposed to work on the web, the page just has a title and blank canvas. I'm not getting an error with trunk itself:
```
test@test-Standard-PC-Q35-ICH9-2009:~/iced/examples/counter$ trunk serve
2024-06-25T17:39:37.269355Z  INFO \U0001f680 Starting trunk 0.20.2
2024-06-25T17:39:37.274371Z  INFO \U0001f4e6 starting build
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.44s
2024-06-25T17:39:45.058106Z  INFO applying new distribution
2024-06-25T17:39:45.060747Z  INFO \u2705 success
2024-06-25T17:39:45.061741Z  INFO \U0001f4e1 serving static assets at -> /
2024-06-25T17:39:45.061967Z  INFO \U0001f4e1 server listening at:
2024-06-25T17:39:45.061991Z  INFO     \U0001f3e0 http://127.0.0.1:8080/
2024-06-25T17:39:45.062031Z  INFO     \U0001f3e0 http://[::1]:8080/
```

To reproduce:
- Clone this repo (commit `b9eb86199afe0f2d936eb4ab90af5b2a2c32a87a`)
- `cd examples/counter`
- `trunk serve`

I tried it on NixOS and also in a Ubuntu VM and both had the same result.

### What is the expected behavior?

The counter and TODO examples should have text and buttons showing up on the web page.

### Version

master

### Operating System

Linux

### Do you have any log output?

```shell
Console at http://localhost:8080:

WEBGL_debug_renderer_info is deprecated in Firefox and will be removed. Please use RENDERER. counter-91af9f8a594c1ec3.js:1366:37
WebGL warning: texSubImage: Uploads from a buffer with a final row with a byte count smaller than the row stride can incur extra overhead. 2
WebGL warning: drawArraysInstanced: Drawing to a destination rect smaller than the viewport rect. (This warning will only be given once) 
```
```



Discussion / hints:

Fix patch:
diff --git a/winit/Cargo.toml b/winit/Cargo.toml
index f5a4795239..bd6feb008a 100644
--- a/winit/Cargo.toml
+++ b/winit/Cargo.toml
@@ -44,5 +44,5 @@ winapi.workspace = true
 
 [target.'cfg(target_arch = "wasm32")'.dependencies]
 web-sys.workspace = true
-web-sys.features = ["Document", "Window"]
+web-sys.features = ["Document", "Window", "HtmlCanvasElement"]
 wasm-bindgen-futures.workspace = true
diff --git a/winit/src/program.rs b/winit/src/program.rs
index 52d8eb5f64..9c6d54e0df 100644
--- a/winit/src/program.rs
+++ b/winit/src/program.rs
@@ -251,7 +251,7 @@ where
         #[cfg(target_arch = "wasm32")]
         is_booted: std::rc::Rc<std::cell::RefCell<bool>>,
         #[cfg(target_arch = "wasm32")]
-        queued_events: Vec<Event<Action<Message>>>,
+        canvas: Option<web_sys::HtmlCanvasElement>,
     }
 
     struct BootConfig<C> {
@@ -276,7 +276,7 @@ where
         #[cfg(target_arch = "wasm32")]
         is_booted: std::rc::Rc::new(std::cell::RefCell::new(false)),
         #[cfg(target_arch = "wasm32")]
-        queued_events: Vec::new(),
+        canvas: None,
     };
 
     impl<Message, F, C> winit::application::ApplicationHandler<Action<Message>>
@@ -307,6 +307,12 @@ where
                 }
             };
 
+            #[cfg(target_arch = "wasm32")]
+            {
+                use winit::platform::web::WindowExtWebSys;
+                self.canvas = window.canvas();
+            }
+
             let finish_boot = async move {
                 let mut compositor =
                     C::new(graphics_settings, window.clone()).await?;
@@ -340,6 +346,9 @@ where
 
                     *is_booted.borrow_mut() = true;
                 });
+
+                event_loop
+                    .set_control_flow(winit::event_loop::ControlFlow::Poll);
             }
         }
 
@@ -352,6 +361,11 @@ where
                 return;
             }
 
+            #[cfg(target_arch = "wasm32")]
+            if !*self.is_booted.borrow() {
+                return;
+            }
+
             self.process_event(
                 event_loop,
                 Event::EventLoopAwakened(winit::event::Event::NewEvents(cause)),
@@ -430,6 +444,11 @@ where
             &mut self,
             event_loop: &winit::event_loop::ActiveEventLoop,
         ) {
+            #[cfg(target_arch = "wasm32")]
+            if !*self.is_booted.borrow() {
+                return;
+            }
+
             self.process_event(
                 event_loop,
                 Event::EventLoopAwakened(winit::event::Event::AboutToWait),
@@ -447,19 +466,6 @@ where
             event_loop: &winit::event_loop::ActiveEventLoop,
             event: Event<Action<Message>>,
         ) {
-            #[cfg(target_arch = "wasm32")]
-            if !*self.is_booted.borrow() {
-                self.queued_events.push(event);
-                return;
-            } else if !self.queued_events.is_empty() {
-                let queued_events = std::mem::take(&mut self.queued_events);
-
-                // This won't infinitely recurse, since we `mem::take`
-                for event in queued_events {
-                    self.process_event(event_loop, event);
-                }
-            }
-
             if event_loop.exiting() {
                 return;
             }
@@ -505,18 +511,27 @@ where
                                 let target =
                                     settings.platform_specific.target.clone();
 
-                                let window = event_loop
-                                    .create_window(
-                                        conversion::window_attributes(
-                                            settings,
-                                            &title,
-                                            monitor
-                                                .or(event_loop
-                                                    .primary_monitor()),
-                                            self.id.clone(),
-                                        )
-                                        .with_visible(false),
+                                let window_attributes =
+                                    conversion::window_attributes(
+                                        settings,
+                                        &title,
+                                        monitor
+                                            .or(event_loop.primary_monitor()),
+                                        self.id.clone(),
                                     )
+                                    .with_visible(false);
+
+                                #[cfg(target_arch = "wasm32")]
+                                let window_attributes = {
+                                    use winit::platform::web::WindowAttributesExtWebSys;
+                                    window_attributes
+                                        .with_canvas(self.canvas.take())
+                                };
+
+                                log::info!("Window attributes for id `{id:#?}`: {window_attributes:#?}");
+
+                                let window = event_loop
+                                    .create_window(window_attributes)
                                     .expect("Create window");
 
                                 #[cfg(target_arch = "wasm32")]


Test patch: