fix: don't have Rc cycles in DioxusElement or Queries (#3618)

This commit is contained in:
Jonathan Kelley authored and GitHub committed 2025-01-21 14:57:31 -08:00
1 parent d42b1d5146
commit d8850c7666
3 files changed
+26 -20

No files matched your search

+1 -2
View File
@@ -88,8 +88,7 @@ pub(crate) struct DesktopEvaluator {
impl DesktopEvaluator { impl DesktopEvaluator {
/// Creates a new evaluator for desktop-based targets. /// Creates a new evaluator for desktop-based targets.
pub fn create(desktop_ctx: DesktopContext, js: String) -> GenerationalBox<Box<dyn Evaluator>> { pub fn create(desktop_ctx: DesktopContext, js: String) -> GenerationalBox<Box<dyn Evaluator>> {
let ctx = desktop_ctx.clone(); let query = desktop_ctx.query.new_query(&js, desktop_ctx.clone());
let query = desktop_ctx.query.new_query(&js, ctx);
// We create a generational box that is owned by the query slot so that when we drop the query slot, the generational box is also dropped. // We create a generational box that is owned by the query slot so that when we drop the query slot, the generational box is also dropped.
let owner = UnsyncStorage::owner(); let owner = UnsyncStorage::owner();
+20 -14
View File
@@ -1,21 +1,24 @@
use std::rc::Rc;
use dioxus_core::ElementId; use dioxus_core::ElementId;
use dioxus_html::{ use dioxus_html::{
geometry::{PixelsRect, PixelsSize, PixelsVector2D}, geometry::{PixelsRect, PixelsSize, PixelsVector2D},
MountedResult, RenderedElementBacking, MountedResult, RenderedElementBacking,
}; };
use crate::{desktop_context::DesktopContext, query::QueryEngine}; use crate::{desktop_context::DesktopContext, query::QueryEngine, WeakDesktopContext};
#[derive(Clone)] #[derive(Clone)]
/// A mounted element passed to onmounted events /// A mounted element passed to onmounted events
pub struct DesktopElement { pub struct DesktopElement {
id: ElementId, id: ElementId,
webview: DesktopContext, webview: WeakDesktopContext,
query: QueryEngine, query: QueryEngine,
} }
impl DesktopElement { impl DesktopElement {
pub(crate) fn new(id: ElementId, webview: DesktopContext, query: QueryEngine) -> Self { pub(crate) fn new(id: ElementId, webview: DesktopContext, query: QueryEngine) -> Self {
let webview = Rc::downgrade(&webview);
Self { id, webview, query } Self { id, webview, query }
} }
} }
@@ -28,10 +31,13 @@ macro_rules! scripted_getter {
Box<dyn futures_util::Future<Output = dioxus_html::MountedResult<$output_type>>>, Box<dyn futures_util::Future<Output = dioxus_html::MountedResult<$output_type>>>,
> { > {
let script = format!($script, id = self.id.0); let script = format!($script, id = self.id.0);
let webview = self
.webview
.upgrade()
.expect("Webview should be alive if the element is being queried");
let fut = self let fut = self
.query .query
.new_query::<Option<$output_type>>(&script, self.webview.clone()) .new_query::<Option<$output_type>>(&script, webview)
.resolve(); .resolve();
Box::pin(async move { Box::pin(async move {
match fut.await { match fut.await {
@@ -80,11 +86,11 @@ impl RenderedElementBacking for DesktopElement {
self.id.0, self.id.0,
serde_json::to_string(&behavior).expect("Failed to serialize ScrollBehavior") serde_json::to_string(&behavior).expect("Failed to serialize ScrollBehavior")
); );
let webview = self
let fut = self .webview
.query .upgrade()
.new_query::<bool>(&script, self.webview.clone()) .expect("Webview should be alive if the element is being queried");
.resolve(); let fut = self.query.new_query::<bool>(&script, webview).resolve();
Box::pin(async move { Box::pin(async move {
match fut.await { match fut.await {
Ok(true) => Ok(()), Ok(true) => Ok(()),
@@ -106,11 +112,11 @@ impl RenderedElementBacking for DesktopElement {
"return window.interpreter.setFocus({}, {});", "return window.interpreter.setFocus({}, {});",
self.id.0, focus self.id.0, focus
); );
let webview = self
let fut = self .webview
.query .upgrade()
.new_query::<bool>(&script, self.webview.clone()) .expect("Webview should be alive if the element is being queried");
.resolve(); let fut = self.query.new_query::<bool>(&script, webview).resolve();
Box::pin(async move { Box::pin(async move {
match fut.await { match fut.await {
+5 -4
View File
@@ -1,4 +1,4 @@
use crate::DesktopContext; use crate::{DesktopContext, WeakDesktopContext};
use futures_util::{FutureExt, StreamExt}; use futures_util::{FutureExt, StreamExt};
use generational_box::Owner; use generational_box::Owner;
use serde::{de::DeserializeOwned, Deserialize}; use serde::{de::DeserializeOwned, Deserialize};
@@ -107,7 +107,7 @@ impl QueryEngine {
id: request_id, id: request_id,
receiver: rx, receiver: rx,
return_receiver: Some(return_rx), return_receiver: Some(return_rx),
desktop: context, desktop: Rc::downgrade(&context),
phantom: std::marker::PhantomData, phantom: std::marker::PhantomData,
} }
} }
@@ -140,7 +140,7 @@ impl QueryEngine {
} }
pub(crate) struct Query<V: DeserializeOwned> { pub(crate) struct Query<V: DeserializeOwned> {
desktop: DesktopContext, desktop: WeakDesktopContext,
receiver: futures_channel::mpsc::UnboundedReceiver<Value>, receiver: futures_channel::mpsc::UnboundedReceiver<Value>,
return_receiver: Option<futures_channel::oneshot::Receiver<Result<Value, String>>>, return_receiver: Option<futures_channel::oneshot::Receiver<Result<Value, String>>>,
pub id: usize, pub id: usize,
@@ -161,7 +161,8 @@ impl<V: DeserializeOwned> Query<V> {
let data = message.to_string(); let data = message.to_string();
let script = format!(r#"window.getQuery({queue_id}).rustSend({data});"#); let script = format!(r#"window.getQuery({queue_id}).rustSend({data});"#);
self.desktop let desktop = self.desktop.upgrade().ok_or(QueryError::Finished)?;
desktop
.webview .webview
.evaluate_script(&script) .evaluate_script(&script)
.map_err(|e| QueryError::Send(e.to_string()))?; .map_err(|e| QueryError::Send(e.to_string()))?;