script/dom/
create.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 html5ever::{LocalName, Prefix, QualName, local_name, ns};
6use js::rust::HandleObject;
7
8use crate::dom::bindings::error::{report_pending_exception, throw_dom_exception};
9use crate::dom::bindings::reflector::DomGlobal;
10use crate::dom::bindings::root::DomRoot;
11use crate::dom::customelementregistry::{
12    CustomElementState, is_valid_custom_element_name, upgrade_element,
13};
14use crate::dom::document::Document;
15use crate::dom::element::{CustomElementCreationMode, Element, ElementCreator};
16use crate::dom::globalscope::GlobalScope;
17use crate::dom::html::htmlanchorelement::HTMLAnchorElement;
18use crate::dom::html::htmlareaelement::HTMLAreaElement;
19use crate::dom::html::htmlaudioelement::HTMLAudioElement;
20use crate::dom::html::htmlbaseelement::HTMLBaseElement;
21use crate::dom::html::htmlbodyelement::HTMLBodyElement;
22use crate::dom::html::htmlbrelement::HTMLBRElement;
23use crate::dom::html::htmlbuttonelement::HTMLButtonElement;
24use crate::dom::html::htmlcanvaselement::HTMLCanvasElement;
25use crate::dom::html::htmldataelement::HTMLDataElement;
26use crate::dom::html::htmldatalistelement::HTMLDataListElement;
27use crate::dom::html::htmldetailselement::HTMLDetailsElement;
28use crate::dom::html::htmldialogelement::HTMLDialogElement;
29use crate::dom::html::htmldirectoryelement::HTMLDirectoryElement;
30use crate::dom::html::htmldivelement::HTMLDivElement;
31use crate::dom::html::htmldlistelement::HTMLDListElement;
32use crate::dom::html::htmlelement::HTMLElement;
33use crate::dom::html::htmlembedelement::HTMLEmbedElement;
34use crate::dom::html::htmlfieldsetelement::HTMLFieldSetElement;
35use crate::dom::html::htmlfontelement::HTMLFontElement;
36use crate::dom::html::htmlformelement::HTMLFormElement;
37use crate::dom::html::htmlframeelement::HTMLFrameElement;
38use crate::dom::html::htmlframesetelement::HTMLFrameSetElement;
39use crate::dom::html::htmlheadelement::HTMLHeadElement;
40use crate::dom::html::htmlheadingelement::{HTMLHeadingElement, HeadingLevel};
41use crate::dom::html::htmlhrelement::HTMLHRElement;
42use crate::dom::html::htmlhtmlelement::HTMLHtmlElement;
43use crate::dom::html::htmliframeelement::HTMLIFrameElement;
44use crate::dom::html::htmlimageelement::HTMLImageElement;
45use crate::dom::html::htmlinputelement::HTMLInputElement;
46use crate::dom::html::htmllabelelement::HTMLLabelElement;
47use crate::dom::html::htmllegendelement::HTMLLegendElement;
48use crate::dom::html::htmllielement::HTMLLIElement;
49use crate::dom::html::htmllinkelement::HTMLLinkElement;
50use crate::dom::html::htmlmapelement::HTMLMapElement;
51use crate::dom::html::htmlmenuelement::HTMLMenuElement;
52use crate::dom::html::htmlmetaelement::HTMLMetaElement;
53use crate::dom::html::htmlmeterelement::HTMLMeterElement;
54use crate::dom::html::htmlmodelement::HTMLModElement;
55use crate::dom::html::htmlobjectelement::HTMLObjectElement;
56use crate::dom::html::htmlolistelement::HTMLOListElement;
57use crate::dom::html::htmloptgroupelement::HTMLOptGroupElement;
58use crate::dom::html::htmloptionelement::HTMLOptionElement;
59use crate::dom::html::htmloutputelement::HTMLOutputElement;
60use crate::dom::html::htmlparagraphelement::HTMLParagraphElement;
61use crate::dom::html::htmlparamelement::HTMLParamElement;
62use crate::dom::html::htmlpictureelement::HTMLPictureElement;
63use crate::dom::html::htmlpreelement::HTMLPreElement;
64use crate::dom::html::htmlprogresselement::HTMLProgressElement;
65use crate::dom::html::htmlquoteelement::HTMLQuoteElement;
66use crate::dom::html::htmlscriptelement::HTMLScriptElement;
67use crate::dom::html::htmlselectelement::HTMLSelectElement;
68use crate::dom::html::htmlslotelement::HTMLSlotElement;
69use crate::dom::html::htmlsourceelement::HTMLSourceElement;
70use crate::dom::html::htmlspanelement::HTMLSpanElement;
71use crate::dom::html::htmlstyleelement::HTMLStyleElement;
72use crate::dom::html::htmltablecaptionelement::HTMLTableCaptionElement;
73use crate::dom::html::htmltablecellelement::HTMLTableCellElement;
74use crate::dom::html::htmltablecolelement::HTMLTableColElement;
75use crate::dom::html::htmltableelement::HTMLTableElement;
76use crate::dom::html::htmltablerowelement::HTMLTableRowElement;
77use crate::dom::html::htmltablesectionelement::HTMLTableSectionElement;
78use crate::dom::html::htmltemplateelement::HTMLTemplateElement;
79use crate::dom::html::htmltextareaelement::HTMLTextAreaElement;
80use crate::dom::html::htmltimeelement::HTMLTimeElement;
81use crate::dom::html::htmltitleelement::HTMLTitleElement;
82use crate::dom::html::htmltrackelement::HTMLTrackElement;
83use crate::dom::html::htmlulistelement::HTMLUListElement;
84use crate::dom::html::htmlunknownelement::HTMLUnknownElement;
85use crate::dom::html::htmlvideoelement::HTMLVideoElement;
86use crate::dom::svgelement::SVGElement;
87use crate::dom::svgimageelement::SVGImageElement;
88use crate::dom::svgsvgelement::SVGSVGElement;
89use crate::realms::{InRealm, enter_realm};
90use crate::script_runtime::CanGc;
91use crate::script_thread::ScriptThread;
92
93fn create_svg_element(
94    name: QualName,
95    prefix: Option<Prefix>,
96    document: &Document,
97    proto: Option<HandleObject>,
98) -> DomRoot<Element> {
99    assert_eq!(name.ns, ns!(svg));
100
101    macro_rules! make(
102        ($ctor:ident) => ({
103            let obj = $ctor::new(name.local, prefix, document, proto, CanGc::note());
104            DomRoot::upcast(obj)
105        });
106        ($ctor:ident, $($arg:expr),+) => ({
107            let obj = $ctor::new(name.local, prefix, document, proto, $($arg),+, CanGc::note());
108            DomRoot::upcast(obj)
109        })
110    );
111
112    match name.local {
113        local_name!("image") => make!(SVGImageElement),
114        local_name!("svg") => make!(SVGSVGElement),
115        _ => make!(SVGElement),
116    }
117}
118
119/// <https://dom.spec.whatwg.org/#concept-create-element>
120#[allow(unsafe_code)]
121#[allow(clippy::too_many_arguments)]
122fn create_html_element(
123    name: QualName,
124    prefix: Option<Prefix>,
125    is: Option<LocalName>,
126    document: &Document,
127    creator: ElementCreator,
128    mode: CustomElementCreationMode,
129    proto: Option<HandleObject>,
130    can_gc: CanGc,
131) -> DomRoot<Element> {
132    assert_eq!(name.ns, ns!(html));
133
134    // Step 2. Let definition be the result of looking up a custom element
135    // definition given document, namespace, localName, and is.
136    let definition = document.lookup_custom_element_definition(&name.ns, &name.local, is.as_ref());
137
138    // Step 3. If definition is non-null...
139    if let Some(definition) = definition {
140        // ...and definition’s name is not equal to its local name
141        // (i.e., definition represents a customized built-in element):
142        if !definition.is_autonomous() {
143            // Step 3.1. Let interface be the element interface for localName and the HTML namespace.
144            // Step 3.2. Set result to a new element that implements interface, with no attributes,
145            // namespace set to the HTML namespace, namespace prefix set to prefix,
146            // local name set to localName, custom element state set to "undefined",
147            // custom element definition set to null, is value set to is,
148            // and node document set to document.
149            let element = create_native_html_element(name, prefix, document, creator, proto);
150            element.set_is(definition.name.clone());
151            element.set_custom_element_state(CustomElementState::Undefined);
152            match mode {
153                // Step 3.3. If synchronousCustomElements is true, then run this step while catching any exceptions:
154                CustomElementCreationMode::Synchronous => {
155                    // Step 3.3.1. Upgrade result using definition.
156                    upgrade_element(definition, &element, can_gc);
157                    // TODO: "If this step threw an exception exception:" steps.
158                },
159                // Step 3.4. Otherwise, enqueue a custom element upgrade reaction given result and definition.
160                CustomElementCreationMode::Asynchronous => {
161                    ScriptThread::enqueue_upgrade_reaction(&element, definition)
162                },
163            }
164            return element;
165        } else {
166            // Step 4. Otherwise, if definition is non-null:
167            match mode {
168                // Step 4.1. If synchronousCustomElements is true, then run these
169                // steps while catching any exceptions:
170                CustomElementCreationMode::Synchronous => {
171                    let local_name = name.local.clone();
172                    // TODO(jdm) Pass proto to create_element?
173                    // Steps 4.1.1-4.1.11
174                    return match definition.create_element(document, prefix.clone(), can_gc) {
175                        Ok(element) => {
176                            element.set_custom_element_definition(definition.clone());
177                            element
178                        },
179                        Err(error) => {
180                            // If any of these steps threw an exception exception:
181                            let global =
182                                GlobalScope::current().unwrap_or_else(|| document.global());
183                            let cx = GlobalScope::get_cx();
184
185                            // Substep 1. Report exception for definition’s constructor’s corresponding
186                            // JavaScript object’s associated realm’s global object.
187
188                            let ar = enter_realm(&*global);
189                            throw_dom_exception(cx, &global, error, can_gc);
190                            report_pending_exception(cx, true, InRealm::Entered(&ar), can_gc);
191
192                            // Substep 2. Set result to a new element that implements the HTMLUnknownElement interface,
193                            // with no attributes, namespace set to the HTML namespace, namespace prefix set to prefix,
194                            // local name set to localName, custom element state set to "failed",
195                            // custom element definition set to null, is value set to null,
196                            // and node document set to document.
197                            let element = DomRoot::upcast::<Element>(HTMLUnknownElement::new(
198                                local_name, prefix, document, proto, can_gc,
199                            ));
200                            element.set_custom_element_state(CustomElementState::Failed);
201                            element
202                        },
203                    };
204                },
205                // Step 4.2. Otherwise:
206                CustomElementCreationMode::Asynchronous => {
207                    // Step 4.2.1. Set result to a new element that implements the HTMLElement interface,
208                    // with no attributes, namespace set to the HTML namespace, namespace prefix set to
209                    // prefix, local name set to localName, custom element state set to "undefined",
210                    // custom element definition set to null, is value set to null, and node document
211                    // set to document.
212                    let result = DomRoot::upcast::<Element>(HTMLElement::new(
213                        name.local.clone(),
214                        prefix.clone(),
215                        document,
216                        proto,
217                        can_gc,
218                    ));
219                    result.set_custom_element_state(CustomElementState::Undefined);
220                    // Step 4.2.2. Enqueue a custom element upgrade reaction given result and definition.
221                    ScriptThread::enqueue_upgrade_reaction(&result, definition);
222                    return result;
223                },
224            }
225        }
226    }
227
228    // Step 5. Otherwise:
229    // Step 5.1. Let interface be the element interface for localName and namespace.
230    // Step 5.2. Set result to a new element that implements interface, with no attributes,
231    // namespace set to namespace, namespace prefix set to prefix, local name set to localName,
232    // custom element state set to "uncustomized", custom element definition set to null,
233    // is value set to is, and node document set to document.
234    let result = create_native_html_element(name.clone(), prefix, document, creator, proto);
235    // Step 5.3. If namespace is the HTML namespace, and either localName is a valid custom element name or
236    // is is non-null, then set result’s custom element state to "undefined".
237    match is {
238        Some(is) => {
239            result.set_is(is);
240            result.set_custom_element_state(CustomElementState::Undefined);
241        },
242        None => {
243            if is_valid_custom_element_name(&name.local) {
244                result.set_custom_element_state(CustomElementState::Undefined);
245            } else {
246                result.set_custom_element_state(CustomElementState::Uncustomized);
247            }
248        },
249    };
250
251    // Step 6. Return result.
252    result
253}
254
255pub(crate) fn create_native_html_element(
256    name: QualName,
257    prefix: Option<Prefix>,
258    document: &Document,
259    creator: ElementCreator,
260    proto: Option<HandleObject>,
261) -> DomRoot<Element> {
262    assert_eq!(name.ns, ns!(html));
263
264    macro_rules! make(
265        ($ctor:ident) => ({
266            let obj = $ctor::new(name.local, prefix, document, proto, CanGc::note());
267            DomRoot::upcast(obj)
268        });
269        ($ctor:ident, $($arg:expr),+) => ({
270            let obj = $ctor::new(name.local, prefix, document, proto, $($arg),+, CanGc::note());
271            DomRoot::upcast(obj)
272        })
273    );
274
275    // This is a big match, and the IDs for inline-interned atoms are not very structured.
276    // Perhaps we should build a perfect hash from those IDs instead.
277    // https://html.spec.whatwg.org/multipage/#elements-in-the-dom
278    match name.local {
279        local_name!("a") => make!(HTMLAnchorElement),
280        local_name!("abbr") => make!(HTMLElement),
281        local_name!("acronym") => make!(HTMLElement),
282        local_name!("address") => make!(HTMLElement),
283        local_name!("area") => make!(HTMLAreaElement),
284        local_name!("article") => make!(HTMLElement),
285        local_name!("aside") => make!(HTMLElement),
286        local_name!("audio") => make!(HTMLAudioElement),
287        local_name!("b") => make!(HTMLElement),
288        local_name!("base") => make!(HTMLBaseElement),
289        local_name!("bdi") => make!(HTMLElement),
290        local_name!("bdo") => make!(HTMLElement),
291        // https://html.spec.whatwg.org/multipage/#other-elements,-attributes-and-apis:bgsound
292        local_name!("bgsound") => make!(HTMLUnknownElement),
293        local_name!("big") => make!(HTMLElement),
294        // https://html.spec.whatwg.org/multipage/#other-elements,-attributes-and-apis:blink
295        local_name!("blink") => make!(HTMLUnknownElement),
296        // https://html.spec.whatwg.org/multipage/#the-blockquote-element
297        local_name!("blockquote") => make!(HTMLQuoteElement),
298        local_name!("body") => make!(HTMLBodyElement),
299        local_name!("br") => make!(HTMLBRElement),
300        local_name!("button") => make!(HTMLButtonElement),
301        local_name!("canvas") => make!(HTMLCanvasElement),
302        local_name!("caption") => make!(HTMLTableCaptionElement),
303        local_name!("center") => make!(HTMLElement),
304        local_name!("cite") => make!(HTMLElement),
305        local_name!("code") => make!(HTMLElement),
306        local_name!("col") => make!(HTMLTableColElement),
307        local_name!("colgroup") => make!(HTMLTableColElement),
308        local_name!("data") => make!(HTMLDataElement),
309        local_name!("datalist") => make!(HTMLDataListElement),
310        local_name!("dd") => make!(HTMLElement),
311        local_name!("del") => make!(HTMLModElement),
312        local_name!("details") => make!(HTMLDetailsElement),
313        local_name!("dfn") => make!(HTMLElement),
314        local_name!("dialog") => make!(HTMLDialogElement),
315        local_name!("dir") => make!(HTMLDirectoryElement),
316        local_name!("div") => make!(HTMLDivElement),
317        local_name!("dl") => make!(HTMLDListElement),
318        local_name!("dt") => make!(HTMLElement),
319        local_name!("em") => make!(HTMLElement),
320        local_name!("embed") => make!(HTMLEmbedElement),
321        local_name!("fieldset") => make!(HTMLFieldSetElement),
322        local_name!("figcaption") => make!(HTMLElement),
323        local_name!("figure") => make!(HTMLElement),
324        local_name!("font") => make!(HTMLFontElement),
325        local_name!("footer") => make!(HTMLElement),
326        local_name!("form") => make!(HTMLFormElement),
327        local_name!("frame") => make!(HTMLFrameElement),
328        local_name!("frameset") => make!(HTMLFrameSetElement),
329        local_name!("h1") => make!(HTMLHeadingElement, HeadingLevel::Heading1),
330        local_name!("h2") => make!(HTMLHeadingElement, HeadingLevel::Heading2),
331        local_name!("h3") => make!(HTMLHeadingElement, HeadingLevel::Heading3),
332        local_name!("h4") => make!(HTMLHeadingElement, HeadingLevel::Heading4),
333        local_name!("h5") => make!(HTMLHeadingElement, HeadingLevel::Heading5),
334        local_name!("h6") => make!(HTMLHeadingElement, HeadingLevel::Heading6),
335        local_name!("head") => make!(HTMLHeadElement),
336        local_name!("header") => make!(HTMLElement),
337        local_name!("hgroup") => make!(HTMLElement),
338        local_name!("hr") => make!(HTMLHRElement),
339        local_name!("html") => make!(HTMLHtmlElement),
340        local_name!("i") => make!(HTMLElement),
341        local_name!("iframe") => make!(HTMLIFrameElement),
342        local_name!("img") => make!(HTMLImageElement, creator),
343        local_name!("input") => make!(HTMLInputElement),
344        local_name!("ins") => make!(HTMLModElement),
345        // https://html.spec.whatwg.org/multipage/#other-elements,-attributes-and-apis:isindex-2
346        local_name!("isindex") => make!(HTMLUnknownElement),
347        local_name!("kbd") => make!(HTMLElement),
348        // https://html.spec.whatwg.org/multipage/#keygen
349        local_name!("keygen") => make!(HTMLUnknownElement),
350        local_name!("label") => make!(HTMLLabelElement),
351        local_name!("legend") => make!(HTMLLegendElement),
352        local_name!("li") => make!(HTMLLIElement),
353        local_name!("link") => make!(HTMLLinkElement, creator),
354        // https://html.spec.whatwg.org/multipage/#other-elements,-attributes-and-apis:listing
355        local_name!("listing") => make!(HTMLPreElement),
356        local_name!("main") => make!(HTMLElement),
357        local_name!("map") => make!(HTMLMapElement),
358        local_name!("mark") => make!(HTMLElement),
359        local_name!("marquee") => make!(HTMLElement),
360        local_name!("menu") => make!(HTMLMenuElement),
361        local_name!("meta") => make!(HTMLMetaElement),
362        local_name!("meter") => make!(HTMLMeterElement),
363        // https://html.spec.whatwg.org/multipage/#other-elements,-attributes-and-apis:multicol
364        local_name!("multicol") => make!(HTMLUnknownElement),
365        local_name!("nav") => make!(HTMLElement),
366        // https://html.spec.whatwg.org/multipage/#other-elements,-attributes-and-apis:nextid
367        local_name!("nextid") => make!(HTMLUnknownElement),
368        local_name!("nobr") => make!(HTMLElement),
369        local_name!("noframes") => make!(HTMLElement),
370        local_name!("noscript") => make!(HTMLElement),
371        local_name!("object") => make!(HTMLObjectElement),
372        local_name!("ol") => make!(HTMLOListElement),
373        local_name!("optgroup") => make!(HTMLOptGroupElement),
374        local_name!("option") => make!(HTMLOptionElement),
375        local_name!("output") => make!(HTMLOutputElement),
376        local_name!("p") => make!(HTMLParagraphElement),
377        local_name!("param") => make!(HTMLParamElement),
378        local_name!("picture") => make!(HTMLPictureElement),
379        local_name!("plaintext") => make!(HTMLPreElement),
380        local_name!("pre") => make!(HTMLPreElement),
381        local_name!("progress") => make!(HTMLProgressElement),
382        local_name!("q") => make!(HTMLQuoteElement),
383        local_name!("rp") => make!(HTMLElement),
384        local_name!("rt") => make!(HTMLElement),
385        local_name!("ruby") => make!(HTMLElement),
386        local_name!("s") => make!(HTMLElement),
387        local_name!("samp") => make!(HTMLElement),
388        local_name!("script") => make!(HTMLScriptElement, creator),
389        local_name!("section") => make!(HTMLElement),
390        local_name!("select") => make!(HTMLSelectElement),
391        local_name!("slot") => make!(HTMLSlotElement),
392        local_name!("small") => make!(HTMLElement),
393        local_name!("source") => make!(HTMLSourceElement),
394        // https://html.spec.whatwg.org/multipage/#other-elements,-attributes-and-apis:spacer
395        local_name!("spacer") => make!(HTMLUnknownElement),
396        local_name!("span") => make!(HTMLSpanElement),
397        local_name!("strike") => make!(HTMLElement),
398        local_name!("strong") => make!(HTMLElement),
399        local_name!("style") => make!(HTMLStyleElement, creator),
400        local_name!("sub") => make!(HTMLElement),
401        local_name!("summary") => make!(HTMLElement),
402        local_name!("sup") => make!(HTMLElement),
403        local_name!("table") => make!(HTMLTableElement),
404        local_name!("tbody") => make!(HTMLTableSectionElement),
405        local_name!("td") => make!(HTMLTableCellElement),
406        local_name!("template") => make!(HTMLTemplateElement),
407        local_name!("textarea") => make!(HTMLTextAreaElement),
408        // https://html.spec.whatwg.org/multipage/#the-tfoot-element:concept-element-dom
409        local_name!("tfoot") => make!(HTMLTableSectionElement),
410        local_name!("th") => make!(HTMLTableCellElement),
411        // https://html.spec.whatwg.org/multipage/#the-thead-element:concept-element-dom
412        local_name!("thead") => make!(HTMLTableSectionElement),
413        local_name!("time") => make!(HTMLTimeElement),
414        local_name!("title") => make!(HTMLTitleElement),
415        local_name!("tr") => make!(HTMLTableRowElement),
416        local_name!("tt") => make!(HTMLElement),
417        local_name!("track") => make!(HTMLTrackElement),
418        local_name!("u") => make!(HTMLElement),
419        local_name!("ul") => make!(HTMLUListElement),
420        local_name!("var") => make!(HTMLElement),
421        local_name!("video") => make!(HTMLVideoElement),
422        local_name!("wbr") => make!(HTMLElement),
423        local_name!("xmp") => make!(HTMLPreElement),
424        _ if is_valid_custom_element_name(&name.local) => make!(HTMLElement),
425        _ => make!(HTMLUnknownElement),
426    }
427}
428
429pub(crate) fn create_element(
430    name: QualName,
431    is: Option<LocalName>,
432    document: &Document,
433    creator: ElementCreator,
434    mode: CustomElementCreationMode,
435    proto: Option<HandleObject>,
436    can_gc: CanGc,
437) -> DomRoot<Element> {
438    let prefix = name.prefix.clone();
439    match name.ns {
440        ns!(html) => create_html_element(name, prefix, is, document, creator, mode, proto, can_gc),
441        ns!(svg) => create_svg_element(name, prefix, document, proto),
442        _ => Element::new(name.local, name.ns, prefix, document, proto, can_gc),
443    }
444}