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
58#[allow(non_snake_case)]
59impl DOMImplementationMethods<crate::DomTypeHolder> for DOMImplementation {
60    /// <https://dom.spec.whatwg.org/#dom-domimplementation-createdocumenttype>
61    fn CreateDocumentType(
62        &self,
63        qualified_name: DOMString,
64        pubid: DOMString,
65        sysid: DOMString,
66        can_gc: CanGc,
67    ) -> Fallible<DomRoot<DocumentType>> {
68        // Step 1. If name is not a valid doctype name, then throw an
69        //      "InvalidCharacterError" DOMException.
70        if !is_valid_doctype_name(&qualified_name) {
71            debug!("Not a valid doctype name");
72            return Err(Error::InvalidCharacter);
73        }
74
75        Ok(DocumentType::new(
76            qualified_name,
77            Some(pubid),
78            Some(sysid),
79            &self.document,
80            can_gc,
81        ))
82    }
83
84    /// <https://dom.spec.whatwg.org/#dom-domimplementation-createdocument>
85    fn CreateDocument(
86        &self,
87        maybe_namespace: Option<DOMString>,
88        qname: DOMString,
89        maybe_doctype: Option<&DocumentType>,
90        can_gc: CanGc,
91    ) -> Fallible<DomRoot<XMLDocument>> {
92        let win = self.document.window();
93        let loader = DocumentLoader::new(&self.document.loader());
94        let namespace = namespace_from_domstring(maybe_namespace.to_owned());
95
96        let content_type = match namespace {
97            ns!(html) => "application/xhtml+xml",
98            ns!(svg) => "image/svg+xml",
99            _ => "application/xml",
100        }
101        .parse()
102        .unwrap();
103
104        // Step 1.
105        let doc = XMLDocument::new(
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            can_gc,
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            match doc
133                .upcast::<Document>()
134                .CreateElementNS(maybe_namespace, qname, options, can_gc)
135            {
136                Err(error) => return Err(error),
137                Ok(elem) => Some(elem),
138            }
139        };
140
141        {
142            let doc_node = doc.upcast::<Node>();
143
144            // Step 4.
145            if let Some(doc_type) = maybe_doctype {
146                doc_node.AppendChild(doc_type.upcast(), can_gc).unwrap();
147            }
148
149            // Step 5.
150            if let Some(ref elem) = maybe_elem {
151                doc_node.AppendChild(elem.upcast(), can_gc).unwrap();
152            }
153        }
154
155        // Step 6.
156        // The origin is already set
157
158        // Step 7.
159        Ok(doc)
160    }
161
162    // https://dom.spec.whatwg.org/#dom-domimplementation-createhtmldocument
163    fn CreateHTMLDocument(&self, title: Option<DOMString>, can_gc: CanGc) -> DomRoot<Document> {
164        let win = self.document.window();
165        let loader = DocumentLoader::new(&self.document.loader());
166
167        // Step 1-2.
168        let doc = Document::new(
169            win,
170            HasBrowsingContext::No,
171            None,
172            self.document.origin().clone(),
173            IsHTMLDocument::HTMLDocument,
174            None,
175            None,
176            DocumentActivity::Inactive,
177            DocumentSource::NotFromParser,
178            loader,
179            None,
180            None,
181            Default::default(),
182            false,
183            self.document.allow_declarative_shadow_roots(),
184            Some(self.document.insecure_requests_policy()),
185            self.document.has_trustworthy_ancestor_or_current_origin(),
186            self.document.custom_element_reaction_stack(),
187            can_gc,
188        );
189
190        {
191            // Step 3.
192            let doc_node = doc.upcast::<Node>();
193            let doc_type = DocumentType::new(DOMString::from("html"), None, None, &doc, can_gc);
194            doc_node.AppendChild(doc_type.upcast(), can_gc).unwrap();
195        }
196
197        {
198            // Step 4.
199            let doc_node = doc.upcast::<Node>();
200            let doc_html = DomRoot::upcast::<Node>(Element::create(
201                QualName::new(None, ns!(html), local_name!("html")),
202                None,
203                &doc,
204                ElementCreator::ScriptCreated,
205                CustomElementCreationMode::Asynchronous,
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>(Element::create(
216                    QualName::new(None, ns!(html), local_name!("head")),
217                    None,
218                    &doc,
219                    ElementCreator::ScriptCreated,
220                    CustomElementCreationMode::Asynchronous,
221                    None,
222                    can_gc,
223                ));
224                doc_html.AppendChild(&doc_head, can_gc).unwrap();
225
226                // Step 6.
227                if let Some(title_str) = title {
228                    // Step 6.1.
229                    let doc_title = DomRoot::upcast::<Node>(Element::create(
230                        QualName::new(None, ns!(html), local_name!("title")),
231                        None,
232                        &doc,
233                        ElementCreator::ScriptCreated,
234                        CustomElementCreationMode::Asynchronous,
235                        None,
236                        can_gc,
237                    ));
238                    doc_head.AppendChild(&doc_title, can_gc).unwrap();
239
240                    // Step 6.2.
241                    let title_text = Text::new(title_str, &doc, can_gc);
242                    doc_title.AppendChild(title_text.upcast(), can_gc).unwrap();
243                }
244            }
245
246            // Step 7.
247            let doc_body = Element::create(
248                QualName::new(None, ns!(html), local_name!("body")),
249                None,
250                &doc,
251                ElementCreator::ScriptCreated,
252                CustomElementCreationMode::Asynchronous,
253                None,
254                can_gc,
255            );
256            doc_html.AppendChild(doc_body.upcast(), can_gc).unwrap();
257        }
258
259        // Step 8.
260        // The origin is already set
261
262        // Step 9.
263        doc
264    }
265
266    // https://dom.spec.whatwg.org/#dom-domimplementation-hasfeature
267    fn HasFeature(&self) -> bool {
268        true
269    }
270}