diff --git a/components/layout/block.rs b/components/layout/block.rs index 53eecad09b6..2797245f03b 100644 --- a/components/layout/block.rs +++ b/components/layout/block.rs @@ -1495,13 +1495,11 @@ impl Flow for BlockFlow { /// Pass 1 of reflow: computes minimum and preferred inline-sizes. /// - /// Recursively (bottom-up) determine the flow's minimum and preferred inline-sizes. When called on - /// this flow, all child flows have had their minimum and preferred inline-sizes set. This function - /// must decide minimum/preferred inline-sizes based on its children's inline-sizes and the dimensions of - /// any fragments it is responsible for flowing. - /// - /// TODO(pcwalton): Inline blocks. - fn bubble_inline_sizes(&mut self, layout_context: &LayoutContext) { + /// Recursively (bottom-up) determine the flow's minimum and preferred inline-sizes. When + /// called on this flow, all child flows have had their minimum and preferred inline-sizes set. + /// This function must decide minimum/preferred inline-sizes based on its children's + /// inline-sizes and the dimensions of any fragments it is responsible for flowing. + fn bubble_inline_sizes(&mut self) { let _scope = layout_debug_scope!("block::bubble_inline_sizes {:s}", self.base.debug_id()); let mut flags = self.base.flags; @@ -1557,13 +1555,16 @@ impl Flow for BlockFlow { max(intrinsic_inline_sizes.preferred_inline_size, left_float_width + right_float_width); - let fragment_intrinsic_inline_sizes = self.fragment.intrinsic_inline_sizes(layout_context); - intrinsic_inline_sizes.minimum_inline_size = max(intrinsic_inline_sizes.minimum_inline_size, - fragment_intrinsic_inline_sizes.minimum_inline_size); - intrinsic_inline_sizes.preferred_inline_size = max(intrinsic_inline_sizes.preferred_inline_size, - fragment_intrinsic_inline_sizes.preferred_inline_size); - intrinsic_inline_sizes.surround_inline_size = intrinsic_inline_sizes.surround_inline_size + - fragment_intrinsic_inline_sizes.surround_inline_size; + let fragment_intrinsic_inline_sizes = self.fragment.intrinsic_inline_sizes(); + intrinsic_inline_sizes.minimum_inline_size = + max(intrinsic_inline_sizes.minimum_inline_size, + fragment_intrinsic_inline_sizes.minimum_inline_size); + intrinsic_inline_sizes.preferred_inline_size = + max(intrinsic_inline_sizes.preferred_inline_size, + fragment_intrinsic_inline_sizes.preferred_inline_size); + intrinsic_inline_sizes.surround_inline_size = + intrinsic_inline_sizes.surround_inline_size + + fragment_intrinsic_inline_sizes.surround_inline_size; self.base.intrinsic_inline_sizes = intrinsic_inline_sizes; match self.fragment.style().get_box().float { diff --git a/components/layout/construct.rs b/components/layout/construct.rs index 82bff208604..4233c565dba 100644 --- a/components/layout/construct.rs +++ b/components/layout/construct.rs @@ -30,10 +30,9 @@ use flow_ref::FlowRef; use fragment::{Fragment, GenericFragment, IframeFragment, IframeFragmentInfo, ImageFragment}; use fragment::{ImageFragmentInfo, InlineAbsoluteHypotheticalFragment}; use fragment::{InlineAbsoluteHypotheticalFragmentInfo, InlineBlockFragment}; -use fragment::{InlineBlockFragmentInfo, InputFragment, InputFragmentInfo, SpecificFragmentInfo}; -use fragment::{TableCellFragment, TableColumnFragment, TableColumnFragmentInfo, TableFragment}; -use fragment::{TableRowFragment, TableWrapperFragment, UnscannedTextFragment}; -use fragment::{UnscannedTextFragmentInfo}; +use fragment::{InlineBlockFragmentInfo, InputFragment, SpecificFragmentInfo, TableCellFragment}; +use fragment::{TableColumnFragment, TableColumnFragmentInfo, TableFragment, TableRowFragment}; +use fragment::{TableWrapperFragment, UnscannedTextFragment, UnscannedTextFragmentInfo}; use inline::{InlineFragments, InlineFlow}; use parallel; use table_wrapper::TableWrapperFlow; @@ -222,21 +221,6 @@ impl<'a> FlowConstructor<'a> { } } - fn build_fragment_info_for_input(&mut self, node: &ThreadSafeLayoutNode) -> SpecificFragmentInfo { - //FIXME: would it make more sense to use HTMLInputElement::input_type instead of the raw - // value? definitely for string comparisons. - let elem = node.as_element(); - let data = match elem.get_attr(&ns!(""), &atom!("type")) { - Some("checkbox") | Some("radio") => None, - Some("button") | Some("submit") | Some("reset") => - Some(node.get_input_value().len() as u32), - Some("file") => Some(node.get_input_size()), - _ => Some(node.get_input_size()), - }; - data.map(|size| InputFragment(InputFragmentInfo { size: size })) - .unwrap_or(GenericFragment) - } - /// Builds specific `Fragment` info for the given node. /// /// This does *not* construct the text for generated content (but, for generated content with @@ -253,7 +237,7 @@ impl<'a> FlowConstructor<'a> { self.build_fragment_info_for_image(node, node.image_url()) } Some(ElementNodeTypeId(HTMLInputElementTypeId)) => { - self.build_fragment_info_for_input(node) + InputFragment } Some(ElementNodeTypeId(HTMLObjectElementTypeId)) => { let data = node.get_object_data(); @@ -1216,7 +1200,7 @@ impl FlowConstructionUtils for FlowRef { /// This must not be public because only the layout constructor can do this. fn finish(&mut self, context: &LayoutContext) { if !context.shared.opts.bubble_inline_sizes_separately { - self.get_mut().bubble_inline_sizes(context) + self.get_mut().bubble_inline_sizes() } } } diff --git a/components/layout/flow.rs b/components/layout/flow.rs index 4520bee4eff..c33f7528c6b 100644 --- a/components/layout/flow.rs +++ b/components/layout/flow.rs @@ -188,7 +188,7 @@ pub trait Flow: fmt::Show + ToString + Sync { /// called on this flow, all child flows have had their minimum and preferred inline-sizes set. /// This function must decide minimum/preferred inline-sizes based on its children's inline- /// sizes and the dimensions of any boxes it is responsible for flowing. - fn bubble_inline_sizes(&mut self, _ctx: &LayoutContext) { + fn bubble_inline_sizes(&mut self) { fail!("bubble_inline_sizes not yet implemented") } diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs index 47a7980d152..e9bd51b3ece 100644 --- a/components/layout/fragment.rs +++ b/components/layout/fragment.rs @@ -138,7 +138,7 @@ pub enum SpecificFragmentInfo { InlineAbsoluteHypotheticalFragment(InlineAbsoluteHypotheticalFragmentInfo), InlineBlockFragment(InlineBlockFragmentInfo), - InputFragment(InputFragmentInfo), + InputFragment, ScannedTextFragment(ScannedTextFragmentInfo), TableFragment, TableCellFragment, @@ -183,22 +183,6 @@ impl InlineBlockFragmentInfo { } } -/// A fragment that represents a displayable form element -#[deriving(Clone)] -pub struct InputFragmentInfo { - pub size: u32, -} - -impl InputFragmentInfo { - /// Returns the original inline-size of the input. - fn input_inline_size(&self, font_style: &FontStyle, layout_context: &LayoutContext) -> Au { - let metrics = text::font_metrics_for_style(layout_context.font_context(), font_style); - - // https://html.spec.whatwg.org/#converting-a-character-width-to-pixels - metrics.average_advance * (self.size as i32 - 1) + metrics.max_advance - } -} - /// A fragment that represents a replaced content image and its accompanying borders, shadows, etc. #[deriving(Clone)] pub struct ImageFragmentInfo { @@ -550,7 +534,7 @@ impl Fragment { fn style_specified_intrinsic_inline_size(&self) -> IntrinsicISizes { let (use_margins, use_padding) = match self.specific { GenericFragment | IframeFragment(_) | ImageFragment(_) | InlineBlockFragment(_) | - InputFragment(_) => (true, true), + InputFragment => (true, true), TableFragment | TableCellFragment => (false, true), TableWrapperFragment => (true, false), TableRowFragment => (false, false), @@ -1195,7 +1179,7 @@ impl Fragment { clip_rect)) } GenericFragment | IframeFragment(..) | TableFragment | TableCellFragment | - TableRowFragment | TableWrapperFragment | InlineBlockFragment(_) | InputFragment(_) | + TableRowFragment | TableWrapperFragment | InlineBlockFragment(_) | InputFragment | InlineAbsoluteHypotheticalFragment(_) => { // FIXME(pcwalton): This is a bit of an abuse of the logging infrastructure. We // should have a real `SERVO_DEBUG` system. @@ -1258,13 +1242,13 @@ impl Fragment { } /// Returns the intrinsic inline-sizes of this fragment. - pub fn intrinsic_inline_sizes(&mut self, layout_context: &LayoutContext) -> IntrinsicISizes { + pub fn intrinsic_inline_sizes(&mut self) -> IntrinsicISizes { let mut result = self.style_specified_intrinsic_inline_size(); match self.specific { GenericFragment | IframeFragment(_) | TableFragment | TableCellFragment | TableColumnFragment(_) | TableRowFragment | TableWrapperFragment | - InlineAbsoluteHypotheticalFragment(_) => {} + InlineAbsoluteHypotheticalFragment(_) | InputFragment => {} InlineBlockFragment(ref mut info) => { let block_flow = info.flow_ref.get_mut().as_block(); result.minimum_inline_size = max(result.minimum_inline_size, @@ -1280,12 +1264,6 @@ impl Fragment { result.preferred_inline_size = max(result.preferred_inline_size, image_inline_size); } - InputFragment(ref input_fragment_info) => { - let font_style = text::computed_style_to_font_style(&*self.style); - let input_inline_size = input_fragment_info.input_inline_size(&font_style, layout_context); - result.minimum_inline_size = input_inline_size; - result.preferred_inline_size = input_inline_size; - } ScannedTextFragment(ref text_fragment_info) => { let range = &text_fragment_info.range; let min_line_inline_size = text_fragment_info.run.min_width_for_range(range); @@ -1333,7 +1311,7 @@ impl Fragment { match self.specific { GenericFragment | IframeFragment(_) | TableFragment | TableCellFragment | TableRowFragment | TableWrapperFragment | InlineBlockFragment(_) | - InputFragment(_) | InlineAbsoluteHypotheticalFragment(_) => Au(0), + InputFragment | InlineAbsoluteHypotheticalFragment(_) => Au(0), ImageFragment(ref image_fragment_info) => { image_fragment_info.computed_inline_size() } @@ -1352,7 +1330,7 @@ impl Fragment { match self.specific { GenericFragment | IframeFragment(_) | TableFragment | TableCellFragment | TableRowFragment | TableWrapperFragment | InlineBlockFragment(_) | - InputFragment(_) | InlineAbsoluteHypotheticalFragment(_) => Au(0), + InputFragment | InlineAbsoluteHypotheticalFragment(_) => Au(0), ImageFragment(ref image_fragment_info) => { image_fragment_info.computed_block_size() } @@ -1386,7 +1364,7 @@ impl Fragment { -> Option<(SplitInfo, Option, Arc> /* TODO(bjz): remove */)> { match self.specific { GenericFragment | IframeFragment(_) | ImageFragment(_) | TableFragment | TableCellFragment | - TableRowFragment | TableWrapperFragment | InputFragment(_) => None, + TableRowFragment | TableWrapperFragment | InputFragment => None, TableColumnFragment(_) => fail!("Table column fragments do not need to split"), UnscannedTextFragment(_) => fail!("Unscanned text fragments should have been scanned by now!"), InlineBlockFragment(_) | InlineAbsoluteHypotheticalFragment(_) => { @@ -1429,7 +1407,7 @@ impl Fragment { -> Option<(Option, Option, Arc> /* TODO(bjz): remove */)> { match self.specific { GenericFragment | IframeFragment(_) | ImageFragment(_) | TableFragment | TableCellFragment | - TableRowFragment | TableWrapperFragment | InlineBlockFragment(_) | InputFragment(_) | + TableRowFragment | TableWrapperFragment | InlineBlockFragment(_) | InputFragment | InlineAbsoluteHypotheticalFragment(_) => None, TableColumnFragment(_) => fail!("Table column fragments do not have inline_size"), UnscannedTextFragment(_) => fail!("Unscanned text fragments should have been scanned by now!"), @@ -1528,7 +1506,7 @@ impl Fragment { container_inline_size: Au) { match self.specific { GenericFragment | IframeFragment(_) | TableFragment | TableCellFragment | - TableRowFragment | TableWrapperFragment | InputFragment(_) => return, + TableRowFragment | TableWrapperFragment | InputFragment => return, TableColumnFragment(_) => fail!("Table column fragments do not have inline_size"), UnscannedTextFragment(_) => { fail!("Unscanned text fragments should have been scanned by now!") @@ -1624,7 +1602,7 @@ impl Fragment { pub fn assign_replaced_block_size_if_necessary(&mut self, containing_block_block_size: Au) { match self.specific { GenericFragment | IframeFragment(_) | TableFragment | TableCellFragment | - TableRowFragment | TableWrapperFragment | InputFragment(_) => return, + TableRowFragment | TableWrapperFragment | InputFragment => return, TableColumnFragment(_) => fail!("Table column fragments do not have block_size"), UnscannedTextFragment(_) => { fail!("Unscanned text fragments should have been scanned by now!") @@ -1787,7 +1765,7 @@ impl Fragment { TableWrapperFragment => false, GenericFragment | IframeFragment(_) | ImageFragment(_) | ScannedTextFragment(_) | TableFragment | TableCellFragment | TableColumnFragment(_) | TableRowFragment | - UnscannedTextFragment(_) | InputFragment(_) => true, + UnscannedTextFragment(_) | InputFragment => true, } } @@ -1844,7 +1822,7 @@ impl fmt::Show for Fragment { ImageFragment(_) => "ImageFragment", InlineAbsoluteHypotheticalFragment(_) => "InlineAbsoluteHypotheticalFragment", InlineBlockFragment(_) => "InlineBlockFragment", - InputFragment(_) => "InputFragment", + InputFragment => "InputFragment", ScannedTextFragment(_) => "ScannedTextFragment", TableFragment => "TableFragment", TableCellFragment => "TableCellFragment", diff --git a/components/layout/inline.rs b/components/layout/inline.rs index eaa9bc19aed..5c55bc600c2 100644 --- a/components/layout/inline.rs +++ b/components/layout/inline.rs @@ -914,7 +914,7 @@ impl Flow for InlineFlow { self } - fn bubble_inline_sizes(&mut self, layout_context: &LayoutContext) { + fn bubble_inline_sizes(&mut self) { let _scope = layout_debug_scope!("inline::bubble_inline_sizes {:s}", self.base.debug_id()); let writing_mode = self.base.writing_mode; @@ -927,7 +927,7 @@ impl Flow for InlineFlow { debug!("Flow: measuring {}", *fragment); let fragment_intrinsic_inline_sizes = - fragment.intrinsic_inline_sizes(layout_context); + fragment.intrinsic_inline_sizes(); intrinsic_inline_sizes.minimum_inline_size = max( intrinsic_inline_sizes.minimum_inline_size, fragment_intrinsic_inline_sizes.minimum_inline_size); diff --git a/components/layout/table.rs b/components/layout/table.rs index 979667d4ba1..7aca57f7fc4 100644 --- a/components/layout/table.rs +++ b/components/layout/table.rs @@ -171,9 +171,9 @@ impl Flow for TableFlow { /// table layout calculation. /// The maximum min/pref inline-sizes of each column are set from the rows for the automatic /// table layout calculation. - fn bubble_inline_sizes(&mut self, layout_context: &LayoutContext) { + fn bubble_inline_sizes(&mut self) { let _scope = layout_debug_scope!("table::bubble_inline_sizes {:s}", - self.block_flow.base.debug_id()); + self.block_flow.base.debug_id()); let mut min_inline_size = Au(0); let mut pref_inline_size = Au(0); @@ -239,8 +239,7 @@ impl Flow for TableFlow { } } - let fragment_intrinsic_inline_sizes = - self.block_flow.fragment.intrinsic_inline_sizes(layout_context); + let fragment_intrinsic_inline_sizes = self.block_flow.fragment.intrinsic_inline_sizes(); self.block_flow.base.intrinsic_inline_sizes.minimum_inline_size = min_inline_size; self.block_flow.base.intrinsic_inline_sizes.preferred_inline_size = max(min_inline_size, pref_inline_size); diff --git a/components/layout/table_caption.rs b/components/layout/table_caption.rs index ddebb01ce3d..7a46fddb833 100644 --- a/components/layout/table_caption.rs +++ b/components/layout/table_caption.rs @@ -48,8 +48,8 @@ impl Flow for TableCaptionFlow { &mut self.block_flow } - fn bubble_inline_sizes(&mut self, ctx: &LayoutContext) { - self.block_flow.bubble_inline_sizes(ctx); + fn bubble_inline_sizes(&mut self) { + self.block_flow.bubble_inline_sizes(); } fn assign_inline_sizes(&mut self, ctx: &LayoutContext) { diff --git a/components/layout/table_cell.rs b/components/layout/table_cell.rs index 0a3300256a3..e1581e2d642 100644 --- a/components/layout/table_cell.rs +++ b/components/layout/table_cell.rs @@ -74,16 +74,21 @@ impl Flow for TableCellFlow { &mut self.block_flow } - /// Minimum/preferred inline-sizes set by this function are used in automatic table layout calculation. - fn bubble_inline_sizes(&mut self, ctx: &LayoutContext) { + /// Minimum/preferred inline-sizes set by this function are used in automatic table layout + /// calculation. + fn bubble_inline_sizes(&mut self) { let _scope = layout_debug_scope!("table_cell::bubble_inline_sizes {:s}", - self.block_flow.base.debug_id()); + self.block_flow.base.debug_id()); - self.block_flow.bubble_inline_sizes(ctx); - let specified_inline_size = MaybeAuto::from_style(self.block_flow.fragment.style().content_inline_size(), - Au::new(0)).specified_or_zero(); - if self.block_flow.base.intrinsic_inline_sizes.minimum_inline_size < specified_inline_size { - self.block_flow.base.intrinsic_inline_sizes.minimum_inline_size = specified_inline_size; + self.block_flow.bubble_inline_sizes(); + let specified_inline_size = MaybeAuto::from_style(self.block_flow + .fragment + .style() + .content_inline_size(), + Au(0)).specified_or_zero(); + if self.block_flow.base.intrinsic_inline_sizes.minimum_inline_size < + specified_inline_size { + self.block_flow.base.intrinsic_inline_sizes.minimum_inline_size = specified_inline_size } if self.block_flow.base.intrinsic_inline_sizes.preferred_inline_size < self.block_flow.base.intrinsic_inline_sizes.minimum_inline_size { diff --git a/components/layout/table_colgroup.rs b/components/layout/table_colgroup.rs index ae49c9a78d3..427cd6f5f1f 100644 --- a/components/layout/table_colgroup.rs +++ b/components/layout/table_colgroup.rs @@ -53,7 +53,7 @@ impl Flow for TableColGroupFlow { self } - fn bubble_inline_sizes(&mut self, _: &LayoutContext) { + fn bubble_inline_sizes(&mut self) { let _scope = layout_debug_scope!("table_colgroup::bubble_inline_sizes {:s}", self.base.debug_id()); diff --git a/components/layout/table_row.rs b/components/layout/table_row.rs index 55cc67632fa..d7236c5c41b 100644 --- a/components/layout/table_row.rs +++ b/components/layout/table_row.rs @@ -171,7 +171,7 @@ impl Flow for TableRowFlow { /// responsible for flowing. /// Min/pref inline-sizes set by this function are used in automatic table layout calculation. /// The specified column inline-sizes of children cells are used in fixed table layout calculation. - fn bubble_inline_sizes(&mut self, _: &LayoutContext) { + fn bubble_inline_sizes(&mut self) { let _scope = layout_debug_scope!("table_row::bubble_inline_sizes {:s}", self.block_flow.base.debug_id()); diff --git a/components/layout/table_rowgroup.rs b/components/layout/table_rowgroup.rs index 1b8cabf63e4..7efb2e96d09 100644 --- a/components/layout/table_rowgroup.rs +++ b/components/layout/table_rowgroup.rs @@ -132,14 +132,16 @@ impl Flow for TableRowGroupFlow { &self.col_pref_inline_sizes } - /// Recursively (bottom-up) determines the context's preferred and minimum inline-sizes. When called - /// on this context, all child contexts have had their min/pref inline-sizes set. This function must - /// decide min/pref inline-sizes based on child context inline-sizes and dimensions of any fragments it is - /// responsible for flowing. + /// Recursively (bottom-up) determines the context's preferred and minimum inline-sizes. When + /// called on this context, all child contexts have had their min/pref inline-sizes set. This + /// function must decide min/pref inline-sizes based on child context inline-sizes and + /// dimensions of any fragments it is responsible for flowing. + /// /// Min/pref inline-sizes set by this function are used in automatic table layout calculation. - /// Also, this function finds the specified column inline-sizes from the first row. - /// Those are used in fixed table layout calculation - fn bubble_inline_sizes(&mut self, _: &LayoutContext) { + /// + /// Also, this function finds the specified column inline-sizes from the first row. These are + /// used in fixed table layout calculation. + fn bubble_inline_sizes(&mut self) { let _scope = layout_debug_scope!("table_rowgroup::bubble_inline_sizes {:s}", self.block_flow.base.debug_id()); diff --git a/components/layout/table_wrapper.rs b/components/layout/table_wrapper.rs index ca05f6e1d53..8938bd190ee 100644 --- a/components/layout/table_wrapper.rs +++ b/components/layout/table_wrapper.rs @@ -260,7 +260,7 @@ impl Flow for TableWrapperFlow { self.block_flow.float_kind() } - fn bubble_inline_sizes(&mut self, ctx: &LayoutContext) { + fn bubble_inline_sizes(&mut self) { // get column inline-sizes info from table flow for kid in self.block_flow.base.child_iter() { assert!(kid.is_table_caption() || kid.is_table()); @@ -270,7 +270,7 @@ impl Flow for TableWrapperFlow { } } - self.block_flow.bubble_inline_sizes(ctx); + self.block_flow.bubble_inline_sizes(); } fn assign_inline_sizes(&mut self, layout_context: &LayoutContext) { diff --git a/components/layout/traversal.rs b/components/layout/traversal.rs index f7726c19a59..41896ba1fad 100644 --- a/components/layout/traversal.rs +++ b/components/layout/traversal.rs @@ -260,7 +260,7 @@ pub struct BubbleISizes<'a> { impl<'a> PostorderFlowTraversal for BubbleISizes<'a> { #[inline] fn process(&mut self, flow: &mut Flow) -> bool { - flow.bubble_inline_sizes(self.layout_context); + flow.bubble_inline_sizes(); true } diff --git a/components/layout/wrapper.rs b/components/layout/wrapper.rs index a4b5903da00..706d8b8d04e 100644 --- a/components/layout/wrapper.rs +++ b/components/layout/wrapper.rs @@ -38,8 +38,9 @@ use css::node_style::StyledNode; use util::{LayoutDataAccess, LayoutDataWrapper, PrivateLayoutData, OpaqueNodeMethods}; use gfx::display_list::OpaqueNode; -use script::dom::bindings::codegen::InheritTypes::{HTMLIFrameElementDerived, HTMLInputElementDerived}; -use script::dom::bindings::codegen::InheritTypes::{HTMLImageElementDerived, TextDerived}; +use script::dom::bindings::codegen::InheritTypes::{HTMLIFrameElementDerived}; +use script::dom::bindings::codegen::InheritTypes::{HTMLImageElementDerived}; +use script::dom::bindings::codegen::InheritTypes::{HTMLInputElementDerived, TextDerived}; use script::dom::bindings::js::JS; use script::dom::element::{Element, HTMLAreaElementTypeId, HTMLAnchorElementTypeId}; use script::dom::element::{HTMLLinkElementTypeId, LayoutElementHelpers, RawLayoutElementHelpers}; @@ -52,13 +53,13 @@ use script::dom::node::{IsDirty, HasDirtyDescendants}; use script::dom::text::Text; use script::layout_interface::LayoutChan; use servo_msg::constellation_msg::{PipelineId, SubpageId}; -use servo_util::str::is_whitespace; +use servo_util::str::{LengthOrPercentageOrAuto, is_whitespace}; use std::cell::{RefCell, Ref, RefMut}; use std::kinds::marker::ContravariantLifetime; use std::mem; use style::computed_values::{content, display, white_space}; -use style::{AnyNamespace, AttrSelector, PropertyDeclarationBlock, SpecificNamespace, TElement}; -use style::{TNode}; +use style::{AnyNamespace, AttrSelector, IntegerAttribute, LengthAttribute}; +use style::{PropertyDeclarationBlock, SpecificNamespace, TElement, TElementAttributes, TNode}; use url::Url; use string_cache::{Atom, Namespace}; @@ -517,6 +518,20 @@ impl<'le> TElement<'le> for LayoutElement<'le> { } } +impl<'le> TElementAttributes for LayoutElement<'le> { + fn get_length_attribute(self, length_attribute: LengthAttribute) -> LengthOrPercentageOrAuto { + unsafe { + self.element.get_length_attribute_for_layout(length_attribute) + } + } + + fn get_integer_attribute(self, integer_attribute: IntegerAttribute) -> Option { + unsafe { + self.element.get_integer_attribute_for_layout(integer_attribute) + } + } +} + fn get_content(content_list: &content::T) -> String { match *content_list { content::Content(ref value) => { diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index ba3642e79af..18fb7c6e8d8 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -53,6 +53,7 @@ use script_traits::UntrustedNodeAddress; use servo_msg::compositor_msg::ScriptListener; use servo_msg::constellation_msg::ConstellationChan; use servo_util::smallvec::{SmallVec1, SmallVec}; +use servo_util::str::LengthOrPercentageOrAuto; use layout_interface::{LayoutRPC, LayoutChan}; use dom::bindings::utils::WindowProxyHandler; @@ -234,3 +235,6 @@ impl JSTraceable for Box { // Do nothing } } + +untraceable!(LengthOrPercentageOrAuto) + diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index 2c1685fb407..cb6a3c21800 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -11,7 +11,8 @@ use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods; use dom::bindings::codegen::Bindings::ElementBinding; use dom::bindings::codegen::Bindings::ElementBinding::ElementMethods; use dom::bindings::codegen::Bindings::NamedNodeMapBinding::NamedNodeMapMethods; -use dom::bindings::codegen::InheritTypes::{ElementDerived, NodeCast}; +use dom::bindings::codegen::InheritTypes::{ElementDerived, HTMLInputElementDerived}; +use dom::bindings::codegen::InheritTypes::{HTMLTableCellElementDerived, NodeCast}; use dom::bindings::js::{MutNullableJS, JS, JSRef, Temporary, TemporaryPushable}; use dom::bindings::js::{OptionalSettable, OptionalRootable, Root}; use dom::bindings::utils::{Reflectable, Reflector}; @@ -23,16 +24,19 @@ use dom::document::{Document, DocumentHelpers}; use dom::domtokenlist::DOMTokenList; use dom::eventtarget::{EventTarget, NodeTargetTypeId}; use dom::htmlcollection::HTMLCollection; +use dom::htmlinputelement::{HTMLInputElement, LayoutHTMLInputElementHelpers}; use dom::htmlserializer::serialize; +use dom::htmltablecellelement::{HTMLTableCellElement, HTMLTableCellElementHelpers}; use dom::node::{ElementNodeTypeId, Node, NodeHelpers, NodeIterator, document_from_node}; use dom::node::{window_from_node, LayoutNodeHelpers}; use dom::nodelist::NodeList; use dom::virtualmethods::{VirtualMethods, vtable_for}; use devtools_traits::AttrInfo; +use style::{IntegerAttribute, LengthAttribute, SizeIntegerAttribute, WidthLengthAttribute}; use style::{matches, parse_selector_list_from_str}; use style; use servo_util::namespace; -use servo_util::str::DOMString; +use servo_util::str::{DOMString, LengthOrPercentageOrAuto}; use std::ascii::StrAsciiExt; use std::cell::{Ref, RefMut, RefCell}; @@ -211,6 +215,10 @@ pub trait RawLayoutElementHelpers { unsafe fn get_attr_atom_for_layout(&self, namespace: &Namespace, name: &Atom) -> Option; unsafe fn has_class_for_layout(&self, name: &Atom) -> bool; unsafe fn get_classes_for_layout(&self) -> Option<&'static [Atom]>; + unsafe fn get_length_attribute_for_layout(&self, length_attribute: LengthAttribute) + -> LengthOrPercentageOrAuto; + unsafe fn get_integer_attribute_for_layout(&self, integer_attribute: IntegerAttribute) + -> Option; } impl RawLayoutElementHelpers for Element { @@ -288,6 +296,36 @@ impl RawLayoutElementHelpers for Element { (*attr).value_tokens_forever() }) } + + #[inline] + #[allow(unrooted_must_root)] + unsafe fn get_length_attribute_for_layout(&self, length_attribute: LengthAttribute) + -> LengthOrPercentageOrAuto { + match length_attribute { + WidthLengthAttribute => { + if !self.is_htmltablecellelement() { + fail!("I'm not a table cell!") + } + let this: &HTMLTableCellElement = mem::transmute(self); + this.get_width() + } + } + } + + #[inline] + #[allow(unrooted_must_root)] + unsafe fn get_integer_attribute_for_layout(&self, integer_attribute: IntegerAttribute) + -> Option { + match integer_attribute { + SizeIntegerAttribute => { + if !self.is_htmlinputelement() { + fail!("I'm not a form input!") + } + let this: &HTMLInputElement = mem::transmute(self); + Some(this.get_size_for_layout() as i32) + } + } + } } pub trait LayoutElementHelpers { @@ -1133,3 +1171,4 @@ impl<'a> style::TElement<'a> for JSRef<'a, Element> { } } } + diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs index b256dab7f3d..776ff10244d 100644 --- a/components/script/dom/htmlinputelement.rs +++ b/components/script/dom/htmlinputelement.rs @@ -88,14 +88,14 @@ pub trait LayoutHTMLInputElementHelpers { unsafe fn get_size_for_layout(&self) -> u32; } -impl LayoutHTMLInputElementHelpers for JS { +impl LayoutHTMLInputElementHelpers for HTMLInputElement { #[allow(unrooted_must_root)] unsafe fn get_value_for_layout(&self) -> String { - unsafe fn get_raw_value(input: &JS) -> Option { - mem::transmute::<&RefCell>, &Option>(&(*input.unsafe_get()).value).clone() + unsafe fn get_raw_value(input: &HTMLInputElement) -> Option { + mem::transmute::<&RefCell>, &Option>(&input.value).clone() } - match (*self.unsafe_get()).input_type.get() { + match self.input_type.get() { InputCheckbox | InputRadio => "".to_string(), InputFile | InputImage => "".to_string(), InputButton(ref default) => get_raw_value(self) @@ -111,7 +111,17 @@ impl LayoutHTMLInputElementHelpers for JS { #[allow(unrooted_must_root)] unsafe fn get_size_for_layout(&self) -> u32 { - (*self.unsafe_get()).size.get() + self.size.get() + } +} + +impl LayoutHTMLInputElementHelpers for JS { + unsafe fn get_value_for_layout(&self) -> String { + (*self.unsafe_get()).get_value_for_layout() + } + + unsafe fn get_size_for_layout(&self) -> u32 { + (*self.unsafe_get()).get_size_for_layout() } } @@ -187,11 +197,12 @@ impl<'a> HTMLInputElementMethods for JSRef<'a, HTMLInputElement> { make_setter!(SetFormTarget, "formtarget") } -trait HTMLInputElementHelpers { +pub trait HTMLInputElementHelpers { fn force_relayout(self); fn radio_group_updated(self, group: Option<&str>); fn get_radio_group(self) -> Option; fn update_checked_state(self, checked: bool); + fn get_size(&self) -> u32; } fn broadcast_radio_checked(broadcaster: JSRef, group: Option<&str>) { @@ -248,6 +259,10 @@ impl<'a> HTMLInputElementHelpers for JSRef<'a, HTMLInputElement> { //TODO: dispatch change event self.force_relayout(); } + + fn get_size(&self) -> u32 { + self.size.get() + } } impl<'a> VirtualMethods for JSRef<'a, HTMLInputElement> { diff --git a/components/script/dom/htmltablecellelement.rs b/components/script/dom/htmltablecellelement.rs index 95bcebf5480..6bd2d735695 100644 --- a/components/script/dom/htmltablecellelement.rs +++ b/components/script/dom/htmltablecellelement.rs @@ -2,21 +2,28 @@ * 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/. */ -use dom::bindings::codegen::InheritTypes::HTMLTableCellElementDerived; +use dom::bindings::codegen::InheritTypes::{HTMLElementCast, HTMLTableCellElementDerived}; use dom::bindings::js::JSRef; use dom::bindings::utils::{Reflectable, Reflector}; use dom::document::Document; -use dom::element::{ElementTypeId, HTMLTableDataCellElementTypeId, HTMLTableHeaderCellElementTypeId}; +use dom::element::{ElementTypeId, HTMLTableDataCellElementTypeId}; +use dom::element::{HTMLTableHeaderCellElementTypeId}; use dom::eventtarget::{EventTarget, NodeTargetTypeId}; use dom::htmlelement::HTMLElement; use dom::node::ElementNodeTypeId; -use servo_util::str::DOMString; +use dom::virtualmethods::VirtualMethods; + +use servo_util::str::{AutoLpa, DOMString, LengthOrPercentageOrAuto}; +use servo_util::str; +use std::cell::Cell; +use string_cache::Atom; #[jstraceable] #[must_root] #[privatize] pub struct HTMLTableCellElement { htmlelement: HTMLElement, + width: Cell, } impl HTMLTableCellElementDerived for EventTarget { @@ -32,16 +39,58 @@ impl HTMLTableCellElementDerived for EventTarget { impl HTMLTableCellElement { pub fn new_inherited(type_id: ElementTypeId, tag_name: DOMString, prefix: Option, document: JSRef) -> HTMLTableCellElement { HTMLTableCellElement { - htmlelement: HTMLElement::new_inherited(type_id, tag_name, prefix, document) + htmlelement: HTMLElement::new_inherited(type_id, tag_name, prefix, document), + width: Cell::new(AutoLpa) } } #[inline] - pub fn htmlelement<'a>(&'a self) -> &'a HTMLElement { + pub fn htmlelement(&self) -> &HTMLElement { &self.htmlelement } } +pub trait HTMLTableCellElementHelpers { + fn get_width(&self) -> LengthOrPercentageOrAuto; +} + +impl HTMLTableCellElementHelpers for HTMLTableCellElement { + fn get_width(&self) -> LengthOrPercentageOrAuto { + self.width.get() + } +} + +impl<'a> VirtualMethods for JSRef<'a, HTMLTableCellElement> { + fn super_type<'a>(&'a self) -> Option<&'a VirtualMethods> { + let htmlelement: &JSRef = HTMLElementCast::from_borrowed_ref(self); + Some(htmlelement as &VirtualMethods) + } + + fn after_set_attr(&self, name: &Atom, value: DOMString) { + match self.super_type() { + Some(ref s) => s.after_set_attr(name, value.clone()), + _ => {} + } + + match name.as_slice() { + "width" => self.width.set(str::parse_length(value.as_slice())), + _ => {} + } + } + + fn before_remove_attr(&self, name: &Atom, value: DOMString) { + match self.super_type() { + Some(ref s) => s.before_remove_attr(name, value), + _ => {} + } + + match name.as_slice() { + "width" => self.width.set(AutoLpa), + _ => {} + } + } +} + impl Reflectable for HTMLTableCellElement { fn reflector<'a>(&'a self) -> &'a Reflector { self.htmlelement.reflector() diff --git a/components/script/dom/virtualmethods.rs b/components/script/dom/virtualmethods.rs index faa3c91aaf2..0e5d0d6d5b3 100644 --- a/components/script/dom/virtualmethods.rs +++ b/components/script/dom/virtualmethods.rs @@ -19,6 +19,7 @@ use dom::bindings::codegen::InheritTypes::HTMLOptGroupElementCast; use dom::bindings::codegen::InheritTypes::HTMLOptionElementCast; use dom::bindings::codegen::InheritTypes::HTMLSelectElementCast; use dom::bindings::codegen::InheritTypes::HTMLStyleElementCast; +use dom::bindings::codegen::InheritTypes::HTMLTableCellElementCast; use dom::bindings::codegen::InheritTypes::HTMLTextAreaElementCast; use dom::bindings::js::JSRef; use dom::element::Element; @@ -37,6 +38,8 @@ use dom::element::HTMLOptGroupElementTypeId; use dom::element::HTMLOptionElementTypeId; use dom::element::HTMLSelectElementTypeId; use dom::element::HTMLStyleElementTypeId; +use dom::element::HTMLTableDataCellElementTypeId; +use dom::element::HTMLTableHeaderCellElementTypeId; use dom::element::HTMLTextAreaElementTypeId; use dom::event::Event; use dom::htmlanchorelement::HTMLAnchorElement; @@ -54,6 +57,7 @@ use dom::htmloptgroupelement::HTMLOptGroupElement; use dom::htmloptionelement::HTMLOptionElement; use dom::htmlselectelement::HTMLSelectElement; use dom::htmlstyleelement::HTMLStyleElement; +use dom::htmltablecellelement::HTMLTableCellElement; use dom::htmltextareaelement::HTMLTextAreaElement; use dom::node::{Node, NodeHelpers, ElementNodeTypeId}; @@ -193,6 +197,11 @@ pub fn vtable_for<'a>(node: &'a JSRef<'a, Node>) -> &'a VirtualMethods + 'a { let element: &'a JSRef<'a, HTMLStyleElement> = HTMLStyleElementCast::to_borrowed_ref(node).unwrap(); element as &'a VirtualMethods + 'a } + ElementNodeTypeId(HTMLTableDataCellElementTypeId) | + ElementNodeTypeId(HTMLTableHeaderCellElementTypeId) => { + let element: &'a JSRef<'a, HTMLTableCellElement> = HTMLTableCellElementCast::to_borrowed_ref(node).unwrap(); + element as &'a VirtualMethods + 'a + } ElementNodeTypeId(HTMLTextAreaElementTypeId) => { let element: &'a JSRef<'a, HTMLTextAreaElement> = HTMLTextAreaElementCast::to_borrowed_ref(node).unwrap(); element as &'a VirtualMethods + 'a diff --git a/components/style/legacy.rs b/components/style/legacy.rs new file mode 100644 index 00000000000..f99314667b8 --- /dev/null +++ b/components/style/legacy.rs @@ -0,0 +1,19 @@ +/* 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/. */ + +//! Legacy presentational attributes defined in the HTML5 specification: ``, +//! ``, and so forth. + +/// Legacy presentational attributes that take a length as defined in HTML5 § 2.4.4.4. +pub enum LengthAttribute { + /// `` + WidthLengthAttribute, +} + +/// Legacy presentational attributes that take an integer as defined in HTML5 § 2.4.4.2. +pub enum IntegerAttribute { + /// `` + SizeIntegerAttribute, +} + diff --git a/components/style/lib.rs b/components/style/lib.rs index 6655613e74a..fc3fd43a94d 100644 --- a/components/style/lib.rs +++ b/components/style/lib.rs @@ -44,11 +44,12 @@ pub use properties::{PropertyDeclaration, ComputedValues, computed_values, style pub use properties::{PropertyDeclarationBlock, parse_style_attribute}; // Style attributes pub use properties::{CSSFloat, DeclaredValue, PropertyDeclarationParseResult}; pub use properties::longhands; -pub use node::{TElement, TNode}; +pub use node::{TElement, TElementAttributes, TNode}; pub use selectors::{PseudoElement, Before, After, SelectorList, parse_selector_list_from_str}; pub use selectors::{AttrSelector, NamespaceConstraint, SpecificNamespace, AnyNamespace}; pub use selectors::{SimpleSelector,LocalNameSelector}; pub use cssparser::{Color, RGBA}; +pub use legacy::{IntegerAttribute, LengthAttribute, SizeIntegerAttribute, WidthLengthAttribute}; pub use font_face::{Source, LocalSource, UrlSource_}; mod stylesheets; @@ -61,3 +62,4 @@ mod node; mod media_queries; mod parsing_utils; mod font_face; +mod legacy; diff --git a/components/style/node.rs b/components/style/node.rs index 3dbde03cd97..fae199364de 100644 --- a/components/style/node.rs +++ b/components/style/node.rs @@ -5,7 +5,9 @@ //! Traits that nodes must implement. Breaks the otherwise-cyclic dependency between layout and //! style. +use legacy::{IntegerAttribute, LengthAttribute}; use selectors::AttrSelector; +use servo_util::str::LengthOrPercentageOrAuto; use string_cache::{Atom, Namespace}; pub trait TNode<'a, E: TElement<'a>> : Clone + Copy { @@ -45,4 +47,8 @@ pub trait TElement<'a> : Copy { fn each_class(self, callback: |&Atom|); } +pub trait TElementAttributes : Copy { + fn get_length_attribute(self, attribute: LengthAttribute) -> LengthOrPercentageOrAuto; + fn get_integer_attribute(self, attribute: IntegerAttribute) -> Option; +} diff --git a/components/style/properties/common_types.rs b/components/style/properties/common_types.rs index 98bdd9b8036..4ed9f6ea4ad 100644 --- a/components/style/properties/common_types.rs +++ b/components/style/properties/common_types.rs @@ -24,6 +24,13 @@ pub mod specified { Au_(Au), // application units Em(CSSFloat), Ex(CSSFloat), + + /// HTML5 "character width", as defined in HTML5 § 14.5.4. + /// + /// This cannot be specified by the user directly and is only generated by + /// `Stylist::synthesize_rules_for_legacy_attributes()`. + ServoCharacterWidth(i32), + // XXX uncomment when supported: // Ch(CSSFloat), // Rem(CSSFloat), @@ -245,6 +252,15 @@ pub mod computed { let x_height = 0.5; // TODO: find that from the font reference_font_size.scale_by(value * x_height) }, + specified::ServoCharacterWidth(value) => { + // This applies the *converting a character width to pixels* algorithm as specified + // in HTML5 § 14.5.4. + // + // TODO(pcwalton): Find these from the font. + let average_advance = reference_font_size.scale_by(0.5); + let max_advance = reference_font_size; + average_advance.scale_by(value as CSSFloat - 1.0) + max_advance + } } } diff --git a/components/style/selector_matching.rs b/components/style/selector_matching.rs index 10447d0c6fa..2c5bc005b26 100644 --- a/components/style/selector_matching.rs +++ b/components/style/selector_matching.rs @@ -11,14 +11,18 @@ use sync::Arc; use url::Url; use servo_util::bloom::BloomFilter; +use servo_util::geometry::Au; use servo_util::resource_files::read_resource_file; use servo_util::smallvec::VecLike; use servo_util::sort; +use servo_util::str::{AutoLpa, LengthLpa, PercentageLpa}; use string_cache::Atom; +use legacy::{SizeIntegerAttribute, WidthLengthAttribute}; use media_queries::{Device, Screen}; -use node::{TElement, TNode}; -use properties::{PropertyDeclaration, PropertyDeclarationBlock}; +use node::{TElement, TElementAttributes, TNode}; +use properties::{PropertyDeclaration, PropertyDeclarationBlock, SpecifiedValue, WidthDeclaration}; +use properties::{specified}; use selectors::*; use stylesheets::{Stylesheet, iter_stylesheet_style_rules}; @@ -80,15 +84,14 @@ impl SelectorMap { /// /// Extract matching rules as per node's ID, classes, tag name, etc.. /// Sort the Rules at the end to maintain cascading order. - fn get_all_matching_rules<'a, - E:TElement<'a>, - N:TNode<'a, E>, - V:VecLike>( - &self, - node: &N, - parent_bf: &Option>, - matching_rules_list: &mut V, - shareable: &mut bool) { + fn get_all_matching_rules<'a,E,N,V>(&self, + node: &N, + parent_bf: &Option>, + matching_rules_list: &mut V, + shareable: &mut bool) + where E: TElement<'a> + TElementAttributes, + N: TNode<'a,E>, + V: VecLike { if self.empty { return } @@ -143,34 +146,36 @@ impl SelectorMap { } } - fn get_matching_rules_from_hash<'a, - E:TElement<'a>, - N:TNode<'a, E>, - V:VecLike>( - node: &N, - parent_bf: &Option>, - hash: &HashMap>, - key: &Atom, - matching_rules: &mut V, - shareable: &mut bool) { + fn get_matching_rules_from_hash<'a,E,N,V>(node: &N, + parent_bf: &Option>, + hash: &HashMap>, + key: &Atom, + matching_rules: &mut V, + shareable: &mut bool) + where E: TElement<'a> + TElementAttributes, + N: TNode<'a,E>, + V: VecLike { match hash.find(key) { Some(rules) => { - SelectorMap::get_matching_rules(node, parent_bf, rules.as_slice(), matching_rules, shareable) + SelectorMap::get_matching_rules(node, + parent_bf, + rules.as_slice(), + matching_rules, + shareable) } None => {} } } /// Adds rules in `rules` that match `node` to the `matching_rules` list. - fn get_matching_rules<'a, - E:TElement<'a>, - N:TNode<'a, E>, - V:VecLike>( - node: &N, - parent_bf: &Option>, - rules: &[Rule], - matching_rules: &mut V, - shareable: &mut bool) { + fn get_matching_rules<'a,E,N,V>(node: &N, + parent_bf: &Option>, + rules: &[Rule], + matching_rules: &mut V, + shareable: &mut bool) + where E: TElement<'a> + TElementAttributes, + N: TNode<'a,E>, + V: VecLike { for rule in rules.iter() { if matches_compound_selector(&*rule.selector, node, parent_bf, shareable) { matching_rules.vec_push(rule.declarations.clone()); @@ -348,17 +353,17 @@ impl Stylist { /// The returned boolean indicates whether the style is *shareable*; that is, whether the /// matched selectors are simple enough to allow the matching logic to be reduced to the logic /// in `css::matching::PrivateMatchMethods::candidate_element_allows_for_style_sharing`. - pub fn push_applicable_declarations<'a, - E:TElement<'a>, - N:TNode<'a, E>, - V:VecLike>( + pub fn push_applicable_declarations<'a,E,N,V>( &self, element: &N, parent_bf: &Option>, style_attribute: Option<&PropertyDeclarationBlock>, pseudo_element: Option, applicable_declarations: &mut V) - -> bool { + -> bool + where E: TElement<'a> + TElementAttributes, + N: TNode<'a,E>, + V: VecLike { assert!(element.is_element()); assert!(style_attribute.is_none() || pseudo_element.is_none(), "Style attributes do not apply to pseudo-elements"); @@ -371,33 +376,46 @@ impl Stylist { let mut shareable = true; - // Step 1: Normal rules. + // Step 1: Virtual rules that are synthesized from legacy HTML attributes. + self.synthesize_presentational_hints_for_legacy_attributes(element, + applicable_declarations, + &mut shareable); + + // Step 2: Normal rules. map.user_agent.normal.get_all_matching_rules(element, parent_bf, applicable_declarations, &mut shareable); - map.user.normal.get_all_matching_rules(element, parent_bf, applicable_declarations, &mut shareable); - map.author.normal.get_all_matching_rules(element, parent_bf, applicable_declarations, &mut shareable); + map.user.normal.get_all_matching_rules(element, + parent_bf, + applicable_declarations, + &mut shareable); + map.author.normal.get_all_matching_rules(element, + parent_bf, + applicable_declarations, + &mut shareable); - // Step 2: Normal style attributes. + // Step 3: Normal style attributes. style_attribute.map(|sa| { shareable = false; - applicable_declarations.vec_push(DeclarationBlock::from_declarations(sa.normal.clone())) + applicable_declarations.vec_push(DeclarationBlock::from_declarations(sa.normal + .clone())) }); - // Step 3: Author-supplied `!important` rules. + // Step 4: Author-supplied `!important` rules. map.author.important.get_all_matching_rules(element, parent_bf, applicable_declarations, &mut shareable); - // Step 4: `!important` style attributes. + // Step 5: `!important` style attributes. style_attribute.map(|sa| { shareable = false; - applicable_declarations.vec_push(DeclarationBlock::from_declarations(sa.important.clone())) + applicable_declarations.vec_push(DeclarationBlock::from_declarations(sa.important + .clone())) }); - // Step 5: User and UA `!important` rules. + // Step 6: User and UA `!important` rules. map.user.important.get_all_matching_rules(element, parent_bf, applicable_declarations, @@ -409,6 +427,62 @@ impl Stylist { shareable } + + /// Synthesizes rules from various HTML attributes (mostly legacy junk from HTML4) that confer + /// *presentational hints* as defined in the HTML5 specification. This handles stuff like + /// ``, ``, ``, and so forth. + fn synthesize_presentational_hints_for_legacy_attributes<'a,E,N,V>( + &self, + node: &N, + matching_rules_list: &mut V, + shareable: &mut bool) + where E: TElement<'a> + + TElementAttributes, + N: TNode<'a,E>, + V: VecLike { + let element = node.as_element(); + match element.get_local_name() { + name if *name == atom!("td") => { + match element.get_length_attribute(WidthLengthAttribute) { + AutoLpa => {} + PercentageLpa(percentage) => { + let width_value = specified::LPA_Percentage(percentage); + matching_rules_list.vec_push(DeclarationBlock::from_declaration( + WidthDeclaration(SpecifiedValue(width_value)))); + *shareable = false + } + LengthLpa(length) => { + let width_value = specified::LPA_Length(specified::Au_(length)); + matching_rules_list.vec_push(DeclarationBlock::from_declaration( + WidthDeclaration(SpecifiedValue(width_value)))); + *shareable = false + } + }; + } + name if *name == atom!("input") => { + match element.get_integer_attribute(SizeIntegerAttribute) { + Some(value) if value != 0 => { + // Per HTML 4.01 § 17.4, this value is in characters if `type` is `text` or + // `password` and in pixels otherwise. + // + // FIXME(pcwalton): More use of atoms, please! + let value = match element.get_attr(&ns!(""), &atom!("type")) { + Some("text") | Some("password") => { + specified::ServoCharacterWidth(value) + } + _ => specified::Au_(Au::from_px(value as int)), + }; + matching_rules_list.vec_push(DeclarationBlock::from_declaration( + WidthDeclaration(SpecifiedValue(specified::LPA_Length( + value))))); + *shareable = false + } + Some(_) | None => {} + } + } + _ => {} + } + } } struct PerOriginSelectorMap { @@ -470,14 +544,20 @@ impl DeclarationBlock { specificity: 0, } } + + /// A convenience function to create a declaration block from a single declaration. This is + /// primarily used in `synthesize_rules_for_legacy_attributes`. + #[inline] + pub fn from_declaration(rule: PropertyDeclaration) -> DeclarationBlock { + DeclarationBlock::from_declarations(Arc::new(vec![rule])) + } } -pub fn matches<'a,E,N>( - selector_list: &SelectorList, - element: &N, - parent_bf: &Option>) - -> bool - where E: TElement<'a>, N: TNode<'a,E> { +pub fn matches<'a,E,N>(selector_list: &SelectorList, + element: &N, + parent_bf: &Option>) + -> bool + where E: TElement<'a>, N: TNode<'a,E> { get_selector_list_selectors(selector_list).iter().any(|selector| selector.pseudo_element.is_none() && matches_compound_selector(&*selector.compound_selectors, element, parent_bf, &mut false)) @@ -489,14 +569,12 @@ pub fn matches<'a,E,N>( /// `shareable` to false unless you are willing to update the style sharing logic. Otherwise things /// will almost certainly break as nodes will start mistakenly sharing styles. (See the code in /// `main/css/matching.rs`.) -fn matches_compound_selector<'a, - E:TElement<'a>, - N:TNode<'a, E>>( - selector: &CompoundSelector, - element: &N, - parent_bf: &Option>, - shareable: &mut bool) - -> bool { +fn matches_compound_selector<'a,E,N>(selector: &CompoundSelector, + element: &N, + parent_bf: &Option>, + shareable: &mut bool) + -> bool + where E: TElement<'a>, N: TNode<'a,E> { match matches_compound_selector_internal(selector, element, parent_bf, shareable) { Matched => true, _ => false @@ -555,13 +633,12 @@ enum SelectorMatchingResult { /// Quickly figures out whether or not the compound selector is worth doing more /// work on. If the simple selectors don't match, or there's a child selector /// that does not appear in the bloom parent bloom filter, we can exit early. -fn can_fast_reject<'a,E,N>( - mut selector: &CompoundSelector, - element: &N, - parent_bf: &Option>, - shareable: &mut bool) - -> Option - where E: TElement<'a>, N: TNode<'a,E> { +fn can_fast_reject<'a,E,N>(mut selector: &CompoundSelector, + element: &N, + parent_bf: &Option>, + shareable: &mut bool) + -> Option + where E: TElement<'a>, N: TNode<'a,E> { if !selector.simple_selectors.iter().all(|simple_selector| { matches_simple_selector(simple_selector, element, shareable) }) { return Some(NotMatchedAndRestartFromClosestLaterSibling); @@ -617,14 +694,12 @@ fn can_fast_reject<'a,E,N>( return None; } -fn matches_compound_selector_internal<'a, - E:TElement<'a>, - N:TNode<'a, E>>( - selector: &CompoundSelector, - element: &N, - parent_bf: &Option>, - shareable: &mut bool) - -> SelectorMatchingResult { +fn matches_compound_selector_internal<'a,E,N>(selector: &CompoundSelector, + element: &N, + parent_bf: &Option>, + shareable: &mut bool) + -> SelectorMatchingResult + where E: TElement<'a>, N: TNode<'a,E> { match can_fast_reject(selector, element, parent_bf, shareable) { None => {}, Some(result) => return result, @@ -693,12 +768,11 @@ fn matches_compound_selector_internal<'a, /// will almost certainly break as nodes will start mistakenly sharing styles. (See the code in /// `main/css/matching.rs`.) #[inline] -pub fn matches_simple_selector<'a,E,N>( - selector: &SimpleSelector, - element: &N, - shareable: &mut bool) - -> bool - where E:TElement<'a>, N:TNode<'a,E> { +pub fn matches_simple_selector<'a,E,N>(selector: &SimpleSelector, + element: &N, + shareable: &mut bool) + -> bool + where E: TElement<'a>, N: TNode<'a,E> { match *selector { LocalNameSelector(LocalName { ref name, ref lower_name }) => { let name = if element.is_html_element_in_html_document() { lower_name } else { name }; @@ -877,14 +951,13 @@ fn url_is_visited(_url: &str) -> bool { } #[inline] -fn matches_generic_nth_child<'a,E,N>( - element: &N, - a: i32, - b: i32, - is_of_type: bool, - is_from_end: bool) - -> bool - where E: TElement<'a>, N: TNode<'a,E> { +fn matches_generic_nth_child<'a,E,N>(element: &N, + a: i32, + b: i32, + is_of_type: bool, + is_from_end: bool) + -> bool + where E: TElement<'a>, N: TNode<'a,E> { let mut node = element.clone(); // fail if we can't find a parent or if the node is the root element // of the document (Cf. Selectors Level 3) @@ -933,7 +1006,7 @@ fn matches_generic_nth_child<'a,E,N>( } #[inline] -fn matches_root<'a, E:TElement<'a>,N:TNode<'a, E>>(element: &N) -> bool { +fn matches_root<'a,E,N>(element: &N) -> bool where E: TElement<'a>, N: TNode<'a,E> { match element.parent_node() { Some(parent) => parent.is_document(), None => false @@ -941,7 +1014,7 @@ fn matches_root<'a, E:TElement<'a>,N:TNode<'a, E>>(element: &N) -> bool { } #[inline] -fn matches_first_child<'a, E:TElement<'a>,N:TNode<'a, E>>(element: &N) -> bool { +fn matches_first_child<'a,E,N>(element: &N) -> bool where E: TElement<'a>, N: TNode<'a,E> { let mut node = element.clone(); loop { match node.prev_sibling() { @@ -963,7 +1036,7 @@ fn matches_first_child<'a, E:TElement<'a>,N:TNode<'a, E>>(element: &N) -> bool { } #[inline] -fn matches_last_child<'a, E:TElement<'a>,N:TNode<'a, E>>(element: &N) -> bool { +fn matches_last_child<'a,E,N>(element: &N) -> bool where E: TElement<'a>, N: TNode<'a,E> { let mut node = element.clone(); loop { match node.next_sibling() { @@ -1081,3 +1154,4 @@ mod tests { assert!(selector_map.class_hash.find(&Atom::from_slice("foo")).is_none()); } } + diff --git a/components/util/str.rs b/components/util/str.rs index 589795c0f7d..6e88f0ba4f8 100644 --- a/components/util/str.rs +++ b/components/util/str.rs @@ -2,8 +2,11 @@ * 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/. */ +use geometry::Au; + +use std::from_str::FromStr; use std::iter::Filter; -use std::str::CharSplits; +use std::str::{CharEq, CharSplits}; pub type DOMString = String; pub type StaticCharVec = &'static [char]; @@ -24,11 +27,26 @@ pub fn null_str_as_empty_ref<'a>(s: &'a Option) -> &'a str { } } +/// Whitespace as defined by HTML5 § 2.4.1. +struct Whitespace; + +impl CharEq for Whitespace { + #[inline] + fn matches(&mut self, ch: char) -> bool { + match ch { + ' ' | '\t' | '\x0a' | '\x0c' | '\x0d' => true, + _ => false, + } + } + + #[inline] + fn only_ascii(&self) -> bool { + true + } +} + pub fn is_whitespace(s: &str) -> bool { - s.chars().all(|c| match c { - '\u0020' | '\u0009' | '\u000D' | '\u000A' => true, - _ => false - }) + s.chars().all(|c| Whitespace.matches(c)) } /// A "space character" according to: @@ -108,3 +126,61 @@ pub fn parse_unsigned_integer>(input: T) -> Option { result.to_u32() }) } + +pub enum LengthOrPercentageOrAuto { + AutoLpa, + PercentageLpa(f64), + LengthLpa(Au), +} + +/// Parses a length per HTML5 § 2.4.4.4. If unparseable, `AutoLpa` is returned. +pub fn parse_length(mut value: &str) -> LengthOrPercentageOrAuto { + value = value.trim_left_chars(Whitespace); + if value.len() == 0 { + return AutoLpa + } + if value.starts_with("+") { + value = value.slice_from(1) + } + value = value.trim_left_chars('0'); + if value.len() == 0 { + return AutoLpa + } + + let mut end_index = value.len(); + let (mut found_full_stop, mut found_percent) = (false, false); + for (i, ch) in value.chars().enumerate() { + match ch { + '0'..'9' => continue, + '%' => { + found_percent = true; + end_index = i; + break + } + '.' if !found_full_stop => { + found_full_stop = true; + continue + } + _ => { + end_index = i; + break + } + } + } + value = value.slice_to(end_index); + + if found_percent { + let result: Option = FromStr::from_str(value); + match result { + Some(number) => return PercentageLpa((number as f64) / 100.0), + None => return AutoLpa, + } + } + + match FromStr::from_str(value) { + Some(number) => LengthLpa(Au::from_px(number)), + None => AutoLpa, + } +} + + diff --git a/resources/servo.css b/resources/servo.css index 77d05782725..8b0acbd2e9c 100644 --- a/resources/servo.css +++ b/resources/servo.css @@ -11,3 +11,7 @@ input[type="checkbox"]::before { content: "[ ]"; padding: 0; } input[type="checkbox"][checked]::before { content: "[✓]"; } input[type="radio"]::before { content: "( )"; padding: 0; } input[type="radio"][checked]::before { content: "(●)"; } + +td[align="left"] { text-align: left; } +td[align="center"] { text-align: center; } +td[align="right"] { text-align: right; } diff --git a/tests/ref/basic.list b/tests/ref/basic.list index 2586fa42ae2..4d3c38e70a2 100644 --- a/tests/ref/basic.list +++ b/tests/ref/basic.list @@ -172,5 +172,7 @@ fragment=top != ../html/acid2.html acid2_ref.html != input_height_a.html input_height_ref.html == pre_ignorable_whitespace_a.html pre_ignorable_whitespace_ref.html == many_brs_a.html many_brs_ref.html +== legacy_input_size_attribute_override_a.html legacy_input_size_attribute_override_ref.html +== legacy_td_width_attribute_a.html legacy_td_width_attribute_ref.html == box_sizing_sanity_check_a.html box_sizing_sanity_check_ref.html == inline_block_overflow_hidden_a.html inline_block_overflow_hidden_ref.html diff --git a/tests/ref/legacy_input_size_attribute_override_a.html b/tests/ref/legacy_input_size_attribute_override_a.html new file mode 100644 index 00000000000..ef0315bc44d --- /dev/null +++ b/tests/ref/legacy_input_size_attribute_override_a.html @@ -0,0 +1,7 @@ + + + + + + + diff --git a/tests/ref/legacy_input_size_attribute_override_ref.html b/tests/ref/legacy_input_size_attribute_override_ref.html new file mode 100644 index 00000000000..c13c9ec3164 --- /dev/null +++ b/tests/ref/legacy_input_size_attribute_override_ref.html @@ -0,0 +1,7 @@ + + + + + + + diff --git a/tests/ref/legacy_td_width_attribute_a.html b/tests/ref/legacy_td_width_attribute_a.html new file mode 100644 index 00000000000..f060ffd637e --- /dev/null +++ b/tests/ref/legacy_td_width_attribute_a.html @@ -0,0 +1,21 @@ + + + + + + +
+
+
+ + + + diff --git a/tests/ref/legacy_td_width_attribute_ref.html b/tests/ref/legacy_td_width_attribute_ref.html new file mode 100644 index 00000000000..d903f2c0357 --- /dev/null +++ b/tests/ref/legacy_td_width_attribute_ref.html @@ -0,0 +1,22 @@ + + + + + + +
+
+
+ + +