Skip to content

Web Components: Shadow Dom + Template Element #295

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 21 commits into from
Nov 1, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ci/run_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
set -euo pipefail
IFS=$'\n\t'

export RUST_BACKTRACE=1

CARGO_WEB=${CARGO_WEB:-cargo-web}

set +e
Expand Down
10 changes: 9 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ pub mod web {
pub use webapi::html_element::{IHtmlElement, HtmlElement, Rect};
pub use webapi::window_or_worker::IWindowOrWorker;
pub use webapi::parent_node::IParentNode;
pub use webapi::slotable::ISlotable;
pub use webapi::non_element_parent_node::INonElementParentNode;
pub use webapi::token_list::TokenList;
pub use webapi::node_list::NodeList;
Expand All @@ -268,6 +269,8 @@ pub mod web {
pub use webapi::child_node::IChildNode;
pub use webapi::gamepad::{Gamepad, GamepadButton, GamepadMappingType};
pub use webapi::selection::Selection;
pub use webapi::shadow_root::{ShadowRootMode, ShadowRoot};
pub use webapi::html_elements::SlotContentKind;

/// A module containing error types.
pub mod error {
Expand Down Expand Up @@ -304,6 +307,8 @@ pub mod web {
pub use webapi::html_elements::CanvasElement;
pub use webapi::html_elements::SelectElement;
pub use webapi::html_elements::OptionElement;
pub use webapi::html_elements::TemplateElement;
pub use webapi::html_elements::SlotElement;
}

/// A module containing JavaScript DOM events.
Expand Down Expand Up @@ -427,6 +432,8 @@ pub mod web {
DataTransferItem,
DataTransferItemKind,
};

pub use webapi::events::slot::SlotChangeEvent;
}

/// APIs related to MIDI.
Expand Down Expand Up @@ -470,7 +477,8 @@ pub mod traits {
IWindowOrWorker,
IParentNode,
INonElementParentNode,
IChildNode
IChildNode,
ISlotable,
};

#[doc(hidden)]
Expand Down
42 changes: 39 additions & 3 deletions src/webapi/document.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
use webcore::value::{Reference, Value};
use webcore::try_from::{TryInto, TryFrom};
use webapi::event_target::{IEventTarget, EventTarget};
use webapi::node::{INode, Node};
use webapi::node::{INode, Node, CloneKind};
use webapi::element::Element;
use webapi::html_element::HtmlElement;
use webapi::document_fragment::DocumentFragment;
use webapi::text_node::TextNode;
use webapi::location::Location;
use webapi::parent_node::IParentNode;
use webapi::non_element_parent_node::INonElementParentNode;
use webapi::dom_exception::{InvalidCharacterError, NamespaceError};
use webapi::dom_exception::{InvalidCharacterError, NamespaceError, NotSupportedError};

/// The `Document` interface represents any web page loaded in the browser and
/// serves as an entry point into the web page's content, which is the DOM tree.
Expand Down Expand Up @@ -181,12 +181,30 @@ impl Document {
@{self}.exitPointerLock();
);
}

/// Import node from another document
///
/// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Document/importNode)
// https://dom.spec.whatwg.org/#ref-for-dom-document-importnode
pub fn import_node<N: INode>( &self, n: &N, kind: CloneKind ) -> Result<Node, NotSupportedError> {
let deep = match kind {
CloneKind::Deep => true,
CloneKind::Shallow => false,
};

js_try!(
return @{self}.importNode( @{n.as_ref()}, @{deep} );
).unwrap()
}
}


#[cfg(all(test, feature = "web_test"))]
mod web_tests {
use super::*;
use webapi::node::{Node, INode, CloneKind};
use webapi::html_elements::TemplateElement;
use webapi::html_element::HtmlElement;

#[test]
fn test_create_element_invalid_character() {
Expand All @@ -211,4 +229,22 @@ mod web_tests {
v => panic!("expected NamespaceError, got {:?}", v),
}
}
}

#[test]
fn test_import_node() {
let document = document();
let tpl: TemplateElement = Node::from_html("<template><span>aaabbbcccddd</span></template>")
.unwrap()
.try_into()
.unwrap();

let n = document.import_node(&tpl.content(), CloneKind::Deep).unwrap();
let child_nodes = n.child_nodes();
assert_eq!(child_nodes.len(), 1);

let span_element: HtmlElement = child_nodes.iter().next().unwrap().try_into().unwrap();

assert_eq!(span_element.node_name(), "SPAN");
assert_eq!(js!( return @{span_element}.innerHTML; ), "aaabbbcccddd");
}
}
4 changes: 3 additions & 1 deletion src/webapi/document_fragment.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use webcore::value::Reference;
use webapi::event_target::{IEventTarget, EventTarget};
use webapi::node::{INode, Node};
use webapi::parent_node::IParentNode;

/// A reference to a JavaScript object DocumentFragment.
///
Expand All @@ -12,4 +13,5 @@ use webapi::node::{INode, Node};
pub struct DocumentFragment( Reference );

impl IEventTarget for DocumentFragment {}
impl INode for DocumentFragment {}
impl INode for DocumentFragment {}
impl IParentNode for DocumentFragment {}
65 changes: 62 additions & 3 deletions src/webapi/element.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,27 @@
use webcore::value::Reference;
use webcore::try_from::TryInto;
use webcore::try_from::{TryFrom, TryInto};
use webapi::dom_exception::{InvalidCharacterError, InvalidPointerId, NoModificationAllowedError, SyntaxError};
use webapi::event_target::{IEventTarget, EventTarget};
use webapi::node::{INode, Node};
use webapi::token_list::TokenList;
use webapi::parent_node::IParentNode;
use webapi::child_node::IChildNode;
use webcore::try_from::TryFrom;
use webapi::slotable::ISlotable;
use webapi::shadow_root::{ShadowRootMode, ShadowRoot};
use webapi::dom_exception::{NotSupportedError, InvalidStateError};

error_enum_boilerplate! {
AttachShadowError,
NotSupportedError, InvalidStateError
}

/// The `IElement` interface represents an object of a [Document](struct.Document.html).
/// This interface describes methods and properties common to all
/// kinds of elements.
///
/// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Element)
// https://dom.spec.whatwg.org/#element
pub trait IElement: INode + IParentNode + IChildNode {
pub trait IElement: INode + IParentNode + IChildNode + ISlotable {
/// The Element.namespaceURI read-only property returns the namespace URI
/// of the element, or null if the element is not in a namespace.
///
Expand Down Expand Up @@ -225,6 +232,40 @@ pub trait IElement: INode + IParentNode + IChildNode {
fn insert_html_after( &self, html: &str ) -> Result<(), InsertAdjacentError> {
self.insert_adjacent_html(InsertPosition::AfterEnd, html)
}

/// The slot property of the Element interface returns the name of the shadow DOM
/// slot the element is inserted in.
///
/// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Element/slot)
// https://dom.spec.whatwg.org/#ref-for-dom-element-slot
fn slot( &self ) -> String {
js!(
return @{self.as_ref()}.slot;
).try_into().unwrap()
}

/// Attach a shadow DOM tree to the specified element and returns a reference to its `ShadowRoot`.
/// It returns a shadow root if successfully attached or `None` if the element cannot be attached.
///
/// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Element/attachShadow)
// https://dom.spec.whatwg.org/#ref-for-dom-element-attachshadow
fn attach_shadow( &self, mode: ShadowRootMode ) -> Result<ShadowRoot, AttachShadowError> {
js_try!(
return @{self.as_ref()}.attachShadow( { mode: @{mode.as_str()}} )
).unwrap()
}

/// Returns the shadow root of the current element or `None`.
///
/// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Element/shadowRoot)
// https://dom.spec.whatwg.org/#ref-for-dom-element-shadowroot
fn shadow_root( &self ) -> Option<ShadowRoot> {
unsafe {
js!(
return @{self.as_ref()}.shadowRoot;
).into_reference_unchecked()
}
}
}


Expand All @@ -244,6 +285,7 @@ impl IElement for Element {}

impl< T: IElement > IParentNode for T {}
impl< T: IElement > IChildNode for T {}
impl< T: IElement > ISlotable for T {}

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum InsertPosition {
Expand Down Expand Up @@ -278,6 +320,7 @@ impl InsertPosition {
mod tests {
use super::*;
use webapi::document::document;
use webapi::shadow_root::ShadowRootMode;

fn div() -> Element {
js!(
Expand Down Expand Up @@ -351,4 +394,20 @@ mod tests {
_ => false,
});
}

#[test]
fn test_attach_shadow_mode_open() {
let element = document().create_element("div").unwrap();
let shadow_root = element.attach_shadow(ShadowRootMode::Open).unwrap();
assert_eq!(shadow_root.mode(), ShadowRootMode::Open);
assert_eq!(element.shadow_root(), Some(shadow_root));
}

#[test]
fn test_attach_shadow_mode_closed() {
let element = document().create_element("div").unwrap();
let shadow_root = element.attach_shadow(ShadowRootMode::Closed).unwrap();
assert_eq!(shadow_root.mode(), ShadowRootMode::Closed);
assert!(element.shadow_root().is_none());
}
}
1 change: 1 addition & 0 deletions src/webapi/events/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ pub mod mouse;
pub mod pointer;
pub mod progress;
pub mod socket;
pub mod slot;
15 changes: 15 additions & 0 deletions src/webapi/events/slot.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use webcore::value::Reference;
use webapi::event::{IEvent, Event};

/// The `slotchange` event is fired on an HTMLSlotElement instance
/// (`<slot>` element) when the node(s) contained in that slot change.
///
/// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/Events/slotchange)
// https://dom.spec.whatwg.org/#mutation-observers
#[derive(Clone, Debug, PartialEq, Eq, ReferenceType)]
#[reference(instance_of = "Event")]
#[reference(event = "slotchange")]
#[reference(subclass_of(Event))]
pub struct SlotChangeEvent( Reference );

impl IEvent for SlotChangeEvent {}
6 changes: 5 additions & 1 deletion src/webapi/html_elements/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@ mod input;
mod textarea;
mod select;
mod option;
mod template;
mod slot;

pub use self::canvas::CanvasElement;
pub use self::image::ImageElement;
pub use self::input::InputElement;
pub use self::textarea::TextAreaElement;
pub use self::select::SelectElement;
pub use self::option::OptionElement;
pub use self::template::TemplateElement;
pub use self::slot::{SlotElement, SlotContentKind};

pub use self::select::UnknownValueError;
pub use self::select::UnknownValueError;
2 changes: 1 addition & 1 deletion src/webapi/html_elements/option.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,4 @@ impl OptionElement {
return @{self}.value;
).try_into().unwrap()
}
}
}
2 changes: 1 addition & 1 deletion src/webapi/html_elements/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,4 +228,4 @@ mod tests{
assert_eq!(se.selected_indices(), vec![0,2,4]);
assert_eq!(se.selected_values(), vec!["first".to_string(), "third".to_string(), "".to_string()]);
}
}
}
Loading