1use std::cell::Cell;
8use std::collections::HashMap;
9use std::path::Path;
10use std::rc::Rc;
11use std::time::Instant;
12use std::{env, fs};
13
14use ::servo::ServoBuilder;
15use constellation_traits::EmbedderToConstellationMessage;
16use crossbeam_channel::unbounded;
17use euclid::{Point2D, Vector2D};
18use ipc_channel::ipc;
19use log::{info, trace, warn};
20use net::protocols::ProtocolRegistry;
21use servo::config::opts::Opts;
22use servo::config::prefs::Preferences;
23use servo::servo_url::ServoUrl;
24use servo::user_content_manager::{UserContentManager, UserScript};
25use servo::webrender_api::ScrollLocation;
26use servo::{
27 EventLoopWaker, ImeEvent, InputEvent, KeyboardEvent, MouseButtonEvent, MouseMoveEvent,
28 WebDriverCommandMsg, WebDriverScriptCommand, WebDriverUserPromptAction, WheelDelta, WheelEvent,
29 WheelMode,
30};
31use url::Url;
32use winit::application::ApplicationHandler;
33use winit::event::WindowEvent;
34use winit::event_loop::{ActiveEventLoop, ControlFlow};
35use winit::window::WindowId;
36
37use super::app_state::AppState;
38use super::events_loop::{AppEvent, EventLoopProxy, EventsLoop};
39use super::minibrowser::{Minibrowser, MinibrowserEvent};
40use super::{headed_window, headless_window};
41use crate::desktop::app_state::RunningAppState;
42use crate::desktop::protocols;
43use crate::desktop::tracing::trace_winit_event;
44use crate::desktop::window_trait::WindowPortsMethods;
45use crate::parser::{get_default_url, location_bar_input_to_url};
46use crate::prefs::ServoShellPreferences;
47
48pub struct App {
49 opts: Opts,
50 preferences: Preferences,
51 servoshell_preferences: ServoShellPreferences,
52 suspended: Cell<bool>,
53 minibrowser: Option<Minibrowser>,
54 waker: Box<dyn EventLoopWaker>,
55 proxy: Option<EventLoopProxy>,
56 initial_url: ServoUrl,
57 t_start: Instant,
58 t: Instant,
59 state: AppState,
60
61 windows: HashMap<WindowId, Rc<dyn WindowPortsMethods>>,
65}
66
67pub(crate) enum PumpResult {
69 Shutdown,
71 Continue {
72 need_update: bool,
73 need_window_redraw: bool,
74 },
75}
76
77impl App {
78 pub fn new(
79 opts: Opts,
80 preferences: Preferences,
81 servo_shell_preferences: ServoShellPreferences,
82 events_loop: &EventsLoop,
83 ) -> Self {
84 let initial_url = get_default_url(
85 servo_shell_preferences.url.as_deref(),
86 env::current_dir().unwrap(),
87 |path| fs::metadata(path).is_ok(),
88 &servo_shell_preferences,
89 );
90
91 let t = Instant::now();
92 App {
93 opts,
94 preferences,
95 servoshell_preferences: servo_shell_preferences,
96 suspended: Cell::new(false),
97 windows: HashMap::new(),
98 minibrowser: None,
99 waker: events_loop.create_event_loop_waker(),
100 proxy: events_loop.event_loop_proxy(),
101 initial_url: initial_url.clone(),
102 t_start: t,
103 t,
104 state: AppState::Initializing,
105 }
106 }
107
108 pub fn init(&mut self, event_loop: Option<&ActiveEventLoop>) {
110 let headless = self.servoshell_preferences.headless;
111
112 assert_eq!(headless, event_loop.is_none());
113 let window = match event_loop {
114 Some(event_loop) => {
115 let proxy = self.proxy.take().expect("Must have a proxy available");
116 let window = headed_window::Window::new(&self.servoshell_preferences, event_loop);
117 self.minibrowser = Some(Minibrowser::new(
118 &window,
119 event_loop,
120 proxy,
121 self.initial_url.clone(),
122 ));
123 Rc::new(window)
124 },
125 None => headless_window::Window::new(&self.servoshell_preferences),
126 };
127
128 self.windows.insert(window.id(), window);
129
130 self.suspended.set(false);
131 let (_, window) = self.windows.iter().next().unwrap();
132
133 let mut user_content_manager = UserContentManager::new();
134 for script in load_userscripts(self.servoshell_preferences.userscripts_directory.as_deref())
135 .expect("Loading userscripts failed")
136 {
137 user_content_manager.add_script(script);
138 }
139
140 let mut protocol_registry = ProtocolRegistry::default();
141 let _ = protocol_registry.register(
142 "urlinfo",
143 protocols::urlinfo::UrlInfoProtocolHander::default(),
144 );
145 let _ =
146 protocol_registry.register("servo", protocols::servo::ServoProtocolHandler::default());
147 let _ = protocol_registry.register(
148 "resource",
149 protocols::resource::ResourceProtocolHandler::default(),
150 );
151
152 let servo_builder = ServoBuilder::new(window.rendering_context())
153 .opts(self.opts.clone())
154 .preferences(self.preferences.clone())
155 .user_content_manager(user_content_manager)
156 .protocol_registry(protocol_registry)
157 .event_loop_waker(self.waker.clone());
158
159 #[cfg(feature = "webxr")]
160 let servo_builder =
161 servo_builder.webxr_registry(super::webxr::XrDiscoveryWebXrRegistry::new_boxed(
162 window.clone(),
163 event_loop,
164 &self.preferences,
165 ));
166
167 let servo = servo_builder.build();
168 servo.setup_logging();
169
170 let webdriver_receiver = self.servoshell_preferences.webdriver_port.map(|port| {
172 let (embedder_sender, embedder_receiver) = unbounded();
173 let (webdriver_response_sender, webdriver_response_receiver) = ipc::channel().unwrap();
174
175 servo
178 .constellation_sender()
179 .send(EmbedderToConstellationMessage::SetWebDriverResponseSender(
180 webdriver_response_sender,
181 ))
182 .expect("Failed to set WebDriver response sender in constellation when initing");
183
184 webdriver_server::start_server(
185 port,
186 embedder_sender,
187 self.waker.clone(),
188 webdriver_response_receiver,
189 );
190
191 embedder_receiver
192 });
193
194 let running_state = Rc::new(RunningAppState::new(
195 servo,
196 window.clone(),
197 self.servoshell_preferences.clone(),
198 webdriver_receiver,
199 ));
200 running_state.create_and_focus_toplevel_webview(self.initial_url.clone().into_url());
201 if let Some(ref mut minibrowser) = self.minibrowser {
202 minibrowser.update(window.as_ref(), &running_state, "init");
203 }
204
205 self.state = AppState::Running(running_state);
206 }
207
208 pub(crate) fn animating(&self) -> bool {
209 match self.state {
210 AppState::Initializing => false,
211 AppState::Running(ref running_app_state) => running_app_state.servo().animating(),
212 AppState::ShuttingDown => false,
213 }
214 }
215
216 pub fn handle_events_with_winit(
218 &mut self,
219 event_loop: &ActiveEventLoop,
220 window: Rc<dyn WindowPortsMethods>,
221 ) {
222 let AppState::Running(state) = &self.state else {
223 return;
224 };
225
226 match state.pump_event_loop() {
227 PumpResult::Shutdown => {
228 state.shutdown();
229 self.state = AppState::ShuttingDown;
230 },
231 PumpResult::Continue {
232 need_update: update,
233 need_window_redraw,
234 } => {
235 let updated = match (update, &mut self.minibrowser) {
236 (true, Some(minibrowser)) => minibrowser.update_webview_data(state),
237 _ => false,
238 };
239
240 if updated || need_window_redraw {
242 if let Some(window) = window.winit_window() {
243 window.request_redraw();
244 }
245 }
246 },
247 }
248
249 if matches!(self.state, AppState::ShuttingDown) {
250 event_loop.exit();
251 }
252 }
253
254 pub fn handle_events_with_headless(&mut self) -> bool {
257 let now = Instant::now();
258 let event = winit::event::Event::UserEvent(AppEvent::Waker);
259 trace_winit_event!(
260 event,
261 "@{:?} (+{:?}) {event:?}",
262 now - self.t_start,
263 now - self.t
264 );
265 self.t = now;
266
267 let AppState::Running(state) = &self.state else {
269 return false;
270 };
271
272 match state.pump_event_loop() {
273 PumpResult::Shutdown => {
274 state.shutdown();
275 self.state = AppState::ShuttingDown;
276 },
277 PumpResult::Continue { .. } => state.repaint_servo_if_necessary(),
278 }
279
280 !matches!(self.state, AppState::ShuttingDown)
281 }
282
283 fn handle_servoshell_ui_events(&mut self) {
285 let Some(minibrowser) = self.minibrowser.as_ref() else {
286 return;
287 };
288 let AppState::Running(state) = &self.state else {
290 return;
291 };
292
293 for event in minibrowser.take_events() {
294 match event {
295 MinibrowserEvent::Go(location) => {
296 minibrowser.update_location_dirty(false);
297 let Some(url) = location_bar_input_to_url(
298 &location.clone(),
299 &self.servoshell_preferences.searchpage,
300 ) else {
301 warn!("failed to parse location");
302 break;
303 };
304 if let Some(focused_webview) = state.focused_webview() {
305 focused_webview.load(url.into_url());
306 }
307 },
308 MinibrowserEvent::Back => {
309 if let Some(focused_webview) = state.focused_webview() {
310 focused_webview.go_back(1);
311 }
312 },
313 MinibrowserEvent::Forward => {
314 if let Some(focused_webview) = state.focused_webview() {
315 focused_webview.go_forward(1);
316 }
317 },
318 MinibrowserEvent::Reload => {
319 minibrowser.update_location_dirty(false);
320 if let Some(focused_webview) = state.focused_webview() {
321 focused_webview.reload();
322 }
323 },
324 MinibrowserEvent::NewWebView => {
325 minibrowser.update_location_dirty(false);
326 state.create_and_focus_toplevel_webview(Url::parse("servo:newtab").unwrap());
327 },
328 MinibrowserEvent::CloseWebView(id) => {
329 minibrowser.update_location_dirty(false);
330 state.close_webview(id);
331 },
332 }
333 }
334 }
335
336 pub fn handle_webdriver_messages(&self) {
337 let AppState::Running(running_state) = &self.state else {
338 return;
339 };
340
341 let Some(webdriver_receiver) = running_state.webdriver_receiver() else {
342 return;
343 };
344
345 while let Ok(msg) = webdriver_receiver.try_recv() {
346 match msg {
347 WebDriverCommandMsg::SetWebDriverResponseSender(..) => {
348 running_state.servo().execute_webdriver_command(msg);
349 },
350 WebDriverCommandMsg::IsWebViewOpen(webview_id, sender) => {
351 let context = running_state.webview_by_id(webview_id);
352
353 if let Err(error) = sender.send(context.is_some()) {
354 warn!("Failed to send response of IsWebViewOpen: {error}");
355 }
356 },
357 WebDriverCommandMsg::IsBrowsingContextOpen(..) => {
358 running_state.servo().execute_webdriver_command(msg);
359 },
360 WebDriverCommandMsg::NewWebView(response_sender, load_status_sender) => {
361 let new_webview =
362 running_state.create_toplevel_webview(Url::parse("about:blank").unwrap());
363
364 if let Err(error) = response_sender.send(new_webview.id()) {
365 warn!("Failed to send response of NewWebview: {error}");
366 }
367 if let Some(load_status_sender) = load_status_sender {
368 running_state.set_load_status_sender(new_webview.id(), load_status_sender);
369 }
370 },
371 WebDriverCommandMsg::CloseWebView(webview_id, response_sender) => {
372 running_state.close_webview(webview_id);
373 if let Err(error) = response_sender.send(()) {
374 warn!("Failed to send response of CloseWebView: {error}");
375 }
376 },
377 WebDriverCommandMsg::FocusWebView(webview_id, response_sender) => {
378 if let Some(webview) = running_state.webview_by_id(webview_id) {
379 let focus_id = webview.focus_and_raise_to_top(true);
380 running_state.set_pending_focus(focus_id, response_sender);
381 }
382 },
383 WebDriverCommandMsg::GetAllWebViews(response_sender) => {
384 let webviews = running_state.webviews().iter().map(|(id, _)| *id).collect();
385
386 if let Err(error) = response_sender.send(webviews) {
387 warn!("Failed to send response of GetAllWebViews: {error}");
388 }
389 },
390 WebDriverCommandMsg::GetWindowRect(_webview_id, response_sender) => {
391 let window = self
392 .windows
393 .values()
394 .next()
395 .expect("Should have at least one window in servoshell");
396
397 if let Err(error) = response_sender.send(window.window_rect()) {
398 warn!("Failed to send response of GetWindowSize: {error}");
399 }
400 },
401 WebDriverCommandMsg::MaximizeWebView(webview_id, response_sender) => {
402 let window = self
403 .windows
404 .values()
405 .next()
406 .expect("Should have at least one window in servoshell");
407 window.maximize(
408 &running_state
409 .webview_by_id(webview_id)
410 .expect("Webview must exists as we just verified"),
411 );
412
413 if let Err(error) = response_sender.send(window.window_rect()) {
414 warn!("Failed to send response of GetWindowSize: {error}");
415 }
416 },
417 WebDriverCommandMsg::SetWindowRect(webview_id, requested_rect, size_sender) => {
418 let Some(webview) = running_state.webview_by_id(webview_id) else {
419 continue;
420 };
421
422 let window = self
423 .windows
424 .values()
425 .next()
426 .expect("Should have at least one window in servoshell");
427 let scale = window.hidpi_scale_factor();
428
429 let requested_physical_rect =
430 (requested_rect.to_f32() * scale).round().to_i32();
431
432 window.request_resize(&webview, requested_physical_rect.size());
434
435 window.set_position(requested_physical_rect.min);
437
438 if let Err(error) = size_sender.send(window.window_rect()) {
439 warn!("Failed to send window size: {error}");
440 }
441 },
442 WebDriverCommandMsg::GetViewportSize(_webview_id, response_sender) => {
443 let window = self
444 .windows
445 .values()
446 .next()
447 .expect("Should have at least one window in servoshell");
448
449 let size = window.rendering_context().size2d();
450
451 if let Err(error) = response_sender.send(size) {
452 warn!("Failed to send response of GetViewportSize: {error}");
453 }
454 },
455 WebDriverCommandMsg::GetFocusedWebView(sender) => {
457 let focused_webview = running_state.focused_webview();
458
459 if let Err(error) = sender.send(focused_webview.map(|w| w.id())) {
460 warn!("Failed to send response of GetFocusedWebView: {error}");
461 };
462 },
463 WebDriverCommandMsg::LoadUrl(webview_id, url, load_status_sender) => {
464 if let Some(webview) = running_state.webview_by_id(webview_id) {
465 running_state.set_load_status_sender(webview_id, load_status_sender);
466 webview.load(url.into_url());
467 }
468 },
469 WebDriverCommandMsg::Refresh(webview_id, load_status_sender) => {
470 if let Some(webview) = running_state.webview_by_id(webview_id) {
471 running_state.set_load_status_sender(webview_id, load_status_sender);
472 webview.reload();
473 }
474 },
475 WebDriverCommandMsg::GoBack(webview_id, load_status_sender) => {
476 if let Some(webview) = running_state.webview_by_id(webview_id) {
477 let traversal_id = webview.go_back(1);
478 running_state.set_pending_traversal(traversal_id, load_status_sender);
479 }
480 },
481 WebDriverCommandMsg::GoForward(webview_id, load_status_sender) => {
482 if let Some(webview) = running_state.webview_by_id(webview_id) {
483 let traversal_id = webview.go_forward(1);
484 running_state.set_pending_traversal(traversal_id, load_status_sender);
485 }
486 },
487 WebDriverCommandMsg::DispatchComposition(webview_id, composition_event) => {
489 if let Some(webview) = running_state.webview_by_id(webview_id) {
490 webview.notify_input_event(InputEvent::Ime(ImeEvent::Composition(
491 composition_event,
492 )));
493 }
494 },
495 WebDriverCommandMsg::KeyboardAction(webview_id, key_event, msg_id) => {
496 if let Some(webview) = running_state.webview_by_id(webview_id) {
498 webview.notify_input_event(
499 InputEvent::Keyboard(KeyboardEvent::new(key_event))
500 .with_webdriver_message_id(msg_id),
501 );
502 }
503 },
504 WebDriverCommandMsg::MouseButtonAction(
505 webview_id,
506 mouse_event_type,
507 mouse_button,
508 x,
509 y,
510 webdriver_message_id,
511 ) => {
512 if let Some(webview) = running_state.webview_by_id(webview_id) {
513 webview.notify_input_event(
514 InputEvent::MouseButton(MouseButtonEvent::new(
515 mouse_event_type,
516 mouse_button,
517 Point2D::new(x, y),
518 ))
519 .with_webdriver_message_id(webdriver_message_id),
520 );
521 }
522 },
523 WebDriverCommandMsg::MouseMoveAction(webview_id, x, y, webdriver_message_id) => {
524 if let Some(webview) = running_state.webview_by_id(webview_id) {
525 webview.notify_input_event(
526 InputEvent::MouseMove(MouseMoveEvent::new(Point2D::new(x, y)))
527 .with_webdriver_message_id(webdriver_message_id),
528 );
529 }
530 },
531 WebDriverCommandMsg::WheelScrollAction(
532 webview_id,
533 x,
534 y,
535 dx,
536 dy,
537 webdriver_message_id,
538 ) => {
539 if let Some(webview) = running_state.webview_by_id(webview_id) {
540 let delta = WheelDelta {
541 x: -dx,
542 y: -dy,
543 z: 0.0,
544 mode: WheelMode::DeltaPixel,
545 };
546
547 let point = Point2D::new(x, y);
548 let scroll_location =
549 ScrollLocation::Delta(Vector2D::new(dx as f32, dy as f32));
550
551 webview.notify_input_event(
552 InputEvent::Wheel(WheelEvent::new(delta, point.to_f32()))
553 .with_webdriver_message_id(webdriver_message_id),
554 );
555
556 webview.notify_scroll_event(scroll_location, point.to_i32());
557 }
558 },
559 WebDriverCommandMsg::ScriptCommand(_, ref webdriver_script_command) => {
560 self.handle_webdriver_script_command(webdriver_script_command, running_state);
561 running_state.servo().execute_webdriver_command(msg);
562 },
563 WebDriverCommandMsg::CurrentUserPrompt(webview_id, response_sender) => {
564 let current_dialog =
565 running_state.get_current_active_dialog_webdriver_type(webview_id);
566 if let Err(error) = response_sender.send(current_dialog) {
567 warn!("Failed to send response of CurrentUserPrompt: {error}");
568 };
569 },
570 WebDriverCommandMsg::HandleUserPrompt(webview_id, action, response_sender) => {
571 let response = if running_state.webview_has_active_dialog(webview_id) {
572 let alert_text = running_state.alert_text_of_newest_dialog(webview_id);
573
574 match action {
575 WebDriverUserPromptAction::Accept => {
576 running_state.accept_active_dialogs(webview_id)
577 },
578 WebDriverUserPromptAction::Dismiss => {
579 running_state.dismiss_active_dialogs(webview_id)
580 },
581 WebDriverUserPromptAction::Ignore => {},
582 };
583
584 Ok(alert_text)
586 } else {
587 Err(())
590 };
591
592 if let Err(error) = response_sender.send(response) {
593 warn!("Failed to send response of HandleUserPrompt: {error}");
594 };
595 },
596 WebDriverCommandMsg::GetAlertText(webview_id, response_sender) => {
597 let response = match running_state.alert_text_of_newest_dialog(webview_id) {
598 Some(text) => Ok(text),
599 None => Err(()),
600 };
601
602 if let Err(error) = response_sender.send(response) {
603 warn!("Failed to send response of GetAlertText: {error}");
604 };
605 },
606 WebDriverCommandMsg::SendAlertText(webview_id, text) => {
607 running_state.set_alert_text_of_newest_dialog(webview_id, text);
608 },
609 WebDriverCommandMsg::TakeScreenshot(..) => {
610 running_state.servo().execute_webdriver_command(msg);
611 },
612 };
613 }
614 }
615
616 fn handle_webdriver_script_command(
617 &self,
618 msg: &WebDriverScriptCommand,
619 running_state: &RunningAppState,
620 ) {
621 match msg {
622 WebDriverScriptCommand::ExecuteScript(_webview_id, response_sender) |
623 WebDriverScriptCommand::ExecuteAsyncScript(_webview_id, response_sender) => {
624 running_state.set_script_command_interrupt_sender(Some(response_sender.clone()));
628 },
629 WebDriverScriptCommand::AddLoadStatusSender(webview_id, load_status_sender) => {
630 running_state.set_load_status_sender(*webview_id, load_status_sender.clone());
631 },
632 WebDriverScriptCommand::RemoveLoadStatusSender(webview_id) => {
633 running_state.remove_load_status_sender(*webview_id);
634 },
635 _ => {
636 running_state.set_script_command_interrupt_sender(None);
637 },
638 }
639 }
640}
641
642impl ApplicationHandler<AppEvent> for App {
643 fn resumed(&mut self, event_loop: &ActiveEventLoop) {
644 self.init(Some(event_loop));
645 }
646
647 fn window_event(
648 &mut self,
649 event_loop: &ActiveEventLoop,
650 window_id: WindowId,
651 event: WindowEvent,
652 ) {
653 let now = Instant::now();
654 trace_winit_event!(
655 event,
656 "@{:?} (+{:?}) {event:?}",
657 now - self.t_start,
658 now - self.t
659 );
660 self.t = now;
661
662 let AppState::Running(state) = &self.state else {
663 return;
664 };
665
666 let Some(window) = self.windows.get(&window_id) else {
667 return;
668 };
669
670 let window = window.clone();
671 if event == WindowEvent::RedrawRequested {
672 trace!("RedrawRequested");
674
675 if let Some(ref mut minibrowser) = self.minibrowser {
678 minibrowser.update(window.as_ref(), state, "RedrawRequested");
679 minibrowser.paint(window.winit_window().unwrap());
680 }
681 }
682
683 let mut consumed = false;
685 if let Some(ref mut minibrowser) = self.minibrowser {
686 match event {
687 WindowEvent::ScaleFactorChanged { scale_factor, .. } => {
688 let desired_scale_factor = window.hidpi_scale_factor().get();
692 let effective_egui_zoom_factor = desired_scale_factor / scale_factor as f32;
693
694 info!(
695 "window scale factor changed to {}, setting egui zoom factor to {}",
696 scale_factor, effective_egui_zoom_factor
697 );
698
699 minibrowser
700 .context
701 .egui_ctx
702 .set_zoom_factor(effective_egui_zoom_factor);
703
704 state.hidpi_scale_factor_changed();
705
706 window.winit_window().unwrap().request_redraw();
709 },
710 ref event => {
711 let response =
712 minibrowser.on_window_event(window.winit_window().unwrap(), state, event);
713 if let WindowEvent::Resized(_) = event {
715 minibrowser.update(
716 window.as_ref(),
717 state,
718 "Sync WebView size with Window Resize event",
719 );
720 }
721 if response.repaint && *event != WindowEvent::RedrawRequested {
722 window.winit_window().unwrap().request_redraw();
725 }
726
727 consumed = response.consumed;
730 },
731 }
732 }
733 if !consumed {
734 window.handle_winit_event(state.clone(), event);
735 }
736
737 if !self.animating() || self.suspended.get() {
739 event_loop.set_control_flow(ControlFlow::Wait);
740 } else {
741 event_loop.set_control_flow(ControlFlow::Poll);
742 }
743
744 self.handle_servoshell_ui_events();
746
747 self.handle_events_with_winit(event_loop, window);
748 }
749
750 fn user_event(&mut self, event_loop: &ActiveEventLoop, event: AppEvent) {
751 if let AppEvent::Accessibility(ref event) = event {
752 let Some(ref mut minibrowser) = self.minibrowser else {
753 return;
754 };
755 if !minibrowser.handle_accesskit_event(&event.window_event) {
756 return;
757 }
758 if let Some(window) = self.windows.get(&event.window_id) {
759 window.winit_window().unwrap().request_redraw();
760 }
761 return;
762 }
763
764 let now = Instant::now();
765 let event = winit::event::Event::UserEvent(event);
766 trace_winit_event!(
767 event,
768 "@{:?} (+{:?}) {event:?}",
769 now - self.t_start,
770 now - self.t
771 );
772 self.t = now;
773
774 if !matches!(self.state, AppState::Running(..)) {
775 return;
776 };
777 let Some(window) = self.windows.values().next() else {
778 return;
779 };
780 let window = window.clone();
781
782 if !self.animating() || self.suspended.get() {
784 event_loop.set_control_flow(ControlFlow::Wait);
785 } else {
786 event_loop.set_control_flow(ControlFlow::Poll);
787 }
788
789 self.handle_servoshell_ui_events();
791
792 self.handle_webdriver_messages();
794
795 self.handle_events_with_winit(event_loop, window);
796 }
797
798 fn suspended(&mut self, _: &ActiveEventLoop) {
799 self.suspended.set(true);
800 }
801}
802
803fn load_userscripts(userscripts_directory: Option<&Path>) -> std::io::Result<Vec<UserScript>> {
804 let mut userscripts = Vec::new();
805 if let Some(userscripts_directory) = &userscripts_directory {
806 let mut files = std::fs::read_dir(userscripts_directory)?
807 .map(|e| e.map(|entry| entry.path()))
808 .collect::<Result<Vec<_>, _>>()?;
809 files.sort_unstable();
810 for file in files {
811 userscripts.push(UserScript {
812 script: std::fs::read_to_string(&file)?,
813 source_file: Some(file),
814 });
815 }
816 }
817 Ok(userscripts)
818}