script/dom/html/
htmltablecellelement.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::{LocalName, Prefix, local_name, ns};
7use js::rust::HandleObject;
8use style::attr::{AttrValue, LengthOrPercentageOrAuto};
9use style::color::AbsoluteColor;
10
11use crate::dom::attr::Attr;
12use crate::dom::bindings::codegen::Bindings::HTMLTableCellElementBinding::HTMLTableCellElementMethods;
13use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
14use crate::dom::bindings::inheritance::Castable;
15use crate::dom::bindings::root::{DomRoot, LayoutDom};
16use crate::dom::bindings::str::DOMString;
17use crate::dom::document::Document;
18use crate::dom::element::{AttributeMutation, Element, LayoutElementHelpers};
19use crate::dom::html::htmlelement::HTMLElement;
20use crate::dom::html::htmltableelement::HTMLTableElement;
21use crate::dom::html::htmltablerowelement::HTMLTableRowElement;
22use crate::dom::html::htmltablesectionelement::HTMLTableSectionElement;
23use crate::dom::node::{LayoutNodeHelpers, Node, NodeDamage};
24use crate::dom::virtualmethods::VirtualMethods;
25use crate::script_runtime::CanGc;
26
27const DEFAULT_COLSPAN: u32 = 1;
28const DEFAULT_ROWSPAN: u32 = 1;
29
30#[dom_struct]
31pub(crate) struct HTMLTableCellElement {
32    htmlelement: HTMLElement,
33}
34
35impl HTMLTableCellElement {
36    fn new_inherited(
37        local_name: LocalName,
38        prefix: Option<Prefix>,
39        document: &Document,
40    ) -> HTMLTableCellElement {
41        HTMLTableCellElement {
42            htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
43        }
44    }
45
46    pub(crate) fn new(
47        local_name: LocalName,
48        prefix: Option<Prefix>,
49        document: &Document,
50        proto: Option<HandleObject>,
51        can_gc: CanGc,
52    ) -> DomRoot<HTMLTableCellElement> {
53        let n = Node::reflect_node_with_proto(
54            Box::new(HTMLTableCellElement::new_inherited(
55                local_name, prefix, document,
56            )),
57            document,
58            proto,
59            can_gc,
60        );
61
62        n.upcast::<Node>().set_weird_parser_insertion_mode();
63        n
64    }
65}
66
67impl HTMLTableCellElementMethods<crate::DomTypeHolder> for HTMLTableCellElement {
68    // https://html.spec.whatwg.org/multipage/#dom-tdth-colspan
69    make_uint_getter!(ColSpan, "colspan", DEFAULT_COLSPAN);
70
71    // https://html.spec.whatwg.org/multipage/#dom-tdth-colspan
72    // > The colSpan IDL attribute must reflect the colspan content attribute. It is clamped to
73    // > the range [1, 1000], and its default value is 1.
74    make_clamped_uint_setter!(SetColSpan, "colspan", 1, 1000, 1);
75
76    // https://html.spec.whatwg.org/multipage/#dom-tdth-rowspan
77    make_uint_getter!(RowSpan, "rowspan", DEFAULT_ROWSPAN);
78
79    // https://html.spec.whatwg.org/multipage/#dom-tdth-rowspan
80    // > The rowSpan IDL attribute must reflect the rowspan content attribute. It is clamped to
81    // > the range [0, 65534], and its default value is 1.
82    make_clamped_uint_setter!(SetRowSpan, "rowspan", 0, 65534, 1);
83
84    // https://html.spec.whatwg.org/multipage/#dom-tdth-bgcolor
85    make_getter!(BgColor, "bgcolor");
86
87    // https://html.spec.whatwg.org/multipage/#dom-tdth-bgcolor
88    make_legacy_color_setter!(SetBgColor, "bgcolor");
89
90    // <https://html.spec.whatwg.org/multipage/#dom-tdth-height>
91    make_getter!(Height, "height");
92
93    // <https://html.spec.whatwg.org/multipage/#dom-tdth-height>
94    make_nonzero_dimension_setter!(SetHeight, "height");
95
96    // <https://html.spec.whatwg.org/multipage/#dom-tdth-width>
97    make_getter!(Width, "width");
98
99    // <https://html.spec.whatwg.org/multipage/#dom-tdth-width>
100    make_nonzero_dimension_setter!(SetWidth, "width");
101
102    /// <https://html.spec.whatwg.org/multipage/#dom-tdth-cellindex>
103    fn CellIndex(&self) -> i32 {
104        let self_node = self.upcast::<Node>();
105
106        let parent_children = match self_node.GetParentNode() {
107            Some(ref parent_node) if parent_node.is::<HTMLTableRowElement>() => {
108                parent_node.children()
109            },
110            _ => return -1,
111        };
112
113        parent_children
114            .filter(|c| c.is::<HTMLTableCellElement>())
115            .position(|c| &*c == self_node)
116            .map_or(-1, |p| p as i32)
117    }
118}
119
120pub(crate) trait HTMLTableCellElementLayoutHelpers<'dom> {
121    fn get_background_color(self) -> Option<AbsoluteColor>;
122    fn get_colspan(self) -> Option<u32>;
123    fn get_rowspan(self) -> Option<u32>;
124    fn get_table(self) -> Option<LayoutDom<'dom, HTMLTableElement>>;
125    fn get_width(self) -> LengthOrPercentageOrAuto;
126    fn get_height(self) -> LengthOrPercentageOrAuto;
127}
128
129impl<'dom> HTMLTableCellElementLayoutHelpers<'dom> for LayoutDom<'dom, HTMLTableCellElement> {
130    fn get_background_color(self) -> Option<AbsoluteColor> {
131        self.upcast::<Element>()
132            .get_attr_for_layout(&ns!(), &local_name!("bgcolor"))
133            .and_then(AttrValue::as_color)
134            .cloned()
135    }
136
137    fn get_colspan(self) -> Option<u32> {
138        self.upcast::<Element>()
139            .get_attr_for_layout(&ns!(), &local_name!("colspan"))
140            .map(AttrValue::as_uint)
141    }
142
143    fn get_rowspan(self) -> Option<u32> {
144        self.upcast::<Element>()
145            .get_attr_for_layout(&ns!(), &local_name!("rowspan"))
146            .map(AttrValue::as_uint)
147    }
148
149    fn get_table(self) -> Option<LayoutDom<'dom, HTMLTableElement>> {
150        let row = self.upcast::<Node>().composed_parent_node_ref()?;
151        row.downcast::<HTMLTableRowElement>()?;
152        let section = row.composed_parent_node_ref()?;
153        section.downcast::<HTMLTableElement>().or_else(|| {
154            section.downcast::<HTMLTableSectionElement>()?;
155            let table = section.composed_parent_node_ref()?;
156            table.downcast::<HTMLTableElement>()
157        })
158    }
159
160    fn get_width(self) -> LengthOrPercentageOrAuto {
161        self.upcast::<Element>()
162            .get_attr_for_layout(&ns!(), &local_name!("width"))
163            .map(AttrValue::as_dimension)
164            .cloned()
165            .unwrap_or(LengthOrPercentageOrAuto::Auto)
166    }
167
168    fn get_height(self) -> LengthOrPercentageOrAuto {
169        self.upcast::<Element>()
170            .get_attr_for_layout(&ns!(), &local_name!("height"))
171            .map(AttrValue::as_dimension)
172            .cloned()
173            .unwrap_or(LengthOrPercentageOrAuto::Auto)
174    }
175}
176
177impl VirtualMethods for HTMLTableCellElement {
178    fn super_type(&self) -> Option<&dyn VirtualMethods> {
179        Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
180    }
181
182    fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
183        if let Some(super_type) = self.super_type() {
184            super_type.attribute_mutated(attr, mutation, can_gc);
185        }
186
187        if matches!(*attr.local_name(), local_name!("colspan")) {
188            self.upcast::<Node>().dirty(NodeDamage::Other);
189        }
190        if matches!(*attr.local_name(), local_name!("rowspan")) {
191            self.upcast::<Node>().dirty(NodeDamage::Other);
192        }
193    }
194
195    fn attribute_affects_presentational_hints(&self, attr: &Attr) -> bool {
196        match attr.local_name() {
197            &local_name!("width") | &local_name!("height") => true,
198            _ => self
199                .super_type()
200                .unwrap()
201                .attribute_affects_presentational_hints(attr),
202        }
203    }
204
205    fn parse_plain_attribute(&self, local_name: &LocalName, value: DOMString) -> AttrValue {
206        match *local_name {
207            local_name!("colspan") => {
208                let mut attr = AttrValue::from_u32(value.into(), DEFAULT_COLSPAN);
209                if let AttrValue::UInt(_, ref mut value) = attr {
210                    // From <https://html.spec.whatwg.org/multipage/#dom-tdth-colspan>:
211                    // > The colSpan IDL attribute must reflect the colspan content attribute. It is clamped to
212                    // > the range [1, 1000], and its default value is 1.
213                    *value = (*value).clamp(1, 1000);
214                }
215                attr
216            },
217            local_name!("rowspan") => {
218                let mut attr = AttrValue::from_u32(value.into(), DEFAULT_ROWSPAN);
219                if let AttrValue::UInt(_, ref mut value) = attr {
220                    // From <https://html.spec.whatwg.org/multipage/#dom-tdth-rowspan>:
221                    // > The rowSpan IDL attribute must reflect the rowspan content attribute. It is clamped to
222                    // > the range [0, 65534], and its default value is 1.
223                    // Note Firefox floors by 1 in quirks mode, but like Chrome and Safari we don't do that.
224                    *value = (*value).clamp(0, 65534);
225                }
226                attr
227            },
228            local_name!("bgcolor") => AttrValue::from_legacy_color(value.into()),
229            local_name!("width") => AttrValue::from_nonzero_dimension(value.into()),
230            local_name!("height") => AttrValue::from_nonzero_dimension(value.into()),
231            _ => self
232                .super_type()
233                .unwrap()
234                .parse_plain_attribute(local_name, value),
235        }
236    }
237}