1 use webcore::value::{Reference, Value};
2 use webcore::try_from::{TryInto, TryFrom};
3 use webcore::promise::{Promise, TypedPromise};
4 use webapi::error::TypeError;
5 use webapi::event_target::{IEventTarget, EventTarget};
6 use webapi::node::{INode, Node, CloneKind};
7 use webapi::element::Element;
8 use webapi::html_element::HtmlElement;
9 use webapi::document_fragment::DocumentFragment;
10 use webapi::text_node::TextNode;
11 use webapi::location::Location;
12 use webapi::parent_node::IParentNode;
13 use webapi::non_element_parent_node::INonElementParentNode;
14 use 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))]
24 pub struct Document( Reference );
25
26 error_enum_boilerplate! {
27 CreateElementNsError,
28 InvalidCharacterError,
29 NamespaceError
30 }
31
32 impl IEventTarget for Document {}
33 impl IParentNode for Document {}
34 impl INode for Document {}
35
36 impl 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)
document() -> Document41 pub fn document() -> Document {
42 unsafe { js!( return document; ).into_reference_unchecked() }.unwrap()
43 }
44
45 impl 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
create_document_fragment( &self ) -> DocumentFragment51 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
create_element( &self, tag: &str ) -> Result< Element, InvalidCharacterError >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
create_element_ns( &self, namespace_uri: &str, tag: &str ) -> Result< Element, CreateElementNsError >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
create_text_node( &self, text: &str ) -> TextNode82 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
location( &self ) -> Option< 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
body( &self ) -> Option< HtmlElement >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
head( &self ) -> Option< HtmlElement >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
title( &self ) -> String131 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
set_title( &self, title: &str )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
document_element( &self ) -> Option< Element >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
pointer_lock_element( &self ) -> Option< Element >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
exit_pointer_lock( &self )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
import_node<N: INode>( &self, n: &N, kind: CloneKind ) -> Result<Node, NotSupportedError>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
fullscreen_enabled( &self ) -> bool200 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
fullscreen_element( &self ) -> Option<Element>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")]
exit_fullscreen(&self) -> TypedPromise<(), TypeError>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"))]
232 mod 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]
test_create_element_invalid_character()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]
test_create_element_ns_invalid_character()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]
test_create_element_ns_namespace_error()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]
test_import_node()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 }
280