script/dom/
domimplementation.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use dom_struct::dom_struct;
6use html5ever::{local_name, ns};
7use script_bindings::error::Error;
8use script_traits::DocumentActivity;
9
10use crate::document_loader::DocumentLoader;
11use crate::dom::bindings::codegen::Bindings::DOMImplementationBinding::DOMImplementationMethods;
12use crate::dom::bindings::codegen::Bindings::DocumentBinding::{
13    DocumentMethods, ElementCreationOptions,
14};
15use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
16use crate::dom::bindings::codegen::UnionTypes::StringOrElementCreationOptions;
17use crate::dom::bindings::domname::{is_valid_doctype_name, namespace_from_domstring};
18use crate::dom::bindings::error::Fallible;
19use crate::dom::bindings::inheritance::Castable;
20use crate::dom::bindings::reflector::{Reflector, reflect_dom_object};
21use crate::dom::bindings::root::{Dom, DomRoot};
22use crate::dom::bindings::str::DOMString;
23use crate::dom::document::{Document, DocumentSource, HasBrowsingContext, IsHTMLDocument};
24use crate::dom::documenttype::DocumentType;
25use crate::dom::html::htmlbodyelement::HTMLBodyElement;
26use crate::dom::html::htmlheadelement::HTMLHeadElement;
27use crate::dom::html::htmlhtmlelement::HTMLHtmlElement;
28use crate::dom::html::htmltitleelement::HTMLTitleElement;
29use crate::dom::node::Node;
30use crate::dom::text::Text;
31use crate::dom::xmldocument::XMLDocument;
32use crate::script_runtime::CanGc;
33
34// https://dom.spec.whatwg.org/#domimplementation
35#[dom_struct]
36pub(crate) struct DOMImplementation {
37    reflector_: Reflector,
38    document: Dom<Document>,
39}
40
41impl DOMImplementation {
42    fn new_inherited(document: &Document) -> DOMImplementation {
43        DOMImplementation {
44            reflector_: Reflector::new(),
45            document: Dom::from_ref(document),
46        }
47    }
48
49    pub(crate) fn new(document: &Document, can_gc: CanGc) -> DomRoot<DOMImplementation> {
50        let window = document.window();
51        reflect_dom_object(
52            Box::new(DOMImplementation::new_inherited(document)),
53            window,
54            can_gc,
55        )
56    }
57}
58
59// https://dom.spec.whatwg.org/#domimplementation
60#[allow(non_snake_case)]
61impl DOMImplementationMethods<crate::DomTypeHolder> for DOMImplementation {
62    /// <https://dom.spec.whatwg.org/#dom-domimplementation-createdocumenttype>
63    fn CreateDocumentType(
64        &self,
65        qualified_name: DOMString,
66        pubid: DOMString,
67        sysid: DOMString,
68        can_gc: CanGc,
69    ) -> Fallible<DomRoot<DocumentType>> {
70        // Step 1. If name is not a valid doctype name, then throw an
71        //      "InvalidCharacterError" DOMException.
72        if !is_valid_doctype_name(&qualified_name) {
73            debug!("Not a valid doctype name");
74            return Err(Error::InvalidCharacter);
75        }
76
77        Ok(DocumentType::new(
78            qualified_name,
79            Some(pubid),
80            Some(sysid),
81            &self.document,
82            can_gc,
83        ))
84    }
85
86    /// <https://dom.spec.whatwg.org/#dom-domimplementation-createdocument>
87    fn CreateDocument(
88        &self,
89        maybe_namespace: Option<DOMString>,
90        qname: DOMString,
91        maybe_doctype: Option<&DocumentType>,
92        can_gc: CanGc,
93    ) -> Fallible<DomRoot<XMLDocument>> {
94        let win = self.document.window();
95        let loader = DocumentLoader::new(&self.document.loader());
96        let namespace = namespace_from_domstring(maybe_namespace.to_owned());
97
98        let content_type = match namespace {
99            ns!(html) => "application/xhtml+xml",
100            ns!(svg) => "image/svg+xml",
101            _ => "application/xml",
102        }
103        .parse()
104        .unwrap();
105
106        // Step 1.
107        let doc = XMLDocument::new(
108            win,
109            HasBrowsingContext::No,
110            None,
111            self.document.origin().clone(),
112            IsHTMLDocument::NonHTMLDocument,
113            Some(content_type),
114            None,
115            DocumentActivity::Inactive,
116            DocumentSource::NotFromParser,
117            loader,
118            Some(self.document.insecure_requests_policy()),
119            self.document.has_trustworthy_ancestor_or_current_origin(),
120            self.document.custom_element_reaction_stack(),
121            can_gc,
122        );
123
124        // Step 2. Let element be null.
125        // Step 3. If qualifiedName is not the empty string, then set element to the result of running
126        // the internal createElementNS steps, given document, namespace, qualifiedName, and an empty dictionary.
127        let maybe_elem = if qname.is_empty() {
128            None
129        } else {
130            let options =
131                StringOrElementCreationOptions::ElementCreationOptions(ElementCreationOptions {
132                    is: None,
133                });
134            match doc
135                .upcast::<Document>()
136                .CreateElementNS(maybe_namespace, qname, options, can_gc)
137            {
138                Err(error) => return Err(error),
139                Ok(elem) => Some(elem),
140            }
141        };
142
143        {
144            let doc_node = doc.upcast::<Node>();
145
146            // Step 4.
147            if let Some(doc_type) = maybe_doctype {
148                doc_node.AppendChild(doc_type.upcast(), can_gc).unwrap();
149            }
150
151            // Step 5.
152            if let Some(ref elem) = maybe_elem {
153                doc_node.AppendChild(elem.upcast(), can_gc).unwrap();
154            }
155        }
156
157        // Step 6.
158        // The origin is already set
159
160        // Step 7.
161        Ok(doc)
162    }
163
164    // https://dom.spec.whatwg.org/#dom-domimplementation-createhtmldocument
165    fn CreateHTMLDocument(&self, title: Option<DOMString>, can_gc: CanGc) -> DomRoot<Document> {
166        let win = self.document.window();
167        let loader = DocumentLoader::new(&self.document.loader());
168
169        // Step 1-2.
170        let doc = Document::new(
171            win,
172            HasBrowsingContext::No,
173            None,
174            self.document.origin().clone(),
175            IsHTMLDocument::HTMLDocument,
176            None,
177            None,
178            DocumentActivity::Inactive,
179            DocumentSource::NotFromParser,
180            loader,
181            None,
182            None,
183            Default::default(),
184            false,
185            self.document.allow_declarative_shadow_roots(),
186            Some(self.document.insecure_requests_policy()),
187            self.document.has_trustworthy_ancestor_or_current_origin(),
188            self.document.custom_element_reaction_stack(),
189            can_gc,
190        );
191
192        {
193            // Step 3.
194            let doc_node = doc.upcast::<Node>();
195            let doc_type = DocumentType::new(DOMString::from("html"), None, None, &doc, can_gc);
196            doc_node.AppendChild(doc_type.upcast(), can_gc).unwrap();
197        }
198
199        {
200            // Step 4.
201            let doc_node = doc.upcast::<Node>();
202            let doc_html = DomRoot::upcast::<Node>(HTMLHtmlElement::new(
203                local_name!("html"),
204                None,
205                &doc,
206                None,
207                can_gc,
208            ));
209            doc_node
210                .AppendChild(&doc_html, can_gc)
211                .expect("Appending failed");
212
213            {
214                // Step 5.
215                let doc_head = DomRoot::upcast::<Node>(HTMLHeadElement::new(
216                    local_name!("head"),
217                    None,
218                    &doc,
219                    None,
220                    can_gc,
221                ));
222                doc_html.AppendChild(&doc_head, can_gc).unwrap();
223
224                // Step 6.
225                if let Some(title_str) = title {
226                    // Step 6.1.
227                    let doc_title = DomRoot::upcast::<Node>(HTMLTitleElement::new(
228                        local_name!("title"),
229                        None,
230                        &doc,
231                        None,
232                        can_gc,
233                    ));
234                    doc_head.AppendChild(&doc_title, can_gc).unwrap();
235
236                    // Step 6.2.
237                    let title_text = Text::new(title_str, &doc, can_gc);
238                    doc_title.AppendChild(title_text.upcast(), can_gc).unwrap();
239                }
240            }
241
242            // Step 7.
243            let doc_body = HTMLBodyElement::new(local_name!("body"), None, &doc, None, can_gc);
244            doc_html.AppendChild(doc_body.upcast(), can_gc).unwrap();
245        }
246
247        // Step 8.
248        // The origin is already set
249
250        // Step 9.
251        doc
252    }
253
254    // https://dom.spec.whatwg.org/#dom-domimplementation-hasfeature
255    fn HasFeature(&self) -> bool {
256        true
257    }
258}