stdweb/webapi/
document.rs

1use webcore::value::{Reference, Value};
2use webcore::try_from::{TryInto, TryFrom};
3use webcore::promise::{Promise, TypedPromise};
4use webapi::error::TypeError;
5use webapi::event_target::{IEventTarget, EventTarget};
6use webapi::node::{INode, Node, CloneKind};
7use webapi::element::Element;
8use webapi::html_element::HtmlElement;
9use webapi::document_fragment::DocumentFragment;
10use webapi::text_node::TextNode;
11use webapi::location::Location;
12use webapi::parent_node::IParentNode;
13use webapi::non_element_parent_node::INonElementParentNode;
14use webapi::dom_exception::{InvalidCharacterError, NamespaceError, NotSupportedError};
15
16/// The `Document` interface represents any web page loaded in the browser and
17/// serves as an entry point into the web page's content, which is the DOM tree.
18///
19/// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Document)
20// https://dom.spec.whatwg.org/#document
21#[derive(Clone, Debug, PartialEq, Eq, ReferenceType)]
22#[reference(instance_of = "Document")]
23#[reference(subclass_of(EventTarget, Node))]
24pub struct Document( Reference );
25
26error_enum_boilerplate! {
27    CreateElementNsError,
28    InvalidCharacterError,
29    NamespaceError
30}
31
32impl IEventTarget for Document {}
33impl IParentNode for Document {}
34impl INode for Document {}
35
36impl INonElementParentNode for Document {}
37
38/// A global instance of [Document](struct.Document.html).
39///
40/// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Document)
41pub fn document() -> Document {
42    unsafe { js!( return document; ).into_reference_unchecked() }.unwrap()
43}
44
45impl Document {
46    /// In an HTML document, the Document.createDocumentFragment() method creates a
47    /// new empty DocumentFragment.
48    ///
49    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Document/createDocumentFragment)
50    // https://dom.spec.whatwg.org/#ref-for-dom-document-createdocumentfragment
51    pub fn create_document_fragment( &self ) -> DocumentFragment {
52        unsafe {
53            js!( return @{self}.createDocumentFragment(); ).into_reference_unchecked().unwrap()
54        }
55    }
56
57    /// In an HTML document, the Document.createElement() method creates the HTML
58    /// element specified by `tag`, or an HTMLUnknownElement if `tag` isn't
59    /// recognized. In other documents, it creates an element with a null namespace URI.
60    ///
61    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement)
62    // https://dom.spec.whatwg.org/#ref-for-dom-document-createelement
63    pub fn create_element( &self, tag: &str ) -> Result< Element, InvalidCharacterError > {
64        js_try!( return @{self}.createElement( @{tag} ); ).unwrap()
65    }
66
67    /// Creates an element with the specified namespace URI and qualified name.
68    /// To create an element without specifying a namespace URI, use the `createElement` method.
69    ///
70    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Document/createElementNS)
71    // https://dom.spec.whatwg.org/#ref-for-dom-document-createelementns
72    pub fn create_element_ns( &self, namespace_uri: &str, tag: &str ) -> Result< Element, CreateElementNsError > {
73        js_try!(
74            return @{self}.createElementNS( @{namespace_uri}, @{tag} );
75        ).unwrap()
76    }
77
78    /// Creates a new text node.
79    ///
80    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Document/createTextNode)
81    // https://dom.spec.whatwg.org/#ref-for-dom-document-createtextnode
82    pub fn create_text_node( &self, text: &str ) -> TextNode {
83        unsafe {
84            js!( return @{self}.createTextNode( @{text} ); ).into_reference_unchecked().unwrap()
85        }
86    }
87
88    /// Returns a [Location](struct.Location.html) object which contains
89    /// information about the URL of the document and provides methods
90    /// for changing that URL and loading another URL.
91    ///
92    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Document/location)
93    // https://html.spec.whatwg.org/#the-document-object:dom-document-location
94    pub fn location( &self ) -> Option< Location > {
95        unsafe {
96            js!(
97                return @{self}.location;
98            ).into_reference_unchecked()
99        }
100    }
101
102    /// Returns the `<body>` or `<frameset>` node of the current document, or null if no such element exists.
103    ///
104    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Document/body)
105    // https://html.spec.whatwg.org/#the-document-object:dom-document-body
106    pub fn body( &self ) -> Option< HtmlElement > {
107        unsafe {
108            js!(
109                return @{self}.body;
110            ).into_reference_unchecked()
111        }
112    }
113
114    /// Returns the `<head>` element of the current document. If there are more than one `<head>`
115    /// elements, the first one is returned.
116    ///
117    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Document/head)
118    // https://html.spec.whatwg.org/#the-document-object:dom-document-head
119    pub fn head( &self ) -> Option< HtmlElement > {
120        unsafe {
121            js!(
122                return @{self}.head;
123            ).into_reference_unchecked()
124        }
125    }
126
127    /// Gets the title of the document.
128    ///
129    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Document/title)
130    // https://html.spec.whatwg.org/#the-document-object:document.title
131    pub fn title( &self ) -> String {
132        js!(
133            return @{self}.title;
134        ).try_into().unwrap()
135    }
136
137    /// Sets the title of the document.
138    ///
139    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Document/title)
140    // https://html.spec.whatwg.org/#the-document-object:document.title
141    pub fn set_title( &self, title: &str ) {
142        js!( @(no_return) @{self}.title = @{title}; );
143    }
144
145    /// Returns the Element that is the root element of the document (for example, the `<html>`
146    /// element for HTML documents).
147    ///
148    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Document/documentElement)
149    // https://dom.spec.whatwg.org/#ref-for-dom-document-documentelement
150    pub fn document_element( &self ) -> Option< Element > {
151        js!(
152            return @{self}.documentElement;
153        ).try_into().unwrap()
154    }
155
156    /// Returns the Element that the pointer is locked to, if it is locked to any
157    ///
158    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/DocumentOrShadowRoot/pointerLockElement)
159    // https://w3c.github.io/pointerlock/#dom-documentorshadowroot-pointerlockelement
160    pub fn pointer_lock_element( &self ) -> Option< Element > {
161        let value = js!(
162            return @{self}.pointerLockElement;
163        );
164        match value {
165            Value::Null | Value::Undefined => None,
166            Value::Reference(reference) => Some(reference.try_into().unwrap()),
167            _ => unreachable!()
168        }
169    }
170
171    /// Exit the pointer lock on the current element
172    ///
173    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Document/exitPointerLock)
174    // https://w3c.github.io/pointerlock/#dom-document-exitpointerlock
175    pub fn exit_pointer_lock( &self ) {
176        js!( @(no_return)
177            @{self}.exitPointerLock();
178        );
179    }
180
181    /// Import node from another document
182    ///
183    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Document/importNode)
184    // https://dom.spec.whatwg.org/#ref-for-dom-document-importnode
185    pub fn import_node<N: INode>( &self, n: &N, kind: CloneKind ) -> Result<Node, NotSupportedError> {
186        let deep = match kind {
187            CloneKind::Deep => true,
188            CloneKind::Shallow => false,
189        };
190
191        js_try!(
192            return @{self}.importNode( @{n.as_ref()}, @{deep} );
193        ).unwrap()
194    }
195
196    /// Check if the fullscreen API is enabled
197    ///
198    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Document/fullscreenEnabled)
199    // https://fullscreen.spec.whatwg.org/#ref-for-dom-document-fullscreenenabled
200    pub fn fullscreen_enabled( &self ) -> bool {
201        match js!( return @{self}.fullscreenEnabled; ) {
202            Value::Bool(value) => value,
203            _ => false, // if the variable is not set as a bool, then assume fullscreen is not supported
204        }
205    }
206
207    /// Get the current fullscreen element, or None if there is nothing fullscreen
208    ///
209    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/DocumentOrShadowRoot/fullscreenElement)
210    // https://fullscreen.spec.whatwg.org/#ref-for-dom-document-fullscreenelement
211    pub fn fullscreen_element( &self ) -> Option<Element> {
212        Some(js!( return @{self}.fullscreenElement; )
213            .into_reference()?
214            .downcast::<Element>()?)
215    }
216
217    /// Request the page return from fullscreen mode to a normal state
218    ///
219    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Document/exitFullscreen)
220    // https://fullscreen.spec.whatwg.org/#dom-document-exitfullscreen
221    #[cfg(feature = "experimental_features_which_may_break_on_minor_version_bumps")]
222    pub fn exit_fullscreen(&self) -> TypedPromise<(), TypeError> {
223        let promise: Promise = js!( return @{self}.exitFullscreen(); )
224            .try_into().unwrap();
225
226        TypedPromise::new( promise )
227    }
228}
229
230
231#[cfg(all(test, feature = "web_test"))]
232mod web_tests {
233    use super::*;
234    use webapi::node::{Node, INode, CloneKind};
235    use webapi::html_elements::TemplateElement;
236    use webapi::html_element::HtmlElement;
237
238    #[test]
239    fn test_create_element_invalid_character() {
240        match document().create_element("-invalid tag") {
241            Err(InvalidCharacterError{..}) => (),
242            v => panic!("expected InvalidCharacterError, got {:?}", v),
243        }
244    }
245
246    #[test]
247    fn test_create_element_ns_invalid_character() {
248        match document().create_element_ns("", "-invalid tag") {
249            Err(CreateElementNsError::InvalidCharacterError(_)) => (),
250            v => panic!("expected InvalidCharacterError, got {:?}", v),
251        }
252    }
253
254    #[test]
255    fn test_create_element_ns_namespace_error() {
256        match document().create_element_ns("", "illegal_prefix:svg") {
257            Err(CreateElementNsError::NamespaceError(_)) => (),
258            v => panic!("expected NamespaceError, got {:?}", v),
259        }
260    }
261
262    #[test]
263    fn test_import_node() {
264        let document = document();
265        let tpl: TemplateElement = Node::from_html("<template><span>aaabbbcccddd</span></template>")
266            .unwrap()
267            .try_into()
268            .unwrap();
269
270        let n = document.import_node(&tpl.content(), CloneKind::Deep).unwrap();
271        let child_nodes = n.child_nodes();
272        assert_eq!(child_nodes.len(), 1);
273
274        let span_element: HtmlElement = child_nodes.iter().next().unwrap().try_into().unwrap();
275
276        assert_eq!(span_element.node_name(), "SPAN");
277        assert_eq!(js!( return @{span_element}.innerHTML; ), "aaabbbcccddd");
278    }
279}