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::{QualName, 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::element::{CustomElementCreationMode, ElementCreator};
26use crate::dom::node::Node;
27use crate::dom::text::Text;
28use crate::dom::types::Element;
29use crate::dom::xmldocument::XMLDocument;
30use crate::script_runtime::CanGc;
31
32// https://dom.spec.whatwg.org/#domimplementation
33#[dom_struct]
34pub(crate) struct DOMImplementation {
35    reflector_: Reflector,
36    document: Dom<Document>,
37}
38
39impl DOMImplementation {
40    fn new_inherited(document: &Document) -> DOMImplementation {
41        DOMImplementation {
42            reflector_: Reflector::new(),
43            document: Dom::from_ref(document),
44        }
45    }
46
47    pub(crate) fn new(document: &Document, can_gc: CanGc) -> DomRoot<DOMImplementation> {
48        let window = document.window();
49        reflect_dom_object(
50            Box::new(DOMImplementation::new_inherited(document)),
51            window,
52            can_gc,
53        )
54    }
55}
56
57// https://dom.spec.whatwg.org/#domimplementation
58impl DOMImplementationMethods<crate::DomTypeHolder> for DOMImplementation {
59    /// <https://dom.spec.whatwg.org/#dom-domimplementation-createdocumenttype>
60    fn CreateDocumentType(
61        &self,
62        qualified_name: DOMString,
63        pubid: DOMString,
64        sysid: DOMString,
65        can_gc: CanGc,
66    ) -> Fallible<DomRoot<DocumentType>> {
67        // Step 1. If name is not a valid doctype name, then throw an
68        //      "InvalidCharacterError" DOMException.
69        if !is_valid_doctype_name(&qualified_name) {
70            debug!("Not a valid doctype name");
71            return Err(Error::InvalidCharacter(None));
72        }
73
74        Ok(DocumentType::new(
75            qualified_name,
76            Some(pubid),
77            Some(sysid),
78            &self.document,
79            can_gc,
80        ))
81    }
82
83    /// <https://dom.spec.whatwg.org/#dom-domimplementation-createdocument>
84    fn CreateDocument(
85        &self,
86        maybe_namespace: Option<DOMString>,
87        qname: DOMString,
88        maybe_doctype: Option<&DocumentType>,
89        can_gc: CanGc,
90    ) -> Fallible<DomRoot<XMLDocument>> {
91        let win = self.document.window();
92        let loader = DocumentLoader::new(&self.document.loader());
93        let namespace = namespace_from_domstring(maybe_namespace.to_owned());
94
95        let content_type = match namespace {
96            ns!(html) => "application/xhtml+xml",
97            ns!(svg) => "image/svg+xml",
98            _ => "application/xml",
99        }
100        .parse()
101        .unwrap();
102
103        // Step 1. Let document be a new XMLDocument.
104        let doc = XMLDocument::new(
105            win,
106            HasBrowsingContext::No,
107            None,
108            self.document.origin().clone(),
109            IsHTMLDocument::NonHTMLDocument,
110            Some(content_type),
111            None,
112            DocumentActivity::Inactive,
113            DocumentSource::NotFromParser,
114            loader,
115            Some(self.document.insecure_requests_policy()),
116            self.document.has_trustworthy_ancestor_or_current_origin(),
117            self.document.custom_element_reaction_stack(),
118            can_gc,
119        );
120
121        // Step 2. Let element be null.
122        // Step 3. If qualifiedName is not the empty string, then set element to the result of running
123        // the internal createElementNS steps, given document, namespace, qualifiedName, and an empty dictionary.
124        let maybe_elem = if qname.is_empty() {
125            None
126        } else {
127            let options =
128                StringOrElementCreationOptions::ElementCreationOptions(ElementCreationOptions {
129                    is: None,
130                });
131            match doc
132                .upcast::<Document>()
133                .CreateElementNS(maybe_namespace, qname, options, can_gc)
134            {
135                Err(error) => return Err(error),
136                Ok(elem) => Some(elem),
137            }
138        };
139
140        {
141            let doc_node = doc.upcast::<Node>();
142
143            // Step 4.
144            if let Some(doc_type) = maybe_doctype {
145                doc_node.AppendChild(doc_type.upcast(), can_gc).unwrap();
146            }
147
148            // Step 5.
149            if let Some(ref elem) = maybe_elem {
150                doc_node.AppendChild(elem.upcast(), can_gc).unwrap();
151            }
152        }
153
154        // Step 6.
155        // The origin is already set
156
157        // Step 7.
158        Ok(doc)
159    }
160
161    /// <https://dom.spec.whatwg.org/#dom-domimplementation-createhtmldocument>
162    fn CreateHTMLDocument(&self, title: Option<DOMString>, can_gc: CanGc) -> DomRoot<Document> {
163        let win = self.document.window();
164        let loader = DocumentLoader::new(&self.document.loader());
165
166        // Step 1. Let doc be a new document that is an HTML document.
167        // Step 2. Set doc’s content type to "text/html".
168        let doc = Document::new(
169            win,
170            HasBrowsingContext::No,
171            None,
172            None,
173            // Step 8. doc’s origin is this’s associated document’s origin.
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            self.document.creation_sandboxing_flag_set(),
190            can_gc,
191        );
192
193        {
194            // Step 3. Append a new doctype, with "html" as its name and with its node document set to doc, to doc.
195            let doc_node = doc.upcast::<Node>();
196            let doc_type = DocumentType::new(DOMString::from("html"), None, None, &doc, can_gc);
197            doc_node.AppendChild(doc_type.upcast(), can_gc).unwrap();
198        }
199
200        {
201            // Step 4. Append the result of creating an element given doc, "html",
202            // and the HTML namespace, to doc.
203            let doc_node = doc.upcast::<Node>();
204            let doc_html = DomRoot::upcast::<Node>(Element::create(
205                QualName::new(None, ns!(html), local_name!("html")),
206                None,
207                &doc,
208                ElementCreator::ScriptCreated,
209                CustomElementCreationMode::Asynchronous,
210                None,
211                can_gc,
212            ));
213            doc_node
214                .AppendChild(&doc_html, can_gc)
215                .expect("Appending failed");
216
217            {
218                // Step 5. Append the result of creating an element given doc, "head",
219                // and the HTML namespace, to the html element created earlier.
220                let doc_head = DomRoot::upcast::<Node>(Element::create(
221                    QualName::new(None, ns!(html), local_name!("head")),
222                    None,
223                    &doc,
224                    ElementCreator::ScriptCreated,
225                    CustomElementCreationMode::Asynchronous,
226                    None,
227                    can_gc,
228                ));
229                doc_html.AppendChild(&doc_head, can_gc).unwrap();
230
231                // Step 6. If title is given:
232                if let Some(title_str) = title {
233                    // Step 6.1. Append the result of creating an element given doc, "title",
234                    // and the HTML namespace, to the head element created earlier.
235                    let doc_title = DomRoot::upcast::<Node>(Element::create(
236                        QualName::new(None, ns!(html), local_name!("title")),
237                        None,
238                        &doc,
239                        ElementCreator::ScriptCreated,
240                        CustomElementCreationMode::Asynchronous,
241                        None,
242                        can_gc,
243                    ));
244                    doc_head.AppendChild(&doc_title, can_gc).unwrap();
245
246                    // Step 6.2. Append a new Text node, with its data set to title (which could be the empty string)
247                    // and its node document set to doc, to the title element created earlier.
248                    let title_text = Text::new(title_str, &doc, can_gc);
249                    doc_title.AppendChild(title_text.upcast(), can_gc).unwrap();
250                }
251            }
252
253            // Step 7. Append the result of creating an element given doc, "body",
254            // and the HTML namespace, to the html element created earlier.
255            let doc_body = Element::create(
256                QualName::new(None, ns!(html), local_name!("body")),
257                None,
258                &doc,
259                ElementCreator::ScriptCreated,
260                CustomElementCreationMode::Asynchronous,
261                None,
262                can_gc,
263            );
264            doc_html.AppendChild(doc_body.upcast(), can_gc).unwrap();
265        }
266
267        // Step 9. Return doc.
268        doc
269    }
270
271    /// <https://dom.spec.whatwg.org/#dom-domimplementation-hasfeature>
272    fn HasFeature(&self) -> bool {
273        true
274    }
275}