layout: Introduce support for legacy presentational attributes to selector

matching, and use it for `<input size>` and `<td width>`.

This implements a general framework for legacy presentational attributes
to the DOM and style calculation, so that adding more of them later will
be straightforward.
This commit is contained in:
Patrick Walton 2014-10-14 11:25:29 -07:00
parent 8077edc062
commit 5f8d3f72d8
31 changed files with 570 additions and 214 deletions

View file

@ -1495,13 +1495,11 @@ impl Flow for BlockFlow {
/// Pass 1 of reflow: computes minimum and preferred inline-sizes. /// 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 /// Recursively (bottom-up) determine the flow's minimum and preferred inline-sizes. When
/// this flow, all child flows have had their minimum and preferred inline-sizes set. This function /// called on this flow, all child flows have had their minimum and preferred inline-sizes set.
/// must decide minimum/preferred inline-sizes based on its children's inline-sizes and the dimensions of /// This function must decide minimum/preferred inline-sizes based on its children's
/// any fragments it is responsible for flowing. /// inline-sizes and the dimensions of any fragments it is responsible for flowing.
/// fn bubble_inline_sizes(&mut self) {
/// TODO(pcwalton): Inline blocks.
fn bubble_inline_sizes(&mut self, layout_context: &LayoutContext) {
let _scope = layout_debug_scope!("block::bubble_inline_sizes {:s}", self.base.debug_id()); let _scope = layout_debug_scope!("block::bubble_inline_sizes {:s}", self.base.debug_id());
let mut flags = self.base.flags; let mut flags = self.base.flags;
@ -1557,13 +1555,16 @@ impl Flow for BlockFlow {
max(intrinsic_inline_sizes.preferred_inline_size, max(intrinsic_inline_sizes.preferred_inline_size,
left_float_width + right_float_width); left_float_width + right_float_width);
let fragment_intrinsic_inline_sizes = self.fragment.intrinsic_inline_sizes(layout_context); let fragment_intrinsic_inline_sizes = self.fragment.intrinsic_inline_sizes();
intrinsic_inline_sizes.minimum_inline_size = max(intrinsic_inline_sizes.minimum_inline_size, intrinsic_inline_sizes.minimum_inline_size =
fragment_intrinsic_inline_sizes.minimum_inline_size); max(intrinsic_inline_sizes.minimum_inline_size,
intrinsic_inline_sizes.preferred_inline_size = max(intrinsic_inline_sizes.preferred_inline_size, fragment_intrinsic_inline_sizes.minimum_inline_size);
fragment_intrinsic_inline_sizes.preferred_inline_size); intrinsic_inline_sizes.preferred_inline_size =
intrinsic_inline_sizes.surround_inline_size = intrinsic_inline_sizes.surround_inline_size + max(intrinsic_inline_sizes.preferred_inline_size,
fragment_intrinsic_inline_sizes.surround_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; self.base.intrinsic_inline_sizes = intrinsic_inline_sizes;
match self.fragment.style().get_box().float { match self.fragment.style().get_box().float {

View file

@ -30,10 +30,9 @@ use flow_ref::FlowRef;
use fragment::{Fragment, GenericFragment, IframeFragment, IframeFragmentInfo, ImageFragment}; use fragment::{Fragment, GenericFragment, IframeFragment, IframeFragmentInfo, ImageFragment};
use fragment::{ImageFragmentInfo, InlineAbsoluteHypotheticalFragment}; use fragment::{ImageFragmentInfo, InlineAbsoluteHypotheticalFragment};
use fragment::{InlineAbsoluteHypotheticalFragmentInfo, InlineBlockFragment}; use fragment::{InlineAbsoluteHypotheticalFragmentInfo, InlineBlockFragment};
use fragment::{InlineBlockFragmentInfo, InputFragment, InputFragmentInfo, SpecificFragmentInfo}; use fragment::{InlineBlockFragmentInfo, InputFragment, SpecificFragmentInfo, TableCellFragment};
use fragment::{TableCellFragment, TableColumnFragment, TableColumnFragmentInfo, TableFragment}; use fragment::{TableColumnFragment, TableColumnFragmentInfo, TableFragment, TableRowFragment};
use fragment::{TableRowFragment, TableWrapperFragment, UnscannedTextFragment}; use fragment::{TableWrapperFragment, UnscannedTextFragment, UnscannedTextFragmentInfo};
use fragment::{UnscannedTextFragmentInfo};
use inline::{InlineFragments, InlineFlow}; use inline::{InlineFragments, InlineFlow};
use parallel; use parallel;
use table_wrapper::TableWrapperFlow; 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. /// Builds specific `Fragment` info for the given node.
/// ///
/// This does *not* construct the text for generated content (but, for generated content with /// 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()) self.build_fragment_info_for_image(node, node.image_url())
} }
Some(ElementNodeTypeId(HTMLInputElementTypeId)) => { Some(ElementNodeTypeId(HTMLInputElementTypeId)) => {
self.build_fragment_info_for_input(node) InputFragment
} }
Some(ElementNodeTypeId(HTMLObjectElementTypeId)) => { Some(ElementNodeTypeId(HTMLObjectElementTypeId)) => {
let data = node.get_object_data(); 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. /// This must not be public because only the layout constructor can do this.
fn finish(&mut self, context: &LayoutContext) { fn finish(&mut self, context: &LayoutContext) {
if !context.shared.opts.bubble_inline_sizes_separately { if !context.shared.opts.bubble_inline_sizes_separately {
self.get_mut().bubble_inline_sizes(context) self.get_mut().bubble_inline_sizes()
} }
} }
} }

View file

@ -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. /// 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- /// 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. /// 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") fail!("bubble_inline_sizes not yet implemented")
} }

View file

@ -138,7 +138,7 @@ pub enum SpecificFragmentInfo {
InlineAbsoluteHypotheticalFragment(InlineAbsoluteHypotheticalFragmentInfo), InlineAbsoluteHypotheticalFragment(InlineAbsoluteHypotheticalFragmentInfo),
InlineBlockFragment(InlineBlockFragmentInfo), InlineBlockFragment(InlineBlockFragmentInfo),
InputFragment(InputFragmentInfo), InputFragment,
ScannedTextFragment(ScannedTextFragmentInfo), ScannedTextFragment(ScannedTextFragmentInfo),
TableFragment, TableFragment,
TableCellFragment, 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. /// A fragment that represents a replaced content image and its accompanying borders, shadows, etc.
#[deriving(Clone)] #[deriving(Clone)]
pub struct ImageFragmentInfo { pub struct ImageFragmentInfo {
@ -550,7 +534,7 @@ impl Fragment {
fn style_specified_intrinsic_inline_size(&self) -> IntrinsicISizes { fn style_specified_intrinsic_inline_size(&self) -> IntrinsicISizes {
let (use_margins, use_padding) = match self.specific { let (use_margins, use_padding) = match self.specific {
GenericFragment | IframeFragment(_) | ImageFragment(_) | InlineBlockFragment(_) | GenericFragment | IframeFragment(_) | ImageFragment(_) | InlineBlockFragment(_) |
InputFragment(_) => (true, true), InputFragment => (true, true),
TableFragment | TableCellFragment => (false, true), TableFragment | TableCellFragment => (false, true),
TableWrapperFragment => (true, false), TableWrapperFragment => (true, false),
TableRowFragment => (false, false), TableRowFragment => (false, false),
@ -1195,7 +1179,7 @@ impl Fragment {
clip_rect)) clip_rect))
} }
GenericFragment | IframeFragment(..) | TableFragment | TableCellFragment | GenericFragment | IframeFragment(..) | TableFragment | TableCellFragment |
TableRowFragment | TableWrapperFragment | InlineBlockFragment(_) | InputFragment(_) | TableRowFragment | TableWrapperFragment | InlineBlockFragment(_) | InputFragment |
InlineAbsoluteHypotheticalFragment(_) => { InlineAbsoluteHypotheticalFragment(_) => {
// FIXME(pcwalton): This is a bit of an abuse of the logging infrastructure. We // FIXME(pcwalton): This is a bit of an abuse of the logging infrastructure. We
// should have a real `SERVO_DEBUG` system. // should have a real `SERVO_DEBUG` system.
@ -1258,13 +1242,13 @@ impl Fragment {
} }
/// Returns the intrinsic inline-sizes of this 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(); let mut result = self.style_specified_intrinsic_inline_size();
match self.specific { match self.specific {
GenericFragment | IframeFragment(_) | TableFragment | TableCellFragment | GenericFragment | IframeFragment(_) | TableFragment | TableCellFragment |
TableColumnFragment(_) | TableRowFragment | TableWrapperFragment | TableColumnFragment(_) | TableRowFragment | TableWrapperFragment |
InlineAbsoluteHypotheticalFragment(_) => {} InlineAbsoluteHypotheticalFragment(_) | InputFragment => {}
InlineBlockFragment(ref mut info) => { InlineBlockFragment(ref mut info) => {
let block_flow = info.flow_ref.get_mut().as_block(); let block_flow = info.flow_ref.get_mut().as_block();
result.minimum_inline_size = max(result.minimum_inline_size, result.minimum_inline_size = max(result.minimum_inline_size,
@ -1280,12 +1264,6 @@ impl Fragment {
result.preferred_inline_size = max(result.preferred_inline_size, result.preferred_inline_size = max(result.preferred_inline_size,
image_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) => { ScannedTextFragment(ref text_fragment_info) => {
let range = &text_fragment_info.range; let range = &text_fragment_info.range;
let min_line_inline_size = text_fragment_info.run.min_width_for_range(range); let min_line_inline_size = text_fragment_info.run.min_width_for_range(range);
@ -1333,7 +1311,7 @@ impl Fragment {
match self.specific { match self.specific {
GenericFragment | IframeFragment(_) | TableFragment | TableCellFragment | GenericFragment | IframeFragment(_) | TableFragment | TableCellFragment |
TableRowFragment | TableWrapperFragment | InlineBlockFragment(_) | TableRowFragment | TableWrapperFragment | InlineBlockFragment(_) |
InputFragment(_) | InlineAbsoluteHypotheticalFragment(_) => Au(0), InputFragment | InlineAbsoluteHypotheticalFragment(_) => Au(0),
ImageFragment(ref image_fragment_info) => { ImageFragment(ref image_fragment_info) => {
image_fragment_info.computed_inline_size() image_fragment_info.computed_inline_size()
} }
@ -1352,7 +1330,7 @@ impl Fragment {
match self.specific { match self.specific {
GenericFragment | IframeFragment(_) | TableFragment | TableCellFragment | GenericFragment | IframeFragment(_) | TableFragment | TableCellFragment |
TableRowFragment | TableWrapperFragment | InlineBlockFragment(_) | TableRowFragment | TableWrapperFragment | InlineBlockFragment(_) |
InputFragment(_) | InlineAbsoluteHypotheticalFragment(_) => Au(0), InputFragment | InlineAbsoluteHypotheticalFragment(_) => Au(0),
ImageFragment(ref image_fragment_info) => { ImageFragment(ref image_fragment_info) => {
image_fragment_info.computed_block_size() image_fragment_info.computed_block_size()
} }
@ -1386,7 +1364,7 @@ impl Fragment {
-> Option<(SplitInfo, Option<SplitInfo>, Arc<Box<TextRun>> /* TODO(bjz): remove */)> { -> Option<(SplitInfo, Option<SplitInfo>, Arc<Box<TextRun>> /* TODO(bjz): remove */)> {
match self.specific { match self.specific {
GenericFragment | IframeFragment(_) | ImageFragment(_) | TableFragment | TableCellFragment | GenericFragment | IframeFragment(_) | ImageFragment(_) | TableFragment | TableCellFragment |
TableRowFragment | TableWrapperFragment | InputFragment(_) => None, TableRowFragment | TableWrapperFragment | InputFragment => None,
TableColumnFragment(_) => fail!("Table column fragments do not need to split"), TableColumnFragment(_) => fail!("Table column fragments do not need to split"),
UnscannedTextFragment(_) => fail!("Unscanned text fragments should have been scanned by now!"), UnscannedTextFragment(_) => fail!("Unscanned text fragments should have been scanned by now!"),
InlineBlockFragment(_) | InlineAbsoluteHypotheticalFragment(_) => { InlineBlockFragment(_) | InlineAbsoluteHypotheticalFragment(_) => {
@ -1429,7 +1407,7 @@ impl Fragment {
-> Option<(Option<SplitInfo>, Option<SplitInfo>, Arc<Box<TextRun>> /* TODO(bjz): remove */)> { -> Option<(Option<SplitInfo>, Option<SplitInfo>, Arc<Box<TextRun>> /* TODO(bjz): remove */)> {
match self.specific { match self.specific {
GenericFragment | IframeFragment(_) | ImageFragment(_) | TableFragment | TableCellFragment | GenericFragment | IframeFragment(_) | ImageFragment(_) | TableFragment | TableCellFragment |
TableRowFragment | TableWrapperFragment | InlineBlockFragment(_) | InputFragment(_) | TableRowFragment | TableWrapperFragment | InlineBlockFragment(_) | InputFragment |
InlineAbsoluteHypotheticalFragment(_) => None, InlineAbsoluteHypotheticalFragment(_) => None,
TableColumnFragment(_) => fail!("Table column fragments do not have inline_size"), TableColumnFragment(_) => fail!("Table column fragments do not have inline_size"),
UnscannedTextFragment(_) => fail!("Unscanned text fragments should have been scanned by now!"), UnscannedTextFragment(_) => fail!("Unscanned text fragments should have been scanned by now!"),
@ -1528,7 +1506,7 @@ impl Fragment {
container_inline_size: Au) { container_inline_size: Au) {
match self.specific { match self.specific {
GenericFragment | IframeFragment(_) | TableFragment | TableCellFragment | GenericFragment | IframeFragment(_) | TableFragment | TableCellFragment |
TableRowFragment | TableWrapperFragment | InputFragment(_) => return, TableRowFragment | TableWrapperFragment | InputFragment => return,
TableColumnFragment(_) => fail!("Table column fragments do not have inline_size"), TableColumnFragment(_) => fail!("Table column fragments do not have inline_size"),
UnscannedTextFragment(_) => { UnscannedTextFragment(_) => {
fail!("Unscanned text fragments should have been scanned by now!") 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) { pub fn assign_replaced_block_size_if_necessary(&mut self, containing_block_block_size: Au) {
match self.specific { match self.specific {
GenericFragment | IframeFragment(_) | TableFragment | TableCellFragment | GenericFragment | IframeFragment(_) | TableFragment | TableCellFragment |
TableRowFragment | TableWrapperFragment | InputFragment(_) => return, TableRowFragment | TableWrapperFragment | InputFragment => return,
TableColumnFragment(_) => fail!("Table column fragments do not have block_size"), TableColumnFragment(_) => fail!("Table column fragments do not have block_size"),
UnscannedTextFragment(_) => { UnscannedTextFragment(_) => {
fail!("Unscanned text fragments should have been scanned by now!") fail!("Unscanned text fragments should have been scanned by now!")
@ -1787,7 +1765,7 @@ impl Fragment {
TableWrapperFragment => false, TableWrapperFragment => false,
GenericFragment | IframeFragment(_) | ImageFragment(_) | ScannedTextFragment(_) | GenericFragment | IframeFragment(_) | ImageFragment(_) | ScannedTextFragment(_) |
TableFragment | TableCellFragment | TableColumnFragment(_) | TableRowFragment | TableFragment | TableCellFragment | TableColumnFragment(_) | TableRowFragment |
UnscannedTextFragment(_) | InputFragment(_) => true, UnscannedTextFragment(_) | InputFragment => true,
} }
} }
@ -1844,7 +1822,7 @@ impl fmt::Show for Fragment {
ImageFragment(_) => "ImageFragment", ImageFragment(_) => "ImageFragment",
InlineAbsoluteHypotheticalFragment(_) => "InlineAbsoluteHypotheticalFragment", InlineAbsoluteHypotheticalFragment(_) => "InlineAbsoluteHypotheticalFragment",
InlineBlockFragment(_) => "InlineBlockFragment", InlineBlockFragment(_) => "InlineBlockFragment",
InputFragment(_) => "InputFragment", InputFragment => "InputFragment",
ScannedTextFragment(_) => "ScannedTextFragment", ScannedTextFragment(_) => "ScannedTextFragment",
TableFragment => "TableFragment", TableFragment => "TableFragment",
TableCellFragment => "TableCellFragment", TableCellFragment => "TableCellFragment",

View file

@ -914,7 +914,7 @@ impl Flow for InlineFlow {
self 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 _scope = layout_debug_scope!("inline::bubble_inline_sizes {:s}", self.base.debug_id());
let writing_mode = self.base.writing_mode; let writing_mode = self.base.writing_mode;
@ -927,7 +927,7 @@ impl Flow for InlineFlow {
debug!("Flow: measuring {}", *fragment); debug!("Flow: measuring {}", *fragment);
let fragment_intrinsic_inline_sizes = 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 = max(
intrinsic_inline_sizes.minimum_inline_size, intrinsic_inline_sizes.minimum_inline_size,
fragment_intrinsic_inline_sizes.minimum_inline_size); fragment_intrinsic_inline_sizes.minimum_inline_size);

View file

@ -171,9 +171,9 @@ impl Flow for TableFlow {
/// table layout calculation. /// table layout calculation.
/// The maximum min/pref inline-sizes of each column are set from the rows for the automatic /// The maximum min/pref inline-sizes of each column are set from the rows for the automatic
/// table layout calculation. /// 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}", 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 min_inline_size = Au(0);
let mut pref_inline_size = Au(0); let mut pref_inline_size = Au(0);
@ -239,8 +239,7 @@ impl Flow for TableFlow {
} }
} }
let fragment_intrinsic_inline_sizes = let fragment_intrinsic_inline_sizes = self.block_flow.fragment.intrinsic_inline_sizes();
self.block_flow.fragment.intrinsic_inline_sizes(layout_context);
self.block_flow.base.intrinsic_inline_sizes.minimum_inline_size = min_inline_size; self.block_flow.base.intrinsic_inline_sizes.minimum_inline_size = min_inline_size;
self.block_flow.base.intrinsic_inline_sizes.preferred_inline_size = self.block_flow.base.intrinsic_inline_sizes.preferred_inline_size =
max(min_inline_size, pref_inline_size); max(min_inline_size, pref_inline_size);

View file

@ -48,8 +48,8 @@ impl Flow for TableCaptionFlow {
&mut self.block_flow &mut self.block_flow
} }
fn bubble_inline_sizes(&mut self, ctx: &LayoutContext) { fn bubble_inline_sizes(&mut self) {
self.block_flow.bubble_inline_sizes(ctx); self.block_flow.bubble_inline_sizes();
} }
fn assign_inline_sizes(&mut self, ctx: &LayoutContext) { fn assign_inline_sizes(&mut self, ctx: &LayoutContext) {

View file

@ -74,16 +74,21 @@ impl Flow for TableCellFlow {
&mut self.block_flow &mut self.block_flow
} }
/// Minimum/preferred inline-sizes set by this function are used in automatic table layout calculation. /// Minimum/preferred inline-sizes set by this function are used in automatic table layout
fn bubble_inline_sizes(&mut self, ctx: &LayoutContext) { /// calculation.
fn bubble_inline_sizes(&mut self) {
let _scope = layout_debug_scope!("table_cell::bubble_inline_sizes {:s}", 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); self.block_flow.bubble_inline_sizes();
let specified_inline_size = MaybeAuto::from_style(self.block_flow.fragment.style().content_inline_size(), let specified_inline_size = MaybeAuto::from_style(self.block_flow
Au::new(0)).specified_or_zero(); .fragment
if self.block_flow.base.intrinsic_inline_sizes.minimum_inline_size < specified_inline_size { .style()
self.block_flow.base.intrinsic_inline_sizes.minimum_inline_size = specified_inline_size; .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 < if self.block_flow.base.intrinsic_inline_sizes.preferred_inline_size <
self.block_flow.base.intrinsic_inline_sizes.minimum_inline_size { self.block_flow.base.intrinsic_inline_sizes.minimum_inline_size {

View file

@ -53,7 +53,7 @@ impl Flow for TableColGroupFlow {
self 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}", let _scope = layout_debug_scope!("table_colgroup::bubble_inline_sizes {:s}",
self.base.debug_id()); self.base.debug_id());

View file

@ -171,7 +171,7 @@ impl Flow for TableRowFlow {
/// responsible for flowing. /// responsible for flowing.
/// Min/pref inline-sizes set by this function are used in automatic table layout calculation. /// 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. /// 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}", let _scope = layout_debug_scope!("table_row::bubble_inline_sizes {:s}",
self.block_flow.base.debug_id()); self.block_flow.base.debug_id());

View file

@ -132,14 +132,16 @@ impl Flow for TableRowGroupFlow {
&self.col_pref_inline_sizes &self.col_pref_inline_sizes
} }
/// Recursively (bottom-up) determines the context's preferred and minimum inline-sizes. When called /// Recursively (bottom-up) determines the context's preferred and minimum inline-sizes. When
/// on this context, all child contexts have had their min/pref inline-sizes set. This function must /// called on this context, all child contexts have had their min/pref inline-sizes set. This
/// decide min/pref inline-sizes based on child context inline-sizes and dimensions of any fragments it is /// function must decide min/pref inline-sizes based on child context inline-sizes and
/// responsible for flowing. /// dimensions of any fragments it is responsible for flowing.
///
/// Min/pref inline-sizes set by this function are used in automatic table layout calculation. /// 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 /// Also, this function finds the specified column inline-sizes from the first row. These are
fn bubble_inline_sizes(&mut self, _: &LayoutContext) { /// used in fixed table layout calculation.
fn bubble_inline_sizes(&mut self) {
let _scope = layout_debug_scope!("table_rowgroup::bubble_inline_sizes {:s}", let _scope = layout_debug_scope!("table_rowgroup::bubble_inline_sizes {:s}",
self.block_flow.base.debug_id()); self.block_flow.base.debug_id());

View file

@ -260,7 +260,7 @@ impl Flow for TableWrapperFlow {
self.block_flow.float_kind() 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 // get column inline-sizes info from table flow
for kid in self.block_flow.base.child_iter() { for kid in self.block_flow.base.child_iter() {
assert!(kid.is_table_caption() || kid.is_table()); 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) { fn assign_inline_sizes(&mut self, layout_context: &LayoutContext) {

View file

@ -260,7 +260,7 @@ pub struct BubbleISizes<'a> {
impl<'a> PostorderFlowTraversal for BubbleISizes<'a> { impl<'a> PostorderFlowTraversal for BubbleISizes<'a> {
#[inline] #[inline]
fn process(&mut self, flow: &mut Flow) -> bool { fn process(&mut self, flow: &mut Flow) -> bool {
flow.bubble_inline_sizes(self.layout_context); flow.bubble_inline_sizes();
true true
} }

View file

@ -38,8 +38,9 @@ use css::node_style::StyledNode;
use util::{LayoutDataAccess, LayoutDataWrapper, PrivateLayoutData, OpaqueNodeMethods}; use util::{LayoutDataAccess, LayoutDataWrapper, PrivateLayoutData, OpaqueNodeMethods};
use gfx::display_list::OpaqueNode; use gfx::display_list::OpaqueNode;
use script::dom::bindings::codegen::InheritTypes::{HTMLIFrameElementDerived, HTMLInputElementDerived}; use script::dom::bindings::codegen::InheritTypes::{HTMLIFrameElementDerived};
use script::dom::bindings::codegen::InheritTypes::{HTMLImageElementDerived, TextDerived}; use script::dom::bindings::codegen::InheritTypes::{HTMLImageElementDerived};
use script::dom::bindings::codegen::InheritTypes::{HTMLInputElementDerived, TextDerived};
use script::dom::bindings::js::JS; use script::dom::bindings::js::JS;
use script::dom::element::{Element, HTMLAreaElementTypeId, HTMLAnchorElementTypeId}; use script::dom::element::{Element, HTMLAreaElementTypeId, HTMLAnchorElementTypeId};
use script::dom::element::{HTMLLinkElementTypeId, LayoutElementHelpers, RawLayoutElementHelpers}; use script::dom::element::{HTMLLinkElementTypeId, LayoutElementHelpers, RawLayoutElementHelpers};
@ -52,13 +53,13 @@ use script::dom::node::{IsDirty, HasDirtyDescendants};
use script::dom::text::Text; use script::dom::text::Text;
use script::layout_interface::LayoutChan; use script::layout_interface::LayoutChan;
use servo_msg::constellation_msg::{PipelineId, SubpageId}; 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::cell::{RefCell, Ref, RefMut};
use std::kinds::marker::ContravariantLifetime; use std::kinds::marker::ContravariantLifetime;
use std::mem; use std::mem;
use style::computed_values::{content, display, white_space}; use style::computed_values::{content, display, white_space};
use style::{AnyNamespace, AttrSelector, PropertyDeclarationBlock, SpecificNamespace, TElement}; use style::{AnyNamespace, AttrSelector, IntegerAttribute, LengthAttribute};
use style::{TNode}; use style::{PropertyDeclarationBlock, SpecificNamespace, TElement, TElementAttributes, TNode};
use url::Url; use url::Url;
use string_cache::{Atom, Namespace}; 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<i32> {
unsafe {
self.element.get_integer_attribute_for_layout(integer_attribute)
}
}
}
fn get_content(content_list: &content::T) -> String { fn get_content(content_list: &content::T) -> String {
match *content_list { match *content_list {
content::Content(ref value) => { content::Content(ref value) => {

View file

@ -53,6 +53,7 @@ use script_traits::UntrustedNodeAddress;
use servo_msg::compositor_msg::ScriptListener; use servo_msg::compositor_msg::ScriptListener;
use servo_msg::constellation_msg::ConstellationChan; use servo_msg::constellation_msg::ConstellationChan;
use servo_util::smallvec::{SmallVec1, SmallVec}; use servo_util::smallvec::{SmallVec1, SmallVec};
use servo_util::str::LengthOrPercentageOrAuto;
use layout_interface::{LayoutRPC, LayoutChan}; use layout_interface::{LayoutRPC, LayoutChan};
use dom::bindings::utils::WindowProxyHandler; use dom::bindings::utils::WindowProxyHandler;
@ -234,3 +235,6 @@ impl JSTraceable for Box<LayoutRPC+'static> {
// Do nothing // Do nothing
} }
} }
untraceable!(LengthOrPercentageOrAuto)

View file

@ -11,7 +11,8 @@ use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
use dom::bindings::codegen::Bindings::ElementBinding; use dom::bindings::codegen::Bindings::ElementBinding;
use dom::bindings::codegen::Bindings::ElementBinding::ElementMethods; use dom::bindings::codegen::Bindings::ElementBinding::ElementMethods;
use dom::bindings::codegen::Bindings::NamedNodeMapBinding::NamedNodeMapMethods; 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::{MutNullableJS, JS, JSRef, Temporary, TemporaryPushable};
use dom::bindings::js::{OptionalSettable, OptionalRootable, Root}; use dom::bindings::js::{OptionalSettable, OptionalRootable, Root};
use dom::bindings::utils::{Reflectable, Reflector}; use dom::bindings::utils::{Reflectable, Reflector};
@ -23,16 +24,19 @@ use dom::document::{Document, DocumentHelpers};
use dom::domtokenlist::DOMTokenList; use dom::domtokenlist::DOMTokenList;
use dom::eventtarget::{EventTarget, NodeTargetTypeId}; use dom::eventtarget::{EventTarget, NodeTargetTypeId};
use dom::htmlcollection::HTMLCollection; use dom::htmlcollection::HTMLCollection;
use dom::htmlinputelement::{HTMLInputElement, LayoutHTMLInputElementHelpers};
use dom::htmlserializer::serialize; use dom::htmlserializer::serialize;
use dom::htmltablecellelement::{HTMLTableCellElement, HTMLTableCellElementHelpers};
use dom::node::{ElementNodeTypeId, Node, NodeHelpers, NodeIterator, document_from_node}; use dom::node::{ElementNodeTypeId, Node, NodeHelpers, NodeIterator, document_from_node};
use dom::node::{window_from_node, LayoutNodeHelpers}; use dom::node::{window_from_node, LayoutNodeHelpers};
use dom::nodelist::NodeList; use dom::nodelist::NodeList;
use dom::virtualmethods::{VirtualMethods, vtable_for}; use dom::virtualmethods::{VirtualMethods, vtable_for};
use devtools_traits::AttrInfo; use devtools_traits::AttrInfo;
use style::{IntegerAttribute, LengthAttribute, SizeIntegerAttribute, WidthLengthAttribute};
use style::{matches, parse_selector_list_from_str}; use style::{matches, parse_selector_list_from_str};
use style; use style;
use servo_util::namespace; use servo_util::namespace;
use servo_util::str::DOMString; use servo_util::str::{DOMString, LengthOrPercentageOrAuto};
use std::ascii::StrAsciiExt; use std::ascii::StrAsciiExt;
use std::cell::{Ref, RefMut, RefCell}; 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<Atom>; unsafe fn get_attr_atom_for_layout(&self, namespace: &Namespace, name: &Atom) -> Option<Atom>;
unsafe fn has_class_for_layout(&self, name: &Atom) -> bool; unsafe fn has_class_for_layout(&self, name: &Atom) -> bool;
unsafe fn get_classes_for_layout(&self) -> Option<&'static [Atom]>; 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<i32>;
} }
impl RawLayoutElementHelpers for Element { impl RawLayoutElementHelpers for Element {
@ -288,6 +296,36 @@ impl RawLayoutElementHelpers for Element {
(*attr).value_tokens_forever() (*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<i32> {
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 { pub trait LayoutElementHelpers {
@ -1133,3 +1171,4 @@ impl<'a> style::TElement<'a> for JSRef<'a, Element> {
} }
} }
} }

View file

@ -88,14 +88,14 @@ pub trait LayoutHTMLInputElementHelpers {
unsafe fn get_size_for_layout(&self) -> u32; unsafe fn get_size_for_layout(&self) -> u32;
} }
impl LayoutHTMLInputElementHelpers for JS<HTMLInputElement> { impl LayoutHTMLInputElementHelpers for HTMLInputElement {
#[allow(unrooted_must_root)] #[allow(unrooted_must_root)]
unsafe fn get_value_for_layout(&self) -> String { unsafe fn get_value_for_layout(&self) -> String {
unsafe fn get_raw_value(input: &JS<HTMLInputElement>) -> Option<String> { unsafe fn get_raw_value(input: &HTMLInputElement) -> Option<String> {
mem::transmute::<&RefCell<Option<String>>, &Option<String>>(&(*input.unsafe_get()).value).clone() mem::transmute::<&RefCell<Option<String>>, &Option<String>>(&input.value).clone()
} }
match (*self.unsafe_get()).input_type.get() { match self.input_type.get() {
InputCheckbox | InputRadio => "".to_string(), InputCheckbox | InputRadio => "".to_string(),
InputFile | InputImage => "".to_string(), InputFile | InputImage => "".to_string(),
InputButton(ref default) => get_raw_value(self) InputButton(ref default) => get_raw_value(self)
@ -111,7 +111,17 @@ impl LayoutHTMLInputElementHelpers for JS<HTMLInputElement> {
#[allow(unrooted_must_root)] #[allow(unrooted_must_root)]
unsafe fn get_size_for_layout(&self) -> u32 { unsafe fn get_size_for_layout(&self) -> u32 {
(*self.unsafe_get()).size.get() self.size.get()
}
}
impl LayoutHTMLInputElementHelpers for JS<HTMLInputElement> {
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") make_setter!(SetFormTarget, "formtarget")
} }
trait HTMLInputElementHelpers { pub trait HTMLInputElementHelpers {
fn force_relayout(self); fn force_relayout(self);
fn radio_group_updated(self, group: Option<&str>); fn radio_group_updated(self, group: Option<&str>);
fn get_radio_group(self) -> Option<String>; fn get_radio_group(self) -> Option<String>;
fn update_checked_state(self, checked: bool); fn update_checked_state(self, checked: bool);
fn get_size(&self) -> u32;
} }
fn broadcast_radio_checked(broadcaster: JSRef<HTMLInputElement>, group: Option<&str>) { fn broadcast_radio_checked(broadcaster: JSRef<HTMLInputElement>, group: Option<&str>) {
@ -248,6 +259,10 @@ impl<'a> HTMLInputElementHelpers for JSRef<'a, HTMLInputElement> {
//TODO: dispatch change event //TODO: dispatch change event
self.force_relayout(); self.force_relayout();
} }
fn get_size(&self) -> u32 {
self.size.get()
}
} }
impl<'a> VirtualMethods for JSRef<'a, HTMLInputElement> { impl<'a> VirtualMethods for JSRef<'a, HTMLInputElement> {

View file

@ -2,21 +2,28 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * 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::js::JSRef;
use dom::bindings::utils::{Reflectable, Reflector}; use dom::bindings::utils::{Reflectable, Reflector};
use dom::document::Document; 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::eventtarget::{EventTarget, NodeTargetTypeId};
use dom::htmlelement::HTMLElement; use dom::htmlelement::HTMLElement;
use dom::node::ElementNodeTypeId; 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] #[jstraceable]
#[must_root] #[must_root]
#[privatize] #[privatize]
pub struct HTMLTableCellElement { pub struct HTMLTableCellElement {
htmlelement: HTMLElement, htmlelement: HTMLElement,
width: Cell<LengthOrPercentageOrAuto>,
} }
impl HTMLTableCellElementDerived for EventTarget { impl HTMLTableCellElementDerived for EventTarget {
@ -32,16 +39,58 @@ impl HTMLTableCellElementDerived for EventTarget {
impl HTMLTableCellElement { impl HTMLTableCellElement {
pub fn new_inherited(type_id: ElementTypeId, tag_name: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) -> HTMLTableCellElement { pub fn new_inherited(type_id: ElementTypeId, tag_name: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) -> HTMLTableCellElement {
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] #[inline]
pub fn htmlelement<'a>(&'a self) -> &'a HTMLElement { pub fn htmlelement(&self) -> &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<HTMLElement> = 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 { impl Reflectable for HTMLTableCellElement {
fn reflector<'a>(&'a self) -> &'a Reflector { fn reflector<'a>(&'a self) -> &'a Reflector {
self.htmlelement.reflector() self.htmlelement.reflector()

View file

@ -19,6 +19,7 @@ use dom::bindings::codegen::InheritTypes::HTMLOptGroupElementCast;
use dom::bindings::codegen::InheritTypes::HTMLOptionElementCast; use dom::bindings::codegen::InheritTypes::HTMLOptionElementCast;
use dom::bindings::codegen::InheritTypes::HTMLSelectElementCast; use dom::bindings::codegen::InheritTypes::HTMLSelectElementCast;
use dom::bindings::codegen::InheritTypes::HTMLStyleElementCast; use dom::bindings::codegen::InheritTypes::HTMLStyleElementCast;
use dom::bindings::codegen::InheritTypes::HTMLTableCellElementCast;
use dom::bindings::codegen::InheritTypes::HTMLTextAreaElementCast; use dom::bindings::codegen::InheritTypes::HTMLTextAreaElementCast;
use dom::bindings::js::JSRef; use dom::bindings::js::JSRef;
use dom::element::Element; use dom::element::Element;
@ -37,6 +38,8 @@ use dom::element::HTMLOptGroupElementTypeId;
use dom::element::HTMLOptionElementTypeId; use dom::element::HTMLOptionElementTypeId;
use dom::element::HTMLSelectElementTypeId; use dom::element::HTMLSelectElementTypeId;
use dom::element::HTMLStyleElementTypeId; use dom::element::HTMLStyleElementTypeId;
use dom::element::HTMLTableDataCellElementTypeId;
use dom::element::HTMLTableHeaderCellElementTypeId;
use dom::element::HTMLTextAreaElementTypeId; use dom::element::HTMLTextAreaElementTypeId;
use dom::event::Event; use dom::event::Event;
use dom::htmlanchorelement::HTMLAnchorElement; use dom::htmlanchorelement::HTMLAnchorElement;
@ -54,6 +57,7 @@ use dom::htmloptgroupelement::HTMLOptGroupElement;
use dom::htmloptionelement::HTMLOptionElement; use dom::htmloptionelement::HTMLOptionElement;
use dom::htmlselectelement::HTMLSelectElement; use dom::htmlselectelement::HTMLSelectElement;
use dom::htmlstyleelement::HTMLStyleElement; use dom::htmlstyleelement::HTMLStyleElement;
use dom::htmltablecellelement::HTMLTableCellElement;
use dom::htmltextareaelement::HTMLTextAreaElement; use dom::htmltextareaelement::HTMLTextAreaElement;
use dom::node::{Node, NodeHelpers, ElementNodeTypeId}; 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(); let element: &'a JSRef<'a, HTMLStyleElement> = HTMLStyleElementCast::to_borrowed_ref(node).unwrap();
element as &'a VirtualMethods + 'a 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) => { ElementNodeTypeId(HTMLTextAreaElementTypeId) => {
let element: &'a JSRef<'a, HTMLTextAreaElement> = HTMLTextAreaElementCast::to_borrowed_ref(node).unwrap(); let element: &'a JSRef<'a, HTMLTextAreaElement> = HTMLTextAreaElementCast::to_borrowed_ref(node).unwrap();
element as &'a VirtualMethods + 'a element as &'a VirtualMethods + 'a

View file

@ -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: `<td width>`,
//! `<input size>`, and so forth.
/// Legacy presentational attributes that take a length as defined in HTML5 § 2.4.4.4.
pub enum LengthAttribute {
/// `<td width>`
WidthLengthAttribute,
}
/// Legacy presentational attributes that take an integer as defined in HTML5 § 2.4.4.2.
pub enum IntegerAttribute {
/// `<input size>`
SizeIntegerAttribute,
}

View file

@ -44,11 +44,12 @@ pub use properties::{PropertyDeclaration, ComputedValues, computed_values, style
pub use properties::{PropertyDeclarationBlock, parse_style_attribute}; // Style attributes pub use properties::{PropertyDeclarationBlock, parse_style_attribute}; // Style attributes
pub use properties::{CSSFloat, DeclaredValue, PropertyDeclarationParseResult}; pub use properties::{CSSFloat, DeclaredValue, PropertyDeclarationParseResult};
pub use properties::longhands; 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::{PseudoElement, Before, After, SelectorList, parse_selector_list_from_str};
pub use selectors::{AttrSelector, NamespaceConstraint, SpecificNamespace, AnyNamespace}; pub use selectors::{AttrSelector, NamespaceConstraint, SpecificNamespace, AnyNamespace};
pub use selectors::{SimpleSelector,LocalNameSelector}; pub use selectors::{SimpleSelector,LocalNameSelector};
pub use cssparser::{Color, RGBA}; pub use cssparser::{Color, RGBA};
pub use legacy::{IntegerAttribute, LengthAttribute, SizeIntegerAttribute, WidthLengthAttribute};
pub use font_face::{Source, LocalSource, UrlSource_}; pub use font_face::{Source, LocalSource, UrlSource_};
mod stylesheets; mod stylesheets;
@ -61,3 +62,4 @@ mod node;
mod media_queries; mod media_queries;
mod parsing_utils; mod parsing_utils;
mod font_face; mod font_face;
mod legacy;

View file

@ -5,7 +5,9 @@
//! Traits that nodes must implement. Breaks the otherwise-cyclic dependency between layout and //! Traits that nodes must implement. Breaks the otherwise-cyclic dependency between layout and
//! style. //! style.
use legacy::{IntegerAttribute, LengthAttribute};
use selectors::AttrSelector; use selectors::AttrSelector;
use servo_util::str::LengthOrPercentageOrAuto;
use string_cache::{Atom, Namespace}; use string_cache::{Atom, Namespace};
pub trait TNode<'a, E: TElement<'a>> : Clone + Copy { pub trait TNode<'a, E: TElement<'a>> : Clone + Copy {
@ -45,4 +47,8 @@ pub trait TElement<'a> : Copy {
fn each_class(self, callback: |&Atom|); 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<i32>;
}

View file

@ -24,6 +24,13 @@ pub mod specified {
Au_(Au), // application units Au_(Au), // application units
Em(CSSFloat), Em(CSSFloat),
Ex(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: // XXX uncomment when supported:
// Ch(CSSFloat), // Ch(CSSFloat),
// Rem(CSSFloat), // Rem(CSSFloat),
@ -245,6 +252,15 @@ pub mod computed {
let x_height = 0.5; // TODO: find that from the font let x_height = 0.5; // TODO: find that from the font
reference_font_size.scale_by(value * x_height) 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
}
} }
} }

View file

@ -11,14 +11,18 @@ use sync::Arc;
use url::Url; use url::Url;
use servo_util::bloom::BloomFilter; use servo_util::bloom::BloomFilter;
use servo_util::geometry::Au;
use servo_util::resource_files::read_resource_file; use servo_util::resource_files::read_resource_file;
use servo_util::smallvec::VecLike; use servo_util::smallvec::VecLike;
use servo_util::sort; use servo_util::sort;
use servo_util::str::{AutoLpa, LengthLpa, PercentageLpa};
use string_cache::Atom; use string_cache::Atom;
use legacy::{SizeIntegerAttribute, WidthLengthAttribute};
use media_queries::{Device, Screen}; use media_queries::{Device, Screen};
use node::{TElement, TNode}; use node::{TElement, TElementAttributes, TNode};
use properties::{PropertyDeclaration, PropertyDeclarationBlock}; use properties::{PropertyDeclaration, PropertyDeclarationBlock, SpecifiedValue, WidthDeclaration};
use properties::{specified};
use selectors::*; use selectors::*;
use stylesheets::{Stylesheet, iter_stylesheet_style_rules}; 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.. /// Extract matching rules as per node's ID, classes, tag name, etc..
/// Sort the Rules at the end to maintain cascading order. /// Sort the Rules at the end to maintain cascading order.
fn get_all_matching_rules<'a, fn get_all_matching_rules<'a,E,N,V>(&self,
E:TElement<'a>, node: &N,
N:TNode<'a, E>, parent_bf: &Option<Box<BloomFilter>>,
V:VecLike<DeclarationBlock>>( matching_rules_list: &mut V,
&self, shareable: &mut bool)
node: &N, where E: TElement<'a> + TElementAttributes,
parent_bf: &Option<Box<BloomFilter>>, N: TNode<'a,E>,
matching_rules_list: &mut V, V: VecLike<DeclarationBlock> {
shareable: &mut bool) {
if self.empty { if self.empty {
return return
} }
@ -143,34 +146,36 @@ impl SelectorMap {
} }
} }
fn get_matching_rules_from_hash<'a, fn get_matching_rules_from_hash<'a,E,N,V>(node: &N,
E:TElement<'a>, parent_bf: &Option<Box<BloomFilter>>,
N:TNode<'a, E>, hash: &HashMap<Atom, Vec<Rule>>,
V:VecLike<DeclarationBlock>>( key: &Atom,
node: &N, matching_rules: &mut V,
parent_bf: &Option<Box<BloomFilter>>, shareable: &mut bool)
hash: &HashMap<Atom, Vec<Rule>>, where E: TElement<'a> + TElementAttributes,
key: &Atom, N: TNode<'a,E>,
matching_rules: &mut V, V: VecLike<DeclarationBlock> {
shareable: &mut bool) {
match hash.find(key) { match hash.find(key) {
Some(rules) => { 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 => {} None => {}
} }
} }
/// Adds rules in `rules` that match `node` to the `matching_rules` list. /// Adds rules in `rules` that match `node` to the `matching_rules` list.
fn get_matching_rules<'a, fn get_matching_rules<'a,E,N,V>(node: &N,
E:TElement<'a>, parent_bf: &Option<Box<BloomFilter>>,
N:TNode<'a, E>, rules: &[Rule],
V:VecLike<DeclarationBlock>>( matching_rules: &mut V,
node: &N, shareable: &mut bool)
parent_bf: &Option<Box<BloomFilter>>, where E: TElement<'a> + TElementAttributes,
rules: &[Rule], N: TNode<'a,E>,
matching_rules: &mut V, V: VecLike<DeclarationBlock> {
shareable: &mut bool) {
for rule in rules.iter() { for rule in rules.iter() {
if matches_compound_selector(&*rule.selector, node, parent_bf, shareable) { if matches_compound_selector(&*rule.selector, node, parent_bf, shareable) {
matching_rules.vec_push(rule.declarations.clone()); 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 /// 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 /// 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`. /// in `css::matching::PrivateMatchMethods::candidate_element_allows_for_style_sharing`.
pub fn push_applicable_declarations<'a, pub fn push_applicable_declarations<'a,E,N,V>(
E:TElement<'a>,
N:TNode<'a, E>,
V:VecLike<DeclarationBlock>>(
&self, &self,
element: &N, element: &N,
parent_bf: &Option<Box<BloomFilter>>, parent_bf: &Option<Box<BloomFilter>>,
style_attribute: Option<&PropertyDeclarationBlock>, style_attribute: Option<&PropertyDeclarationBlock>,
pseudo_element: Option<PseudoElement>, pseudo_element: Option<PseudoElement>,
applicable_declarations: &mut V) applicable_declarations: &mut V)
-> bool { -> bool
where E: TElement<'a> + TElementAttributes,
N: TNode<'a,E>,
V: VecLike<DeclarationBlock> {
assert!(element.is_element()); assert!(element.is_element());
assert!(style_attribute.is_none() || pseudo_element.is_none(), assert!(style_attribute.is_none() || pseudo_element.is_none(),
"Style attributes do not apply to pseudo-elements"); "Style attributes do not apply to pseudo-elements");
@ -371,33 +376,46 @@ impl Stylist {
let mut shareable = true; 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, map.user_agent.normal.get_all_matching_rules(element,
parent_bf, parent_bf,
applicable_declarations, applicable_declarations,
&mut shareable); &mut shareable);
map.user.normal.get_all_matching_rules(element, parent_bf, applicable_declarations, &mut shareable); map.user.normal.get_all_matching_rules(element,
map.author.normal.get_all_matching_rules(element, parent_bf, applicable_declarations, &mut shareable); 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| { style_attribute.map(|sa| {
shareable = false; 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, map.author.important.get_all_matching_rules(element,
parent_bf, parent_bf,
applicable_declarations, applicable_declarations,
&mut shareable); &mut shareable);
// Step 4: `!important` style attributes. // Step 5: `!important` style attributes.
style_attribute.map(|sa| { style_attribute.map(|sa| {
shareable = false; 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, map.user.important.get_all_matching_rules(element,
parent_bf, parent_bf,
applicable_declarations, applicable_declarations,
@ -409,6 +427,62 @@ impl Stylist {
shareable 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
/// `<body bgcolor>`, `<input size>`, `<td width>`, 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<DeclarationBlock> {
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 { struct PerOriginSelectorMap {
@ -470,14 +544,20 @@ impl DeclarationBlock {
specificity: 0, 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>( pub fn matches<'a,E,N>(selector_list: &SelectorList,
selector_list: &SelectorList, element: &N,
element: &N, parent_bf: &Option<Box<BloomFilter>>)
parent_bf: &Option<Box<BloomFilter>>) -> bool
-> bool where E: TElement<'a>, N: TNode<'a,E> {
where E: TElement<'a>, N: TNode<'a,E> {
get_selector_list_selectors(selector_list).iter().any(|selector| get_selector_list_selectors(selector_list).iter().any(|selector|
selector.pseudo_element.is_none() && selector.pseudo_element.is_none() &&
matches_compound_selector(&*selector.compound_selectors, element, parent_bf, &mut false)) 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 /// `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 /// will almost certainly break as nodes will start mistakenly sharing styles. (See the code in
/// `main/css/matching.rs`.) /// `main/css/matching.rs`.)
fn matches_compound_selector<'a, fn matches_compound_selector<'a,E,N>(selector: &CompoundSelector,
E:TElement<'a>, element: &N,
N:TNode<'a, E>>( parent_bf: &Option<Box<BloomFilter>>,
selector: &CompoundSelector, shareable: &mut bool)
element: &N, -> bool
parent_bf: &Option<Box<BloomFilter>>, where E: TElement<'a>, N: TNode<'a,E> {
shareable: &mut bool)
-> bool {
match matches_compound_selector_internal(selector, element, parent_bf, shareable) { match matches_compound_selector_internal(selector, element, parent_bf, shareable) {
Matched => true, Matched => true,
_ => false _ => false
@ -555,13 +633,12 @@ enum SelectorMatchingResult {
/// Quickly figures out whether or not the compound selector is worth doing more /// 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 /// 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. /// that does not appear in the bloom parent bloom filter, we can exit early.
fn can_fast_reject<'a,E,N>( fn can_fast_reject<'a,E,N>(mut selector: &CompoundSelector,
mut selector: &CompoundSelector, element: &N,
element: &N, parent_bf: &Option<Box<BloomFilter>>,
parent_bf: &Option<Box<BloomFilter>>, shareable: &mut bool)
shareable: &mut bool) -> Option<SelectorMatchingResult>
-> Option<SelectorMatchingResult> where E: TElement<'a>, N: TNode<'a,E> {
where E: TElement<'a>, N: TNode<'a,E> {
if !selector.simple_selectors.iter().all(|simple_selector| { if !selector.simple_selectors.iter().all(|simple_selector| {
matches_simple_selector(simple_selector, element, shareable) }) { matches_simple_selector(simple_selector, element, shareable) }) {
return Some(NotMatchedAndRestartFromClosestLaterSibling); return Some(NotMatchedAndRestartFromClosestLaterSibling);
@ -617,14 +694,12 @@ fn can_fast_reject<'a,E,N>(
return None; return None;
} }
fn matches_compound_selector_internal<'a, fn matches_compound_selector_internal<'a,E,N>(selector: &CompoundSelector,
E:TElement<'a>, element: &N,
N:TNode<'a, E>>( parent_bf: &Option<Box<BloomFilter>>,
selector: &CompoundSelector, shareable: &mut bool)
element: &N, -> SelectorMatchingResult
parent_bf: &Option<Box<BloomFilter>>, where E: TElement<'a>, N: TNode<'a,E> {
shareable: &mut bool)
-> SelectorMatchingResult {
match can_fast_reject(selector, element, parent_bf, shareable) { match can_fast_reject(selector, element, parent_bf, shareable) {
None => {}, None => {},
Some(result) => return result, 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 /// will almost certainly break as nodes will start mistakenly sharing styles. (See the code in
/// `main/css/matching.rs`.) /// `main/css/matching.rs`.)
#[inline] #[inline]
pub fn matches_simple_selector<'a,E,N>( pub fn matches_simple_selector<'a,E,N>(selector: &SimpleSelector,
selector: &SimpleSelector, element: &N,
element: &N, shareable: &mut bool)
shareable: &mut bool) -> bool
-> bool where E: TElement<'a>, N: TNode<'a,E> {
where E:TElement<'a>, N:TNode<'a,E> {
match *selector { match *selector {
LocalNameSelector(LocalName { ref name, ref lower_name }) => { LocalNameSelector(LocalName { ref name, ref lower_name }) => {
let name = if element.is_html_element_in_html_document() { lower_name } else { 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] #[inline]
fn matches_generic_nth_child<'a,E,N>( fn matches_generic_nth_child<'a,E,N>(element: &N,
element: &N, a: i32,
a: i32, b: i32,
b: i32, is_of_type: bool,
is_of_type: bool, is_from_end: bool)
is_from_end: bool) -> bool
-> bool where E: TElement<'a>, N: TNode<'a,E> {
where E: TElement<'a>, N: TNode<'a,E> {
let mut node = element.clone(); let mut node = element.clone();
// fail if we can't find a parent or if the node is the root element // fail if we can't find a parent or if the node is the root element
// of the document (Cf. Selectors Level 3) // of the document (Cf. Selectors Level 3)
@ -933,7 +1006,7 @@ fn matches_generic_nth_child<'a,E,N>(
} }
#[inline] #[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() { match element.parent_node() {
Some(parent) => parent.is_document(), Some(parent) => parent.is_document(),
None => false None => false
@ -941,7 +1014,7 @@ fn matches_root<'a, E:TElement<'a>,N:TNode<'a, E>>(element: &N) -> bool {
} }
#[inline] #[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(); let mut node = element.clone();
loop { loop {
match node.prev_sibling() { match node.prev_sibling() {
@ -963,7 +1036,7 @@ fn matches_first_child<'a, E:TElement<'a>,N:TNode<'a, E>>(element: &N) -> bool {
} }
#[inline] #[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(); let mut node = element.clone();
loop { loop {
match node.next_sibling() { match node.next_sibling() {
@ -1081,3 +1154,4 @@ mod tests {
assert!(selector_map.class_hash.find(&Atom::from_slice("foo")).is_none()); assert!(selector_map.class_hash.find(&Atom::from_slice("foo")).is_none());
} }
} }

View file

@ -2,8 +2,11 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * 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::iter::Filter;
use std::str::CharSplits; use std::str::{CharEq, CharSplits};
pub type DOMString = String; pub type DOMString = String;
pub type StaticCharVec = &'static [char]; pub type StaticCharVec = &'static [char];
@ -24,11 +27,26 @@ pub fn null_str_as_empty_ref<'a>(s: &'a Option<DOMString>) -> &'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 { pub fn is_whitespace(s: &str) -> bool {
s.chars().all(|c| match c { s.chars().all(|c| Whitespace.matches(c))
'\u0020' | '\u0009' | '\u000D' | '\u000A' => true,
_ => false
})
} }
/// A "space character" according to: /// A "space character" according to:
@ -108,3 +126,61 @@ pub fn parse_unsigned_integer<T: Iterator<char>>(input: T) -> Option<u32> {
result.to_u32() 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<f64> = 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,
}
}

View file

@ -11,3 +11,7 @@ input[type="checkbox"]::before { content: "[ ]"; padding: 0; }
input[type="checkbox"][checked]::before { content: "[✓]"; } input[type="checkbox"][checked]::before { content: "[✓]"; }
input[type="radio"]::before { content: "( )"; padding: 0; } input[type="radio"]::before { content: "( )"; padding: 0; }
input[type="radio"][checked]::before { content: "(●)"; } input[type="radio"][checked]::before { content: "(●)"; }
td[align="left"] { text-align: left; }
td[align="center"] { text-align: center; }
td[align="right"] { text-align: right; }

View file

@ -172,5 +172,7 @@ fragment=top != ../html/acid2.html acid2_ref.html
!= input_height_a.html input_height_ref.html != input_height_a.html input_height_ref.html
== pre_ignorable_whitespace_a.html pre_ignorable_whitespace_ref.html == pre_ignorable_whitespace_a.html pre_ignorable_whitespace_ref.html
== many_brs_a.html many_brs_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 == box_sizing_sanity_check_a.html box_sizing_sanity_check_ref.html
== inline_block_overflow_hidden_a.html inline_block_overflow_hidden_ref.html == inline_block_overflow_hidden_a.html inline_block_overflow_hidden_ref.html

View file

@ -0,0 +1,7 @@
<!DOCTYPE html>
<html>
<body>
<input type=text size=12345 style="width: 128px;"></input>
</body>
</html>

View file

@ -0,0 +1,7 @@
<!DOCTYPE html>
<html>
<body>
<input type=text style="width: 128px;"></input>
</body>
</html>

View file

@ -0,0 +1,21 @@
<!DOCTYPE html>
<html>
<head>
<style>
.a {
height: 16px;
background: blue;
}
.b {
background: green;
}
</style>
</head>
<body>
<table><tr><td width=30 class=a></td><td width=200 class=b></td></tr></table>
<table><tr><td width="30 " class=a></td><td width=" 200 " class=b></td></tr></table>
<table><tr><td width="30asdf" class=a></td><td width=200haha class=b></td></tr></table>
</table>
</body>
</html>

View file

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html>
<head>
<style>
.a {
height: 16px;
width: 30px;
background: blue;
}
.b {
width: 200px;
background: green;
}
</style>
</head>
<body>
<table><tr><td class=a></td><td class=b></td></tr></table>
<table><tr><td class=a></td><td class=b></td></tr></table>
<table><tr><td class=a></td><td class=b></td></tr></table>
</body>
</html>