/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ //! Element nodes. use devtools_traits::AttrInfo; use crate::dom::activation::Activatable; use crate::dom::attr::{Attr, AttrHelpersForLayout}; use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::codegen::Bindings::AttrBinding::AttrMethods; use crate::dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods; use crate::dom::bindings::codegen::Bindings::ElementBinding; use crate::dom::bindings::codegen::Bindings::ElementBinding::ElementMethods; use crate::dom::bindings::codegen::Bindings::EventBinding::EventMethods; use crate::dom::bindings::codegen::Bindings::FunctionBinding::Function; use crate::dom::bindings::codegen::Bindings::HTMLTemplateElementBinding::HTMLTemplateElementMethods; use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; use crate::dom::bindings::codegen::Bindings::WindowBinding::{ScrollBehavior, ScrollToOptions}; use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; use crate::dom::bindings::codegen::UnionTypes::NodeOrString; use crate::dom::bindings::conversions::DerivedFrom; use crate::dom::bindings::error::{Error, ErrorResult, Fallible}; use crate::dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId}; use crate::dom::bindings::refcounted::{Trusted, TrustedPromise}; use crate::dom::bindings::reflector::DomObject; use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom, RootedReference}; use crate::dom::bindings::str::DOMString; use crate::dom::bindings::xmlname::{namespace_from_domstring, validate_and_extract, xml_name_type}; use crate::dom::bindings::xmlname::XMLName::InvalidXMLName; use crate::dom::characterdata::CharacterData; use crate::dom::create::create_element; use crate::dom::customelementregistry::{CallbackReaction, CustomElementDefinition, CustomElementReaction}; use crate::dom::document::{Document, LayoutDocumentHelpers}; use crate::dom::documentfragment::DocumentFragment; use crate::dom::domrect::DOMRect; use crate::dom::domtokenlist::DOMTokenList; use crate::dom::event::Event; use crate::dom::eventtarget::EventTarget; use crate::dom::htmlanchorelement::HTMLAnchorElement; use crate::dom::htmlbodyelement::{HTMLBodyElement, HTMLBodyElementLayoutHelpers}; use crate::dom::htmlbuttonelement::HTMLButtonElement; use crate::dom::htmlcanvaselement::{HTMLCanvasElement, LayoutHTMLCanvasElementHelpers}; use crate::dom::htmlcollection::HTMLCollection; use crate::dom::htmlelement::HTMLElement; use crate::dom::htmlfieldsetelement::HTMLFieldSetElement; use crate::dom::htmlfontelement::{HTMLFontElement, HTMLFontElementLayoutHelpers}; use crate::dom::htmlformelement::FormControlElementHelpers; use crate::dom::htmlhrelement::{HTMLHRElement, HTMLHRLayoutHelpers}; use crate::dom::htmliframeelement::{HTMLIFrameElement, HTMLIFrameElementLayoutMethods}; use crate::dom::htmlimageelement::{HTMLImageElement, LayoutHTMLImageElementHelpers}; use crate::dom::htmlinputelement::{HTMLInputElement, LayoutHTMLInputElementHelpers}; use crate::dom::htmllabelelement::HTMLLabelElement; use crate::dom::htmllegendelement::HTMLLegendElement; use crate::dom::htmllinkelement::HTMLLinkElement; use crate::dom::htmlobjectelement::HTMLObjectElement; use crate::dom::htmloptgroupelement::HTMLOptGroupElement; use crate::dom::htmlselectelement::HTMLSelectElement; use crate::dom::htmlstyleelement::HTMLStyleElement; use crate::dom::htmltablecellelement::{HTMLTableCellElement, HTMLTableCellElementLayoutHelpers}; use crate::dom::htmltableelement::{HTMLTableElement, HTMLTableElementLayoutHelpers}; use crate::dom::htmltablerowelement::{HTMLTableRowElement, HTMLTableRowElementLayoutHelpers}; use crate::dom::htmltablesectionelement::{HTMLTableSectionElement, HTMLTableSectionElementLayoutHelpers}; use crate::dom::htmltemplateelement::HTMLTemplateElement; use crate::dom::htmltextareaelement::{HTMLTextAreaElement, LayoutHTMLTextAreaElementHelpers}; use crate::dom::mutationobserver::{Mutation, MutationObserver}; use crate::dom::namednodemap::NamedNodeMap; use crate::dom::node::{ChildrenMutation, LayoutNodeHelpers, Node}; use crate::dom::node::{NodeDamage, NodeFlags, UnbindContext}; use crate::dom::node::{document_from_node, window_from_node}; use crate::dom::nodelist::NodeList; use crate::dom::promise::Promise; use crate::dom::servoparser::ServoParser; use crate::dom::text::Text; use crate::dom::validation::Validatable; use crate::dom::virtualmethods::{VirtualMethods, vtable_for}; use crate::dom::window::ReflowReason; use dom_struct::dom_struct; use html5ever::{Prefix, LocalName, Namespace, QualName}; use html5ever::serialize; use html5ever::serialize::SerializeOpts; use html5ever::serialize::TraversalScope; use html5ever::serialize::TraversalScope::{ChildrenOnly, IncludeNode}; use js::jsapi::Heap; use js::jsval::JSVal; use msg::constellation_msg::InputMethodType; use net_traits::request::CorsSettings; use ref_filter_map::ref_filter_map; use script_layout_interface::message::ReflowGoal; use crate::script_thread::ScriptThread; use selectors::Element as SelectorsElement; use selectors::attr::{AttrSelectorOperation, NamespaceConstraint, CaseSensitivity}; use selectors::matching::{ElementSelectorFlags, MatchingContext}; use selectors::sink::Push; use servo_arc::Arc; use servo_atoms::Atom; use std::borrow::Cow; use std::cell::{Cell, Ref}; use std::default::Default; use std::fmt; use std::mem; use std::rc::Rc; use std::str::FromStr; use style::CaseSensitivityExt; use style::applicable_declarations::ApplicableDeclarationBlock; use style::attr::{AttrValue, LengthOrPercentageOrAuto}; use style::context::QuirksMode; use style::dom_apis; use style::element_state::ElementState; use style::invalidation::element::restyle_hints::RestyleHint; use style::properties::{ComputedValues, Importance, PropertyDeclaration}; use style::properties::{PropertyDeclarationBlock, parse_style_attribute}; use style::properties::longhands::{self, background_image, border_spacing, font_family, font_size}; use style::properties::longhands::{overflow_x, overflow_y}; use style::rule_tree::CascadeLevel; use style::selector_parser::{NonTSPseudoClass, PseudoElement, RestyleDamage, SelectorImpl, SelectorParser}; use style::selector_parser::extended_filtering; use style::shared_lock::{SharedRwLock, Locked}; use style::thread_state; use style::values::{CSSFloat, Either}; use style::values::{specified, computed}; use crate::stylesheet_loader::StylesheetOwner; use crate::task::TaskOnce; use xml5ever::serialize as xmlSerialize; use xml5ever::serialize::SerializeOpts as XmlSerializeOpts; use xml5ever::serialize::TraversalScope as XmlTraversalScope; use xml5ever::serialize::TraversalScope::ChildrenOnly as XmlChildrenOnly; use xml5ever::serialize::TraversalScope::IncludeNode as XmlIncludeNode; // TODO: Update focus state when the top-level browsing context gains or loses system focus, // and when the element enters or leaves a browsing context container. // https://html.spec.whatwg.org/multipage/#selector-focus #[dom_struct] pub struct Element { node: Node, local_name: LocalName, tag_name: TagName, namespace: Namespace, prefix: DomRefCell>, attrs: DomRefCell>>, id_attribute: DomRefCell>, is: DomRefCell>, #[ignore_malloc_size_of = "Arc"] style_attribute: DomRefCell>>>, attr_list: MutNullableDom, class_list: MutNullableDom, state: Cell, /// These flags are set by the style system to indicate the that certain /// operations may require restyling this element or its descendants. The /// flags are not atomic, so the style system takes care of only set them /// when it has exclusive access to the element. #[ignore_malloc_size_of = "bitflags defined in rust-selectors"] selector_flags: Cell, /// custom_element_reaction_queue: DomRefCell>, /// #[ignore_malloc_size_of = "Rc"] custom_element_definition: DomRefCell>>, /// custom_element_state: Cell, } impl fmt::Debug for Element { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "<{}", self.local_name)?; if let Some(ref id) = *self.id_attribute.borrow() { write!(f, " id={}", id)?; } write!(f, ">") } } impl fmt::Debug for DomRoot { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { (**self).fmt(f) } } #[derive(MallocSizeOf, PartialEq)] pub enum ElementCreator { ParserCreated(u64), ScriptCreated, } pub enum CustomElementCreationMode { Synchronous, Asynchronous, } /// #[derive(Clone, Copy, Eq, JSTraceable, MallocSizeOf, PartialEq)] pub enum CustomElementState { Undefined, Failed, Uncustomized, Custom, } impl ElementCreator { pub fn is_parser_created(&self) -> bool { match *self { ElementCreator::ParserCreated(_) => true, ElementCreator::ScriptCreated => false, } } pub fn return_line_number(&self) -> u64 { match *self { ElementCreator::ParserCreated(l) => l, ElementCreator::ScriptCreated => 1, } } } pub enum AdjacentPosition { BeforeBegin, AfterEnd, AfterBegin, BeforeEnd, } impl FromStr for AdjacentPosition { type Err = Error; fn from_str(position: &str) -> Result { match_ignore_ascii_case! { &*position, "beforebegin" => Ok(AdjacentPosition::BeforeBegin), "afterbegin" => Ok(AdjacentPosition::AfterBegin), "beforeend" => Ok(AdjacentPosition::BeforeEnd), "afterend" => Ok(AdjacentPosition::AfterEnd), _ => Err(Error::Syntax) } } } // // Element methods // impl Element { pub fn create( name: QualName, is: Option, document: &Document, creator: ElementCreator, mode: CustomElementCreationMode, ) -> DomRoot { create_element(name, is, document, creator, mode) } pub fn new_inherited( local_name: LocalName, namespace: Namespace, prefix: Option, document: &Document, ) -> Element { Element::new_inherited_with_state( ElementState::empty(), local_name, namespace, prefix, document, ) } pub fn new_inherited_with_state( state: ElementState, local_name: LocalName, namespace: Namespace, prefix: Option, document: &Document, ) -> Element { Element { node: Node::new_inherited(document), local_name: local_name, tag_name: TagName::new(), namespace: namespace, prefix: DomRefCell::new(prefix), attrs: DomRefCell::new(vec![]), id_attribute: DomRefCell::new(None), is: DomRefCell::new(None), style_attribute: DomRefCell::new(None), attr_list: Default::default(), class_list: Default::default(), state: Cell::new(state), selector_flags: Cell::new(ElementSelectorFlags::empty()), custom_element_reaction_queue: Default::default(), custom_element_definition: Default::default(), custom_element_state: Cell::new(CustomElementState::Uncustomized), } } pub fn new( local_name: LocalName, namespace: Namespace, prefix: Option, document: &Document, ) -> DomRoot { Node::reflect_node( Box::new(Element::new_inherited( local_name, namespace, prefix, document, )), document, ElementBinding::Wrap, ) } pub fn restyle(&self, damage: NodeDamage) { let doc = self.node.owner_doc(); let mut restyle = doc.ensure_pending_restyle(self); // FIXME(bholley): I think we should probably only do this for // NodeStyleDamaged, but I'm preserving existing behavior. restyle.hint.insert(RestyleHint::RESTYLE_SELF); if damage == NodeDamage::OtherNodeDamage { restyle.damage = RestyleDamage::rebuild_and_reflow(); } } pub fn set_is(&self, is: LocalName) { *self.is.borrow_mut() = Some(is); } pub fn get_is(&self) -> Option { self.is.borrow().clone() } pub fn set_custom_element_state(&self, state: CustomElementState) { self.custom_element_state.set(state); } pub fn get_custom_element_state(&self) -> CustomElementState { self.custom_element_state.get() } pub fn set_custom_element_definition(&self, definition: Rc) { *self.custom_element_definition.borrow_mut() = Some(definition); } pub fn get_custom_element_definition(&self) -> Option> { (*self.custom_element_definition.borrow()).clone() } pub fn push_callback_reaction(&self, function: Rc, args: Box<[Heap]>) { self.custom_element_reaction_queue .borrow_mut() .push(CustomElementReaction::Callback(function, args)); } pub fn push_upgrade_reaction(&self, definition: Rc) { self.custom_element_reaction_queue .borrow_mut() .push(CustomElementReaction::Upgrade(definition)); } pub fn clear_reaction_queue(&self) { self.custom_element_reaction_queue.borrow_mut().clear(); } pub fn invoke_reactions(&self) { // TODO: This is not spec compliant, as this will allow some reactions to be processed // after clear_reaction_queue has been called. rooted_vec!(let mut reactions); while !self.custom_element_reaction_queue.borrow().is_empty() { mem::swap( &mut *reactions, &mut *self.custom_element_reaction_queue.borrow_mut(), ); for reaction in reactions.iter() { reaction.invoke(self); } reactions.clear(); } } /// style will be `None` for elements in a `display: none` subtree. otherwise, the element has a /// layout box iff it doesn't have `display: none`. pub fn style(&self) -> Option> { window_from_node(self).style_query(self.upcast::().to_trusted_node_address()) } // https://drafts.csswg.org/cssom-view/#css-layout-box pub fn has_css_layout_box(&self) -> bool { self.style() .map_or(false, |s| !s.get_box().clone_display().is_none()) } // https://drafts.csswg.org/cssom-view/#potentially-scrollable fn potentially_scrollable(&self) -> bool { self.has_css_layout_box() && !self.has_any_visible_overflow() } // https://drafts.csswg.org/cssom-view/#scrolling-box fn has_scrolling_box(&self) -> bool { // TODO: scrolling mechanism, such as scrollbar (We don't have scrollbar yet) // self.has_scrolling_mechanism() self.has_any_hidden_overflow() } fn has_overflow(&self) -> bool { self.ScrollHeight() > self.ClientHeight() || self.ScrollWidth() > self.ClientWidth() } // TODO: Once #19183 is closed (overflow-x/y types moved out of mako), then we could implement // a more generic `fn has_some_overflow(&self, overflow: Overflow)` rather than have // these two `has_any_{visible,hidden}_overflow` methods which are very structurally // similar. /// Computed value of overflow-x or overflow-y is "visible" fn has_any_visible_overflow(&self) -> bool { self.style().map_or(false, |s| { let box_ = s.get_box(); box_.clone_overflow_x() == overflow_x::computed_value::T::Visible || box_.clone_overflow_y() == overflow_y::computed_value::T::Visible }) } /// Computed value of overflow-x or overflow-y is "hidden" fn has_any_hidden_overflow(&self) -> bool { self.style().map_or(false, |s| { let box_ = s.get_box(); box_.clone_overflow_x() == overflow_x::computed_value::T::Hidden || box_.clone_overflow_y() == overflow_y::computed_value::T::Hidden }) } } #[allow(unsafe_code)] pub trait RawLayoutElementHelpers { unsafe fn get_attr_for_layout<'a>( &'a self, namespace: &Namespace, name: &LocalName, ) -> Option<&'a AttrValue>; unsafe fn get_attr_val_for_layout<'a>( &'a self, namespace: &Namespace, name: &LocalName, ) -> Option<&'a str>; unsafe fn get_attr_vals_for_layout<'a>(&'a self, name: &LocalName) -> Vec<&'a AttrValue>; } #[inline] #[allow(unsafe_code)] pub unsafe fn get_attr_for_layout<'a>( elem: &'a Element, namespace: &Namespace, name: &LocalName, ) -> Option> { // cast to point to T in RefCell directly let attrs = elem.attrs.borrow_for_layout(); attrs .iter() .find(|attr| { let attr = attr.to_layout(); *name == attr.local_name_atom_forever() && (*attr.unsafe_get()).namespace() == namespace }).map(|attr| attr.to_layout()) } #[allow(unsafe_code)] impl RawLayoutElementHelpers for Element { #[inline] unsafe fn get_attr_for_layout<'a>( &'a self, namespace: &Namespace, name: &LocalName, ) -> Option<&'a AttrValue> { get_attr_for_layout(self, namespace, name).map(|attr| attr.value_forever()) } #[inline] unsafe fn get_attr_val_for_layout<'a>( &'a self, namespace: &Namespace, name: &LocalName, ) -> Option<&'a str> { get_attr_for_layout(self, namespace, name).map(|attr| attr.value_ref_forever()) } #[inline] unsafe fn get_attr_vals_for_layout<'a>(&'a self, name: &LocalName) -> Vec<&'a AttrValue> { let attrs = self.attrs.borrow_for_layout(); attrs .iter() .filter_map(|attr| { let attr = attr.to_layout(); if *name == attr.local_name_atom_forever() { Some(attr.value_forever()) } else { None } }).collect() } } pub trait LayoutElementHelpers { #[allow(unsafe_code)] unsafe fn has_class_for_layout(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool; #[allow(unsafe_code)] unsafe fn get_classes_for_layout(&self) -> Option<&'static [Atom]>; #[allow(unsafe_code)] unsafe fn synthesize_presentational_hints_for_legacy_attributes(&self, _: &mut V) where V: Push; #[allow(unsafe_code)] unsafe fn get_colspan(self) -> u32; #[allow(unsafe_code)] unsafe fn get_rowspan(self) -> u32; #[allow(unsafe_code)] unsafe fn is_html_element(&self) -> bool; fn id_attribute(&self) -> *const Option; fn style_attribute(&self) -> *const Option>>; fn local_name(&self) -> &LocalName; fn namespace(&self) -> &Namespace; fn get_lang_for_layout(&self) -> String; fn get_checked_state_for_layout(&self) -> bool; fn get_indeterminate_state_for_layout(&self) -> bool; fn get_state_for_layout(&self) -> ElementState; fn insert_selector_flags(&self, flags: ElementSelectorFlags); fn has_selector_flags(&self, flags: ElementSelectorFlags) -> bool; } impl LayoutElementHelpers for LayoutDom { #[allow(unsafe_code)] #[inline] unsafe fn has_class_for_layout(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool { get_attr_for_layout(&*self.unsafe_get(), &ns!(), &local_name!("class")).map_or( false, |attr| { attr.value_tokens_forever() .unwrap() .iter() .any(|atom| case_sensitivity.eq_atom(atom, name)) }, ) } #[allow(unsafe_code)] #[inline] unsafe fn get_classes_for_layout(&self) -> Option<&'static [Atom]> { get_attr_for_layout(&*self.unsafe_get(), &ns!(), &local_name!("class")) .map(|attr| attr.value_tokens_forever().unwrap()) } #[allow(unsafe_code)] unsafe fn synthesize_presentational_hints_for_legacy_attributes(&self, hints: &mut V) where V: Push, { // FIXME(emilio): Just a single PDB should be enough. #[inline] fn from_declaration( shared_lock: &SharedRwLock, declaration: PropertyDeclaration, ) -> ApplicableDeclarationBlock { ApplicableDeclarationBlock::from_declarations( Arc::new(shared_lock.wrap(PropertyDeclarationBlock::with_one( declaration, Importance::Normal, ))), CascadeLevel::PresHints, ) } let document = self.upcast::().owner_doc_for_layout(); let shared_lock = document.style_shared_lock(); let bgcolor = if let Some(this) = self.downcast::() { this.get_background_color() } else if let Some(this) = self.downcast::() { this.get_background_color() } else if let Some(this) = self.downcast::() { this.get_background_color() } else if let Some(this) = self.downcast::() { this.get_background_color() } else if let Some(this) = self.downcast::() { this.get_background_color() } else { None }; if let Some(color) = bgcolor { hints.push(from_declaration( shared_lock, PropertyDeclaration::BackgroundColor(color.into()), )); } let background = if let Some(this) = self.downcast::() { this.get_background() } else { None }; if let Some(url) = background { hints.push(from_declaration( shared_lock, PropertyDeclaration::BackgroundImage(background_image::SpecifiedValue(vec![ Either::Second(specified::Image::for_cascade(url.into())), ])), )); } let color = if let Some(this) = self.downcast::() { this.get_color() } else if let Some(this) = self.downcast::() { // https://html.spec.whatwg.org/multipage/#the-page:the-body-element-20 this.get_color() } else if let Some(this) = self.downcast::() { // https://html.spec.whatwg.org/multipage/#the-hr-element-2:presentational-hints-5 this.get_color() } else { None }; if let Some(color) = color { hints.push(from_declaration( shared_lock, PropertyDeclaration::Color(longhands::color::SpecifiedValue(color.into())), )); } let font_family = if let Some(this) = self.downcast::() { this.get_face() } else { None }; if let Some(font_family) = font_family { hints.push(from_declaration( shared_lock, PropertyDeclaration::FontFamily(font_family::SpecifiedValue::Values( computed::font::FontFamilyList::new(Box::new([ computed::font::SingleFontFamily::from_atom(font_family), ])), )), )); } let font_size = self .downcast::() .and_then(|this| this.get_size()); if let Some(font_size) = font_size { hints.push(from_declaration( shared_lock, PropertyDeclaration::FontSize(font_size::SpecifiedValue::from_html_size( font_size as u8, )), )) } let cellspacing = if let Some(this) = self.downcast::() { this.get_cellspacing() } else { None }; if let Some(cellspacing) = cellspacing { let width_value = specified::Length::from_px(cellspacing as f32); hints.push(from_declaration( shared_lock, PropertyDeclaration::BorderSpacing(Box::new(border_spacing::SpecifiedValue::new( width_value.clone().into(), width_value.into(), ))), )); } let size = if let Some(this) = self.downcast::() { // FIXME(pcwalton): More use of atoms, please! match (*self.unsafe_get()).get_attr_val_for_layout(&ns!(), &local_name!("type")) { // Not text entry widget Some("hidden") | Some("date") | Some("month") | Some("week") | Some("time") | Some("datetime-local") | Some("number") | Some("range") | Some("color") | Some("checkbox") | Some("radio") | Some("file") | Some("submit") | Some("image") | Some("reset") | Some("button") => None, // Others _ => match this.size_for_layout() { 0 => None, s => Some(s as i32), }, } } else { None }; if let Some(size) = size { let value = specified::NoCalcLength::ServoCharacterWidth(specified::CharacterWidth(size)); hints.push(from_declaration( shared_lock, PropertyDeclaration::Width(specified::LengthOrPercentageOrAuto::Length(value)), )); } let width = if let Some(this) = self.downcast::() { this.get_width() } else if let Some(this) = self.downcast::() { this.get_width() } else if let Some(this) = self.downcast::() { this.get_width() } else if let Some(this) = self.downcast::() { this.get_width() } else if let Some(this) = self.downcast::() { // https://html.spec.whatwg.org/multipage/#the-hr-element-2:attr-hr-width this.get_width() } else if let Some(this) = self.downcast::() { this.get_width() } else { LengthOrPercentageOrAuto::Auto }; // FIXME(emilio): Use from_computed value here and below. match width { LengthOrPercentageOrAuto::Auto => {}, LengthOrPercentageOrAuto::Percentage(percentage) => { let width_value = specified::LengthOrPercentageOrAuto::Percentage( computed::Percentage(percentage), ); hints.push(from_declaration( shared_lock, PropertyDeclaration::Width(width_value), )); }, LengthOrPercentageOrAuto::Length(length) => { let width_value = specified::LengthOrPercentageOrAuto::Length(specified::NoCalcLength::Absolute( specified::AbsoluteLength::Px(length.to_f32_px()), )); hints.push(from_declaration( shared_lock, PropertyDeclaration::Width(width_value), )); }, } let height = if let Some(this) = self.downcast::() { this.get_height() } else if let Some(this) = self.downcast::() { this.get_height() } else if let Some(this) = self.downcast::() { this.get_height() } else { LengthOrPercentageOrAuto::Auto }; match height { LengthOrPercentageOrAuto::Auto => {}, LengthOrPercentageOrAuto::Percentage(percentage) => { let height_value = specified::LengthOrPercentageOrAuto::Percentage( computed::Percentage(percentage), ); hints.push(from_declaration( shared_lock, PropertyDeclaration::Height(height_value), )); }, LengthOrPercentageOrAuto::Length(length) => { let height_value = specified::LengthOrPercentageOrAuto::Length(specified::NoCalcLength::Absolute( specified::AbsoluteLength::Px(length.to_f32_px()), )); hints.push(from_declaration( shared_lock, PropertyDeclaration::Height(height_value), )); }, } let cols = if let Some(this) = self.downcast::() { match this.get_cols() { 0 => None, c => Some(c as i32), } } else { None }; if let Some(cols) = cols { // TODO(mttr) ServoCharacterWidth uses the size math for , but // the math for