arwa/
document.rs

1use std::convert::TryFrom;
2
3use wasm_bindgen::JsCast;
4
5use crate::console::{Write, Writer};
6use crate::error::{AdoptNodeError, HierarchyRequestError, NotSupportedError, SyntaxError};
7use crate::event::{OnFullscreenChange, OnFullscreenError, OnReadyStateChange, OnVisibilityChange};
8use crate::html::{
9    GenericHtmlElement, HtmlBodyElement, HtmlFormElement, HtmlHeadElement, HtmlImageElement,
10};
11use crate::{
12    DocumentFragment, DocumentType, Element, GenericElement, GenericNode, GlobalEventHandlers,
13    Location, Node, QuerySelectorAll, TextDirectionality,
14};
15
16pub trait Document: AsRef<web_sys::Document> {
17    // TODO: implement new() for concrete implementations rather than trait.
18
19    // TODO: all create element methods
20
21    fn body(&self) -> Option<HtmlBodyElement> {
22        // Disregard deprecated frameset element
23        self.as_ref()
24            .body()
25            .and_then(|e| e.dyn_into::<web_sys::HtmlBodyElement>().ok())
26            .map(|body| body.into())
27    }
28
29    fn set_body(&self, body: Option<&HtmlBodyElement>) {
30        self.as_ref().set_body(body.map(|b| b.as_ref()));
31    }
32
33    fn character_set(&self) -> String {
34        self.as_ref().character_set()
35    }
36
37    fn child_elements(&self) -> DocumentChildElements {
38        DocumentChildElements {
39            document: self.as_ref(),
40            children: self.as_ref().children(),
41        }
42    }
43
44    fn dir(&self) -> TextDirectionality {
45        match &*self.as_ref().dir().to_lowercase() {
46            "ltr" => TextDirectionality::LeftToRight,
47            "rtl" => TextDirectionality::RightToLeft,
48            _ => TextDirectionality::Auto,
49        }
50    }
51
52    fn set_dir(&self, dir: TextDirectionality) {
53        let text_directionality = match dir {
54            TextDirectionality::Auto => "auto",
55            TextDirectionality::LeftToRight => "ltr",
56            TextDirectionality::RightToLeft => "rtl",
57        };
58
59        self.as_ref().set_dir(text_directionality);
60    }
61
62    fn doctype(&self) -> Option<DocumentType> {
63        self.as_ref().doctype().map(|d| d.into())
64    }
65
66    fn document_element(&self) -> Option<GenericElement> {
67        self.as_ref().document_element().map(|e| e.into())
68    }
69
70    fn document_uri(&self) -> String {
71        // No indication in the WHATWG spec that this can actually fail, unwrap for now.
72        self.as_ref().document_uri().unwrap()
73    }
74
75    // TODO: embeds. Unclear what the exact element is/can be. WHATWG implies only HtmlEmbedElement,
76    // MDN implies HTMLObjectElement. Do we need to defensively use GenericHtmlElement?
77
78    fn forms(&self) -> DocumentForms {
79        DocumentForms {
80            inner: self.as_ref().forms(),
81        }
82    }
83
84    fn fullscreen_enabled(&self) -> bool {
85        self.as_ref().fullscreen_enabled()
86    }
87
88    fn has_focus(&self) -> bool {
89        // No indication in the spec that this can actually fail, unwrap for now.
90        self.as_ref().has_focus().unwrap()
91    }
92
93    fn head(&self) -> Option<HtmlHeadElement> {
94        self.as_ref().head().map(|h| h.into())
95    }
96
97    fn hidden(&self) -> bool {
98        self.as_ref().hidden()
99    }
100
101    fn images(&self) -> DocumentImages {
102        DocumentImages {
103            inner: self.as_ref().images(),
104        }
105    }
106
107    fn last_modified(&self) -> String {
108        self.as_ref().last_modified()
109    }
110
111    fn links(&self) -> DocumentLinks {
112        DocumentLinks {
113            inner: self.as_ref().links(),
114        }
115    }
116
117    fn location(&self) -> Option<Location> {
118        self.as_ref().location().map(|l| l.into())
119    }
120
121    fn referrer(&self) -> String {
122        self.as_ref().referrer()
123    }
124
125    fn title(&self) -> String {
126        self.as_ref().title()
127    }
128
129    fn set_title(&self, title: &str) {
130        self.as_ref().set_title(title);
131    }
132
133    // TODO: default_view when Window has been figured out
134
135    fn on_fullscreen_change(&self) -> OnFullscreenChange {
136        OnFullscreenChange::new(self.as_ref().clone().into())
137    }
138
139    fn on_fullscreen_error(&self) -> OnFullscreenError {
140        OnFullscreenError::new(self.as_ref().clone().into())
141    }
142
143    fn on_ready_state_change(&self) -> OnReadyStateChange {
144        OnReadyStateChange::new(self.as_ref().clone().into())
145    }
146
147    fn on_visibility_change(&self) -> OnVisibilityChange {
148        OnVisibilityChange::new(self.as_ref().clone().into())
149    }
150
151    fn adopt_node<T>(&self, node: &T) -> Result<GenericNode, AdoptNodeError>
152    where
153        T: Node,
154    {
155        self.as_ref()
156            .adopt_node(node.as_ref())
157            .map(|ok| ok.into())
158            .map_err(|err| {
159                let err: web_sys::DomException = err.unchecked_into();
160
161                match &*err.name() {
162                    "NotSupportedError" => NotSupportedError::new(err).into(),
163                    "HierarchyRequestError" => HierarchyRequestError::new(err).into(),
164                    _ => unreachable!(),
165                }
166            })
167    }
168
169    fn import_node<T>(&self, node: &T) -> Result<GenericNode, NotSupportedError>
170    where
171        T: Node,
172    {
173        self.as_ref()
174            .import_node(node.as_ref())
175            .map(|ok| ok.into())
176            .map_err(|err| NotSupportedError::new(err.unchecked_into()))
177    }
178
179    fn import_node_deep<T>(&self, node: &T) -> Result<GenericNode, NotSupportedError>
180    where
181        T: Node,
182    {
183        self.as_ref()
184            .import_node_with_deep(node.as_ref(), true)
185            .map(|ok| ok.into())
186            .map_err(|err| NotSupportedError::new(err.unchecked_into()))
187    }
188
189    // TODO: modify `query_id` or add additional method that incorporates the cast for convenience?
190    // E.g.: fn query_id<T>(&self, id: &str) -> Option<T> where T: TryFrom<GenericElement>;
191
192    fn query_id(&self, id: &str) -> Option<GenericElement> {
193        self.as_ref().get_element_by_id(id).map(|e| e.into())
194    }
195
196    fn query_selector_first(&self, selector: &str) -> Result<Option<GenericElement>, SyntaxError> {
197        self.as_ref()
198            .query_selector(selector)
199            .map(|ok| ok.map(|e| e.into()))
200            .map_err(|err| SyntaxError::new(err.unchecked_into()))
201    }
202
203    fn query_selector_all(&self, selector: &str) -> Result<QuerySelectorAll, SyntaxError> {
204        self.as_ref()
205            .query_selector_all(selector)
206            .map(|inner| QuerySelectorAll::new(inner))
207            .map_err(|err| SyntaxError::new(err.unchecked_into()))
208    }
209
210    // TODO: decide on get_elements_by_tag_name and get_elements_by_class_name, see comment in
211    // `Element`.
212
213    fn enable_style_sheets_for_set(&self, set_name: Option<&str>) {
214        self.as_ref().enable_style_sheets_for_set(set_name);
215    }
216
217    // TODO: has_storage_access and request_storage_access seem to be missing from web_sys.
218
219    // TODO: XPath?
220
221    fn create_document_fragment(&self) -> DocumentFragment {
222        DocumentFragment::from(self.as_ref().create_document_fragment())
223    }
224
225    fn element_from_point(&self, x: f32, y: f32) -> Option<GenericElement> {
226        self.as_ref().element_from_point(x, y).map(|e| e.into())
227    }
228}
229
230pub struct GenericDocument {
231    inner: web_sys::Document,
232}
233
234impl From<web_sys::Document> for GenericDocument {
235    fn from(inner: web_sys::Document) -> Self {
236        GenericDocument { inner }
237    }
238}
239
240impl AsRef<web_sys::Document> for GenericDocument {
241    fn as_ref(&self) -> &web_sys::Document {
242        &self.inner
243    }
244}
245
246impl AsRef<web_sys::Node> for GenericDocument {
247    fn as_ref(&self) -> &web_sys::Node {
248        self.inner.as_ref()
249    }
250}
251
252impl AsRef<web_sys::EventTarget> for GenericDocument {
253    fn as_ref(&self) -> &web_sys::EventTarget {
254        self.inner.as_ref()
255    }
256}
257
258impl Write for GenericDocument {
259    fn write(&self, writer: &mut Writer) {
260        writer.write_1(self.inner.as_ref());
261    }
262}
263
264impl GlobalEventHandlers for GenericDocument {}
265impl Node for GenericDocument {}
266impl Document for GenericDocument {}
267
268pub struct DocumentChildElements<'a> {
269    document: &'a web_sys::Document,
270    children: web_sys::HtmlCollection,
271}
272
273impl<'a> DocumentChildElements<'a> {
274    pub fn get(&self, index: usize) -> Option<GenericElement> {
275        u32::try_from(index)
276            .ok()
277            .and_then(|index| self.children.get_with_index(index))
278            .map(|e| e.into())
279    }
280
281    pub fn find_by_id(&self, id: &str) -> Option<GenericElement> {
282        self.children.get_with_name(id).map(|e| e.into())
283    }
284
285    pub fn len(&self) -> usize {
286        self.document.child_element_count() as usize
287    }
288
289    pub fn is_empty(&self) -> bool {
290        self.len() == 0
291    }
292
293    pub fn is_not_empty(&self) -> bool {
294        !self.is_empty()
295    }
296
297    pub fn first(&self) -> Option<GenericElement> {
298        self.document.first_element_child().map(|e| e.into())
299    }
300
301    pub fn last(&self) -> Option<GenericElement> {
302        self.document.last_element_child().map(|e| e.into())
303    }
304
305    pub fn append<C>(&self, child: &C)
306    where
307        C: DocumentChild,
308    {
309        child.append_to(self);
310    }
311
312    pub fn prepend<C>(&self, child: &C)
313    where
314        C: DocumentChild,
315    {
316        child.prepend_to(self);
317    }
318
319    pub fn iter(&self) -> DocumentChildElementsIter {
320        DocumentChildElementsIter {
321            document_child_elements: self,
322            current: 0,
323        }
324    }
325}
326
327impl<'a> Write for DocumentChildElements<'a> {
328    fn write(&self, writer: &mut Writer) {
329        writer.write_1(self.children.as_ref());
330    }
331}
332
333impl<'a> IntoIterator for DocumentChildElements<'a> {
334    type Item = GenericElement;
335    type IntoIter = DocumentChildElementsIntoIter<'a>;
336
337    fn into_iter(self) -> Self::IntoIter {
338        DocumentChildElementsIntoIter {
339            document_child_elements: self,
340            current: 0,
341        }
342    }
343}
344
345pub trait DocumentChild: document_child_seal::Sealed {
346    // Note, this construct *should* be locked down enough to avoid any "HierarchyRequestError" and
347    // thus these operations should never fail.
348
349    fn prepend_to(&self, document_children: &DocumentChildElements);
350
351    fn append_to(&self, document_children: &DocumentChildElements);
352}
353
354impl<T> DocumentChild for T
355where
356    T: Element,
357{
358    // TODO: decide on panic vs error on append/prepend when trying to insert an element into an
359    // element that is a descendant. May be argued to be in the same class of errors as indexing
360    // error, std handles these with panics (e.g. Vec::insert).
361
362    fn prepend_to(&self, document_children: &DocumentChildElements) {
363        document_children
364            .document
365            .prepend_with_node_1(self.as_ref())
366            .expect(
367                "Element cannot be an ancestor of the element into which it is being inserted.",
368            );
369    }
370
371    fn append_to(&self, document_children: &DocumentChildElements) {
372        document_children
373            .document
374            .append_with_node_1(self.as_ref())
375            .expect(
376                "Element cannot be an ancestor of the element into which it is being inserted.",
377            );
378    }
379}
380
381impl DocumentChild for str {
382    fn prepend_to(&self, document_children: &DocumentChildElements) {
383        document_children.document.prepend_with_str_1(self).unwrap();
384    }
385
386    fn append_to(&self, document_children: &DocumentChildElements) {
387        document_children.document.append_with_str_1(self).unwrap();
388    }
389}
390
391mod document_child_seal {
392    use super::*;
393
394    pub trait Sealed {}
395
396    impl<T> Sealed for T where T: Element {}
397    impl Sealed for str {}
398}
399
400pub struct DocumentChildElementsIter<'a> {
401    document_child_elements: &'a DocumentChildElements<'a>,
402    current: usize,
403}
404
405impl<'a> Iterator for DocumentChildElementsIter<'a> {
406    type Item = GenericElement;
407
408    fn next(&mut self) -> Option<Self::Item> {
409        let current = self.current;
410
411        self.current += 1;
412
413        self.document_child_elements.get(current)
414    }
415}
416
417pub struct DocumentChildElementsIntoIter<'a> {
418    document_child_elements: DocumentChildElements<'a>,
419    current: usize,
420}
421
422impl<'a> Iterator for DocumentChildElementsIntoIter<'a> {
423    type Item = GenericElement;
424
425    fn next(&mut self) -> Option<Self::Item> {
426        let current = self.current;
427
428        self.current += 1;
429
430        self.document_child_elements.get(current)
431    }
432}
433
434pub struct DocumentForms {
435    inner: web_sys::HtmlCollection,
436}
437
438impl DocumentForms {
439    pub fn get(&self, index: usize) -> Option<HtmlFormElement> {
440        u32::try_from(index)
441            .ok()
442            .and_then(|index| self.inner.get_with_index(index))
443            .map(|e| {
444                let e: web_sys::HtmlFormElement = e.unchecked_into();
445
446                e.into()
447            })
448    }
449
450    pub fn find_by_id(&self, id: &str) -> Option<HtmlFormElement> {
451        self.inner.get_with_name(id).map(|e| {
452            let e: web_sys::HtmlFormElement = e.unchecked_into();
453
454            e.into()
455        })
456    }
457
458    pub fn len(&self) -> usize {
459        self.inner.length() as usize
460    }
461
462    pub fn is_empty(&self) -> bool {
463        self.len() == 0
464    }
465
466    pub fn is_not_empty(&self) -> bool {
467        !self.is_empty()
468    }
469
470    pub fn first(&self) -> Option<HtmlFormElement> {
471        self.get(0)
472    }
473
474    pub fn last(&self) -> Option<HtmlFormElement> {
475        let len = self.len();
476
477        if len > 0 {
478            self.get(len - 1)
479        } else {
480            None
481        }
482    }
483
484    pub fn iter(&self) -> DocumentFormsIter {
485        DocumentFormsIter {
486            document_forms: self,
487            current: 0,
488        }
489    }
490}
491
492impl Write for DocumentForms {
493    fn write(&self, writer: &mut Writer) {
494        writer.write_1(self.inner.as_ref());
495    }
496}
497
498impl IntoIterator for DocumentForms {
499    type Item = HtmlFormElement;
500    type IntoIter = DocumentFormsIntoIter;
501
502    fn into_iter(self) -> Self::IntoIter {
503        DocumentFormsIntoIter {
504            document_forms: self,
505            current: 0,
506        }
507    }
508}
509
510pub struct DocumentFormsIter<'a> {
511    document_forms: &'a DocumentForms,
512    current: usize,
513}
514
515impl<'a> Iterator for DocumentFormsIter<'a> {
516    type Item = HtmlFormElement;
517
518    fn next(&mut self) -> Option<Self::Item> {
519        let current = self.current;
520
521        self.current += 1;
522
523        self.document_forms.get(current)
524    }
525}
526
527pub struct DocumentFormsIntoIter {
528    document_forms: DocumentForms,
529    current: usize,
530}
531
532impl Iterator for DocumentFormsIntoIter {
533    type Item = HtmlFormElement;
534
535    fn next(&mut self) -> Option<Self::Item> {
536        let current = self.current;
537
538        self.current += 1;
539
540        self.document_forms.get(current)
541    }
542}
543
544pub struct DocumentImages {
545    inner: web_sys::HtmlCollection,
546}
547
548impl DocumentImages {
549    pub fn get(&self, index: usize) -> Option<HtmlImageElement> {
550        u32::try_from(index)
551            .ok()
552            .and_then(|index| self.inner.get_with_index(index))
553            .map(|e| {
554                let e: web_sys::HtmlImageElement = e.unchecked_into();
555
556                e.into()
557            })
558    }
559
560    pub fn find_by_id(&self, id: &str) -> Option<HtmlImageElement> {
561        self.inner.get_with_name(id).map(|e| {
562            let e: web_sys::HtmlImageElement = e.unchecked_into();
563
564            e.into()
565        })
566    }
567
568    pub fn len(&self) -> usize {
569        self.inner.length() as usize
570    }
571
572    pub fn is_empty(&self) -> bool {
573        self.len() == 0
574    }
575
576    pub fn is_not_empty(&self) -> bool {
577        !self.is_empty()
578    }
579
580    pub fn first(&self) -> Option<HtmlImageElement> {
581        self.get(0)
582    }
583
584    pub fn last(&self) -> Option<HtmlImageElement> {
585        let len = self.len();
586
587        if len > 0 {
588            self.get(len - 1)
589        } else {
590            None
591        }
592    }
593
594    pub fn iter(&self) -> DocumentImagesIter {
595        DocumentImagesIter {
596            document_images: self,
597            current: 0,
598        }
599    }
600}
601
602impl Write for DocumentImages {
603    fn write(&self, writer: &mut Writer) {
604        writer.write_1(self.inner.as_ref());
605    }
606}
607
608impl IntoIterator for DocumentImages {
609    type Item = HtmlImageElement;
610    type IntoIter = DocumentImagesIntoIter;
611
612    fn into_iter(self) -> Self::IntoIter {
613        DocumentImagesIntoIter {
614            document_images: self,
615            current: 0,
616        }
617    }
618}
619
620pub struct DocumentImagesIter<'a> {
621    document_images: &'a DocumentImages,
622    current: usize,
623}
624
625impl<'a> Iterator for DocumentImagesIter<'a> {
626    type Item = HtmlImageElement;
627
628    fn next(&mut self) -> Option<Self::Item> {
629        let current = self.current;
630
631        self.current += 1;
632
633        self.document_images.get(current)
634    }
635}
636
637pub struct DocumentImagesIntoIter {
638    document_images: DocumentImages,
639    current: usize,
640}
641
642impl Iterator for DocumentImagesIntoIter {
643    type Item = HtmlImageElement;
644
645    fn next(&mut self) -> Option<Self::Item> {
646        let current = self.current;
647
648        self.current += 1;
649
650        self.document_images.get(current)
651    }
652}
653
654pub struct DocumentLinks {
655    inner: web_sys::HtmlCollection,
656}
657
658impl DocumentLinks {
659    pub fn get(&self, index: usize) -> Option<GenericHtmlElement> {
660        u32::try_from(index)
661            .ok()
662            .and_then(|index| self.inner.get_with_index(index))
663            .map(|e| {
664                let e: web_sys::HtmlElement = e.unchecked_into();
665
666                e.into()
667            })
668    }
669
670    pub fn find_by_id(&self, id: &str) -> Option<GenericHtmlElement> {
671        self.inner.get_with_name(id).map(|e| {
672            let e: web_sys::HtmlElement = e.unchecked_into();
673
674            e.into()
675        })
676    }
677
678    pub fn len(&self) -> usize {
679        self.inner.length() as usize
680    }
681
682    pub fn is_empty(&self) -> bool {
683        self.len() == 0
684    }
685
686    pub fn is_not_empty(&self) -> bool {
687        !self.is_empty()
688    }
689
690    pub fn first(&self) -> Option<GenericHtmlElement> {
691        self.get(0)
692    }
693
694    pub fn last(&self) -> Option<GenericHtmlElement> {
695        let len = self.len();
696
697        if len > 0 {
698            self.get(len - 1)
699        } else {
700            None
701        }
702    }
703
704    pub fn iter(&self) -> DocumentLinksIter {
705        DocumentLinksIter {
706            document_links: self,
707            current: 0,
708        }
709    }
710}
711
712impl Write for DocumentLinks {
713    fn write(&self, writer: &mut Writer) {
714        writer.write_1(self.inner.as_ref());
715    }
716}
717
718impl IntoIterator for DocumentLinks {
719    type Item = GenericHtmlElement;
720    type IntoIter = DocumentLinksIntoIter;
721
722    fn into_iter(self) -> Self::IntoIter {
723        DocumentLinksIntoIter {
724            document_links: self,
725            current: 0,
726        }
727    }
728}
729
730pub struct DocumentLinksIter<'a> {
731    document_links: &'a DocumentLinks,
732    current: usize,
733}
734
735impl<'a> Iterator for DocumentLinksIter<'a> {
736    type Item = GenericHtmlElement;
737
738    fn next(&mut self) -> Option<Self::Item> {
739        let current = self.current;
740
741        self.current += 1;
742
743        self.document_links.get(current)
744    }
745}
746
747pub struct DocumentLinksIntoIter {
748    document_links: DocumentLinks,
749    current: usize,
750}
751
752impl Iterator for DocumentLinksIntoIter {
753    type Item = GenericHtmlElement;
754
755    fn next(&mut self) -> Option<Self::Item> {
756        let current = self.current;
757
758        self.current += 1;
759
760        self.document_links.get(current)
761    }
762}