Allow specifying the window to create elements in

This commit is contained in:
Jeremy Wall 2022-10-09 15:51:31 -04:00
parent ff6e9a5079
commit 7ffcab1416
3 changed files with 89 additions and 35 deletions

View File

@ -126,12 +126,10 @@ fn expand_struct_trait_shim(struct_name: &Ident, observed_attrs: Literal) -> syn
connectedCallback() {{ connectedCallback() {{
this._impl.connected_impl(this); this._impl.connected_impl(this);
console.log(this.textContent);
}} }}
disconnectedCallback() {{ disconnectedCallback() {{
this._impl.disconnected_impl(this); this._impl.disconnected_impl(this);
console.log(this.textContent);
}} }}
static get observedAttributes() {{ static get observedAttributes() {{
@ -139,7 +137,6 @@ fn expand_struct_trait_shim(struct_name: &Ident, observed_attrs: Literal) -> syn
}} }}
adoptedCallback() {{ adoptedCallback() {{
console.log('In adoptedCallback');
this._impl.adopted_impl(this); this._impl.adopted_impl(this);
}} }}
@ -185,16 +182,6 @@ fn expand_wasm_shim(struct_name: &Ident) -> syn::ItemImpl {
Self::default() Self::default()
} }
#[wasm_bindgen::prelude::wasm_bindgen]
pub fn create() -> web_sys::Element {
window()
.unwrap()
.document()
.unwrap()
.create_element(Self::element_name())
.unwrap()
}
#[wasm_bindgen::prelude::wasm_bindgen] #[wasm_bindgen::prelude::wasm_bindgen]
pub fn connected_impl(&self, element: &web_sys::HtmlElement) { pub fn connected_impl(&self, element: &web_sys::HtmlElement) {
use #trait_path; use #trait_path;

View File

@ -42,4 +42,5 @@ features = [
"ShadowRootInit", "ShadowRootInit",
"ShadowRootMode", "ShadowRootMode",
"Window", "Window",
"console"
] ]

View File

@ -1,6 +1,6 @@
use js_sys::Function; use js_sys::Function;
use wasm_bindgen::{convert::IntoWasmAbi, prelude::Closure, JsValue}; use wasm_bindgen::{convert::IntoWasmAbi, prelude::Closure, JsValue};
use web_sys::{window, Element, HtmlElement}; use web_sys::{window, Element, Event, HtmlElement, Window};
/// This attribute proc-macro will generate the following trait implementations /// This attribute proc-macro will generate the following trait implementations
/// * [WebComponentDef](trait@WebComponentDef) /// * [WebComponentDef](trait@WebComponentDef)
@ -27,8 +27,11 @@ pub trait WebComponentDef: IntoWasmAbi + Default {
} }
fn create() -> Element { fn create() -> Element {
window() Self::create_in_window(window().unwrap())
.unwrap() }
fn create_in_window(window: Window) -> Element {
window
.document() .document()
.unwrap() .unwrap()
.create_element(Self::element_name()) .create_element(Self::element_name())
@ -88,7 +91,7 @@ mod tests {
use wasm_bindgen::{JsCast, JsValue}; use wasm_bindgen::{JsCast, JsValue};
use wasm_bindgen_test::wasm_bindgen_test; use wasm_bindgen_test::wasm_bindgen_test;
use web_sys::Text; use web_sys::Text;
use web_sys::{window, HtmlElement}; use web_sys::{console, window, HtmlElement};
use wasm_web_component_macros::web_component; use wasm_web_component_macros::web_component;
@ -102,6 +105,76 @@ mod tests {
pub fn log_with_val(message: String, val: &JsValue); pub fn log_with_val(message: String, val: &JsValue);
} }
pub struct Timer<'a> {
name: &'a str,
}
impl<'a> Timer<'a> {
pub fn new(name: &'a str) -> Timer<'a> {
console::time_with_label(name);
Timer { name }
}
}
impl<'a> Drop for Timer<'a> {
fn drop(&mut self) {
console::time_end_with_label(self.name);
}
}
#[wasm_bindgen_test]
pub fn bench_mark_elements() {
#[web_component(observed_attrs = "['class']")]
pub struct BenchElement {}
impl WebComponentBinding for BenchElement {
fn connected(&self, element: &HtmlElement) {
let node = Text::new().unwrap();
node.set_text_content(Some("Added a text node on connect".into()));
element.append_child(&node).unwrap();
}
fn disconnected(&self, element: &HtmlElement) {
let node = element.first_child().unwrap();
element.remove_child(&node).unwrap();
}
fn adopted(&self, element: &HtmlElement) {
let node = Text::new().unwrap();
node.set_text_content(Some("Added a text node on adopt".into()));
element.append_child(&node).unwrap();
}
fn attribute_changed(
&self,
element: &HtmlElement,
name: JsValue,
old_value: JsValue,
new_value: JsValue,
) {
let node = element.first_child().unwrap();
node.set_text_content(Some(&format!(
"Setting {} from {} to {}",
name.as_string().unwrap_or("None".to_owned()),
old_value.as_string().unwrap_or("None".to_owned()),
new_value.as_string().unwrap_or("None".to_owned()),
)));
element.append_child(&node).unwrap();
}
}
Timer::new("custom-element::timing");
let handle = BenchElement::define();
let body = window().unwrap().document().unwrap().body().unwrap();
for _ in 1..100000 {
let el = BenchElement::create();
body.append_child(&el).unwrap();
el.set_attribute("class", "foo").unwrap();
body.remove_child(&el).unwrap();
}
}
// NOTE(jwall): We can only construct the web component once and since the lifetime of the component internals is tied // NOTE(jwall): We can only construct the web component once and since the lifetime of the component internals is tied
// to the handle we run this all in one single function. // to the handle we run this all in one single function.
#[wasm_bindgen_test] #[wasm_bindgen_test]
@ -115,28 +188,20 @@ mod tests {
impl WebComponentBinding for MyElementImpl { impl WebComponentBinding for MyElementImpl {
fn connected(&self, element: &HtmlElement) { fn connected(&self, element: &HtmlElement) {
log("Firing connected call back".to_owned());
let node = Text::new().unwrap(); let node = Text::new().unwrap();
node.set_text_content(Some("Added a text node on connect".into())); node.set_text_content(Some("Added a text node on connect".into()));
element.append_child(&node).unwrap(); element.append_child(&node).unwrap();
log(format!(
"element contents: {}",
&element.text_content().unwrap()
));
} }
fn disconnected(&self, element: &HtmlElement) { fn disconnected(&self, element: &HtmlElement) {
log("Firing discconnected call back".to_owned());
let node = element.first_child().unwrap(); let node = element.first_child().unwrap();
element.remove_child(&node).unwrap(); element.remove_child(&node).unwrap();
} }
fn adopted(&self, element: &HtmlElement) { fn adopted(&self, element: &HtmlElement) {
log("Firing adopted call back".to_owned());
let node = Text::new().unwrap(); let node = Text::new().unwrap();
node.set_text_content(Some("Added a text node on adopt".into())); node.set_text_content(Some("Added a text node on adopt".into()));
element.append_child(&node).unwrap(); element.append_child(&node).unwrap();
log_with_val("element: ".to_owned(), element);
} }
fn attribute_changed( fn attribute_changed(
@ -146,7 +211,6 @@ mod tests {
old_value: JsValue, old_value: JsValue,
new_value: JsValue, new_value: JsValue,
) { ) {
log("Firing attribute changed callback".to_owned());
let node = element.first_child().unwrap(); let node = element.first_child().unwrap();
node.set_text_content(Some(&format!( node.set_text_content(Some(&format!(
"Setting {} from {} to {}", "Setting {} from {} to {}",
@ -155,7 +219,6 @@ mod tests {
new_value.as_string().unwrap_or("None".to_owned()), new_value.as_string().unwrap_or("None".to_owned()),
))); )));
element.append_child(&node).unwrap(); element.append_child(&node).unwrap();
log_with_val("element: ".to_owned(), element);
} }
} }
let obj = MyElementImpl::define().expect("Failed to define web component"); let obj = MyElementImpl::define().expect("Failed to define web component");
@ -187,16 +250,19 @@ mod tests {
"Setting class from None to foo" "Setting class from None to foo"
); );
// NOTE(jwall): If we are running headless then this can fail sometimes.
// We don't fail the test when that happens.
if let Ok(Some(new_window)) = window().unwrap().open() {
// Test the adopted callback // Test the adopted callback
// First we need a new window with a new document to perform the adoption with. // First we need a new window with a new document to perform the adoption with.
let new_window = window().unwrap().open().unwrap().unwrap();
// Then we can have the new document adopt this node.
new_window.document().unwrap().adopt_node(&element).unwrap(); new_window.document().unwrap().adopt_node(&element).unwrap();
assert_eq!( assert_eq!(
element.text_content().unwrap(), element.text_content().unwrap(),
"Added a text node on adopt" "Added a text node on adopt"
); );
} }
// Then we can have the new document adopt this node.
}
#[wasm_bindgen_test] #[wasm_bindgen_test]
fn test_component_no_element_name() { fn test_component_no_element_name() {