style: Address review comments relating to bgcolor and column spans

This commit is contained in:
Patrick Walton 2014-12-11 09:58:00 -08:00
parent 17835ba0cb
commit a1ea44b294
33 changed files with 712 additions and 422 deletions

View file

@ -31,6 +31,9 @@ path = "../net"
[dependencies.util] [dependencies.util]
path = "../util" path = "../util"
[dependencies.cssparser]
git = "https://github.com/servo/rust-cssparser"
[dependencies.encoding] [dependencies.encoding]
git = "https://github.com/lifthrasiir/rust-encoding" git = "https://github.com/lifthrasiir/rust-encoding"

View file

@ -14,6 +14,7 @@
#[phase(plugin, link)] #[phase(plugin, link)]
extern crate log; extern crate log;
extern crate cssparser;
extern crate geom; extern crate geom;
extern crate gfx; extern crate gfx;
extern crate layout_traits; extern crate layout_traits;

View file

@ -106,7 +106,8 @@ impl TableFlow {
/// Update the corresponding value of `self_inline_sizes` if a value of `kid_inline_sizes` has /// Update the corresponding value of `self_inline_sizes` if a value of `kid_inline_sizes` has
/// a larger value than one of `self_inline_sizes`. Returns the minimum and preferred inline /// a larger value than one of `self_inline_sizes`. Returns the minimum and preferred inline
/// sizes. /// sizes.
fn update_automatic_column_inline_sizes(parent_inline_sizes: &mut Vec<ColumnInlineSize>, fn update_automatic_column_inline_sizes(
parent_inline_sizes: &mut Vec<ColumnIntrinsicInlineSize>,
child_cell_inline_sizes: &[CellIntrinsicInlineSize]) child_cell_inline_sizes: &[CellIntrinsicInlineSize])
-> IntrinsicISizes { -> IntrinsicISizes {
let mut total_inline_sizes = IntrinsicISizes::new(); let mut total_inline_sizes = IntrinsicISizes::new();
@ -122,7 +123,7 @@ impl TableFlow {
// 4. For now we make this column contribute no width. // 4. For now we make this column contribute no width.
} else { } else {
let column_size = &child_cell_inline_size.column_size; let column_size = &child_cell_inline_size.column_size;
*parent_sizes = ColumnInlineSize { *parent_sizes = ColumnIntrinsicInlineSize {
minimum_length: max(parent_sizes.minimum_length, minimum_length: max(parent_sizes.minimum_length,
column_size.minimum_length), column_size.minimum_length),
percentage: parent_sizes.greatest_percentage(column_size), percentage: parent_sizes.greatest_percentage(column_size),
@ -136,7 +137,7 @@ impl TableFlow {
if child_cell_inline_size.column_span > 1 { if child_cell_inline_size.column_span > 1 {
// TODO(pcwalton): Perform the recursive algorithm specified in INTRINSIC § // TODO(pcwalton): Perform the recursive algorithm specified in INTRINSIC §
// 4. For now we make this column contribute no width. // 4. For now we make this column contribute no width.
parent_inline_sizes.push(ColumnInlineSize::new()) parent_inline_sizes.push(ColumnIntrinsicInlineSize::new())
} else { } else {
parent_inline_sizes.push(child_cell_inline_size.column_size) parent_inline_sizes.push(child_cell_inline_size.column_size)
} }
@ -169,7 +170,7 @@ impl TableFlow {
/// Updates the minimum and preferred inline-size calculation for a single row. This is /// Updates the minimum and preferred inline-size calculation for a single row. This is
/// factored out into a separate function because we process children of rowgroups too. /// factored out into a separate function because we process children of rowgroups too.
fn update_column_inline_sizes_for_row(child: &mut Flow, fn update_column_inline_sizes_for_row(child: &mut Flow,
column_inline_sizes: &mut Vec<ColumnInlineSize>, column_inline_sizes: &mut Vec<ColumnIntrinsicInlineSize>,
computation: &mut IntrinsicISizesContribution, computation: &mut IntrinsicISizesContribution,
did_first_row: &mut bool, did_first_row: &mut bool,
table_layout: TableLayout) { table_layout: TableLayout) {
@ -254,15 +255,17 @@ impl Flow for TableFlow {
} }
} else if kid.is_table_rowgroup() { } else if kid.is_table_rowgroup() {
for grandkid in flow::mut_base(kid).child_iter() { for grandkid in flow::mut_base(kid).child_iter() {
TableFlow::update_column_inline_sizes_for_row(grandkid, TableFlow::update_column_inline_sizes_for_row(
&mut self.column_inline_sizes, grandkid,
&mut self.column_intrinsic_inline_sizes,
&mut computation, &mut computation,
&mut did_first_row, &mut did_first_row,
self.table_layout) self.table_layout)
} }
} else if kid.is_table_row() { } else if kid.is_table_row() {
TableFlow::update_column_inline_sizes_for_row(kid, TableFlow::update_column_inline_sizes_for_row(
&mut self.column_inline_sizes, kid,
&mut self.column_intrinsic_inline_sizes,
&mut computation, &mut computation,
&mut did_first_row, &mut did_first_row,
self.table_layout) self.table_layout)
@ -309,8 +312,7 @@ impl Flow for TableFlow {
// In fixed table layout, we distribute extra space among the unspecified columns // In fixed table layout, we distribute extra space among the unspecified columns
// if there are any, or among all the columns if all are specified. // if there are any, or among all the columns if all are specified.
self.column_computed_inline_sizes.clear(); self.column_computed_inline_sizes.clear();
if total_column_inline_size < content_inline_size && if num_unspecified_inline_sizes == 0 {
num_unspecified_inline_sizes == 0 {
let ratio = content_inline_size.to_subpx() / let ratio = content_inline_size.to_subpx() /
total_column_inline_size.to_subpx(); total_column_inline_size.to_subpx();
for column_inline_size in self.column_intrinsic_inline_sizes.iter() { for column_inline_size in self.column_intrinsic_inline_sizes.iter() {
@ -445,6 +447,16 @@ pub struct ColumnIntrinsicInlineSize {
} }
impl ColumnIntrinsicInlineSize { impl ColumnIntrinsicInlineSize {
/// Returns a newly-initialized `ColumnIntrinsicInlineSize` with all fields blank.
pub fn new() -> ColumnIntrinsicInlineSize {
ColumnIntrinsicInlineSize {
preferred: Au(0),
minimum_length: Au(0),
percentage: 0.0,
constrained: false,
}
}
/// Returns the true minimum size of this column, given the containing block's inline size. /// Returns the true minimum size of this column, given the containing block's inline size.
/// Beware that this is generally only correct for fixed table layout. (Compare CSS 2.1 § /// Beware that this is generally only correct for fixed table layout. (Compare CSS 2.1 §
/// 17.5.2.1 with the algorithm in INTRINSIC § 4.) /// 17.5.2.1 with the algorithm in INTRINSIC § 4.)

View file

@ -17,7 +17,7 @@ use wrapper::ThreadSafeLayoutNode;
use servo_util::geometry::Au; use servo_util::geometry::Au;
use std::fmt; use std::fmt;
use style::ComputedValues; use style::{ColSpanUnsignedIntegerAttribute, ComputedValues};
use sync::Arc; use sync::Arc;
/// A table formatting context. /// A table formatting context.
@ -25,13 +25,17 @@ use sync::Arc;
pub struct TableCellFlow { pub struct TableCellFlow {
/// Data common to all block flows. /// Data common to all block flows.
pub block_flow: BlockFlow, pub block_flow: BlockFlow,
/// The column span of this cell.
pub column_span: u32,
} }
impl TableCellFlow { impl TableCellFlow {
pub fn from_node_and_fragment(node: &ThreadSafeLayoutNode, fragment: Fragment) pub fn from_node_and_fragment(node: &ThreadSafeLayoutNode, fragment: Fragment)
-> TableCellFlow { -> TableCellFlow {
TableCellFlow { TableCellFlow {
block_flow: BlockFlow::from_node_and_fragment(node, fragment) block_flow: BlockFlow::from_node_and_fragment(node, fragment),
column_span: node.get_unsigned_integer_attribute(ColSpanUnsignedIntegerAttribute)
.unwrap_or(1),
} }
} }

View file

@ -41,7 +41,7 @@ pub struct TableRowFlow {
#[deriving(Encodable)] #[deriving(Encodable)]
pub struct CellIntrinsicInlineSize { pub struct CellIntrinsicInlineSize {
/// Inline sizes that this cell contributes to the column. /// Inline sizes that this cell contributes to the column.
pub column_size: ColumnInlineSize, pub column_size: ColumnIntrinsicInlineSize,
/// The column span of this cell. /// The column span of this cell.
pub column_span: u32, pub column_span: u32,
} }
@ -165,7 +165,7 @@ impl Flow for TableRowFlow {
} }
fn column_intrinsic_inline_sizes<'a>(&'a mut self) -> &'a mut Vec<ColumnIntrinsicInlineSize> { fn column_intrinsic_inline_sizes<'a>(&'a mut self) -> &'a mut Vec<ColumnIntrinsicInlineSize> {
&mut self.column_intrinsic_inline_sizes panic!("can't call column_intrinsic_inline_sizes() on table row")
} }
fn column_computed_inline_sizes<'a>(&'a mut self) -> &'a mut Vec<ColumnComputedInlineSize> { fn column_computed_inline_sizes<'a>(&'a mut self) -> &'a mut Vec<ColumnComputedInlineSize> {
@ -193,9 +193,11 @@ impl Flow for TableRowFlow {
let child_specified_inline_size; let child_specified_inline_size;
let child_column_span; let child_column_span;
{ {
let child_style = kid.as_table_cell().fragment().style(); let child_table_cell = kid.as_table_cell();
child_specified_inline_size = child_style.content_inline_size(); child_specified_inline_size = child_table_cell.fragment()
child_column_span = child_style.get_table()._servo_column_span .style()
.content_inline_size();
child_column_span = child_table_cell.column_span
} }
// Collect minimum and preferred inline-sizes of the cell for automatic table layout // Collect minimum and preferred inline-sizes of the cell for automatic table layout
@ -248,31 +250,36 @@ impl Flow for TableRowFlow {
// Spread out the completed inline sizes among columns with spans > 1. // Spread out the completed inline sizes among columns with spans > 1.
let mut computed_inline_size_for_cells = Vec::new(); let mut computed_inline_size_for_cells = Vec::new();
let mut column_inline_size_iterator = self.column_inline_sizes.iter(); let mut column_computed_inline_size_iterator = self.column_computed_inline_sizes.iter();
for cell_intrinsic_inline_size in self.cell_intrinsic_inline_sizes.iter() { for cell_intrinsic_inline_size in self.cell_intrinsic_inline_sizes.iter() {
//(intrinsic_inline_size_for_column, computed_inline_size_for_column) in
// Start with the computed inline size for the first column in the span. // Start with the computed inline size for the first column in the span.
let mut column_inline_size = match column_inline_size_iterator.next() { let mut column_computed_inline_size =
Some(column_inline_size) => *column_inline_size, match column_computed_inline_size_iterator.next() {
Some(column_computed_inline_size) => *column_computed_inline_size,
None => { None => {
// This could happen if there are too few cells in this row. Don't crash. // We're in fixed layout mode and there are more cells in this row than
break // columns we know about. According to CSS 2.1 § 17.5.2.1, the behavior is
// now undefined. So just use zero.
//
// FIXME(pcwalton): $10 says this isn't Web compatible.
ColumnComputedInlineSize {
size: Au(0),
}
} }
}; };
// Add in computed inline sizes for any extra columns in the span. // Add in computed inline sizes for any extra columns in the span.
for _ in range(1, cell_intrinsic_inline_size.column_span) { for _ in range(1, cell_intrinsic_inline_size.column_span) {
let extra_column_inline_size = match column_inline_size_iterator.next() { let extra_column_computed_inline_size =
Some(column_inline_size) => column_inline_size, match column_computed_inline_size_iterator.next() {
Some(column_computed_inline_size) => column_computed_inline_size,
None => break, None => break,
}; };
column_inline_size.minimum_length = column_inline_size.minimum_length + column_computed_inline_size.size = column_computed_inline_size.size +
extra_column_inline_size.minimum_length; extra_column_computed_inline_size.size;
column_inline_size.preferred = column_inline_size.preferred +
extra_column_inline_size.preferred;
} }
computed_inline_size_for_cells.push(column_inline_size) computed_inline_size_for_cells.push(column_computed_inline_size)
} }
// Push those inline sizes down to the cells. // Push those inline sizes down to the cells.

View file

@ -6,13 +6,13 @@
#![deny(unsafe_blocks)] #![deny(unsafe_blocks)]
use block::{BlockFlow, ISizeAndMarginsComputer}; use block::{BlockFlow, ISizeAndMarginsComputer, MarginsMayNotCollapse};
use construct::FlowConstructor; use construct::FlowConstructor;
use context::LayoutContext; use context::LayoutContext;
use flow::{mod, Flow, FlowClass, TableRowGroupFlowClass}; use flow::{Flow, FlowClass, TableRowGroupFlowClass};
use fragment::{Fragment, FragmentBoundsIterator}; use fragment::{Fragment, FragmentBoundsIterator};
use layout_debug; use layout_debug;
use table::{ColumnComputedInlineSize, ColumnIntrinsicInlineSize, InternalTable, TableFlow}; use table::{ColumnComputedInlineSize, ColumnIntrinsicInlineSize, InternalTable};
use wrapper::ThreadSafeLayoutNode; use wrapper::ThreadSafeLayoutNode;
use servo_util::geometry::Au; use servo_util::geometry::Au;

View file

@ -31,7 +31,7 @@ use style::{ComputedValues, CSSFloat};
use style::computed_values::table_layout; use style::computed_values::table_layout;
use sync::Arc; use sync::Arc;
#[deriving(Encodable)] #[deriving(Encodable, Show)]
pub enum TableLayout { pub enum TableLayout {
FixedLayout, FixedLayout,
AutoLayout AutoLayout

View file

@ -36,6 +36,7 @@ use incremental::RestyleDamage;
use util::{LayoutDataAccess, LayoutDataFlags, LayoutDataWrapper, OpaqueNodeMethods}; use util::{LayoutDataAccess, LayoutDataFlags, LayoutDataWrapper, OpaqueNodeMethods};
use util::{PrivateLayoutData}; use util::{PrivateLayoutData};
use cssparser::RGBA;
use gfx::display_list::OpaqueNode; use gfx::display_list::OpaqueNode;
use script::dom::bindings::codegen::InheritTypes::{ElementCast, HTMLIFrameElementCast}; use script::dom::bindings::codegen::InheritTypes::{ElementCast, HTMLIFrameElementCast};
use script::dom::bindings::codegen::InheritTypes::{HTMLImageElementCast, HTMLInputElementCast}; use script::dom::bindings::codegen::InheritTypes::{HTMLImageElementCast, HTMLInputElementCast};
@ -53,7 +54,7 @@ use script::dom::node::{HAS_CHANGED, IS_DIRTY, HAS_DIRTY_SIBLINGS, HAS_DIRTY_DES
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::{LengthOrPercentageOrAuto, SimpleColor, is_whitespace}; use servo_util::str::{LengthOrPercentageOrAuto, is_whitespace};
use std::kinds::marker::ContravariantLifetime; use std::kinds::marker::ContravariantLifetime;
use std::mem; use std::mem;
use string_cache::{Atom, Namespace}; use string_cache::{Atom, Namespace};
@ -613,7 +614,7 @@ impl<'le> TElementAttributes for LayoutElement<'le> {
} }
} }
fn get_simple_color_attribute(self, attribute: SimpleColorAttribute) -> Option<SimpleColor> { fn get_simple_color_attribute(self, attribute: SimpleColorAttribute) -> Option<RGBA> {
unsafe { unsafe {
self.element.get_simple_color_attribute_for_layout(attribute) self.element.get_simple_color_attribute_for_layout(attribute)
} }
@ -937,6 +938,18 @@ impl<'ln> ThreadSafeLayoutNode<'ln> {
} }
} }
pub fn get_unsigned_integer_attribute(self, attribute: UnsignedIntegerAttribute)
-> Option<u32> {
unsafe {
match ElementCast::to_js(self.get_jsmanaged()) {
Some(element) => {
(*element.unsafe_get()).get_unsigned_integer_attribute_for_layout(attribute)
}
None => panic!("not an element!")
}
}
}
/// Get the description of how to account for recent style changes. /// Get the description of how to account for recent style changes.
/// This is a simple bitfield and fine to copy by value. /// This is a simple bitfield and fine to copy by value.
pub fn restyle_damage(self) -> RestyleDamage { pub fn restyle_damage(self) -> RestyleDamage {

View file

@ -32,6 +32,7 @@ use dom::bindings::utils::{Reflectable, Reflector, WindowProxyHandler};
use dom::node::{Node, TrustedNodeAddress}; use dom::node::{Node, TrustedNodeAddress};
use collections::hash::{Hash, Hasher}; use collections::hash::{Hash, Hasher};
use cssparser::RGBA;
use geom::rect::Rect; use geom::rect::Rect;
use html5ever::tree_builder::QuirksMode; use html5ever::tree_builder::QuirksMode;
use hyper::header::Headers; use hyper::header::Headers;
@ -48,7 +49,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, SimpleColor}; use servo_util::str::{LengthOrPercentageOrAuto};
use std::cell::{Cell, RefCell}; use std::cell::{Cell, RefCell};
use std::collections::HashMap; use std::collections::HashMap;
use std::comm::{Receiver, Sender}; use std::comm::{Receiver, Sender};
@ -214,7 +215,7 @@ no_jsmanaged_fields!(LayoutChan)
no_jsmanaged_fields!(WindowProxyHandler) no_jsmanaged_fields!(WindowProxyHandler)
no_jsmanaged_fields!(UntrustedNodeAddress) no_jsmanaged_fields!(UntrustedNodeAddress)
no_jsmanaged_fields!(LengthOrPercentageOrAuto) no_jsmanaged_fields!(LengthOrPercentageOrAuto)
no_jsmanaged_fields!(SimpleColor) no_jsmanaged_fields!(RGBA)
impl<'a> JSTraceable for &'a str { impl<'a> JSTraceable for &'a str {
#[inline] #[inline]

View file

@ -19,7 +19,8 @@ use dom::bindings::codegen::InheritTypes::{ElementCast, ElementDerived, EventTar
use dom::bindings::codegen::InheritTypes::{HTMLBodyElementDerived, HTMLInputElementCast}; use dom::bindings::codegen::InheritTypes::{HTMLBodyElementDerived, HTMLInputElementCast};
use dom::bindings::codegen::InheritTypes::{HTMLInputElementDerived, HTMLTableElementCast}; use dom::bindings::codegen::InheritTypes::{HTMLInputElementDerived, HTMLTableElementCast};
use dom::bindings::codegen::InheritTypes::{HTMLTableElementDerived, HTMLTableCellElementDerived}; use dom::bindings::codegen::InheritTypes::{HTMLTableElementDerived, HTMLTableCellElementDerived};
use dom::bindings::codegen::InheritTypes::{NodeCast}; use dom::bindings::codegen::InheritTypes::{HTMLTableRowElementDerived};
use dom::bindings::codegen::InheritTypes::{HTMLTableSectionElementDerived, NodeCast};
use dom::bindings::js::{MutNullableJS, JS, JSRef, Temporary, TemporaryPushable}; use dom::bindings::js::{MutNullableJS, JS, JSRef, Temporary, TemporaryPushable};
use dom::bindings::js::{OptionalRootable, Root}; use dom::bindings::js::{OptionalRootable, Root};
use dom::bindings::utils::{Reflectable, Reflector}; use dom::bindings::utils::{Reflectable, Reflector};
@ -38,19 +39,22 @@ use dom::htmlinputelement::{HTMLInputElement, RawLayoutHTMLInputElementHelpers};
use dom::htmlserializer::serialize; use dom::htmlserializer::serialize;
use dom::htmltableelement::{HTMLTableElement, HTMLTableElementHelpers}; use dom::htmltableelement::{HTMLTableElement, HTMLTableElementHelpers};
use dom::htmltablecellelement::{HTMLTableCellElement, HTMLTableCellElementHelpers}; use dom::htmltablecellelement::{HTMLTableCellElement, HTMLTableCellElementHelpers};
use dom::htmltablerowelement::{HTMLTableRowElement, HTMLTableRowElementHelpers};
use dom::htmltablesectionelement::{HTMLTableSectionElement, HTMLTableSectionElementHelpers};
use dom::node::{CLICK_IN_PROGRESS, ElementNodeTypeId, LayoutNodeHelpers, Node, NodeHelpers}; use dom::node::{CLICK_IN_PROGRESS, ElementNodeTypeId, LayoutNodeHelpers, Node, NodeHelpers};
use dom::node::{NodeIterator, NodeStyleDamaged, OtherNodeDamage, document_from_node}; use dom::node::{NodeIterator, NodeStyleDamaged, OtherNodeDamage, document_from_node};
use dom::node::{window_from_node}; use dom::node::{window_from_node};
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::{mod, BgColorSimpleColorAttribute, BorderUnsignedIntegerAttribute}; use style::{mod, AuthorOrigin, BgColorSimpleColorAttribute, BorderUnsignedIntegerAttribute};
use style::{ColSpanUnsignedIntegerAttribute, IntegerAttribute, LengthAttribute}; use style::{ColSpanUnsignedIntegerAttribute, IntegerAttribute, LengthAttribute, ParserContext};
use style::{SimpleColorAttribute, SizeIntegerAttribute, UnsignedIntegerAttribute}; use style::{SimpleColorAttribute, SizeIntegerAttribute, UnsignedIntegerAttribute};
use style::{WidthLengthAttribute, matches, parse_selector_list_from_str}; use style::{WidthLengthAttribute, matches};
use servo_util::namespace; use servo_util::namespace;
use servo_util::str::{DOMString, LengthOrPercentageOrAuto, SimpleColor}; use servo_util::str::{DOMString, LengthOrPercentageOrAuto};
use cssparser::RGBA;
use std::ascii::AsciiExt; use std::ascii::AsciiExt;
use std::cell::{Ref, RefMut}; use std::cell::{Ref, RefMut};
use std::default::Default; use std::default::Default;
@ -210,7 +214,7 @@ pub trait RawLayoutElementHelpers {
unsafe fn get_unsigned_integer_attribute_for_layout(&self, attribute: UnsignedIntegerAttribute) unsafe fn get_unsigned_integer_attribute_for_layout(&self, attribute: UnsignedIntegerAttribute)
-> Option<u32>; -> Option<u32>;
unsafe fn get_simple_color_attribute_for_layout(&self, attribute: SimpleColorAttribute) unsafe fn get_simple_color_attribute_for_layout(&self, attribute: SimpleColorAttribute)
-> Option<SimpleColor>; -> Option<RGBA>;
fn local_name<'a>(&'a self) -> &'a Atom; fn local_name<'a>(&'a self) -> &'a Atom;
fn namespace<'a>(&'a self) -> &'a Namespace; fn namespace<'a>(&'a self) -> &'a Namespace;
fn style_attribute<'a>(&'a self) -> &'a DOMRefCell<Option<style::PropertyDeclarationBlock>>; fn style_attribute<'a>(&'a self) -> &'a DOMRefCell<Option<style::PropertyDeclarationBlock>>;
@ -341,9 +345,6 @@ impl RawLayoutElementHelpers for Element {
if self.is_htmltableelement() { if self.is_htmltableelement() {
let this: &HTMLTableElement = mem::transmute(self); let this: &HTMLTableElement = mem::transmute(self);
this.get_border() this.get_border()
} else if self.is_htmltablecellelement() {
let this: &HTMLTableCellElement = mem::transmute(self);
this.get_border()
} else { } else {
// Don't panic since `:-servo-nonzero-border` can cause this to be called on // Don't panic since `:-servo-nonzero-border` can cause this to be called on
// arbitrary elements. // arbitrary elements.
@ -355,7 +356,9 @@ impl RawLayoutElementHelpers for Element {
let this: &HTMLTableCellElement = mem::transmute(self); let this: &HTMLTableCellElement = mem::transmute(self);
this.get_colspan() this.get_colspan()
} else { } else {
panic!("I'm not a table cell!") // Don't panic since `display` can cause this to be called on arbitrary
// elements.
None
} }
} }
} }
@ -364,7 +367,7 @@ impl RawLayoutElementHelpers for Element {
#[inline] #[inline]
#[allow(unrooted_must_root)] #[allow(unrooted_must_root)]
unsafe fn get_simple_color_attribute_for_layout(&self, attribute: SimpleColorAttribute) unsafe fn get_simple_color_attribute_for_layout(&self, attribute: SimpleColorAttribute)
-> Option<SimpleColor> { -> Option<RGBA> {
match attribute { match attribute {
BgColorSimpleColorAttribute => { BgColorSimpleColorAttribute => {
if self.is_htmlbodyelement() { if self.is_htmlbodyelement() {
@ -376,8 +379,14 @@ impl RawLayoutElementHelpers for Element {
} else if self.is_htmltablecellelement() { } else if self.is_htmltablecellelement() {
let this: &HTMLTableCellElement = mem::transmute(self); let this: &HTMLTableCellElement = mem::transmute(self);
this.get_background_color() this.get_background_color()
} else if self.is_htmltablerowelement() {
let this: &HTMLTableRowElement = mem::transmute(self);
this.get_background_color()
} else if self.is_htmltablesectionelement() {
let this: &HTMLTableSectionElement = mem::transmute(self);
this.get_background_color()
} else { } else {
panic!("I'm not a body, table, or table cell!") None
} }
} }
} }
@ -1011,7 +1020,10 @@ impl<'a> ElementMethods for JSRef<'a, Element> {
// http://dom.spec.whatwg.org/#dom-element-matches // http://dom.spec.whatwg.org/#dom-element-matches
fn Matches(self, selectors: DOMString) -> Fallible<bool> { fn Matches(self, selectors: DOMString) -> Fallible<bool> {
match parse_selector_list_from_str(selectors.as_slice()) { let parser_context = ParserContext {
origin: AuthorOrigin,
};
match style::parse_selector_list_from_str(&parser_context, selectors.as_slice()) {
Err(()) => Err(Syntax), Err(()) => Err(Syntax),
Ok(ref selectors) => { Ok(ref selectors) => {
let root: JSRef<Node> = NodeCast::from_ref(self); let root: JSRef<Node> = NodeCast::from_ref(self);

View file

@ -17,13 +17,14 @@ use dom::htmlelement::HTMLElement;
use dom::node::{Node, ElementNodeTypeId, window_from_node}; use dom::node::{Node, ElementNodeTypeId, window_from_node};
use dom::virtualmethods::VirtualMethods; use dom::virtualmethods::VirtualMethods;
use servo_util::str::{mod, DOMString, SimpleColor}; use cssparser::RGBA;
use servo_util::str::{mod, DOMString};
use std::cell::Cell; use std::cell::Cell;
#[dom_struct] #[dom_struct]
pub struct HTMLBodyElement { pub struct HTMLBodyElement {
htmlelement: HTMLElement, htmlelement: HTMLElement,
background_color: Cell<Option<SimpleColor>>, background_color: Cell<Option<RGBA>>,
} }
impl HTMLBodyElementDerived for EventTarget { impl HTMLBodyElementDerived for EventTarget {
@ -65,11 +66,11 @@ impl<'a> HTMLBodyElementMethods for JSRef<'a, HTMLBodyElement> {
} }
pub trait HTMLBodyElementHelpers { pub trait HTMLBodyElementHelpers {
fn get_background_color(&self) -> Option<SimpleColor>; fn get_background_color(&self) -> Option<RGBA>;
} }
impl HTMLBodyElementHelpers for HTMLBodyElement { impl HTMLBodyElementHelpers for HTMLBodyElement {
fn get_background_color(&self) -> Option<SimpleColor> { fn get_background_color(&self) -> Option<RGBA> {
self.background_color.get() self.background_color.get()
} }
} }

View file

@ -14,14 +14,14 @@ use dom::htmlelement::HTMLElement;
use dom::node::ElementNodeTypeId; use dom::node::ElementNodeTypeId;
use dom::virtualmethods::VirtualMethods; use dom::virtualmethods::VirtualMethods;
use servo_util::str::{mod, AutoLpa, DOMString, LengthOrPercentageOrAuto, SimpleColor}; use cssparser::RGBA;
use servo_util::str::{mod, AutoLpa, DOMString, LengthOrPercentageOrAuto};
use std::cell::Cell; use std::cell::Cell;
#[dom_struct] #[dom_struct]
pub struct HTMLTableCellElement { pub struct HTMLTableCellElement {
htmlelement: HTMLElement, htmlelement: HTMLElement,
background_color: Cell<Option<SimpleColor>>, background_color: Cell<Option<RGBA>>,
border: Cell<Option<u32>>,
colspan: Cell<Option<u32>>, colspan: Cell<Option<u32>>,
width: Cell<LengthOrPercentageOrAuto>, width: Cell<LengthOrPercentageOrAuto>,
} }
@ -45,7 +45,6 @@ impl HTMLTableCellElement {
HTMLTableCellElement { HTMLTableCellElement {
htmlelement: HTMLElement::new_inherited(type_id, tag_name, prefix, document), htmlelement: HTMLElement::new_inherited(type_id, tag_name, prefix, document),
background_color: Cell::new(None), background_color: Cell::new(None),
border: Cell::new(None),
colspan: Cell::new(None), colspan: Cell::new(None),
width: Cell::new(AutoLpa), width: Cell::new(AutoLpa),
} }
@ -58,21 +57,16 @@ impl HTMLTableCellElement {
} }
pub trait HTMLTableCellElementHelpers { pub trait HTMLTableCellElementHelpers {
fn get_background_color(&self) -> Option<SimpleColor>; fn get_background_color(&self) -> Option<RGBA>;
fn get_border(&self) -> Option<u32>;
fn get_colspan(&self) -> Option<u32>; fn get_colspan(&self) -> Option<u32>;
fn get_width(&self) -> LengthOrPercentageOrAuto; fn get_width(&self) -> LengthOrPercentageOrAuto;
} }
impl HTMLTableCellElementHelpers for HTMLTableCellElement { impl HTMLTableCellElementHelpers for HTMLTableCellElement {
fn get_background_color(&self) -> Option<SimpleColor> { fn get_background_color(&self) -> Option<RGBA> {
self.background_color.get() self.background_color.get()
} }
fn get_border(&self) -> Option<u32> {
self.border.get()
}
fn get_colspan(&self) -> Option<u32> { fn get_colspan(&self) -> Option<u32> {
self.colspan.get() self.colspan.get()
} }
@ -98,12 +92,6 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLTableCellElement> {
&atom!("bgcolor") => { &atom!("bgcolor") => {
self.background_color.set(str::parse_legacy_color(attr.value().as_slice()).ok()) self.background_color.set(str::parse_legacy_color(attr.value().as_slice()).ok())
} }
&atom!("border") => {
// According to HTML5 § 14.3.9, invalid values map to 1px.
self.border.set(Some(str::parse_unsigned_integer(attr.value()
.as_slice()
.chars()).unwrap_or(1)))
}
&atom!("colspan") => { &atom!("colspan") => {
self.colspan.set(str::parse_unsigned_integer(attr.value().as_slice().chars())); self.colspan.set(str::parse_unsigned_integer(attr.value().as_slice().chars()));
} }
@ -120,7 +108,6 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLTableCellElement> {
match attr.local_name() { match attr.local_name() {
&atom!("bgcolor") => self.background_color.set(None), &atom!("bgcolor") => self.background_color.set(None),
&atom!("border") => self.border.set(None),
&atom!("colspan") => self.colspan.set(None), &atom!("colspan") => self.colspan.set(None),
&atom!("width") => self.width.set(AutoLpa), &atom!("width") => self.width.set(AutoLpa),
_ => () _ => ()

View file

@ -18,13 +18,14 @@ use dom::htmltablecaptionelement::HTMLTableCaptionElement;
use dom::node::{Node, NodeHelpers, ElementNodeTypeId}; use dom::node::{Node, NodeHelpers, ElementNodeTypeId};
use dom::virtualmethods::VirtualMethods; use dom::virtualmethods::VirtualMethods;
use servo_util::str::{mod, AutoLpa, DOMString, LengthOrPercentageOrAuto, SimpleColor}; use cssparser::RGBA;
use servo_util::str::{mod, AutoLpa, DOMString, LengthOrPercentageOrAuto};
use std::cell::Cell; use std::cell::Cell;
#[dom_struct] #[dom_struct]
pub struct HTMLTableElement { pub struct HTMLTableElement {
htmlelement: HTMLElement, htmlelement: HTMLElement,
background_color: Cell<Option<SimpleColor>>, background_color: Cell<Option<RGBA>>,
border: Cell<Option<u32>>, border: Cell<Option<u32>>,
width: Cell<LengthOrPercentageOrAuto>, width: Cell<LengthOrPercentageOrAuto>,
} }
@ -95,13 +96,13 @@ impl<'a> HTMLTableElementMethods for JSRef<'a, HTMLTableElement> {
} }
pub trait HTMLTableElementHelpers { pub trait HTMLTableElementHelpers {
fn get_background_color(&self) -> Option<SimpleColor>; fn get_background_color(&self) -> Option<RGBA>;
fn get_border(&self) -> Option<u32>; fn get_border(&self) -> Option<u32>;
fn get_width(&self) -> LengthOrPercentageOrAuto; fn get_width(&self) -> LengthOrPercentageOrAuto;
} }
impl HTMLTableElementHelpers for HTMLTableElement { impl HTMLTableElementHelpers for HTMLTableElement {
fn get_background_color(&self) -> Option<SimpleColor> { fn get_background_color(&self) -> Option<RGBA> {
self.background_color.get() self.background_color.get()
} }

View file

@ -2,8 +2,9 @@
* 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::attr::{Attr, AttrHelpers};
use dom::bindings::codegen::Bindings::HTMLTableRowElementBinding; use dom::bindings::codegen::Bindings::HTMLTableRowElementBinding;
use dom::bindings::codegen::InheritTypes::HTMLTableRowElementDerived; use dom::bindings::codegen::InheritTypes::{HTMLElementCast, HTMLTableRowElementDerived};
use dom::bindings::js::{JSRef, Temporary}; use dom::bindings::js::{JSRef, Temporary};
use dom::bindings::utils::{Reflectable, Reflector}; use dom::bindings::utils::{Reflectable, Reflector};
use dom::document::Document; use dom::document::Document;
@ -11,11 +12,16 @@ use dom::element::HTMLTableRowElementTypeId;
use dom::eventtarget::{EventTarget, NodeTargetTypeId}; use dom::eventtarget::{EventTarget, NodeTargetTypeId};
use dom::htmlelement::HTMLElement; use dom::htmlelement::HTMLElement;
use dom::node::{Node, ElementNodeTypeId}; use dom::node::{Node, ElementNodeTypeId};
use servo_util::str::DOMString; use dom::virtualmethods::VirtualMethods;
use cssparser::RGBA;
use servo_util::str::{mod, DOMString};
use std::cell::Cell;
#[dom_struct] #[dom_struct]
pub struct HTMLTableRowElement { pub struct HTMLTableRowElement {
htmlelement: HTMLElement, htmlelement: HTMLElement,
background_color: Cell<Option<RGBA>>,
} }
impl HTMLTableRowElementDerived for EventTarget { impl HTMLTableRowElementDerived for EventTarget {
@ -25,9 +31,14 @@ impl HTMLTableRowElementDerived for EventTarget {
} }
impl HTMLTableRowElement { impl HTMLTableRowElement {
fn new_inherited(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) -> HTMLTableRowElement { fn new_inherited(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>)
-> HTMLTableRowElement {
HTMLTableRowElement { HTMLTableRowElement {
htmlelement: HTMLElement::new_inherited(HTMLTableRowElementTypeId, localName, prefix, document) htmlelement: HTMLElement::new_inherited(HTMLTableRowElementTypeId,
localName,
prefix,
document),
background_color: Cell::new(None),
} }
} }
@ -45,3 +56,47 @@ impl Reflectable for HTMLTableRowElement {
self.htmlelement.reflector() self.htmlelement.reflector()
} }
} }
pub trait HTMLTableRowElementHelpers {
fn get_background_color(&self) -> Option<RGBA>;
}
impl HTMLTableRowElementHelpers for HTMLTableRowElement {
fn get_background_color(&self) -> Option<RGBA> {
self.background_color.get()
}
}
impl<'a> VirtualMethods for JSRef<'a, HTMLTableRowElement> {
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, attr: JSRef<Attr>) {
match self.super_type() {
Some(ref s) => s.after_set_attr(attr),
_ => ()
}
match attr.local_name() {
&atom!("bgcolor") => {
self.background_color.set(str::parse_legacy_color(attr.value().as_slice()).ok())
}
_ => {}
}
}
fn before_remove_attr(&self, attr: JSRef<Attr>) {
match self.super_type() {
Some(ref s) => s.before_remove_attr(attr),
_ => ()
}
match attr.local_name() {
&atom!("bgcolor") => self.background_color.set(None),
_ => {}
}
}
}

View file

@ -2,8 +2,9 @@
* 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::attr::{Attr, AttrHelpers};
use dom::bindings::codegen::Bindings::HTMLTableSectionElementBinding; use dom::bindings::codegen::Bindings::HTMLTableSectionElementBinding;
use dom::bindings::codegen::InheritTypes::HTMLTableSectionElementDerived; use dom::bindings::codegen::InheritTypes::{HTMLElementCast, HTMLTableSectionElementDerived};
use dom::bindings::js::{JSRef, Temporary}; use dom::bindings::js::{JSRef, Temporary};
use dom::bindings::utils::{Reflectable, Reflector}; use dom::bindings::utils::{Reflectable, Reflector};
use dom::document::Document; use dom::document::Document;
@ -11,11 +12,16 @@ use dom::element::HTMLTableSectionElementTypeId;
use dom::eventtarget::{EventTarget, NodeTargetTypeId}; use dom::eventtarget::{EventTarget, NodeTargetTypeId};
use dom::htmlelement::HTMLElement; use dom::htmlelement::HTMLElement;
use dom::node::{Node, ElementNodeTypeId}; use dom::node::{Node, ElementNodeTypeId};
use servo_util::str::DOMString; use dom::virtualmethods::VirtualMethods;
use cssparser::RGBA;
use servo_util::str::{mod, DOMString};
use std::cell::Cell;
#[dom_struct] #[dom_struct]
pub struct HTMLTableSectionElement { pub struct HTMLTableSectionElement {
htmlelement: HTMLElement, htmlelement: HTMLElement,
background_color: Cell<Option<RGBA>>,
} }
impl HTMLTableSectionElementDerived for EventTarget { impl HTMLTableSectionElementDerived for EventTarget {
@ -25,14 +31,20 @@ impl HTMLTableSectionElementDerived for EventTarget {
} }
impl HTMLTableSectionElement { impl HTMLTableSectionElement {
fn new_inherited(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) -> HTMLTableSectionElement { fn new_inherited(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>)
-> HTMLTableSectionElement {
HTMLTableSectionElement { HTMLTableSectionElement {
htmlelement: HTMLElement::new_inherited(HTMLTableSectionElementTypeId, localName, prefix, document) htmlelement: HTMLElement::new_inherited(HTMLTableSectionElementTypeId,
localName,
prefix,
document),
background_color: Cell::new(None),
} }
} }
#[allow(unrooted_must_root)] #[allow(unrooted_must_root)]
pub fn new(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) -> Temporary<HTMLTableSectionElement> { pub fn new(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>)
-> Temporary<HTMLTableSectionElement> {
let element = HTMLTableSectionElement::new_inherited(localName, prefix, document); let element = HTMLTableSectionElement::new_inherited(localName, prefix, document);
Node::reflect_node(box element, document, HTMLTableSectionElementBinding::Wrap) Node::reflect_node(box element, document, HTMLTableSectionElementBinding::Wrap)
} }
@ -43,3 +55,47 @@ impl Reflectable for HTMLTableSectionElement {
self.htmlelement.reflector() self.htmlelement.reflector()
} }
} }
pub trait HTMLTableSectionElementHelpers {
fn get_background_color(&self) -> Option<RGBA>;
}
impl HTMLTableSectionElementHelpers for HTMLTableSectionElement {
fn get_background_color(&self) -> Option<RGBA> {
self.background_color.get()
}
}
impl<'a> VirtualMethods for JSRef<'a, HTMLTableSectionElement> {
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, attr: JSRef<Attr>) {
match self.super_type() {
Some(ref s) => s.after_set_attr(attr),
_ => ()
}
match attr.local_name() {
&atom!("bgcolor") => {
self.background_color.set(str::parse_legacy_color(attr.value().as_slice()).ok())
}
_ => {}
}
}
fn before_remove_attr(&self, attr: JSRef<Attr>) {
match self.super_type() {
Some(ref s) => s.before_remove_attr(attr),
_ => ()
}
match attr.local_name() {
&atom!("bgcolor") => self.background_color.set(None),
_ => {}
}
}
}

View file

@ -50,7 +50,7 @@ use devtools_traits::NodeInfo;
use script_traits::UntrustedNodeAddress; use script_traits::UntrustedNodeAddress;
use servo_util::geometry::Au; use servo_util::geometry::Au;
use servo_util::str::{DOMString, null_str_as_empty}; use servo_util::str::{DOMString, null_str_as_empty};
use style::{parse_selector_list_from_str, matches, SelectorList}; use style::{matches, AuthorOrigin, ParserContext, SelectorList};
use js::jsapi::{JSContext, JSObject, JSTracer, JSRuntime}; use js::jsapi::{JSContext, JSObject, JSTracer, JSRuntime};
use js::jsfriendapi; use js::jsfriendapi;
@ -60,8 +60,7 @@ use std::cell::{Cell, RefCell, Ref, RefMut};
use std::default::Default; use std::default::Default;
use std::iter::{FilterMap, Peekable}; use std::iter::{FilterMap, Peekable};
use std::mem; use std::mem;
use style; use style::{mod, ComputedValues};
use style::ComputedValues;
use sync::Arc; use sync::Arc;
use uuid; use uuid;
use string_cache::QualName; use string_cache::QualName;
@ -741,7 +740,10 @@ impl<'a> NodeHelpers<'a> for JSRef<'a, Node> {
// http://dom.spec.whatwg.org/#dom-parentnode-queryselector // http://dom.spec.whatwg.org/#dom-parentnode-queryselector
fn query_selector(self, selectors: DOMString) -> Fallible<Option<Temporary<Element>>> { fn query_selector(self, selectors: DOMString) -> Fallible<Option<Temporary<Element>>> {
// Step 1. // Step 1.
match parse_selector_list_from_str(selectors.as_slice()) { let parser_context = ParserContext {
origin: AuthorOrigin,
};
match style::parse_selector_list_from_str(&parser_context, selectors.as_slice()) {
// Step 2. // Step 2.
Err(()) => return Err(Syntax), Err(()) => return Err(Syntax),
// Step 3. // Step 3.
@ -758,11 +760,15 @@ impl<'a> NodeHelpers<'a> for JSRef<'a, Node> {
/// Get an iterator over all nodes which match a set of selectors /// Get an iterator over all nodes which match a set of selectors
/// Be careful not to do anything which may manipulate the DOM tree whilst iterating, otherwise /// Be careful not to do anything which may manipulate the DOM tree whilst iterating, otherwise
/// the iterator may be invalidated /// the iterator may be invalidated
unsafe fn query_selector_iter(self, selectors: DOMString) -> Fallible<QuerySelectorIterator<'a>> { unsafe fn query_selector_iter(self, selectors: DOMString)
-> Fallible<QuerySelectorIterator<'a>> {
// Step 1. // Step 1.
let nodes; let nodes;
let root = self.ancestors().last().unwrap_or(self.clone()); let root = self.ancestors().last().unwrap_or(self.clone());
match parse_selector_list_from_str(selectors.as_slice()) { let parser_context = ParserContext {
origin: AuthorOrigin,
};
match style::parse_selector_list_from_str(&parser_context, selectors.as_slice()) {
// Step 2. // Step 2.
Err(()) => return Err(Syntax), Err(()) => return Err(Syntax),
// Step 3. // Step 3.

View file

@ -24,6 +24,8 @@ use dom::bindings::codegen::InheritTypes::HTMLSelectElementCast;
use dom::bindings::codegen::InheritTypes::HTMLStyleElementCast; use dom::bindings::codegen::InheritTypes::HTMLStyleElementCast;
use dom::bindings::codegen::InheritTypes::HTMLTableElementCast; use dom::bindings::codegen::InheritTypes::HTMLTableElementCast;
use dom::bindings::codegen::InheritTypes::HTMLTableCellElementCast; use dom::bindings::codegen::InheritTypes::HTMLTableCellElementCast;
use dom::bindings::codegen::InheritTypes::HTMLTableRowElementCast;
use dom::bindings::codegen::InheritTypes::HTMLTableSectionElementCast;
use dom::bindings::codegen::InheritTypes::HTMLTextAreaElementCast; use dom::bindings::codegen::InheritTypes::HTMLTextAreaElementCast;
use dom::bindings::codegen::InheritTypes::HTMLTitleElementCast; use dom::bindings::codegen::InheritTypes::HTMLTitleElementCast;
use dom::bindings::js::JSRef; use dom::bindings::js::JSRef;
@ -49,6 +51,8 @@ use dom::element::HTMLStyleElementTypeId;
use dom::element::HTMLTableDataCellElementTypeId; use dom::element::HTMLTableDataCellElementTypeId;
use dom::element::HTMLTableElementTypeId; use dom::element::HTMLTableElementTypeId;
use dom::element::HTMLTableHeaderCellElementTypeId; use dom::element::HTMLTableHeaderCellElementTypeId;
use dom::element::HTMLTableRowElementTypeId;
use dom::element::HTMLTableSectionElementTypeId;
use dom::element::HTMLTextAreaElementTypeId; use dom::element::HTMLTextAreaElementTypeId;
use dom::element::HTMLTitleElementTypeId; use dom::element::HTMLTitleElementTypeId;
use dom::event::Event; use dom::event::Event;
@ -71,6 +75,8 @@ use dom::htmlselectelement::HTMLSelectElement;
use dom::htmlstyleelement::HTMLStyleElement; use dom::htmlstyleelement::HTMLStyleElement;
use dom::htmltableelement::HTMLTableElement; use dom::htmltableelement::HTMLTableElement;
use dom::htmltablecellelement::HTMLTableCellElement; use dom::htmltablecellelement::HTMLTableCellElement;
use dom::htmltablerowelement::HTMLTableRowElement;
use dom::htmltablesectionelement::HTMLTableSectionElement;
use dom::htmltextareaelement::HTMLTextAreaElement; use dom::htmltextareaelement::HTMLTextAreaElement;
use dom::htmltitleelement::HTMLTitleElement; use dom::htmltitleelement::HTMLTitleElement;
use dom::node::{Node, NodeHelpers, ElementNodeTypeId, CloneChildrenFlag}; use dom::node::{Node, NodeHelpers, ElementNodeTypeId, CloneChildrenFlag};
@ -236,7 +242,18 @@ pub fn vtable_for<'a>(node: &'a JSRef<'a, Node>) -> &'a VirtualMethods + 'a {
} }
ElementNodeTypeId(HTMLTableDataCellElementTypeId) | ElementNodeTypeId(HTMLTableDataCellElementTypeId) |
ElementNodeTypeId(HTMLTableHeaderCellElementTypeId) => { ElementNodeTypeId(HTMLTableHeaderCellElementTypeId) => {
let element: &'a JSRef<'a, HTMLTableCellElement> = HTMLTableCellElementCast::to_borrowed_ref(node).unwrap(); let element: &'a JSRef<'a, HTMLTableCellElement> =
HTMLTableCellElementCast::to_borrowed_ref(node).unwrap();
element as &'a VirtualMethods + 'a
}
ElementNodeTypeId(HTMLTableRowElementTypeId) => {
let element: &'a JSRef<'a, HTMLTableRowElement> =
HTMLTableRowElementCast::to_borrowed_ref(node).unwrap();
element as &'a VirtualMethods + 'a
}
ElementNodeTypeId(HTMLTableSectionElementTypeId) => {
let element: &'a JSRef<'a, HTMLTableSectionElement> =
HTMLTableSectionElementCast::to_borrowed_ref(node).unwrap();
element as &'a VirtualMethods + 'a element as &'a VirtualMethods + 'a
} }
ElementNodeTypeId(HTMLTextAreaElementTypeId) => { ElementNodeTypeId(HTMLTextAreaElementTypeId) => {

View file

@ -410,6 +410,7 @@ dependencies = [
name = "layout" name = "layout"
version = "0.0.1" version = "0.0.1"
dependencies = [ dependencies = [
"cssparser 0.1.0 (git+https://github.com/servo/rust-cssparser)",
"encoding 0.2.0 (git+https://github.com/lifthrasiir/rust-encoding)", "encoding 0.2.0 (git+https://github.com/lifthrasiir/rust-encoding)",
"geom 0.1.0 (git+https://github.com/servo/rust-geom)", "geom 0.1.0 (git+https://github.com/servo/rust-geom)",
"gfx 0.0.1", "gfx 0.0.1",

View file

@ -8,11 +8,10 @@
use node::{TElement, TElementAttributes, TNode}; use node::{TElement, TElementAttributes, TNode};
use properties::{BackgroundColorDeclaration, BorderBottomWidthDeclaration}; use properties::{BackgroundColorDeclaration, BorderBottomWidthDeclaration};
use properties::{BorderLeftWidthDeclaration, BorderRightWidthDeclaration}; use properties::{BorderLeftWidthDeclaration, BorderRightWidthDeclaration};
use properties::{BorderTopWidthDeclaration, ServoColumnSpanDeclaration, SpecifiedValue}; use properties::{BorderTopWidthDeclaration, SpecifiedValue, WidthDeclaration, specified};
use properties::{WidthDeclaration, specified};
use selector_matching::{DeclarationBlock, Stylist}; use selector_matching::{DeclarationBlock, Stylist};
use cssparser::{RGBA, RGBAColor}; use cssparser::RGBAColor;
use servo_util::geometry::Au; use servo_util::geometry::Au;
use servo_util::smallvec::VecLike; use servo_util::smallvec::VecLike;
use servo_util::str::{AutoLpa, LengthLpa, PercentageLpa}; use servo_util::str::{AutoLpa, LengthLpa, PercentageLpa};
@ -114,14 +113,6 @@ impl PresentationalHintSynthesis for Stylist {
*shareable = false *shareable = false
} }
} }
match element.get_unsigned_integer_attribute(ColSpanUnsignedIntegerAttribute) {
None => {}
Some(value) => {
matching_rules_list.vec_push(DeclarationBlock::from_declaration(
ServoColumnSpanDeclaration(SpecifiedValue(value))));
*shareable = false
}
}
self.synthesize_presentational_hint_for_legacy_background_color_attribute( self.synthesize_presentational_hint_for_legacy_background_color_attribute(
element, element,
matching_rules_list, matching_rules_list,
@ -141,7 +132,8 @@ impl PresentationalHintSynthesis for Stylist {
matching_rules_list, matching_rules_list,
shareable); shareable);
} }
name if *name == atom!("body") => { name if *name == atom!("body") || *name == atom!("tr") || *name == atom!("thead") ||
*name == atom!("tbody") || *name == atom!("tfoot") => {
self.synthesize_presentational_hint_for_legacy_background_color_attribute( self.synthesize_presentational_hint_for_legacy_background_color_attribute(
element, element,
matching_rules_list, matching_rules_list,
@ -187,12 +179,7 @@ impl PresentationalHintSynthesis for Stylist {
None => {} None => {}
Some(color) => { Some(color) => {
matching_rules_list.vec_push(DeclarationBlock::from_declaration( matching_rules_list.vec_push(DeclarationBlock::from_declaration(
BackgroundColorDeclaration(SpecifiedValue(RGBAColor(RGBA { BackgroundColorDeclaration(SpecifiedValue(RGBAColor(color)))));
red: color.red as f32 / 255.0,
green: color.green as f32 / 255.0,
blue: color.blue as f32 / 255.0,
alpha: 1.0,
})))));
*shareable = false *shareable = false
} }
} }

View file

@ -50,9 +50,9 @@ pub use properties::{CSSFloat, DeclaredValue, PropertyDeclarationParseResult};
pub use properties::{Angle, AngleOrCorner, AngleAoc, CornerAoc}; pub use properties::{Angle, AngleOrCorner, AngleAoc, CornerAoc};
pub use properties::{Left, Right, Bottom, Top}; pub use properties::{Left, Right, Bottom, Top};
pub use node::{TElement, TElementAttributes, 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, ParserContext, SelectorList};
pub use selectors::{AttrSelector, NamespaceConstraint, SpecificNamespace, AnyNamespace}; pub use selectors::{AttrSelector, NamespaceConstraint, SpecificNamespace, AnyNamespace};
pub use selectors::{SimpleSelector, LocalNameSelector}; pub use selectors::{SimpleSelector, LocalNameSelector, parse_selector_list_from_str};
pub use cssparser::{Color, RGBA}; pub use cssparser::{Color, RGBA};
pub use legacy::{BgColorSimpleColorAttribute, BorderUnsignedIntegerAttribute}; pub use legacy::{BgColorSimpleColorAttribute, BorderUnsignedIntegerAttribute};
pub use legacy::{ColSpanUnsignedIntegerAttribute, IntegerAttribute, LengthAttribute}; pub use legacy::{ColSpanUnsignedIntegerAttribute, IntegerAttribute, LengthAttribute};

View file

@ -8,12 +8,14 @@ use cssparser::ast::*;
use errors::{ErrorLoggerIterator, log_css_error}; use errors::{ErrorLoggerIterator, log_css_error};
use geom::size::TypedSize2D; use geom::size::TypedSize2D;
use stylesheets::{CSSRule, CSSMediaRule, parse_style_rule, parse_nested_at_rule}; use selectors::ParserContext;
use stylesheets::{CSSRule, CSSMediaRule};
use namespaces::NamespaceMap; use namespaces::NamespaceMap;
use parsing_utils::{BufferedIter, ParserIter}; use parsing_utils::{BufferedIter, ParserIter};
use properties::common_types::*; use properties::common_types::*;
use properties::longhands; use properties::longhands;
use servo_util::geometry::ViewportPx; use servo_util::geometry::ViewportPx;
use stylesheets;
use url::Url; use url::Url;
pub struct MediaRule { pub struct MediaRule {
@ -95,8 +97,11 @@ impl Device {
} }
} }
pub fn parse_media_rule(rule: AtRule, parent_rules: &mut Vec<CSSRule>, pub fn parse_media_rule(context: &ParserContext,
namespaces: &NamespaceMap, base_url: &Url) { rule: AtRule,
parent_rules: &mut Vec<CSSRule>,
namespaces: &NamespaceMap,
base_url: &Url) {
let media_queries = parse_media_query_list(rule.prelude.as_slice()); let media_queries = parse_media_query_list(rule.prelude.as_slice());
let block = match rule.block { let block = match rule.block {
Some(block) => block, Some(block) => block,
@ -108,9 +113,17 @@ pub fn parse_media_rule(rule: AtRule, parent_rules: &mut Vec<CSSRule>,
let mut rules = vec!(); let mut rules = vec!();
for rule in ErrorLoggerIterator(parse_rule_list(block.into_iter())) { for rule in ErrorLoggerIterator(parse_rule_list(block.into_iter())) {
match rule { match rule {
QualifiedRule_(rule) => parse_style_rule(rule, &mut rules, namespaces, base_url), QualifiedRule_(rule) => {
AtRule_(rule) => parse_nested_at_rule( stylesheets::parse_style_rule(context, rule, &mut rules, namespaces, base_url)
rule.name.as_slice().to_ascii_lower().as_slice(), rule, &mut rules, namespaces, base_url), }
AtRule_(rule) => {
stylesheets::parse_nested_at_rule(context,
rule.name.as_slice().to_ascii_lower().as_slice(),
rule,
&mut rules,
namespaces,
base_url)
}
} }
} }
parent_rules.push(CSSMediaRule(MediaRule { parent_rules.push(CSSMediaRule(MediaRule {

View file

@ -5,9 +5,10 @@
//! 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 cssparser::RGBA;
use legacy::{IntegerAttribute, LengthAttribute, SimpleColorAttribute, UnsignedIntegerAttribute}; use legacy::{IntegerAttribute, LengthAttribute, SimpleColorAttribute, UnsignedIntegerAttribute};
use selectors::AttrSelector; use selectors::AttrSelector;
use servo_util::str::{LengthOrPercentageOrAuto, SimpleColor}; 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 {
@ -60,5 +61,5 @@ pub trait TElementAttributes : Copy {
fn get_length_attribute(self, attribute: LengthAttribute) -> LengthOrPercentageOrAuto; fn get_length_attribute(self, attribute: LengthAttribute) -> LengthOrPercentageOrAuto;
fn get_integer_attribute(self, attribute: IntegerAttribute) -> Option<i32>; fn get_integer_attribute(self, attribute: IntegerAttribute) -> Option<i32>;
fn get_unsigned_integer_attribute(self, attribute: UnsignedIntegerAttribute) -> Option<u32>; fn get_unsigned_integer_attribute(self, attribute: UnsignedIntegerAttribute) -> Option<u32>;
fn get_simple_color_attribute(self, attribute: SimpleColorAttribute) -> Option<SimpleColor>; fn get_simple_color_attribute(self, attribute: SimpleColorAttribute) -> Option<RGBA>;
} }

View file

@ -1316,34 +1316,6 @@ pub mod longhands {
${single_keyword("table-layout", "auto fixed")} ${single_keyword("table-layout", "auto fixed")}
<%self:single_component_value name="-servo-column-span">
// The handling of this property is not well-specified by INTRINSIC, but its presence is
// assumed. HTML5 14.3.9 specifies that the `colspan` attribute is to be a nonnegative
// integer.
pub use super::computed_as_specified as to_computed_value;
pub mod computed_value {
pub type T = u32;
}
pub type SpecifiedValue = computed_value::T;
#[inline]
pub fn get_initial_value() -> computed_value::T {
1
}
pub fn from_component_value(input: &ComponentValue, _: &Url) -> Result<SpecifiedValue,()> {
match input {
&Number(ref value) => {
match value.int_value {
None => Err(()),
Some(n) => Ok(n as SpecifiedValue),
}
}
_ => Err(()),
}
}
</%self:single_component_value>
// CSS 2.1, Section 18 - User interface // CSS 2.1, Section 18 - User interface

View file

@ -20,9 +20,22 @@ use legacy::PresentationalHintSynthesis;
use media_queries::Device; use media_queries::Device;
use node::{TElement, TElementAttributes, TNode}; use node::{TElement, TElementAttributes, TNode};
use properties::{PropertyDeclaration, PropertyDeclarationBlock}; use properties::{PropertyDeclaration, PropertyDeclarationBlock};
use selectors::*; use selectors::{After, AnyLink, AttrDashMatch, AttrEqual};
use selectors::{AttrExists, AttrIncludes, AttrPrefixMatch};
use selectors::{AttrSubstringMatch, AttrSuffixMatch, Before, CaseInsensitive, CaseSensitive};
use selectors::{Checked, Child, ClassSelector};
use selectors::{CompoundSelector, Descendant, Disabled, Enabled, FirstChild, FirstOfType};
use selectors::{Hover, IDSelector, LastChild, LastOfType};
use selectors::{LaterSibling, LocalName, LocalNameSelector};
use selectors::{NamespaceSelector, Link, Negation};
use selectors::{NextSibling, NthChild};
use selectors::{NthLastChild, NthLastOfType};
use selectors::{NthOfType, OnlyChild, OnlyOfType, PseudoElement, Root};
use selectors::{SelectorList, ServoNonzeroBorder, SimpleSelector, Visited};
use selectors::{get_selector_list_selectors};
use stylesheets::{Stylesheet, iter_stylesheet_media_rules, iter_stylesheet_style_rules}; use stylesheets::{Stylesheet, iter_stylesheet_media_rules, iter_stylesheet_style_rules};
#[deriving(Clone, PartialEq)]
pub enum StylesheetOrigin { pub enum StylesheetOrigin {
UserAgentOrigin, UserAgentOrigin,
AuthorOrigin, AuthorOrigin,
@ -1166,12 +1179,16 @@ mod tests {
/// Each sublist of the result contains the Rules for one StyleRule. /// Each sublist of the result contains the Rules for one StyleRule.
fn get_mock_rules(css_selectors: &[&str]) -> Vec<Vec<Rule>> { fn get_mock_rules(css_selectors: &[&str]) -> Vec<Vec<Rule>> {
use namespaces::NamespaceMap; use namespaces::NamespaceMap;
use selectors::parse_selector_list; use selectors::{ParserContext, parse_selector_list};
use selector_matching::AuthorOrigin;
use cssparser::tokenize; use cssparser::tokenize;
let namespaces = NamespaceMap::new(); let namespaces = NamespaceMap::new();
css_selectors.iter().enumerate().map(|(i, selectors)| { css_selectors.iter().enumerate().map(|(i, selectors)| {
parse_selector_list(tokenize(*selectors).map(|(c, _)| c), &namespaces) let context = ParserContext {
origin: AuthorOrigin,
};
parse_selector_list(&context, tokenize(*selectors).map(|(c, _)| c), &namespaces)
.unwrap().into_iter().map(|s| { .unwrap().into_iter().map(|s| {
Rule { Rule {
selector: s.compound_selectors.clone(), selector: s.compound_selectors.clone(),

View file

@ -9,10 +9,16 @@ use sync::Arc;
use cssparser::ast::*; use cssparser::ast::*;
use cssparser::{tokenize, parse_nth}; use cssparser::{tokenize, parse_nth};
use selector_matching::{StylesheetOrigin, UserAgentOrigin};
use string_cache::{Atom, Namespace}; use string_cache::{Atom, Namespace};
use namespaces::NamespaceMap; use namespaces::NamespaceMap;
/// Ambient data used by the parser.
pub struct ParserContext {
/// The origin of this stylesheet.
pub origin: StylesheetOrigin,
}
#[deriving(PartialEq, Clone)] #[deriving(PartialEq, Clone)]
pub struct Selector { pub struct Selector {
@ -112,12 +118,6 @@ pub enum NamespaceConstraint {
} }
pub fn parse_selector_list_from_str(input: &str) -> Result<SelectorList, ()> {
let namespaces = NamespaceMap::new();
let iter = tokenize(input).map(|(token, _)| token);
parse_selector_list(iter, &namespaces).map(|s| SelectorList { selectors: s })
}
/// Re-exported to script, but opaque. /// Re-exported to script, but opaque.
pub struct SelectorList { pub struct SelectorList {
selectors: Vec<Selector> selectors: Vec<Selector>
@ -128,70 +128,9 @@ pub fn get_selector_list_selectors<'a>(selector_list: &'a SelectorList) -> &'a [
selector_list.selectors.as_slice() selector_list.selectors.as_slice()
} }
/// Parse a comma-separated list of Selectors.
/// aka Selector Group in http://www.w3.org/TR/css3-selectors/#grouping
///
/// Return the Selectors or None if there is an invalid selector.
pub fn parse_selector_list<I: Iterator<ComponentValue>>(
iter: I, namespaces: &NamespaceMap)
-> Result<Vec<Selector>, ()> {
let iter = &mut iter.peekable();
let mut results = vec![try!(parse_selector(iter, namespaces))];
loop {
skip_whitespace(iter);
match iter.peek() {
None => break, // EOF
Some(&Comma) => {
iter.next();
}
_ => return Err(()),
}
results.push(try!(parse_selector(iter, namespaces)));
}
Ok(results)
}
type Iter<I> = iter::Peekable<ComponentValue, I>; type Iter<I> = iter::Peekable<ComponentValue, I>;
/// Build up a Selector.
/// selector : simple_selector_sequence [ combinator simple_selector_sequence ]* ;
///
/// `Err` means invalid selector.
fn parse_selector<I: Iterator<ComponentValue>>(
iter: &mut Iter<I>, namespaces: &NamespaceMap)
-> Result<Selector, ()> {
let (first, mut pseudo_element) = try!(parse_simple_selectors(iter, namespaces));
let mut compound = CompoundSelector{ simple_selectors: first, next: None };
while pseudo_element.is_none() {
let any_whitespace = skip_whitespace(iter);
let combinator = match iter.peek() {
None => break, // EOF
Some(&Comma) => break,
Some(&Delim('>')) => { iter.next(); Child },
Some(&Delim('+')) => { iter.next(); NextSibling },
Some(&Delim('~')) => { iter.next(); LaterSibling },
Some(_) => {
if any_whitespace { Descendant }
else { return Err(()) }
}
};
let (simple_selectors, pseudo) = try!(parse_simple_selectors(iter, namespaces));
compound = CompoundSelector {
simple_selectors: simple_selectors,
next: Some((box compound, combinator))
};
pseudo_element = pseudo;
}
Ok(Selector {
specificity: compute_specificity(&compound, &pseudo_element),
compound_selectors: Arc::new(compound),
pseudo_element: pseudo_element,
})
}
fn compute_specificity(mut selector: &CompoundSelector, fn compute_specificity(mut selector: &CompoundSelector,
pseudo_element: &Option<PseudoElement>) -> u32 { pseudo_element: &Option<PseudoElement>) -> u32 {
@ -248,32 +187,6 @@ fn compute_specificity(mut selector: &CompoundSelector,
} }
/// simple_selector_sequence
/// : [ type_selector | universal ] [ HASH | class | attrib | pseudo | negation ]*
/// | [ HASH | class | attrib | pseudo | negation ]+
///
/// `Err(())` means invalid selector
fn parse_simple_selectors<I: Iterator<ComponentValue>>(
iter: &mut Iter<I>, namespaces: &NamespaceMap)
-> Result<(Vec<SimpleSelector>, Option<PseudoElement>), ()> {
let mut empty = true;
let mut simple_selectors = match try!(parse_type_selector(iter, namespaces)) {
None => vec![],
Some(s) => { empty = false; s }
};
let mut pseudo_element = None;
loop {
match try!(parse_one_simple_selector(iter, namespaces, /* inside_negation = */ false)) {
None => break,
Some(SimpleSelectorResult(s)) => { simple_selectors.push(s); empty = false },
Some(PseudoElementResult(p)) => { pseudo_element = Some(p); empty = false; break },
}
}
if empty { Err(()) } // An empty selector is invalid
else { Ok((simple_selectors, pseudo_element)) }
}
/// * `Err(())`: Invalid selector, abort /// * `Err(())`: Invalid selector, abort
/// * `Ok(None)`: Not a type selector, could be something else. `iter` was not consumed. /// * `Ok(None)`: Not a type selector, could be something else. `iter` was not consumed.
@ -310,67 +223,6 @@ enum SimpleSelectorParseResult {
PseudoElementResult(PseudoElement), PseudoElementResult(PseudoElement),
} }
/// Parse a simple selector other than a type selector.
///
/// * `Err(())`: Invalid selector, abort
/// * `Ok(None)`: Not a simple selector, could be something else. `iter` was not consumed.
/// * `Ok(Some(_))`: Parsed a simple selector or pseudo-element
fn parse_one_simple_selector<I: Iterator<ComponentValue>>(
iter: &mut Iter<I>, namespaces: &NamespaceMap, inside_negation: bool)
-> Result<Option<SimpleSelectorParseResult>, ()> {
match iter.peek() {
Some(&IDHash(_)) => match iter.next() {
Some(IDHash(id)) => Ok(Some(SimpleSelectorResult(
IDSelector(Atom::from_slice(id.as_slice()))))),
_ => panic!("Implementation error, this should not happen."),
},
Some(&Delim('.')) => {
iter.next();
match iter.next() {
Some(Ident(class)) => Ok(Some(SimpleSelectorResult(
ClassSelector(Atom::from_slice(class.as_slice()))))),
_ => Err(()),
}
}
Some(&SquareBracketBlock(_)) => match iter.next() {
Some(SquareBracketBlock(content))
=> Ok(Some(SimpleSelectorResult(try!(parse_attribute_selector(content, namespaces))))),
_ => panic!("Implementation error, this should not happen."),
},
Some(&Colon) => {
iter.next();
match iter.next() {
Some(Ident(name)) => match parse_simple_pseudo_class(name.as_slice()) {
Err(()) => {
match name.as_slice().to_ascii_lower().as_slice() {
// Supported CSS 2.1 pseudo-elements only.
// ** Do not add to this list! **
"before" => Ok(Some(PseudoElementResult(Before))),
"after" => Ok(Some(PseudoElementResult(After))),
// "first-line" => PseudoElementResult(FirstLine),
// "first-letter" => PseudoElementResult(FirstLetter),
_ => Err(())
}
},
Ok(result) => Ok(Some(SimpleSelectorResult(result))),
},
Some(Function(name, arguments))
=> Ok(Some(SimpleSelectorResult(try!(parse_functional_pseudo_class(
name, arguments, namespaces, inside_negation))))),
Some(Colon) => {
match iter.next() {
Some(Ident(name))
=> Ok(Some(PseudoElementResult(try!(parse_pseudo_element(name))))),
_ => Err(()),
}
}
_ => Err(()),
}
}
_ => Ok(None),
}
}
/// * `Err(())`: Invalid selector, abort /// * `Err(())`: Invalid selector, abort
/// * `Ok(None)`: Not a simple selector, could be something else. `iter` was not consumed. /// * `Ok(None)`: Not a simple selector, could be something else. `iter` was not consumed.
@ -490,8 +342,224 @@ fn parse_attribute_flags<I: Iterator<ComponentValue>>(iter: &mut Iter<I>)
} }
} }
pub fn parse_selector_list_from_str(context: &ParserContext, input: &str)
-> Result<SelectorList,()> {
let namespaces = NamespaceMap::new();
let iter = tokenize(input).map(|(token, _)| token);
parse_selector_list(context, iter, &namespaces).map(|s| SelectorList { selectors: s })
}
fn parse_simple_pseudo_class(name: &str) -> Result<SimpleSelector, ()> { /// Parse a comma-separated list of Selectors.
/// aka Selector Group in http://www.w3.org/TR/css3-selectors/#grouping
///
/// Return the Selectors or None if there is an invalid selector.
pub fn parse_selector_list<I>(context: &ParserContext, iter: I, namespaces: &NamespaceMap)
-> Result<Vec<Selector>,()>
where I: Iterator<ComponentValue> {
let iter = &mut iter.peekable();
let mut results = vec![try!(parse_selector(context, iter, namespaces))];
loop {
skip_whitespace(iter);
match iter.peek() {
None => break, // EOF
Some(&Comma) => {
iter.next();
}
_ => return Err(()),
}
results.push(try!(parse_selector(context, iter, namespaces)));
}
Ok(results)
}
/// Build up a Selector.
/// selector : simple_selector_sequence [ combinator simple_selector_sequence ]* ;
///
/// `Err` means invalid selector.
fn parse_selector<I>(context: &ParserContext, iter: &mut Iter<I>, namespaces: &NamespaceMap)
-> Result<Selector,()>
where I: Iterator<ComponentValue> {
let (first, mut pseudo_element) = try!(parse_simple_selectors(context, iter, namespaces));
let mut compound = CompoundSelector{ simple_selectors: first, next: None };
while pseudo_element.is_none() {
let any_whitespace = skip_whitespace(iter);
let combinator = match iter.peek() {
None => break, // EOF
Some(&Comma) => break,
Some(&Delim('>')) => { iter.next(); Child },
Some(&Delim('+')) => { iter.next(); NextSibling },
Some(&Delim('~')) => { iter.next(); LaterSibling },
Some(_) => {
if any_whitespace { Descendant }
else { return Err(()) }
}
};
let (simple_selectors, pseudo) = try!(parse_simple_selectors(context, iter, namespaces));
compound = CompoundSelector {
simple_selectors: simple_selectors,
next: Some((box compound, combinator))
};
pseudo_element = pseudo;
}
Ok(Selector {
specificity: compute_specificity(&compound, &pseudo_element),
compound_selectors: Arc::new(compound),
pseudo_element: pseudo_element,
})
}
/// Level 3: Parse **one** simple_selector
fn parse_negation(context: &ParserContext,
arguments: Vec<ComponentValue>,
namespaces: &NamespaceMap)
-> Result<SimpleSelector,()> {
let iter = &mut arguments.into_iter().peekable();
match try!(parse_type_selector(iter, namespaces)) {
Some(type_selector) => Ok(Negation(type_selector)),
None => {
match try!(parse_one_simple_selector(context,
iter,
namespaces,
/* inside_negation = */ true)) {
Some(SimpleSelectorResult(simple_selector)) => {
Ok(Negation(vec![simple_selector]))
}
_ => Err(())
}
},
}
}
/// simple_selector_sequence
/// : [ type_selector | universal ] [ HASH | class | attrib | pseudo | negation ]*
/// | [ HASH | class | attrib | pseudo | negation ]+
///
/// `Err(())` means invalid selector
fn parse_simple_selectors<I>(context: &ParserContext,
iter: &mut Iter<I>,
namespaces: &NamespaceMap)
-> Result<(Vec<SimpleSelector>, Option<PseudoElement>),()>
where I: Iterator<ComponentValue> {
let mut empty = true;
let mut simple_selectors = match try!(parse_type_selector(iter, namespaces)) {
None => vec![],
Some(s) => { empty = false; s }
};
let mut pseudo_element = None;
loop {
match try!(parse_one_simple_selector(context,
iter,
namespaces,
/* inside_negation = */ false)) {
None => break,
Some(SimpleSelectorResult(s)) => { simple_selectors.push(s); empty = false },
Some(PseudoElementResult(p)) => { pseudo_element = Some(p); empty = false; break },
}
}
if empty {
// An empty selector is invalid.
Err(())
} else {
Ok((simple_selectors, pseudo_element))
}
}
fn parse_functional_pseudo_class(context: &ParserContext,
name: String,
arguments: Vec<ComponentValue>,
namespaces: &NamespaceMap,
inside_negation: bool)
-> Result<SimpleSelector,()> {
match name.as_slice().to_ascii_lower().as_slice() {
// "lang" => parse_lang(arguments),
"nth-child" => parse_nth(arguments.as_slice()).map(|(a, b)| NthChild(a, b)),
"nth-last-child" => parse_nth(arguments.as_slice()).map(|(a, b)| NthLastChild(a, b)),
"nth-of-type" => parse_nth(arguments.as_slice()).map(|(a, b)| NthOfType(a, b)),
"nth-last-of-type" => parse_nth(arguments.as_slice()).map(|(a, b)| NthLastOfType(a, b)),
"not" => {
if inside_negation {
Err(())
} else {
parse_negation(context, arguments, namespaces)
}
}
_ => Err(())
}
}
/// Parse a simple selector other than a type selector.
///
/// * `Err(())`: Invalid selector, abort
/// * `Ok(None)`: Not a simple selector, could be something else. `iter` was not consumed.
/// * `Ok(Some(_))`: Parsed a simple selector or pseudo-element
fn parse_one_simple_selector<I>(context: &ParserContext,
iter: &mut Iter<I>,
namespaces: &NamespaceMap,
inside_negation: bool)
-> Result<Option<SimpleSelectorParseResult>,()>
where I: Iterator<ComponentValue> {
match iter.peek() {
Some(&IDHash(_)) => match iter.next() {
Some(IDHash(id)) => Ok(Some(SimpleSelectorResult(
IDSelector(Atom::from_slice(id.as_slice()))))),
_ => panic!("Implementation error, this should not happen."),
},
Some(&Delim('.')) => {
iter.next();
match iter.next() {
Some(Ident(class)) => Ok(Some(SimpleSelectorResult(
ClassSelector(Atom::from_slice(class.as_slice()))))),
_ => Err(()),
}
}
Some(&SquareBracketBlock(_)) => match iter.next() {
Some(SquareBracketBlock(content))
=> Ok(Some(SimpleSelectorResult(try!(parse_attribute_selector(content, namespaces))))),
_ => panic!("Implementation error, this should not happen."),
},
Some(&Colon) => {
iter.next();
match iter.next() {
Some(Ident(name)) => match parse_simple_pseudo_class(context, name.as_slice()) {
Err(()) => {
match name.as_slice().to_ascii_lower().as_slice() {
// Supported CSS 2.1 pseudo-elements only.
// ** Do not add to this list! **
"before" => Ok(Some(PseudoElementResult(Before))),
"after" => Ok(Some(PseudoElementResult(After))),
// "first-line" => PseudoElementResult(FirstLine),
// "first-letter" => PseudoElementResult(FirstLetter),
_ => Err(())
}
},
Ok(result) => Ok(Some(SimpleSelectorResult(result))),
},
Some(Function(name, arguments))
=> {
Ok(Some(SimpleSelectorResult(try!(parse_functional_pseudo_class(
context,
name,
arguments,
namespaces,
inside_negation)))))
}
Some(Colon) => {
match iter.next() {
Some(Ident(name))
=> Ok(Some(PseudoElementResult(try!(parse_pseudo_element(name))))),
_ => Err(()),
}
}
_ => Err(()),
}
}
_ => Ok(None),
}
}
fn parse_simple_pseudo_class(context: &ParserContext, name: &str) -> Result<SimpleSelector,()> {
match name.to_ascii_lower().as_slice() { match name.to_ascii_lower().as_slice() {
"any-link" => Ok(AnyLink), "any-link" => Ok(AnyLink),
"link" => Ok(Link), "link" => Ok(Link),
@ -507,31 +575,12 @@ fn parse_simple_pseudo_class(name: &str) -> Result<SimpleSelector, ()> {
"first-of-type" => Ok(FirstOfType), "first-of-type" => Ok(FirstOfType),
"last-of-type" => Ok(LastOfType), "last-of-type" => Ok(LastOfType),
"only-of-type" => Ok(OnlyOfType), "only-of-type" => Ok(OnlyOfType),
"-servo-nonzero-border" => { "-servo-nonzero-border" if context.origin == UserAgentOrigin => Ok(ServoNonzeroBorder),
// TODO(pcwalton): Have some mechanism whereby we forbid Web content from using this.
Ok(ServoNonzeroBorder)
}
// "empty" => Ok(Empty), // "empty" => Ok(Empty),
_ => Err(()) _ => Err(())
} }
} }
fn parse_functional_pseudo_class(name: String, arguments: Vec<ComponentValue>,
namespaces: &NamespaceMap, inside_negation: bool)
-> Result<SimpleSelector, ()> {
match name.as_slice().to_ascii_lower().as_slice() {
// "lang" => parse_lang(arguments),
"nth-child" => parse_nth(arguments.as_slice()).map(|(a, b)| NthChild(a, b)),
"nth-last-child" => parse_nth(arguments.as_slice()).map(|(a, b)| NthLastChild(a, b)),
"nth-of-type" => parse_nth(arguments.as_slice()).map(|(a, b)| NthOfType(a, b)),
"nth-last-of-type" => parse_nth(arguments.as_slice()).map(|(a, b)| NthLastOfType(a, b)),
"not" => if inside_negation { Err(()) } else { parse_negation(arguments, namespaces) },
_ => Err(())
}
}
fn parse_pseudo_element(name: String) -> Result<PseudoElement, ()> { fn parse_pseudo_element(name: String) -> Result<PseudoElement, ()> {
match name.as_slice().to_ascii_lower().as_slice() { match name.as_slice().to_ascii_lower().as_slice() {
// All supported pseudo-elements // All supported pseudo-elements
@ -556,21 +605,6 @@ fn parse_pseudo_element(name: String) -> Result<PseudoElement, ()> {
//} //}
/// Level 3: Parse **one** simple_selector
fn parse_negation(arguments: Vec<ComponentValue>, namespaces: &NamespaceMap)
-> Result<SimpleSelector, ()> {
let iter = &mut arguments.into_iter().peekable();
match try!(parse_type_selector(iter, namespaces)) {
Some(type_selector) => Ok(Negation(type_selector)),
None => {
match try!(parse_one_simple_selector(iter, namespaces, /* inside_negation = */ true)) {
Some(SimpleSelectorResult(simple_selector)) => Ok(Negation(vec![simple_selector])),
_ => Err(())
}
},
}
}
/// Assuming the next token is an ident, consume it and return its value /// Assuming the next token is an ident, consume it and return its value
#[inline] #[inline]
@ -598,6 +632,7 @@ mod tests {
use sync::Arc; use sync::Arc;
use cssparser; use cssparser;
use namespaces::NamespaceMap; use namespaces::NamespaceMap;
use selector_matching::AuthorOrigin;
use string_cache::Atom; use string_cache::Atom;
use super::*; use super::*;
@ -606,7 +641,10 @@ mod tests {
} }
fn parse_ns(input: &str, namespaces: &NamespaceMap) -> Result<Vec<Selector>, ()> { fn parse_ns(input: &str, namespaces: &NamespaceMap) -> Result<Vec<Selector>, ()> {
parse_selector_list(cssparser::tokenize(input).map(|(v, _)| v), namespaces) let context = ParserContext {
origin: AuthorOrigin,
};
parse_selector_list(&context, cssparser::tokenize(input).map(|(v, _)| v), namespaces)
} }
fn specificity(a: u32, b: u32, c: u32) -> u32 { fn specificity(a: u32, b: u32, c: u32) -> u32 {

View file

@ -10,12 +10,11 @@ use encoding::EncodingRef;
use cssparser::{decode_stylesheet_bytes, tokenize, parse_stylesheet_rules, ToCss}; use cssparser::{decode_stylesheet_bytes, tokenize, parse_stylesheet_rules, ToCss};
use cssparser::ast::*; use cssparser::ast::*;
use selectors; use selectors::{mod, ParserContext};
use properties; use properties;
use errors::{ErrorLoggerIterator, log_css_error}; use errors::{ErrorLoggerIterator, log_css_error};
use namespaces::{NamespaceMap, parse_namespace_rule}; use namespaces::{NamespaceMap, parse_namespace_rule};
use media_queries::{Device, MediaRule, parse_media_rule}; use media_queries::{mod, Device, MediaRule};
use media_queries;
use font_face::{FontFaceRule, Source, parse_font_face_rule, iter_font_face_rules_inner}; use font_face::{FontFaceRule, Source, parse_font_face_rule, iter_font_face_rules_inner};
use selector_matching::StylesheetOrigin; use selector_matching::StylesheetOrigin;
@ -53,9 +52,12 @@ impl Stylesheet {
Stylesheet::from_bytes(bytes.as_slice(), base_url, protocol_encoding_label, environment_encoding, origin) Stylesheet::from_bytes(bytes.as_slice(), base_url, protocol_encoding_label, environment_encoding, origin)
} }
pub fn from_bytes( pub fn from_bytes(bytes: &[u8],
bytes: &[u8], base_url: Url, protocol_encoding_label: Option<&str>, base_url: Url,
environment_encoding: Option<EncodingRef>, origin: StylesheetOrigin) -> Stylesheet { protocol_encoding_label: Option<&str>,
environment_encoding: Option<EncodingRef>,
origin: StylesheetOrigin)
-> Stylesheet {
// TODO: bytes.as_slice could be bytes.container_as_bytes() // TODO: bytes.as_slice could be bytes.container_as_bytes()
let (string, _) = decode_stylesheet_bytes( let (string, _) = decode_stylesheet_bytes(
bytes.as_slice(), protocol_encoding_label, environment_encoding); bytes.as_slice(), protocol_encoding_label, environment_encoding);
@ -67,6 +69,11 @@ impl Stylesheet {
static STATE_IMPORTS: uint = 2; static STATE_IMPORTS: uint = 2;
static STATE_NAMESPACES: uint = 3; static STATE_NAMESPACES: uint = 3;
static STATE_BODY: uint = 4; static STATE_BODY: uint = 4;
let parser_context = ParserContext {
origin: origin,
};
let mut state: uint = STATE_CHARSET; let mut state: uint = STATE_CHARSET;
let mut rules = vec!(); let mut rules = vec!();
@ -77,7 +84,7 @@ impl Stylesheet {
match rule { match rule {
QualifiedRule_(rule) => { QualifiedRule_(rule) => {
next_state = STATE_BODY; next_state = STATE_BODY;
parse_style_rule(rule, &mut rules, &namespaces, &base_url) parse_style_rule(&parser_context, rule, &mut rules, &namespaces, &base_url)
}, },
AtRule_(rule) => { AtRule_(rule) => {
let lower_name = rule.name.as_slice().to_ascii_lower(); let lower_name = rule.name.as_slice().to_ascii_lower();
@ -114,7 +121,12 @@ impl Stylesheet {
}, },
_ => { _ => {
next_state = STATE_BODY; next_state = STATE_BODY;
parse_nested_at_rule(lower_name.as_slice(), rule, &mut rules, &namespaces, &base_url) parse_nested_at_rule(&parser_context,
lower_name.as_slice(),
rule,
&mut rules,
&namespaces,
&base_url)
}, },
} }
}, },
@ -128,13 +140,36 @@ impl Stylesheet {
} }
} }
// lower_name is passed explicitly to avoid computing it twice.
pub fn parse_nested_at_rule(context: &ParserContext,
lower_name: &str,
rule: AtRule,
parent_rules: &mut Vec<CSSRule>,
namespaces: &NamespaceMap,
base_url: &Url) {
match lower_name {
"media" => {
media_queries::parse_media_rule(context, rule, parent_rules, namespaces, base_url)
}
"font-face" => parse_font_face_rule(rule, parent_rules, base_url),
_ => log_css_error(rule.location,
format!("Unsupported at-rule: @{:s}", lower_name).as_slice())
}
}
pub fn parse_style_rule(rule: QualifiedRule, parent_rules: &mut Vec<CSSRule>, pub fn parse_style_rule(context: &ParserContext,
namespaces: &NamespaceMap, base_url: &Url) { rule: QualifiedRule,
let QualifiedRule { location, prelude, block} = rule; parent_rules: &mut Vec<CSSRule>,
namespaces: &NamespaceMap,
base_url: &Url) {
let QualifiedRule {
location,
prelude,
block
} = rule;
// FIXME: avoid doing this for valid selectors // FIXME: avoid doing this for valid selectors
let serialized = prelude.iter().to_css(); let serialized = prelude.iter().to_css();
match selectors::parse_selector_list(prelude.into_iter(), namespaces) { match selectors::parse_selector_list(context, prelude.into_iter(), namespaces) {
Ok(selectors) => parent_rules.push(CSSStyleRule(StyleRule{ Ok(selectors) => parent_rules.push(CSSStyleRule(StyleRule{
selectors: selectors, selectors: selectors,
declarations: properties::parse_property_declaration_list(block.into_iter(), base_url) declarations: properties::parse_property_declaration_list(block.into_iter(), base_url)
@ -144,19 +179,6 @@ pub fn parse_style_rule(rule: QualifiedRule, parent_rules: &mut Vec<CSSRule>,
} }
} }
// lower_name is passed explicitly to avoid computing it twice.
pub fn parse_nested_at_rule(lower_name: &str, rule: AtRule,
parent_rules: &mut Vec<CSSRule>, namespaces: &NamespaceMap, base_url: &Url) {
match lower_name {
"media" => parse_media_rule(rule, parent_rules, namespaces, base_url),
"font-face" => parse_font_face_rule(rule, parent_rules, base_url),
_ => log_css_error(rule.location,
format!("Unsupported at-rule: @{:s}", lower_name).as_slice())
}
}
pub fn iter_style_rules<'a>(rules: &[CSSRule], device: &media_queries::Device, pub fn iter_style_rules<'a>(rules: &[CSSRule], device: &media_queries::Device,
callback: |&StyleRule|) { callback: |&StyleRule|) {
for rule in rules.iter() { for rule in rules.iter() {

View file

@ -4,7 +4,7 @@
use geometry::Au; use geometry::Au;
use cssparser::{mod, RGBAColor}; use cssparser::{mod, RGBA, RGBAColor};
use std::ascii::AsciiExt; use std::ascii::AsciiExt;
use std::from_str::FromStr; use std::from_str::FromStr;
use std::iter::Filter; use std::iter::Filter;
@ -186,19 +186,8 @@ pub fn parse_length(mut value: &str) -> LengthOrPercentageOrAuto {
} }
} }
/// A "simple color" per HTML5 § 2.4.6.
#[deriving(Show)]
pub struct SimpleColor {
/// The red component of the color, [0, 255].
pub red: u8,
/// The green component of the color, [0, 255].
pub green: u8,
/// The blue component of the color, [0, 255].
pub blue: u8,
}
/// Parses a legacy color per HTML5 § 2.4.6. If unparseable, `Err` is returned. /// Parses a legacy color per HTML5 § 2.4.6. If unparseable, `Err` is returned.
pub fn parse_legacy_color(mut input: &str) -> Result<SimpleColor,()> { pub fn parse_legacy_color(mut input: &str) -> Result<RGBA,()> {
// Steps 1 and 2. // Steps 1 and 2.
if input.len() == 0 { if input.len() == 0 {
return Err(()) return Err(())
@ -214,27 +203,22 @@ pub fn parse_legacy_color(mut input: &str) -> Result<SimpleColor,()> {
// Step 5. // Step 5.
match cssparser::parse_color_keyword(input) { match cssparser::parse_color_keyword(input) {
Ok(RGBAColor(rgba)) => { Ok(RGBAColor(rgba)) => return Ok(rgba),
return Ok(SimpleColor {
red: (rgba.red * 255.0) as u8,
green: (rgba.green * 255.0) as u8,
blue: (rgba.blue * 255.0) as u8,
})
}
_ => {} _ => {}
} }
// Step 6. // Step 6.
if input.len() == 4 { if input.len() == 4 {
match (input.char_at(0), match (input.as_bytes()[0],
hex(input.char_at(1)), hex(input.as_bytes()[1] as char),
hex(input.char_at(2)), hex(input.as_bytes()[2] as char),
hex(input.char_at(3))) { hex(input.as_bytes()[3] as char)) {
('#', Ok(r), Ok(g), Ok(b)) => { (b'#', Ok(r), Ok(g), Ok(b)) => {
return Ok(SimpleColor { return Ok(RGBA {
red: r * 17, red: (r as f32) * 17.0 / 255.0,
green: g * 17, green: (g as f32) * 17.0 / 255.0,
blue: b * 17, blue: (b as f32) * 17.0 / 255.0,
alpha: 1.0,
}) })
} }
_ => {} _ => {}
@ -253,8 +237,11 @@ pub fn parse_legacy_color(mut input: &str) -> Result<SimpleColor,()> {
let mut input = new_input.as_slice(); let mut input = new_input.as_slice();
// Step 8. // Step 8.
if input.len() > 128 { for (char_count, (index, _)) in input.char_indices().enumerate() {
input = input.slice_to(128) if char_count == 128 {
input = input.slice_to(index);
break
}
} }
// Step 9. // Step 9.
@ -301,10 +288,11 @@ pub fn parse_legacy_color(mut input: &str) -> Result<SimpleColor,()> {
} }
// Steps 15-20. // Steps 15-20.
return Ok(SimpleColor { return Ok(RGBA {
red: (hex(red[0] as char).unwrap() << 4) | hex(red[1] as char).unwrap(), red: hex_string(red).unwrap() as f32 / 255.0,
green: (hex(green[0] as char).unwrap() << 4) | hex(green[1] as char).unwrap(), green: hex_string(green).unwrap() as f32 / 255.0,
blue: (hex(blue[0] as char).unwrap() << 4) | hex(blue[1] as char).unwrap(), blue: hex_string(blue).unwrap() as f32 / 255.0,
alpha: 1.0,
}); });
fn hex(ch: char) -> Result<u8,()> { fn hex(ch: char) -> Result<u8,()> {
@ -315,6 +303,18 @@ pub fn parse_legacy_color(mut input: &str) -> Result<SimpleColor,()> {
_ => Err(()), _ => Err(()),
} }
} }
fn hex_string(string: &[u8]) -> Result<u8,()> {
match string.len() {
0 => Err(()),
1 => hex(string[0] as char),
_ => {
let upper = try!(hex(string[0] as char));
let lower = try!(hex(string[1] as char));
Ok((upper << 4) | lower)
}
}
}
} }

2
ports/cef/Cargo.lock generated
View file

@ -380,6 +380,7 @@ dependencies = [
name = "layout" name = "layout"
version = "0.0.1" version = "0.0.1"
dependencies = [ dependencies = [
"cssparser 0.1.0 (git+https://github.com/servo/rust-cssparser)",
"encoding 0.2.0 (git+https://github.com/lifthrasiir/rust-encoding)", "encoding 0.2.0 (git+https://github.com/lifthrasiir/rust-encoding)",
"geom 0.1.0 (git+https://github.com/servo/rust-geom)", "geom 0.1.0 (git+https://github.com/servo/rust-geom)",
"gfx 0.0.1", "gfx 0.0.1",
@ -657,6 +658,7 @@ dependencies = [
name = "util" name = "util"
version = "0.0.1" version = "0.0.1"
dependencies = [ dependencies = [
"cssparser 0.1.0 (git+https://github.com/servo/rust-cssparser)",
"geom 0.1.0 (git+https://github.com/servo/rust-geom)", "geom 0.1.0 (git+https://github.com/servo/rust-geom)",
"layers 0.1.0 (git+https://github.com/servo/rust-layers)", "layers 0.1.0 (git+https://github.com/servo/rust-layers)",
"string_cache 0.0.0 (git+https://github.com/servo/string-cache)", "string_cache 0.0.0 (git+https://github.com/servo/string-cache)",

View file

@ -210,5 +210,6 @@ fragment=top != ../html/acid2.html acid2_ref.html
!= list_style_type_a.html list_style_type_ref.html != list_style_type_a.html list_style_type_ref.html
== list_style_position_a.html list_style_position_ref.html == list_style_position_a.html list_style_position_ref.html
== table_colspan_simple_a.html table_colspan_simple_ref.html == table_colspan_simple_a.html table_colspan_simple_ref.html
== table_colspan_fixed_a.html table_colspan_fixed_ref.html
== legacy_td_bgcolor_attribute_a.html legacy_td_bgcolor_attribute_ref.html == legacy_td_bgcolor_attribute_a.html legacy_td_bgcolor_attribute_ref.html
== legacy_table_border_attribute_a.html legacy_table_border_attribute_ref.html == legacy_table_border_attribute_a.html legacy_table_border_attribute_ref.html

View file

@ -1,9 +1,16 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head>
<style>
table {
border-color: red;
}
</style>
</head>
<body> <body>
<table border=10 style="border-style: solid; border-color: black"><tr><td style="border: none">:-)</td></tr></table> <table border=10><tr><td style="border: none">:-)</td></tr></table>
<table border=mimi style="border-style: solid; border-color: black"><tr><td style="border: none">:-)</td></tr></table> <table border=mimi><tr><td style="border: none">:-)</td></tr></table>
<table border=0 style="border-style: solid; border-color: black"><tr><td style="border: none">:-)</td></tr></table> <table border=0><tr><td style="border: none">:-)</td></tr></table>
</body> </body>
</html> </html>

View file

@ -1,8 +1,9 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head>
<body> <body>
<table style="border: solid black 10px"><tr><td>:-)</td></tr></table> <table style="border: outset red 10px"><tr><td>:-)</td></tr></table>
<table style="border: solid black 1px"><tr><td>:-)</td></tr></table> <table style="border: outset red 1px"><tr><td>:-)</td></tr></table>
<table style="border: none"><tr><td>:-)</td></tr></table> <table style="border: none"><tr><td>:-)</td></tr></table>
</body> </body>
</html> </html>

View file

@ -0,0 +1,26 @@
<!DOCTYPE html>
<html>
<style>
table {
width: 300px;
table-layout: fixed;
}
td[colspan="2"] {
background-color: blue;
color: white;
}
td[colspan="3"] {
background-color: green;
color: white;
}
</style>
<body>
<table border=0 cellspacing=0 cellpadding=0>
<tr><td width=100>&nbsp;</td><td width=100>&nbsp;</td><td width=100>&nbsp;</td></tr>
<tr><td colspan=2>&nbsp;</td><td>&nbsp;</td></tr>
<tr><td>&nbsp;</td><td colspan=2>&nbsp;</td></tr>
<tr><td colspan=3>&nbsp;</td></tr>
</table>
</body>
</html>

View file

@ -0,0 +1,26 @@
<!DOCTYPE html>
<html>
<style>
table {
table-layout: fixed;
width: 300px;
}
td.two {
background-color: blue;
color: white;
}
td.three {
background-color: green;
color: white;
}
</style>
<body>
<table border=0 cellspacing=0 cellpadding=0>
<tr><td width=100>&nbsp;</td><td width=100>&nbsp;</td><td width=100>&nbsp;</td></tr>
<tr><td class=two>&nbsp;</td><td class=two></td><td>&nbsp;</td></tr>
<tr><td>&nbsp;<td class=two>&nbsp;</td><td class=two></td></tr>
<tr><td class=three>&nbsp;</td><td class=three></td><td class=three></td></tr>
</table>
</body>
</html>