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