aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/layout/block.rs14
-rw-r--r--components/layout/construct.rs135
-rw-r--r--components/layout/display_list_builder.rs76
-rw-r--r--components/layout/flow.rs1
-rw-r--r--components/layout/lib.rs1
-rw-r--r--components/layout/list_item.rs135
-rw-r--r--components/layout/table_cell.rs2
-rw-r--r--components/style/properties/mod.rs.mako137
-rw-r--r--tests/ref/basic.list2
-rw-r--r--tests/ref/list_style_position_a.html29
-rw-r--r--tests/ref/list_style_position_ref.html28
-rw-r--r--tests/ref/list_style_type_a.html19
-rw-r--r--tests/ref/list_style_type_ref.html14
-rw-r--r--tests/ref/smiling.pngbin0 -> 3390 bytes
14 files changed, 540 insertions, 53 deletions
diff --git a/components/layout/block.rs b/components/layout/block.rs
index 61fff95ec09..079a1961073 100644
--- a/components/layout/block.rs
+++ b/components/layout/block.rs
@@ -30,7 +30,7 @@
use construct::FlowConstructor;
use context::LayoutContext;
use css::node_style::StyledNode;
-use display_list_builder::{BlockFlowDisplayListBuilding, BlockLevel, FragmentDisplayListBuilding};
+use display_list_builder::{BlockFlowDisplayListBuilding, FragmentDisplayListBuilding};
use floats::{ClearBoth, ClearLeft, ClearRight, FloatKind, FloatLeft, Floats, PlacementInfo};
use flow::{AbsolutePositionInfo, BaseFlow, BlockFlowClass, FloatIfNecessary, FlowClass, Flow};
use flow::{ForceNonfloated, ImmutableFlowUtils, MutableFlowUtils, PreorderFlowTraversal};
@@ -51,6 +51,7 @@ use table::ColumnComputedInlineSize;
use wrapper::ThreadSafeLayoutNode;
use geom::Size2D;
+use gfx::display_list::DisplayList;
use serialize::{Encoder, Encodable};
use servo_msg::compositor_msg::LayerId;
use servo_util::geometry::{Au, MAX_AU, MAX_RECT, ZERO_POINT};
@@ -1853,16 +1854,7 @@ impl Flow for BlockFlow {
}
fn build_display_list(&mut self, layout_context: &LayoutContext) {
- if self.base.flags.is_float() {
- // TODO(#2009, pcwalton): This is a pseudo-stacking context. We need to merge `z-index:
- // auto` kids into the parent stacking context, when that is supported.
- self.build_display_list_for_floating_block(layout_context)
- } else if self.base.flags.contains(IS_ABSOLUTELY_POSITIONED) {
- self.build_display_list_for_absolutely_positioned_block(layout_context)
- } else {
- self.build_display_list_for_block(layout_context, BlockLevel)
- }
-
+ self.build_display_list_for_block(box DisplayList::new(), layout_context);
if opts::get().validate_display_list_geometry {
self.base.validate_display_list_geometry();
}
diff --git a/components/layout/construct.rs b/components/layout/construct.rs
index 21a24af7efb..ec8533058cd 100644
--- a/components/layout/construct.rs
+++ b/components/layout/construct.rs
@@ -30,6 +30,7 @@ use fragment::{TableColumnFragment, TableColumnFragmentInfo, TableFragment, Tabl
use fragment::{TableWrapperFragment, UnscannedTextFragment, UnscannedTextFragmentInfo};
use incremental::{RECONSTRUCT_FLOW, RestyleDamage};
use inline::InlineFlow;
+use list_item::{mod, ListItemFlow};
use parallel;
use table_wrapper::TableWrapperFlow;
use table::TableFlow;
@@ -59,7 +60,7 @@ use std::collections::DList;
use std::mem;
use std::sync::atomic::Relaxed;
use style::ComputedValues;
-use style::computed_values::{display, position, float};
+use style::computed_values::{display, position, float, list_style_position};
use sync::Arc;
use url::Url;
@@ -471,38 +472,25 @@ impl<'a> FlowConstructor<'a> {
}
}
- /// Build block flow for current node using information from children nodes.
- ///
- /// Consume results from children and combine them, handling {ib} splits.
- /// Block flows and inline flows thus created will become the children of
- /// this block flow.
- /// Also, deal with the absolute and fixed descendants bubbled up by
- /// children nodes.
- fn build_flow_for_block(&mut self, mut flow: FlowRef, node: &ThreadSafeLayoutNode)
- -> ConstructionResult {
+ /// Constructs a block flow, beginning with the given `initial_fragment` if present and then
+ /// appending the construction results of children to the child list of the block flow. {ib}
+ /// splits and absolutely-positioned descendants are handled correctly.
+ fn build_flow_for_block_starting_with_fragment(&mut self,
+ mut flow: FlowRef,
+ node: &ThreadSafeLayoutNode,
+ initial_fragment: Option<Fragment>)
+ -> ConstructionResult {
// Gather up fragments for the inline flows we might need to create.
let mut inline_fragment_accumulator = InlineFragmentsAccumulator::new();
let mut consecutive_siblings = vec!();
- let mut first_fragment = true;
- // Special case: If this is generated content, then we need to initialize the accumulator
- // with the fragment corresponding to that content.
- if node.get_pseudo_element_type() != Normal ||
- node.type_id() == Some(ElementNodeTypeId(HTMLInputElementTypeId)) ||
- node.type_id() == Some(ElementNodeTypeId(HTMLTextAreaElementTypeId)) {
- // A TextArea's text contents are displayed through the input text
- // box, so don't construct them.
- // TODO Maybe this belongs somewhere else?
- if node.type_id() == Some(ElementNodeTypeId(HTMLTextAreaElementTypeId)) {
- for kid in node.children() {
- kid.set_flow_construction_result(NoConstructionResult)
- }
+ let mut first_fragment = match initial_fragment {
+ None => true,
+ Some(initial_fragment) => {
+ inline_fragment_accumulator.fragments.push_back(initial_fragment);
+ false
}
- let fragment_info = UnscannedTextFragment(UnscannedTextFragmentInfo::new(node));
- let fragment = Fragment::new_from_specific_info(node, fragment_info);
- inline_fragment_accumulator.fragments.push_back(fragment);
- first_fragment = false;
- }
+ };
// List of absolute descendants, in tree order.
let mut abs_descendants = Descendants::new();
@@ -552,6 +540,39 @@ impl<'a> FlowConstructor<'a> {
FlowConstructionResult(flow, abs_descendants)
}
+ /// Constructs a flow for the given block node and its children. This method creates an
+ /// initial fragment as appropriate and then dispatches to
+ /// `build_flow_for_block_starting_with_fragment`. Currently the following kinds of flows get
+ /// initial content:
+ ///
+ /// * Generated content gets the initial content specified by the `content` attribute of the
+ /// CSS.
+ /// * `<input>` and `<textarea>` elements get their content.
+ ///
+ /// FIXME(pcwalton): It is not clear to me that there isn't a cleaner way to handle
+ /// `<textarea>`.
+ fn build_flow_for_block(&mut self, flow: FlowRef, node: &ThreadSafeLayoutNode)
+ -> ConstructionResult {
+ let initial_fragment = if node.get_pseudo_element_type() != Normal ||
+ node.type_id() == Some(ElementNodeTypeId(HTMLInputElementTypeId)) ||
+ node.type_id() == Some(ElementNodeTypeId(HTMLTextAreaElementTypeId)) {
+ // A TextArea's text contents are displayed through the input text
+ // box, so don't construct them.
+ if node.type_id() == Some(ElementNodeTypeId(HTMLTextAreaElementTypeId)) {
+ for kid in node.children() {
+ kid.set_flow_construction_result(NoConstructionResult)
+ }
+ }
+ Some(Fragment::new_from_specific_info(
+ node,
+ UnscannedTextFragment(UnscannedTextFragmentInfo::new(node))))
+ } else {
+ None
+ };
+
+ self.build_flow_for_block_starting_with_fragment(flow, node, initial_fragment)
+ }
+
/// Builds a flow for a node with `display: block`. This yields a `BlockFlow` with possibly
/// other `BlockFlow`s or `InlineFlow`s underneath it, depending on whether {ib} splits needed
/// to happen.
@@ -905,6 +926,59 @@ impl<'a> FlowConstructor<'a> {
self.build_flow_for_block(FlowRef::new(flow), node)
}
+ /// Builds a flow for a node with `display: list-item`. This yields a `ListItemFlow` with
+ /// possibly other `BlockFlow`s or `InlineFlow`s underneath it.
+ fn build_flow_for_list_item(&mut self, node: &ThreadSafeLayoutNode) -> ConstructionResult {
+ let marker_fragment = match node.style().get_list().list_style_image {
+ Some(ref url) => {
+ Some(Fragment::new_from_specific_info(
+ node,
+ self.build_fragment_info_for_image(node, Some((*url).clone()))))
+ }
+ None => {
+ match list_item::static_text_for_list_style_type(node.style()
+ .get_list()
+ .list_style_type) {
+ None => None,
+ Some(text) => {
+ let text = text.to_string();
+ let mut unscanned_marker_fragments = DList::new();
+ unscanned_marker_fragments.push_back(Fragment::new_from_specific_info(
+ node,
+ UnscannedTextFragment(UnscannedTextFragmentInfo::from_text(text))));
+ let marker_fragments = TextRunScanner::new().scan_for_runs(
+ self.layout_context.font_context(),
+ unscanned_marker_fragments);
+ debug_assert!(marker_fragments.len() == 1);
+ marker_fragments.fragments.into_iter().next()
+ }
+ }
+ }
+ };
+
+ // If the list marker is outside, it becomes the special "outside fragment" that list item
+ // flows have. If it's inside, it's just a plain old fragment. Note that this means that
+ // we adopt Gecko's behavior rather than WebKit's when the marker causes an {ib} split,
+ // which has caused some malaise (Bugzilla #36854) but CSS 2.1 § 12.5.1 lets me do it, so
+ // there.
+ let flow;
+ let initial_fragment;
+ match node.style().get_list().list_style_position {
+ list_style_position::outside => {
+ flow = box ListItemFlow::from_node_and_marker(self, node, marker_fragment);
+ initial_fragment = None;
+ }
+ list_style_position::inside => {
+ flow = box ListItemFlow::from_node_and_marker(self, node, None);
+ initial_fragment = marker_fragment;
+ }
+ }
+
+ self.build_flow_for_block_starting_with_fragment(FlowRef::new(flow as Box<Flow>),
+ node,
+ initial_fragment)
+ }
+
/// Creates a fragment for a node with `display: table-column`.
fn build_fragments_for_table_column(&mut self, node: &ThreadSafeLayoutNode)
-> ConstructionResult {
@@ -1058,6 +1132,11 @@ impl<'a> PostorderNodeMutTraversal for FlowConstructor<'a> {
node.set_flow_construction_result(self.build_flow_for_nonfloated_block(node))
}
+ // List items contribute their own special flows.
+ (display::list_item, _, _) => {
+ node.set_flow_construction_result(self.build_flow_for_list_item(node))
+ }
+
// Inline items that are absolutely-positioned contribute inline fragment construction
// results with a hypothetical fragment.
(display::inline, _, position::absolute) => {
diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs
index b11a14033c0..c8db1d4f992 100644
--- a/components/layout/display_list_builder.rs
+++ b/components/layout/display_list_builder.rs
@@ -12,12 +12,13 @@
use block::BlockFlow;
use context::LayoutContext;
-use flow::{mod, Flow, NEEDS_LAYER};
+use flow::{mod, Flow, IS_ABSOLUTELY_POSITIONED, NEEDS_LAYER};
use fragment::{Fragment, GenericFragment, IframeFragment, IframeFragmentInfo, ImageFragment};
use fragment::{ImageFragmentInfo, InlineAbsoluteHypotheticalFragment, InlineBlockFragment};
use fragment::{ScannedTextFragment, ScannedTextFragmentInfo, TableFragment};
use fragment::{TableCellFragment, TableColumnFragment, TableRowFragment, TableWrapperFragment};
use fragment::{UnscannedTextFragment};
+use list_item::ListItemFlow;
use model;
use util::{OpaqueNodeMethods, ToGfxColor};
@@ -914,12 +915,19 @@ pub trait BlockFlowDisplayListBuilding {
display_list: &mut DisplayList,
layout_context: &LayoutContext,
background_border_level: BackgroundAndBorderLevel);
- fn build_display_list_for_block(&mut self,
- layout_context: &LayoutContext,
- background_border_level: BackgroundAndBorderLevel);
+ fn build_display_list_for_static_block(&mut self,
+ display_list: Box<DisplayList>,
+ layout_context: &LayoutContext,
+ background_border_level: BackgroundAndBorderLevel);
fn build_display_list_for_absolutely_positioned_block(&mut self,
+ display_list: Box<DisplayList>,
layout_context: &LayoutContext);
- fn build_display_list_for_floating_block(&mut self, layout_context: &LayoutContext);
+ fn build_display_list_for_floating_block(&mut self,
+ display_list: Box<DisplayList>,
+ layout_context: &LayoutContext);
+ fn build_display_list_for_block(&mut self,
+ display_list: Box<DisplayList>,
+ layout_context: &LayoutContext);
fn create_stacking_context(&self,
display_list: Box<DisplayList>,
layer: Option<Arc<PaintLayer>>)
@@ -945,10 +953,10 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
}
}
- fn build_display_list_for_block(&mut self,
- layout_context: &LayoutContext,
- background_border_level: BackgroundAndBorderLevel) {
- let mut display_list = box DisplayList::new();
+ fn build_display_list_for_static_block(&mut self,
+ mut display_list: Box<DisplayList>,
+ layout_context: &LayoutContext,
+ background_border_level: BackgroundAndBorderLevel) {
self.build_display_list_for_block_base(&mut *display_list,
layout_context,
background_border_level);
@@ -961,8 +969,8 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
}
fn build_display_list_for_absolutely_positioned_block(&mut self,
+ mut display_list: Box<DisplayList>,
layout_context: &LayoutContext) {
- let mut display_list = box DisplayList::new();
self.build_display_list_for_block_base(&mut *display_list,
layout_context,
RootOfStackingContextLevel);
@@ -991,8 +999,9 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
self.base.display_list_building_result = StackingContextResult(stacking_context)
}
- fn build_display_list_for_floating_block(&mut self, layout_context: &LayoutContext) {
- let mut display_list = box DisplayList::new();
+ fn build_display_list_for_floating_block(&mut self,
+ mut display_list: Box<DisplayList>,
+ layout_context: &LayoutContext) {
self.build_display_list_for_block_base(&mut *display_list,
layout_context,
RootOfStackingContextLevel);
@@ -1005,6 +1014,20 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
}
}
+ fn build_display_list_for_block(&mut self,
+ display_list: Box<DisplayList>,
+ layout_context: &LayoutContext) {
+ if self.base.flags.is_float() {
+ // TODO(#2009, pcwalton): This is a pseudo-stacking context. We need to merge `z-index:
+ // auto` kids into the parent stacking context, when that is supported.
+ self.build_display_list_for_floating_block(display_list, layout_context)
+ } else if self.base.flags.contains(IS_ABSOLUTELY_POSITIONED) {
+ self.build_display_list_for_absolutely_positioned_block(display_list, layout_context)
+ } else {
+ self.build_display_list_for_static_block(display_list, layout_context, BlockLevel)
+ }
+ }
+
fn create_stacking_context(&self,
display_list: Box<DisplayList>,
layer: Option<Arc<PaintLayer>>)
@@ -1017,6 +1040,35 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
}
}
+pub trait ListItemFlowDisplayListBuilding {
+ fn build_display_list_for_list_item(&mut self,
+ display_list: Box<DisplayList>,
+ layout_context: &LayoutContext);
+}
+
+impl ListItemFlowDisplayListBuilding for ListItemFlow {
+ fn build_display_list_for_list_item(&mut self,
+ mut display_list: Box<DisplayList>,
+ layout_context: &LayoutContext) {
+ // Draw the marker, if applicable.
+ match self.marker {
+ None => {}
+ Some(ref mut marker) => {
+ let stacking_relative_fragment_origin =
+ self.block_flow.base.stacking_relative_position_of_child_fragment(marker);
+ marker.build_display_list(&mut *display_list,
+ layout_context,
+ stacking_relative_fragment_origin,
+ ContentLevel,
+ &self.block_flow.base.clip_rect);
+ }
+ }
+
+ // Draw the rest of the block.
+ self.block_flow.build_display_list_for_block(display_list, layout_context)
+ }
+}
+
// A helper data structure for gradients.
struct StopRun {
start_offset: f32,
diff --git a/components/layout/flow.rs b/components/layout/flow.rs
index bfca9c6168c..20645e70123 100644
--- a/components/layout/flow.rs
+++ b/components/layout/flow.rs
@@ -430,6 +430,7 @@ pub trait MutableOwnedFlowUtils {
pub enum FlowClass {
BlockFlowClass,
InlineFlowClass,
+ ListItemFlowClass,
TableWrapperFlowClass,
TableFlowClass,
TableColGroupFlowClass,
diff --git a/components/layout/lib.rs b/components/layout/lib.rs
index 2d045508088..1fa21311373 100644
--- a/components/layout/lib.rs
+++ b/components/layout/lib.rs
@@ -52,6 +52,7 @@ pub mod flow_ref;
pub mod fragment;
pub mod layout_task;
pub mod inline;
+pub mod list_item;
pub mod model;
pub mod parallel;
pub mod sequential;
diff --git a/components/layout/list_item.rs b/components/layout/list_item.rs
new file mode 100644
index 00000000000..fbf40c7747c
--- /dev/null
+++ b/components/layout/list_item.rs
@@ -0,0 +1,135 @@
+/* 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/. */
+
+//! Layout for elements with a CSS `display` property of `list-item`. These elements consist of a
+//! block and an extra inline fragment for the marker.
+
+#![deny(unsafe_blocks)]
+
+use block::BlockFlow;
+use construct::FlowConstructor;
+use context::LayoutContext;
+use display_list_builder::ListItemFlowDisplayListBuilding;
+use flow::{Flow, FlowClass, ListItemFlowClass};
+use fragment::{Fragment, FragmentBoundsIterator};
+use wrapper::ThreadSafeLayoutNode;
+
+use gfx::display_list::DisplayList;
+use servo_util::geometry::Au;
+use servo_util::opts;
+use style::ComputedValues;
+use style::computed_values::list_style_type;
+use sync::Arc;
+
+/// A block with the CSS `display` property equal to `list-item`.
+#[deriving(Show)]
+pub struct ListItemFlow {
+ /// Data common to all block flows.
+ pub block_flow: BlockFlow,
+ /// The marker, if outside. (Markers that are inside are instead just fragments on the interior
+ /// `InlineFlow`.)
+ pub marker: Option<Fragment>,
+}
+
+impl ListItemFlow {
+ pub fn from_node_and_marker(constructor: &mut FlowConstructor,
+ node: &ThreadSafeLayoutNode,
+ marker_fragment: Option<Fragment>)
+ -> ListItemFlow {
+ ListItemFlow {
+ block_flow: BlockFlow::from_node(constructor, node),
+ marker: marker_fragment,
+ }
+ }
+}
+
+impl Flow for ListItemFlow {
+ fn class(&self) -> FlowClass {
+ ListItemFlowClass
+ }
+
+ fn as_block<'a>(&'a mut self) -> &'a mut BlockFlow {
+ &mut self.block_flow
+ }
+
+ fn bubble_inline_sizes(&mut self) {
+ // The marker contributes no intrinsic inline-size, so…
+ self.block_flow.bubble_inline_sizes()
+ }
+
+ fn assign_inline_sizes(&mut self, layout_context: &LayoutContext) {
+ self.block_flow.assign_inline_sizes(layout_context);
+
+ match self.marker {
+ None => {}
+ Some(ref mut marker) => {
+ // Do this now. There's no need to do this in bubble-widths, since markers do not
+ // contribute to the inline size of this flow.
+ let intrinsic_inline_sizes = marker.compute_intrinsic_inline_sizes();
+
+ marker.border_box.size.inline =
+ intrinsic_inline_sizes.content_intrinsic_sizes.preferred_inline_size;
+ marker.border_box.start.i = self.block_flow.fragment.border_box.start.i -
+ marker.border_box.size.inline;
+ }
+ }
+ }
+
+ fn assign_block_size<'a>(&mut self, layout_context: &'a LayoutContext<'a>) {
+ self.block_flow.assign_block_size(layout_context);
+
+ match self.marker {
+ None => {}
+ Some(ref mut marker) => {
+ marker.border_box.start.b = Au(0);
+ marker.border_box.size.block = marker.calculate_line_height(layout_context);
+ }
+ }
+ }
+
+ fn compute_absolute_position(&mut self) {
+ self.block_flow.compute_absolute_position()
+ }
+
+ fn update_late_computed_inline_position_if_necessary(&mut self, inline_position: Au) {
+ self.block_flow.update_late_computed_inline_position_if_necessary(inline_position)
+ }
+
+ fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au) {
+ self.block_flow.update_late_computed_block_position_if_necessary(block_position)
+ }
+
+ fn build_display_list(&mut self, layout_context: &LayoutContext) {
+ self.build_display_list_for_list_item(box DisplayList::new(), layout_context);
+ if opts::get().validate_display_list_geometry {
+ self.block_flow.base.validate_display_list_geometry();
+ }
+ }
+
+ fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
+ self.block_flow.repair_style(new_style)
+ }
+
+ fn iterate_through_fragment_bounds(&self, iterator: &mut FragmentBoundsIterator) {
+ self.block_flow.iterate_through_fragment_bounds(iterator);
+ }
+}
+
+/// Returns the static text to be used for the given value of the `list-style-type` property.
+///
+/// TODO(pcwalton): Return either a string or a counter descriptor, once we support counters.
+pub fn static_text_for_list_style_type(list_style_type: list_style_type::T)
+ -> Option<&'static str> {
+ // Just to keep things simple, use a nonbreaking space (Unicode 0xa0) to provide the marker
+ // separation.
+ match list_style_type {
+ list_style_type::none => None,
+ list_style_type::disc => Some("•\u00a0"),
+ list_style_type::circle => Some("◦\u00a0"),
+ list_style_type::square => Some("▪\u00a0"),
+ list_style_type::disclosure_open => Some("▾\u00a0"),
+ list_style_type::disclosure_closed => Some("‣\u00a0"),
+ }
+}
+
diff --git a/components/layout/table_cell.rs b/components/layout/table_cell.rs
index 8dd0817b6da..280e86408cb 100644
--- a/components/layout/table_cell.rs
+++ b/components/layout/table_cell.rs
@@ -23,7 +23,7 @@ use sync::Arc;
/// A table formatting context.
#[deriving(Encodable)]
pub struct TableCellFlow {
- /// Data common to all flows.
+ /// Data common to all block flows.
pub block_flow: BlockFlow,
}
diff --git a/components/style/properties/mod.rs.mako b/components/style/properties/mod.rs.mako
index 9453b32f05c..2107ac86c7b 100644
--- a/components/style/properties/mod.rs.mako
+++ b/components/style/properties/mod.rs.mako
@@ -398,7 +398,7 @@ pub mod longhands {
${new_style_struct("Box", is_inherited=False)}
- // TODO: don't parse values we don't support
+ // TODO(SimonSapin): don't parse `inline-table`, since we don't support it
<%self:single_keyword_computed name="display"
values="inline block inline-block
table inline-table table-row-group table-header-group table-footer-group
@@ -698,6 +698,45 @@ pub mod longhands {
Ok(Content(content))
}
</%self:longhand>
+
+ ${new_style_struct("List", is_inherited=True)}
+
+ ${single_keyword("list-style-position", "outside inside")}
+
+ // TODO(pcwalton): Implement the full set of counter styles per CSS-COUNTER-STYLES [1] 6.1:
+ //
+ // decimal, decimal-leading-zero, arabic-indic, armenian, upper-armenian, lower-armenian,
+ // bengali, cambodian, khmer, cjk-decimal, devanagiri, georgian, gujarati, gurmukhi,
+ // hebrew, kannada, lao, malayalam, mongolian, myanmar, oriya, persian, lower-roman,
+ // upper-roman, telugu, thai, tibetan
+ //
+ // [1]: http://dev.w3.org/csswg/css-counter-styles/
+ ${single_keyword("list-style-type",
+ "disc none circle square disclosure-open disclosure-closed")}
+
+ <%self:single_component_value name="list-style-image">
+ pub use super::computed_as_specified as to_computed_value;
+ #[deriving(Clone)]
+ pub type SpecifiedValue = Option<Url>;
+ pub mod computed_value {
+ use url::Url;
+ #[deriving(Clone, PartialEq)]
+ pub type T = Option<Url>;
+ }
+ pub fn from_component_value(input: &ComponentValue, base_url: &Url)
+ -> Result<SpecifiedValue,()> {
+ match *input {
+ URL(ref url) => Ok(Some(super::parse_url(url.as_slice(), base_url))),
+ Ident(ref value) if value.as_slice().eq_ignore_ascii_case("none") => Ok(None),
+ _ => Err(()),
+ }
+ }
+ #[inline]
+ pub fn get_initial_value() -> computed_value::T {
+ None
+ }
+ </%self:single_component_value>
+
// CSS 2.1, Section 13 - Paged media
// CSS 2.1, Section 14 - Colors and Backgrounds
@@ -1903,6 +1942,102 @@ pub mod shorthands {
}
})
</%self:shorthand>
+
+ <%self:shorthand name="list-style"
+ sub_properties="list-style-image list-style-position list-style-type">
+ // `none` is ambiguous until we've finished parsing the shorthands, so we count the number
+ // of times we see it.
+ let mut nones = 0u8;
+ let (mut image, mut position, mut list_style_type, mut any) = (None, None, None, false);
+ for component_value in input.skip_whitespace() {
+ match component_value {
+ &Ident(ref value) if value.eq_ignore_ascii_case("none") => {
+ nones = nones + 1;
+ if nones > 2 {
+ return Err(())
+ }
+ any = true;
+ continue
+ }
+ _ => {}
+ }
+
+ if list_style_type.is_none() {
+ match list_style_type::from_component_value(component_value, base_url) {
+ Ok(v) => {
+ list_style_type = Some(v);
+ any = true;
+ continue
+ },
+ Err(()) => ()
+ }
+ }
+
+ if image.is_none() {
+ match list_style_image::from_component_value(component_value, base_url) {
+ Ok(v) => {
+ image = Some(v);
+ any = true;
+ continue
+ },
+ Err(()) => (),
+ }
+ }
+
+ if position.is_none() {
+ match list_style_position::from_component_value(component_value, base_url) {
+ Ok(v) => {
+ position = Some(v);
+ any = true;
+ continue
+ },
+ Err(()) => ()
+ }
+ }
+ }
+
+ // If there are two `none`s, then we can't have a type or image; if there is one `none`,
+ // then we can't have both a type *and* an image; if there is no `none` then we're fine as
+ // long as we parsed something.
+ match (any, nones, list_style_type, image) {
+ (true, 2, None, None) => {
+ Ok(Longhands {
+ list_style_position: position,
+ list_style_image: Some(None),
+ list_style_type: Some(list_style_type::none),
+ })
+ }
+ (true, 1, None, Some(image)) => {
+ Ok(Longhands {
+ list_style_position: position,
+ list_style_image: Some(image),
+ list_style_type: Some(list_style_type::none),
+ })
+ }
+ (true, 1, Some(list_style_type), None) => {
+ Ok(Longhands {
+ list_style_position: position,
+ list_style_image: Some(None),
+ list_style_type: Some(list_style_type),
+ })
+ }
+ (true, 1, None, None) => {
+ Ok(Longhands {
+ list_style_position: position,
+ list_style_image: Some(None),
+ list_style_type: Some(list_style_type::none),
+ })
+ }
+ (true, 0, list_style_type, image) => {
+ Ok(Longhands {
+ list_style_position: position,
+ list_style_image: image,
+ list_style_type: list_style_type,
+ })
+ }
+ _ => Err(()),
+ }
+ </%self:shorthand>
}
diff --git a/tests/ref/basic.list b/tests/ref/basic.list
index 49a0ff9e959..d4b0571a7da 100644
--- a/tests/ref/basic.list
+++ b/tests/ref/basic.list
@@ -207,3 +207,5 @@ fragment=top != ../html/acid2.html acid2_ref.html
== box_shadow_spread_a.html box_shadow_spread_ref.html
== box_shadow_inset_a.html box_shadow_inset_ref.html
== box_shadow_inset_parsing_a.html box_shadow_inset_parsing_ref.html
+!= list_style_type_a.html list_style_type_ref.html
+== list_style_position_a.html list_style_position_ref.html
diff --git a/tests/ref/list_style_position_a.html b/tests/ref/list_style_position_a.html
new file mode 100644
index 00000000000..eb32fb5de0e
--- /dev/null
+++ b/tests/ref/list_style_position_a.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!--
+ Tests that `list-style-position: inside` and `list-style-image` work. This is deliberately
+ conservative because the exact placement of the marker is unspecified.
+-->
+<html>
+<head>
+<style>
+ul {
+ margin: 0;
+ padding: 0;
+}
+li {
+ list-style-image: url(smiling.png);
+ list-style-position: inside;
+ margin: 0;
+ padding: 0;
+}
+</style>
+</head>
+<body>
+<ul>
+<li></li>
+<li></li>
+<li></li>
+</ul>
+</body>
+</html>
+
diff --git a/tests/ref/list_style_position_ref.html b/tests/ref/list_style_position_ref.html
new file mode 100644
index 00000000000..d637d4b495c
--- /dev/null
+++ b/tests/ref/list_style_position_ref.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!--
+ Tests that `list-style-position: inside` and `list-style-image` work. This is deliberately
+ conservative because the exact placement of the marker is unspecified.
+-->
+<html>
+<head>
+<style>
+ul {
+ margin: 0;
+ padding: 0;
+}
+li {
+ display: block;
+ margin: 0;
+ padding: 0;
+}
+</style>
+</head>
+<body>
+<ul>
+<li><img src=smiling.png></li>
+<li><img src=smiling.png></li>
+<li><img src=smiling.png></li>
+</ul>
+</body>
+</html>
+
diff --git a/tests/ref/list_style_type_a.html b/tests/ref/list_style_type_a.html
new file mode 100644
index 00000000000..f0bad6fde52
--- /dev/null
+++ b/tests/ref/list_style_type_a.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<!--
+ Tests that `list-style-type` does something. This is deliberately conservative because the
+ exact placement of the marker is unspecified.
+-->
+<html>
+<head>
+<style>
+li {
+ list-style: square;
+}
+</style>
+<body>
+<ul>
+<li>Cheetahmen</li>
+</ul>
+</body>
+</html>
+
diff --git a/tests/ref/list_style_type_ref.html b/tests/ref/list_style_type_ref.html
new file mode 100644
index 00000000000..d9ce190ca18
--- /dev/null
+++ b/tests/ref/list_style_type_ref.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<!--
+ Tests that `list-style-type` does something. This is deliberately conservative because the
+ exact placement of the marker is unspecified.
+-->
+<html>
+<head>
+<body>
+<ul>
+<li>Cheetahmen</li>
+</ul>
+</body>
+</html>
+
diff --git a/tests/ref/smiling.png b/tests/ref/smiling.png
new file mode 100644
index 00000000000..725eef526d7
--- /dev/null
+++ b/tests/ref/smiling.png
Binary files differ