1use crate::result::*;
12use crate::utils::*;
13use js_sys::{Array, Function, Uint8Array};
14use web_sys::Element;
15use web_sys::{Blob, Url};
16use workflow_core::channel::oneshot;
17use workflow_wasm::callback::*;
18
19pub type CustomEventCallback = Callback<CallbackClosureWithoutResult<web_sys::CustomEvent>>;
20
21pub enum Content<'content> {
24 Script(Option<&'content str>, &'content [u8]),
26 Module(Option<&'content str>, &'content [u8]),
28 Style(Option<&'content str>, &'content [u8]),
30}
31
32pub fn inject_css(id: Option<&str>, css: &str) -> Result<()> {
36 let doc = document();
37 let head = doc
38 .get_elements_by_tag_name("head")
39 .item(0)
40 .ok_or("Unable to locate head element")?;
41
42 let style_el = if let Some(id) = id {
43 if let Some(old_el) = doc.get_element_by_id(id) {
44 old_el
45 } else {
46 let style_el = doc.create_element("style")?;
47 style_el.set_attribute("id", id)?;
48 head.append_child(&style_el)?;
49 style_el
50 }
51 } else {
52 let style_el = doc.create_element("style")?;
53 head.append_child(&style_el)?;
54 style_el
55 };
56
57 style_el.set_inner_html(css);
58 Ok(())
59}
60
61pub fn inject_blob_nowait(content: Content) -> Result<()> {
65 inject_blob_with_callback::<CustomEventCallback>(content, None)
66}
67
68pub async fn inject_blob(content: Content<'_>) -> Result<()> {
73 let (sender, receiver) = oneshot();
74 let callback = callback!(move |event: web_sys::CustomEvent| {
75 sender
76 .try_send(event)
77 .expect("inject_blob_with_callback(): unable to send load notification");
78 });
79 inject_blob_with_callback(content, Some(&callback))?;
80 let _notification = receiver.recv().await?;
81 Ok(())
82}
83
84pub fn inject_script<C>(
89 root: Element,
90 id: Option<&str>,
91 content: &[u8],
92 content_type: &str,
93 callback: Option<&C>,
94) -> Result<()>
95where
96 C: AsRef<Function>,
97{
98 let doc = document();
99 let string = String::from_utf8_lossy(content);
100 let regex = regex::Regex::new(r"//# sourceMappingURL.*$").unwrap();
101 let content = regex.replace(&string, "");
102
103 let args = Array::new_with_length(1);
104 args.set(0, unsafe { Uint8Array::view(content.as_bytes()).into() });
105 let options = web_sys::BlobPropertyBag::new();
106 options.set_type("application/javascript");
107 let blob = Blob::new_with_u8_array_sequence_and_options(&args, &options)?;
108 let url = Url::create_object_url_with_blob(&blob)?;
109
110 let script = doc.create_element("script")?;
111 if let Some(callback) = callback {
112 script.add_event_listener_with_callback("load", callback.as_ref())?;
113 }
114 if let Some(id) = id {
115 script.set_attribute("id", id)?;
116 }
117 script.set_attribute("type", content_type)?;
118 script.set_attribute("src", &url)?;
119 root.append_child(&script)?;
120
121 Ok(())
122}
123
124pub fn inject_stylesheet<C>(
125 root: Element,
126 id: Option<&str>,
127 content: &[u8],
128 callback: Option<&C>,
129) -> Result<()>
130where
131 C: AsRef<Function>,
132{
133 let args = Array::new_with_length(1);
134 args.set(0, unsafe { Uint8Array::view(content).into() });
135 let blob = Blob::new_with_u8_array_sequence(&args)?;
136 let url = Url::create_object_url_with_blob(&blob)?;
137
138 let style = document().create_element("link")?;
139 if let Some(callback) = callback {
140 style.add_event_listener_with_callback("load", callback.as_ref())?;
141 }
143 if let Some(id) = id {
144 style.set_attribute("id", id)?;
145 }
146 style.set_attribute("type", "text/css")?;
147 style.set_attribute("rel", "stylesheet")?;
148 style.set_attribute("href", &url)?;
149 root.append_child(&style)?;
150 Ok(())
151}
152
153pub fn inject_blob_with_callback<C>(content: Content, callback: Option<&C>) -> Result<()>
157where
159 C: AsRef<Function>,
160{
161 let doc = document();
162 let root = {
163 let collection = doc.get_elements_by_tag_name("head");
164 if collection.length() > 0 {
165 collection.item(0).unwrap()
166 } else {
167 doc.get_elements_by_tag_name("body").item(0).unwrap()
168 }
169 };
170
171 match content {
172 Content::Script(id, content) => {
173 inject_script(root, id, content, "text/javascript", callback)?;
174 }
175 Content::Module(id, content) => {
176 inject_script(root, id, content, "module", callback)?;
177 }
178 Content::Style(id, content) => {
179 inject_stylesheet(root, id, content, callback)?;
180 }
181 }
182
183 Ok(())
184}