1
0

document.rs 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. use crate::{query::Query, DesktopContext, WeakDesktopContext};
  2. use dioxus_core::prelude::queue_effect;
  3. use dioxus_document::{
  4. create_element_in_head, Document, Eval, EvalError, Evaluator, LinkProps, MetaProps,
  5. ScriptProps, StyleProps,
  6. };
  7. use generational_box::{AnyStorage, GenerationalBox, UnsyncStorage};
  8. /// Code for the Dioxus channel used to communicate between the dioxus and javascript code
  9. pub const NATIVE_EVAL_JS: &str = include_str!("./js/native_eval.js");
  10. /// Represents the desktop-target's provider of evaluators.
  11. #[derive(Clone)]
  12. pub struct DesktopDocument {
  13. pub(crate) desktop_ctx: WeakDesktopContext,
  14. }
  15. impl DesktopDocument {
  16. pub fn new(desktop_ctx: DesktopContext) -> Self {
  17. let desktop_ctx = std::rc::Rc::downgrade(&desktop_ctx);
  18. Self { desktop_ctx }
  19. }
  20. }
  21. impl Document for DesktopDocument {
  22. fn eval(&self, js: String) -> Eval {
  23. Eval::new(DesktopEvaluator::create(
  24. self.desktop_ctx
  25. .upgrade()
  26. .expect("Window to exist when document is alive"),
  27. js,
  28. ))
  29. }
  30. fn set_title(&self, title: String) {
  31. if let Some(ctx) = self.desktop_ctx.upgrade() {
  32. ctx.set_title(&title);
  33. }
  34. }
  35. /// Create a new meta tag in the head
  36. fn create_meta(&self, props: MetaProps) {
  37. let myself = self.clone();
  38. queue_effect(move || {
  39. myself.eval(create_element_in_head("meta", &props.attributes(), None));
  40. });
  41. }
  42. /// Create a new script tag in the head
  43. fn create_script(&self, props: ScriptProps) {
  44. let myself = self.clone();
  45. queue_effect(move || {
  46. myself.eval(create_element_in_head(
  47. "script",
  48. &props.attributes(),
  49. props.script_contents().ok(),
  50. ));
  51. });
  52. }
  53. /// Create a new style tag in the head
  54. fn create_style(&self, props: StyleProps) {
  55. let myself = self.clone();
  56. queue_effect(move || {
  57. myself.eval(create_element_in_head(
  58. "style",
  59. &props.attributes(),
  60. props.style_contents().ok(),
  61. ));
  62. });
  63. }
  64. /// Create a new link tag in the head
  65. fn create_link(&self, props: LinkProps) {
  66. let myself = self.clone();
  67. queue_effect(move || {
  68. myself.eval(create_element_in_head("link", &props.attributes(), None));
  69. });
  70. }
  71. }
  72. /// Represents a desktop-target's JavaScript evaluator.
  73. pub(crate) struct DesktopEvaluator {
  74. query: Query<serde_json::Value>,
  75. }
  76. impl DesktopEvaluator {
  77. /// Creates a new evaluator for desktop-based targets.
  78. pub fn create(desktop_ctx: DesktopContext, js: String) -> GenerationalBox<Box<dyn Evaluator>> {
  79. let query = desktop_ctx.query.new_query(&js, desktop_ctx.clone());
  80. // 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.
  81. let owner = UnsyncStorage::owner();
  82. let query_id = query.id;
  83. let query = owner.insert(Box::new(DesktopEvaluator { query }) as Box<dyn Evaluator>);
  84. desktop_ctx.query.active_requests.slab.borrow_mut()[query_id].owner = Some(owner);
  85. query
  86. }
  87. }
  88. impl Evaluator for DesktopEvaluator {
  89. fn poll_join(
  90. &mut self,
  91. cx: &mut std::task::Context<'_>,
  92. ) -> std::task::Poll<Result<serde_json::Value, EvalError>> {
  93. self.query
  94. .poll_result(cx)
  95. .map_err(|e| EvalError::Communication(e.to_string()))
  96. }
  97. /// Sends a message to the evaluated JavaScript.
  98. fn send(&self, data: serde_json::Value) -> Result<(), EvalError> {
  99. if let Err(e) = self.query.send(data) {
  100. return Err(EvalError::Communication(e.to_string()));
  101. }
  102. Ok(())
  103. }
  104. /// Gets an UnboundedReceiver to receive messages from the evaluated JavaScript.
  105. fn poll_recv(
  106. &mut self,
  107. cx: &mut std::task::Context<'_>,
  108. ) -> std::task::Poll<Result<serde_json::Value, EvalError>> {
  109. self.query
  110. .poll_recv(cx)
  111. .map_err(|e| EvalError::Communication(e.to_string()))
  112. }
  113. }