Skip to main content

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 js::context::JSContext;
8use script_bindings::error::Error;
9use script_bindings::reflector::{Reflector, reflect_dom_object_with_cx};
10use script_traits::DocumentActivity;
11
12use crate::dom::bindings::codegen::Bindings::DOMImplementationBinding::DOMImplementationMethods;
13use crate::dom::bindings::codegen::Bindings::DocumentBinding::{
14    DocumentMethods, ElementCreationOptions,
15};
16use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
17use crate::dom::bindings::codegen::UnionTypes::StringOrElementCreationOptions;
18use crate::dom::bindings::domname::{is_valid_doctype_name, namespace_from_domstring};
19use crate::dom::bindings::error::Fallible;
20use crate::dom::bindings::inheritance::Castable;
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::event_loop::document_loader::DocumentLoader;
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(cx: &mut JSContext, document: &Document) -> DomRoot<DOMImplementation> {
48        let window = document.window();
49        reflect_dom_object_with_cx(
50            Box::new(DOMImplementation::new_inherited(document)),
51            window,
52            cx,
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        cx: &mut js::context::JSContext,
63        qualified_name: DOMString,
64        pubid: DOMString,
65        sysid: DOMString,
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            cx,
76            qualified_name,
77            Some(pubid),
78            Some(sysid),
79            &self.document,
80        ))
81    }
82
83    /// <https://dom.spec.whatwg.org/#dom-domimplementation-createdocument>
84    fn CreateDocument(
85        &self,
86        cx: &mut JSContext,
87        maybe_namespace: Option<DOMString>,
88        qname: DOMString,
89        maybe_doctype: Option<&DocumentType>,
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            cx,
106            win,
107            HasBrowsingContext::No,
108            None,
109            self.document.origin().clone(),
110            IsHTMLDocument::NonHTMLDocument,
111            Some(content_type),
112            None,
113            DocumentActivity::Inactive,
114            DocumentSource::NotFromParser,
115            loader,
116            Some(self.document.insecure_requests_policy()),
117            self.document.has_trustworthy_ancestor_or_current_origin(),
118            self.document.custom_element_reaction_stack(),
119            self.document.image_cache(),
120        );
121
122        // Step 2. Let element be null.
123        // Step 3. If qualifiedName is not the empty string, then set element to the result of running
124        // the internal createElementNS steps, given document, namespace, qualifiedName, and an empty dictionary.
125        let maybe_elem = if qname.is_empty() {
126            None
127        } else {
128            let options =
129                StringOrElementCreationOptions::ElementCreationOptions(ElementCreationOptions {
130                    is: None,
131                });
132            Some(
133                doc.upcast::<Document>()
134                    .CreateElementNS(cx, maybe_namespace, qname, options)?,
135            )
136        };
137
138        {
139            let doc_node = doc.upcast::<Node>();
140
141            // Step 4.
142            if let Some(doc_type) = maybe_doctype {
143                doc_node.AppendChild(cx, doc_type.upcast()).unwrap();
144            }
145
146            // Step 5.
147            if let Some(ref elem) = maybe_elem {
148                doc_node.AppendChild(cx, elem.upcast()).unwrap();
149            }
150        }
151
152        // Step 6.
153        // The origin is already set
154
155        // Step 7.
156        Ok(doc)
157    }
158
159    /// <https://dom.spec.whatwg.org/#dom-domimplementation-createhtmldocument>
160    fn CreateHTMLDocument(
161        &self,
162        cx: &mut JSContext,
163        title: Option<DOMString>,
164    ) -> DomRoot<Document> {
165        let win = self.document.window();
166        let loader = DocumentLoader::new(&self.document.loader());
167
168        // Step 1. Let doc be a new document that is an HTML document.
169        // Step 2. Set doc’s content type to "text/html".
170        let doc = Document::new(
171            cx,
172            win,
173            HasBrowsingContext::No,
174            None,
175            None,
176            // Step 8. doc’s origin is this’s associated document’s origin.
177            self.document.origin().clone(),
178            IsHTMLDocument::HTMLDocument,
179            None,
180            None,
181            DocumentActivity::Inactive,
182            DocumentSource::NotFromParser,
183            loader,
184            None,
185            None,
186            Default::default(),
187            false,
188            self.document.allow_declarative_shadow_roots(),
189            Some(self.document.insecure_requests_policy()),
190            self.document.has_trustworthy_ancestor_or_current_origin(),
191            self.document.custom_element_reaction_stack(),
192            self.document.creation_sandboxing_flag_set(),
193            self.document.pipeline_id(),
194            self.document.image_cache(),
195        );
196
197        {
198            // Step 3. Append a new doctype, with "html" as its name and with its node document set to doc, to doc.
199            let doc_node = doc.upcast::<Node>();
200            let doc_type = DocumentType::new(cx, DOMString::from("html"), None, None, &doc);
201            doc_node.AppendChild(cx, doc_type.upcast()).unwrap();
202        }
203
204        {
205            // Step 4. Append the result of creating an element given doc, "html",
206            // and the HTML namespace, to doc.
207            let doc_node = doc.upcast::<Node>();
208            let doc_html = DomRoot::upcast::<Node>(Element::create(
209                cx,
210                QualName::new(None, ns!(html), local_name!("html")),
211                None,
212                &doc,
213                ElementCreator::ScriptCreated,
214                CustomElementCreationMode::Asynchronous,
215                None,
216            ));
217            doc_node
218                .AppendChild(cx, &doc_html)
219                .expect("Appending failed");
220
221            {
222                // Step 5. Append the result of creating an element given doc, "head",
223                // and the HTML namespace, to the html element created earlier.
224                let doc_head = DomRoot::upcast::<Node>(Element::create(
225                    cx,
226                    QualName::new(None, ns!(html), local_name!("head")),
227                    None,
228                    &doc,
229                    ElementCreator::ScriptCreated,
230                    CustomElementCreationMode::Asynchronous,
231                    None,
232                ));
233                doc_html.AppendChild(cx, &doc_head).unwrap();
234
235                // Step 6. If title is given:
236                if let Some(title_str) = title {
237                    // Step 6.1. Append the result of creating an element given doc, "title",
238                    // and the HTML namespace, to the head element created earlier.
239                    let doc_title = DomRoot::upcast::<Node>(Element::create(
240                        cx,
241                        QualName::new(None, ns!(html), local_name!("title")),
242                        None,
243                        &doc,
244                        ElementCreator::ScriptCreated,
245                        CustomElementCreationMode::Asynchronous,
246                        None,
247                    ));
248                    doc_head.AppendChild(cx, &doc_title).unwrap();
249
250                    // Step 6.2. Append a new Text node, with its data set to title (which could be the empty string)
251                    // and its node document set to doc, to the title element created earlier.
252                    let title_text = Text::new(cx, title_str, &doc);
253                    doc_title.AppendChild(cx, title_text.upcast()).unwrap();
254                }
255            }
256
257            // Step 7. Append the result of creating an element given doc, "body",
258            // and the HTML namespace, to the html element created earlier.
259            let doc_body = Element::create(
260                cx,
261                QualName::new(None, ns!(html), local_name!("body")),
262                None,
263                &doc,
264                ElementCreator::ScriptCreated,
265                CustomElementCreationMode::Asynchronous,
266                None,
267            );
268            doc_html.AppendChild(cx, doc_body.upcast()).unwrap();
269        }
270
271        // Step 9. Return doc.
272        doc
273    }
274
275    /// <https://dom.spec.whatwg.org/#dom-domimplementation-hasfeature>
276    fn HasFeature(&self) -> bool {
277        true
278    }
279}