Skip to content

Commit

Permalink
Shadow DOM and web components related APIs (#295)
Browse files Browse the repository at this point in the history
  • Loading branch information
futursolo authored and koute committed Nov 1, 2018
1 parent 21edc80 commit 85e4e82
Show file tree
Hide file tree
Showing 17 changed files with 590 additions and 11 deletions.
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}
SKIP_RUNTIME_COMPATIBILITY_CHECK=${SKIP_RUNTIME_COMPATIBILITY_CHECK:-0}

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;
}

#[cfg(feature = "experimental_features_which_may_break_on_minor_version_bumps")]
Expand Down Expand Up @@ -471,7 +478,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

0 comments on commit 85e4e82

Please sign in to comment.