aboutsummaryrefslogtreecommitdiffstats
path: root/components
diff options
context:
space:
mode:
authorbors-servo <metajack+bors@gmail.com>2014-12-15 19:33:46 -0700
committerbors-servo <metajack+bors@gmail.com>2014-12-15 19:33:46 -0700
commit8e31e5f98747e4b42dafcc4b076fac46aeb09310 (patch)
treee7797b853ac10f987b3623fd184bb515cc6bdadd /components
parent88ec52dd617bafe384553ef38325c552fb4f1e23 (diff)
parenta1ea44b294e73f4397887fe806fc5bc95499efda (diff)
downloadservo-8e31e5f98747e4b42dafcc4b076fac46aeb09310.tar.gz
servo-8e31e5f98747e4b42dafcc4b076fac46aeb09310.zip
auto merge of #4289 : pcwalton/servo/hacker-news, r=SimonSapin
This patch provides some of the groundwork for column spans greater than 1. It implements the column-span CSS property as well as the corresponding colspan attribute; although the former is not well-specified outside of CSS multi-column layout, INTRINSIC refers to it. Although width is distributed to spanning columns, they do not yet contribute minimum and preferred widths; this will be implemented in a follow-up. The parsing for the legacy bgcolor and border attributes is implemented according to the WHATWG HTML specification. Additionally, this patch cleans up some miscellaneous formatting issues, refactors layout/css somewhat to eliminate needless levels of indirection, and cleans up the handling of table rowgroups. New Hacker News screenshot: http://i.imgur.com/hnl2a7E.png
Diffstat (limited to 'components')
-rw-r--r--components/layout/Cargo.toml3
-rw-r--r--components/layout/css/matching.rs9
-rw-r--r--components/layout/css/node_style.rs58
-rw-r--r--components/layout/css/node_util.rs75
-rw-r--r--components/layout/layout_task.rs12
-rw-r--r--components/layout/lib.rs3
-rw-r--r--components/layout/table.rs170
-rw-r--r--components/layout/table_cell.rs8
-rw-r--r--components/layout/table_row.rs78
-rw-r--r--components/layout/table_rowgroup.rs53
-rw-r--r--components/layout/table_wrapper.rs2
-rw-r--r--components/layout/wrapper.rs43
-rw-r--r--components/script/dom/bindings/trace.rs4
-rw-r--r--components/script/dom/document.rs10
-rw-r--r--components/script/dom/element.rs113
-rw-r--r--components/script/dom/htmlbodyelement.rs54
-rw-r--r--components/script/dom/htmltablecellelement.rs37
-rw-r--r--components/script/dom/htmltableelement.rs92
-rw-r--r--components/script/dom/htmltablerowelement.rs63
-rw-r--r--components/script/dom/htmltablesectionelement.rs66
-rw-r--r--components/script/dom/node.rs18
-rw-r--r--components/script/dom/virtualmethods.rs27
-rw-r--r--components/script/layout_interface.rs3
-rw-r--r--components/servo/Cargo.lock4
-rw-r--r--components/style/legacy.rs195
-rw-r--r--components/style/lib.rs12
-rw-r--r--components/style/media_queries.rs25
-rw-r--r--components/style/node.rs6
-rw-r--r--components/style/selector_matching.rs109
-rw-r--r--components/style/selectors.rs423
-rw-r--r--components/style/stylesheets.rs72
-rw-r--r--components/util/Cargo.toml3
-rw-r--r--components/util/lib.rs1
-rw-r--r--components/util/str.rs137
34 files changed, 1429 insertions, 559 deletions
diff --git a/components/layout/Cargo.toml b/components/layout/Cargo.toml
index edd6ed0ad1b..ca77107965b 100644
--- a/components/layout/Cargo.toml
+++ b/components/layout/Cargo.toml
@@ -31,6 +31,9 @@ path = "../net"
[dependencies.util]
path = "../util"
+[dependencies.cssparser]
+git = "https://github.com/servo/rust-cssparser"
+
[dependencies.encoding]
git = "https://github.com/lifthrasiir/rust-encoding"
diff --git a/components/layout/css/matching.rs b/components/layout/css/matching.rs
index f1615aa005d..86968a6d9c2 100644
--- a/components/layout/css/matching.rs
+++ b/components/layout/css/matching.rs
@@ -268,6 +268,9 @@ impl StyleSharingCandidate {
return false
}
+ // FIXME(pcwalton): It's probably faster to iterate over all the element's attributes and
+ // use the {common, rare}-style-affecting-attributes tables as lookup tables.
+
for attribute_info in style::common_style_affecting_attributes().iter() {
match attribute_info.mode {
AttrIsPresentMode(flag) => {
@@ -295,6 +298,12 @@ impl StyleSharingCandidate {
}
}
+ for attribute_name in style::rare_style_affecting_attributes().iter() {
+ if element.get_attr(&ns!(""), attribute_name).is_some() {
+ return false
+ }
+ }
+
if element.get_link().is_some() != self.link {
return false
}
diff --git a/components/layout/css/node_style.rs b/components/layout/css/node_style.rs
index ecfb3383453..c3eaa04ddd2 100644
--- a/components/layout/css/node_style.rs
+++ b/components/layout/css/node_style.rs
@@ -2,27 +2,75 @@
* 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/. */
-// Style retrieval from DOM elements.
+//! Style retrieval from DOM elements.
-use css::node_util::NodeUtil;
-use wrapper::ThreadSafeLayoutNode;
+use wrapper::{After, Before, Normal, ThreadSafeLayoutNode};
+use std::mem;
use style::ComputedValues;
use sync::Arc;
/// Node mixin providing `style` method that returns a `NodeStyle`
pub trait StyledNode {
+ /// Returns the style results for the given node. If CSS selector matching has not yet been
+ /// performed, fails.
fn style<'a>(&'a self) -> &'a Arc<ComputedValues>;
+ /// Does this node have a computed style yet?
+ fn has_style(&self) -> bool;
+ /// Removes the style from this node.
fn unstyle(self);
}
impl<'ln> StyledNode for ThreadSafeLayoutNode<'ln> {
#[inline]
fn style<'a>(&'a self) -> &'a Arc<ComputedValues> {
- self.get_css_select_results()
+ unsafe {
+ let layout_data_ref = self.borrow_layout_data();
+ match self.get_pseudo_element_type() {
+ Before(_) => {
+ mem::transmute(layout_data_ref.as_ref()
+ .unwrap()
+ .data
+ .before_style
+ .as_ref()
+ .unwrap())
+ }
+ After(_) => {
+ mem::transmute(layout_data_ref.as_ref()
+ .unwrap()
+ .data
+ .after_style
+ .as_ref()
+ .unwrap())
+ }
+ Normal => {
+ mem::transmute(layout_data_ref.as_ref()
+ .unwrap()
+ .shared_data
+ .style
+ .as_ref()
+ .unwrap())
+ }
+ }
+ }
+ }
+
+ fn has_style(&self) -> bool {
+ let layout_data_ref = self.borrow_layout_data();
+ layout_data_ref.as_ref().unwrap().shared_data.style.is_some()
}
fn unstyle(self) {
- self.remove_css_select_results()
+ let mut layout_data_ref = self.mutate_layout_data();
+ let layout_data = layout_data_ref.as_mut().expect("no layout data");
+
+ let style =
+ match self.get_pseudo_element_type() {
+ Before(_) => &mut layout_data.data.before_style,
+ After (_) => &mut layout_data.data.after_style,
+ Normal => &mut layout_data.shared_data.style,
+ };
+
+ *style = None;
}
}
diff --git a/components/layout/css/node_util.rs b/components/layout/css/node_util.rs
deleted file mode 100644
index eb447b0cf6b..00000000000
--- a/components/layout/css/node_util.rs
+++ /dev/null
@@ -1,75 +0,0 @@
-/* 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/. */
-
-use util::LayoutDataAccess;
-use wrapper::ThreadSafeLayoutNode;
-use wrapper::{After, Before, Normal};
-
-use std::mem;
-use style::ComputedValues;
-use sync::Arc;
-
-pub trait NodeUtil {
- fn get_css_select_results<'a>(&'a self) -> &'a Arc<ComputedValues>;
- fn have_css_select_results(&self) -> bool;
- fn remove_css_select_results(self);
-}
-
-impl<'ln> NodeUtil for ThreadSafeLayoutNode<'ln> {
- /// Returns the style results for the given node. If CSS selector
- /// matching has not yet been performed, fails.
- #[inline]
- fn get_css_select_results<'a>(&'a self) -> &'a Arc<ComputedValues> {
- unsafe {
- let layout_data_ref = self.borrow_layout_data();
- match self.get_pseudo_element_type() {
- Before(_) => {
- mem::transmute(layout_data_ref.as_ref()
- .unwrap()
- .data
- .before_style
- .as_ref()
- .unwrap())
- }
- After(_) => {
- mem::transmute(layout_data_ref.as_ref()
- .unwrap()
- .data
- .after_style
- .as_ref()
- .unwrap())
- }
- Normal => {
- mem::transmute(layout_data_ref.as_ref()
- .unwrap()
- .shared_data
- .style
- .as_ref()
- .unwrap())
- }
- }
- }
- }
-
- /// Does this node have a computed style yet?
- fn have_css_select_results(&self) -> bool {
- let layout_data_ref = self.borrow_layout_data();
- layout_data_ref.as_ref().unwrap().shared_data.style.is_some()
- }
-
- fn remove_css_select_results(self) {
- let mut layout_data_ref = self.mutate_layout_data();
- let layout_data = layout_data_ref.as_mut().expect("no layout data");
-
- let style =
- match self.get_pseudo_element_type() {
- Before(_) => &mut layout_data.data.before_style,
- After (_) => &mut layout_data.data.after_style,
- Normal => &mut layout_data.shared_data.style,
- };
-
- *style = None;
- }
-}
-
diff --git a/components/layout/layout_task.rs b/components/layout/layout_task.rs
index deda7d481c7..d187aa1a37f 100644
--- a/components/layout/layout_task.rs
+++ b/components/layout/layout_task.rs
@@ -40,7 +40,7 @@ use script::layout_interface::{ContentBoxesQuery, ContentBoxQuery, ExitNowMsg, G
use script::layout_interface::{HitTestResponse, LayoutChan, LayoutRPC, LoadStylesheetMsg};
use script::layout_interface::{MouseOverResponse, Msg, NoQuery, PrepareToExitMsg};
use script::layout_interface::{ReapLayoutDataMsg, Reflow, ReflowForDisplay, ReflowMsg};
-use script::layout_interface::{ScriptLayoutChan, TrustedNodeAddress};
+use script::layout_interface::{ScriptLayoutChan, SetQuirksModeMsg, TrustedNodeAddress};
use script_traits::{SendEventMsg, ReflowEvent, ReflowCompleteMsg, OpaqueScriptLayoutChannel};
use script_traits::{ScriptControlChan, UntrustedNodeAddress};
use servo_msg::compositor_msg::Scrollable;
@@ -390,6 +390,7 @@ impl LayoutTask {
match request {
AddStylesheetMsg(sheet) => self.handle_add_stylesheet(sheet, possibly_locked_rw_data),
LoadStylesheetMsg(url) => self.handle_load_stylesheet(url, possibly_locked_rw_data),
+ SetQuirksModeMsg => self.handle_set_quirks_mode(possibly_locked_rw_data),
GetRPCMsg(response_chan) => {
response_chan.send(box LayoutRPCImpl(self.rw_data.clone()) as
Box<LayoutRPC + Send>);
@@ -500,6 +501,15 @@ impl LayoutTask {
LayoutTask::return_rw_data(possibly_locked_rw_data, rw_data);
}
+ /// Sets quirks mode for the document, causing the quirks mode stylesheet to be loaded.
+ fn handle_set_quirks_mode<'a>(&'a self,
+ possibly_locked_rw_data:
+ &mut Option<MutexGuard<'a, LayoutTaskData>>) {
+ let mut rw_data = self.lock_rw_data(possibly_locked_rw_data);
+ rw_data.stylist.add_quirks_mode_stylesheet();
+ LayoutTask::return_rw_data(possibly_locked_rw_data, rw_data);
+ }
+
/// Retrieves the flow tree root from the root node.
fn try_get_layout_root(&self, node: LayoutNode) -> Option<FlowRef> {
let mut layout_data_ref = node.mutate_layout_data();
diff --git a/components/layout/lib.rs b/components/layout/lib.rs
index 1fa21311373..26054f0dae0 100644
--- a/components/layout/lib.rs
+++ b/components/layout/lib.rs
@@ -14,6 +14,7 @@
#[phase(plugin, link)]
extern crate log;
+extern crate cssparser;
extern crate geom;
extern crate gfx;
extern crate layout_traits;
@@ -70,8 +71,6 @@ pub mod incremental;
pub mod wrapper;
pub mod css {
- mod node_util;
-
pub mod matching;
pub mod node_style;
}
diff --git a/components/layout/table.rs b/components/layout/table.rs
index 37f589917a2..0e2af0b2f68 100644
--- a/components/layout/table.rs
+++ b/components/layout/table.rs
@@ -11,11 +11,12 @@ use block::{ISizeConstraintInput, ISizeConstraintSolution};
use construct::FlowConstructor;
use context::LayoutContext;
use floats::FloatKind;
-use flow::{Flow, FlowClass, IMPACTED_BY_LEFT_FLOATS, IMPACTED_BY_RIGHT_FLOATS, ImmutableFlowUtils};
-use flow::{TableFlowClass};
+use flow::{mod, Flow, FlowClass, IMPACTED_BY_LEFT_FLOATS, IMPACTED_BY_RIGHT_FLOATS};
+use flow::{ImmutableFlowUtils, TableFlowClass};
use fragment::{Fragment, FragmentBoundsIterator};
use layout_debug;
use model::{IntrinsicISizes, IntrinsicISizesContribution};
+use table_row::CellIntrinsicInlineSize;
use table_wrapper::{TableLayout, FixedLayout, AutoLayout};
use wrapper::ThreadSafeLayoutNode;
@@ -105,24 +106,53 @@ impl TableFlow {
/// 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
/// sizes.
- pub fn update_column_inline_sizes(parent_inline_sizes: &mut Vec<ColumnIntrinsicInlineSize>,
- child_inline_sizes: &Vec<ColumnIntrinsicInlineSize>)
- -> IntrinsicISizes {
+ fn update_automatic_column_inline_sizes(
+ parent_inline_sizes: &mut Vec<ColumnIntrinsicInlineSize>,
+ child_cell_inline_sizes: &[CellIntrinsicInlineSize])
+ -> IntrinsicISizes {
let mut total_inline_sizes = IntrinsicISizes::new();
- for (parent_sizes, child_sizes) in parent_inline_sizes.iter_mut()
- .zip(child_inline_sizes.iter()) {
- *parent_sizes = ColumnIntrinsicInlineSize {
- minimum_length: max(parent_sizes.minimum_length, child_sizes.minimum_length),
- percentage: parent_sizes.greatest_percentage(child_sizes),
- preferred: max(parent_sizes.preferred, child_sizes.preferred),
- constrained: parent_sizes.constrained || child_sizes.constrained
- };
-
- total_inline_sizes.minimum_inline_size = total_inline_sizes.minimum_inline_size +
- parent_sizes.minimum_length;
- total_inline_sizes.preferred_inline_size = total_inline_sizes.preferred_inline_size +
- parent_sizes.preferred;
+ let mut column_index = 0;
+ for child_cell_inline_size in child_cell_inline_sizes.iter() {
+ for _ in range(0, child_cell_inline_size.column_span) {
+ if column_index < parent_inline_sizes.len() {
+ // We already have some intrinsic size information for this column. Merge it in
+ // according to the rules specified in INTRINSIC § 4.
+ let parent_sizes = &mut parent_inline_sizes[column_index];
+ if child_cell_inline_size.column_span > 1 {
+ // TODO(pcwalton): Perform the recursive algorithm specified in INTRINSIC §
+ // 4. For now we make this column contribute no width.
+ } else {
+ let column_size = &child_cell_inline_size.column_size;
+ *parent_sizes = ColumnIntrinsicInlineSize {
+ minimum_length: max(parent_sizes.minimum_length,
+ column_size.minimum_length),
+ percentage: parent_sizes.greatest_percentage(column_size),
+ preferred: max(parent_sizes.preferred, column_size.preferred),
+ constrained: parent_sizes.constrained || column_size.constrained,
+ }
+ }
+ } else {
+ // We discovered a new column. Initialize its data.
+ debug_assert!(column_index == parent_inline_sizes.len());
+ if child_cell_inline_size.column_span > 1 {
+ // TODO(pcwalton): Perform the recursive algorithm specified in INTRINSIC §
+ // 4. For now we make this column contribute no width.
+ parent_inline_sizes.push(ColumnIntrinsicInlineSize::new())
+ } else {
+ parent_inline_sizes.push(child_cell_inline_size.column_size)
+ }
+ }
+
+ total_inline_sizes.minimum_inline_size = total_inline_sizes.minimum_inline_size +
+ parent_inline_sizes[column_index].minimum_length;
+ total_inline_sizes.preferred_inline_size =
+ total_inline_sizes.preferred_inline_size +
+ parent_inline_sizes[column_index].preferred;
+
+ column_index += 1
+ }
}
+
total_inline_sizes
}
@@ -136,6 +166,39 @@ impl TableFlow {
fn assign_block_size_table_base<'a>(&mut self, layout_context: &'a LayoutContext<'a>) {
self.block_flow.assign_block_size_block_base(layout_context, MarginsMayNotCollapse);
}
+
+ /// 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.
+ fn update_column_inline_sizes_for_row(child: &mut Flow,
+ column_inline_sizes: &mut Vec<ColumnIntrinsicInlineSize>,
+ computation: &mut IntrinsicISizesContribution,
+ did_first_row: &mut bool,
+ table_layout: TableLayout) {
+ // Read column inline-sizes from the table-row, and assign inline-size=0 for the columns
+ // not defined in the column group.
+ //
+ // FIXME: Need to read inline-sizes from either table-header-group OR the first table-row.
+ debug_assert!(child.is_table_row());
+ let row = child.as_table_row();
+ match table_layout {
+ FixedLayout => {
+ // Fixed table layout only looks at the first row.
+ //
+ // FIXME(pcwalton): This is really inefficient. We should stop after the first row!
+ if !*did_first_row {
+ *did_first_row = true;
+ for cell_inline_size in row.cell_intrinsic_inline_sizes.iter() {
+ column_inline_sizes.push(cell_inline_size.column_size);
+ }
+ }
+ }
+ AutoLayout => {
+ computation.union_block(&TableFlow::update_automatic_column_inline_sizes(
+ column_inline_sizes,
+ row.cell_intrinsic_inline_sizes.as_slice()))
+ }
+ }
+ }
}
impl Flow for TableFlow {
@@ -190,50 +253,22 @@ impl Flow for TableFlow {
constrained: false,
})
}
- } else if kid.is_table_rowgroup() || kid.is_table_row() {
- // Read column inline-sizes from the table-row-group/table-row, and assign
- // inline-size=0 for the columns not defined in the column group.
- // FIXME: Need to read inline-sizes from either table-header-group OR the first
- // table-row.
- match self.table_layout {
- FixedLayout => {
- // Fixed table layout only looks at the first row.
- if !did_first_row {
- did_first_row = true;
- for child_column_inline_size in kid.column_intrinsic_inline_sizes()
- .iter() {
- self.column_intrinsic_inline_sizes.push(*child_column_inline_size);
- }
- }
- }
- AutoLayout => {
- let child_column_inline_sizes = kid.column_intrinsic_inline_sizes();
- let mut child_intrinsic_sizes = TableFlow::update_column_inline_sizes(
- &mut self.column_intrinsic_inline_sizes,
- child_column_inline_sizes);
-
- // Add new columns if processing this row caused us to discover them.
- let child_column_count = child_column_inline_sizes.len();
- let parent_column_count = self.column_intrinsic_inline_sizes.len();
- debug!("table until the previous row has {} column(s) and this row has {} \
- column(s)",
- parent_column_count,
- child_column_count);
- self.column_intrinsic_inline_sizes.reserve(child_column_count);
- for i in range(parent_column_count, child_column_count) {
- let inline_size_for_new_column = (*child_column_inline_sizes)[i];
- child_intrinsic_sizes.minimum_inline_size =
- child_intrinsic_sizes.minimum_inline_size +
- inline_size_for_new_column.minimum_length;
- child_intrinsic_sizes.preferred_inline_size =
- child_intrinsic_sizes.preferred_inline_size +
- inline_size_for_new_column.preferred;
- self.column_intrinsic_inline_sizes.push(inline_size_for_new_column);
- }
-
- computation.union_block(&child_intrinsic_sizes)
- }
+ } else if kid.is_table_rowgroup() {
+ for grandkid in flow::mut_base(kid).child_iter() {
+ TableFlow::update_column_inline_sizes_for_row(
+ grandkid,
+ &mut self.column_intrinsic_inline_sizes,
+ &mut computation,
+ &mut did_first_row,
+ self.table_layout)
}
+ } else if kid.is_table_row() {
+ TableFlow::update_column_inline_sizes_for_row(
+ kid,
+ &mut self.column_intrinsic_inline_sizes,
+ &mut computation,
+ &mut did_first_row,
+ self.table_layout)
}
}
@@ -277,8 +312,7 @@ impl Flow for TableFlow {
// 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.
self.column_computed_inline_sizes.clear();
- if total_column_inline_size < content_inline_size &&
- num_unspecified_inline_sizes == 0 {
+ if num_unspecified_inline_sizes == 0 {
let ratio = content_inline_size.to_subpx() /
total_column_inline_size.to_subpx();
for column_inline_size in self.column_intrinsic_inline_sizes.iter() {
@@ -413,6 +447,16 @@ pub struct 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.
/// 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.)
diff --git a/components/layout/table_cell.rs b/components/layout/table_cell.rs
index 280e86408cb..0a4e7d05e24 100644
--- a/components/layout/table_cell.rs
+++ b/components/layout/table_cell.rs
@@ -17,7 +17,7 @@ use wrapper::ThreadSafeLayoutNode;
use servo_util::geometry::Au;
use std::fmt;
-use style::ComputedValues;
+use style::{ColSpanUnsignedIntegerAttribute, ComputedValues};
use sync::Arc;
/// A table formatting context.
@@ -25,13 +25,17 @@ use sync::Arc;
pub struct TableCellFlow {
/// Data common to all block flows.
pub block_flow: BlockFlow,
+ /// The column span of this cell.
+ pub column_span: u32,
}
impl TableCellFlow {
pub fn from_node_and_fragment(node: &ThreadSafeLayoutNode, fragment: Fragment)
-> 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),
}
}
diff --git a/components/layout/table_row.rs b/components/layout/table_row.rs
index 307892635a0..e50f5b2f33e 100644
--- a/components/layout/table_row.rs
+++ b/components/layout/table_row.rs
@@ -30,20 +30,29 @@ use sync::Arc;
pub struct TableRowFlow {
pub block_flow: BlockFlow,
- /// Information about the intrinsic inline-sizes of each column.
- pub column_intrinsic_inline_sizes: Vec<ColumnIntrinsicInlineSize>,
+ /// Information about the intrinsic inline-sizes of each cell.
+ pub cell_intrinsic_inline_sizes: Vec<CellIntrinsicInlineSize>,
/// Information about the computed inline-sizes of each column.
pub column_computed_inline_sizes: Vec<ColumnComputedInlineSize>,
}
+/// Information about the column inline size and span for each cell.
+#[deriving(Encodable)]
+pub struct CellIntrinsicInlineSize {
+ /// Inline sizes that this cell contributes to the column.
+ pub column_size: ColumnIntrinsicInlineSize,
+ /// The column span of this cell.
+ pub column_span: u32,
+}
+
impl TableRowFlow {
pub fn from_node_and_fragment(node: &ThreadSafeLayoutNode,
fragment: Fragment)
-> TableRowFlow {
TableRowFlow {
block_flow: BlockFlow::from_node_and_fragment(node, fragment),
- column_intrinsic_inline_sizes: Vec::new(),
+ cell_intrinsic_inline_sizes: Vec::new(),
column_computed_inline_sizes: Vec::new(),
}
}
@@ -53,8 +62,8 @@ impl TableRowFlow {
-> TableRowFlow {
TableRowFlow {
block_flow: BlockFlow::from_node(constructor, node),
- column_intrinsic_inline_sizes: Vec::new(),
- column_computed_inline_sizes: Vec::new(),
+ cell_intrinsic_inline_sizes: Vec::new(),
+ column_computed_inline_sizes: Vec::new()
}
}
@@ -156,7 +165,7 @@ impl Flow for TableRowFlow {
}
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> {
@@ -181,10 +190,15 @@ impl Flow for TableRowFlow {
// Collect the specified column inline-size of the cell. This is used in both fixed and
// automatic table layout calculation.
- let child_specified_inline_size = kid.as_table_cell()
- .fragment()
- .style()
- .content_inline_size();
+ let child_specified_inline_size;
+ let child_column_span;
+ {
+ let child_table_cell = kid.as_table_cell();
+ child_specified_inline_size = child_table_cell.fragment()
+ .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
// calculation.
@@ -208,15 +222,16 @@ impl Flow for TableRowFlow {
};
min_inline_size = min_inline_size + child_column_inline_size.minimum_length;
pref_inline_size = pref_inline_size + child_column_inline_size.preferred;
- self.column_intrinsic_inline_sizes.push(child_column_inline_size);
+ self.cell_intrinsic_inline_sizes.push(CellIntrinsicInlineSize {
+ column_size: child_column_inline_size,
+ column_span: child_column_span,
+ });
}
self.block_flow.base.intrinsic_inline_sizes.minimum_inline_size = min_inline_size;
self.block_flow.base.intrinsic_inline_sizes.preferred_inline_size = max(min_inline_size,
pref_inline_size);
}
- /// Recursively (top-down) determines the actual inline-size of child contexts and fragments.
- /// When called on this context, the context has had its inline-size set by the parent context.
fn assign_inline_sizes(&mut self, layout_context: &LayoutContext) {
let _scope = layout_debug_scope!("table_row::assign_inline_sizes {:x}",
self.block_flow.base.debug_id());
@@ -233,11 +248,46 @@ impl Flow for TableRowFlow {
layout_context,
containing_block_inline_size);
+ // Spread out the completed inline sizes among columns with spans > 1.
+ let mut computed_inline_size_for_cells = Vec::new();
+ 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() {
+ // Start with the computed inline size for the first column in the span.
+ let mut column_computed_inline_size =
+ match column_computed_inline_size_iterator.next() {
+ Some(column_computed_inline_size) => *column_computed_inline_size,
+ None => {
+ // We're in fixed layout mode and there are more cells in this row than
+ // 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.
+ for _ in range(1, cell_intrinsic_inline_size.column_span) {
+ let extra_column_computed_inline_size =
+ match column_computed_inline_size_iterator.next() {
+ Some(column_computed_inline_size) => column_computed_inline_size,
+ None => break,
+ };
+ column_computed_inline_size.size = column_computed_inline_size.size +
+ extra_column_computed_inline_size.size;
+ }
+
+ computed_inline_size_for_cells.push(column_computed_inline_size)
+ }
+
+ // Push those inline sizes down to the cells.
self.block_flow.propagate_assigned_inline_size_to_children(
layout_context,
inline_start_content_edge,
containing_block_inline_size,
- Some(self.column_computed_inline_sizes.as_slice()));
+ Some(computed_inline_size_for_cells.as_slice()));
}
fn assign_block_size<'a>(&mut self, ctx: &'a LayoutContext<'a>) {
diff --git a/components/layout/table_rowgroup.rs b/components/layout/table_rowgroup.rs
index c3837185a47..4f62cd10a75 100644
--- a/components/layout/table_rowgroup.rs
+++ b/components/layout/table_rowgroup.rs
@@ -9,11 +9,10 @@
use block::{BlockFlow, ISizeAndMarginsComputer, MarginsMayNotCollapse};
use construct::FlowConstructor;
use context::LayoutContext;
-use flow::{TableRowGroupFlowClass, FlowClass, Flow, ImmutableFlowUtils};
+use flow::{Flow, FlowClass, TableRowGroupFlowClass};
use fragment::{Fragment, FragmentBoundsIterator};
use layout_debug;
-use model::IntrinsicISizesContribution;
-use table::{ColumnComputedInlineSize, ColumnIntrinsicInlineSize, InternalTable, TableFlow};
+use table::{ColumnComputedInlineSize, ColumnIntrinsicInlineSize, InternalTable};
use wrapper::ThreadSafeLayoutNode;
use servo_util::geometry::Au;
@@ -24,6 +23,7 @@ use sync::Arc;
/// A table formatting context.
#[deriving(Encodable)]
pub struct TableRowGroupFlow {
+ /// Fields common to all block flows.
pub block_flow: BlockFlow,
/// Information about the intrinsic inline-sizes of each column.
@@ -91,54 +91,11 @@ impl Flow for TableRowGroupFlow {
&mut self.column_computed_inline_sizes
}
-
- /// Recursively (bottom-up) determines the context's preferred and minimum inline-sizes. When
- /// called on this context, all child contexts have had their min/pref inline-sizes set. This
- /// function must decide min/pref inline-sizes based on child context inline-sizes and
- /// dimensions of any fragments it is responsible for flowing.
- ///
- /// Min/pref inline-sizes set by this function are used in automatic table layout calculation.
- ///
- /// Also, this function finds the specified column inline-sizes from the first row. These are
- /// used in fixed table layout calculation.
fn bubble_inline_sizes(&mut self) {
let _scope = layout_debug_scope!("table_rowgroup::bubble_inline_sizes {:x}",
self.block_flow.base.debug_id());
-
- let mut computation = IntrinsicISizesContribution::new();
- for kid in self.block_flow.base.child_iter() {
- assert!(kid.is_table_row());
-
- // Calculate minimum and preferred inline sizes for automatic table layout.
- if self.column_intrinsic_inline_sizes.is_empty() {
- // We're the first row.
- self.column_intrinsic_inline_sizes = kid.column_intrinsic_inline_sizes().clone();
- } else {
- let mut child_intrinsic_sizes =
- TableFlow::update_column_inline_sizes(&mut self.column_intrinsic_inline_sizes,
- kid.column_intrinsic_inline_sizes());
-
- // update the number of column inline-sizes from table-rows.
- let column_count = self.column_intrinsic_inline_sizes.len();
- let child_column_count = kid.column_intrinsic_inline_sizes().len();
- for i in range(column_count, child_column_count) {
- let this_column_inline_size = (*kid.column_intrinsic_inline_sizes())[i];
-
- // FIXME(pcwalton): Ignoring the percentage here seems dubious.
- child_intrinsic_sizes.minimum_inline_size =
- child_intrinsic_sizes.minimum_inline_size +
- this_column_inline_size.minimum_length;
- child_intrinsic_sizes.preferred_inline_size =
- child_intrinsic_sizes.preferred_inline_size +
- this_column_inline_size.preferred;
- self.column_intrinsic_inline_sizes.push(this_column_inline_size);
- }
-
- computation.union_block(&child_intrinsic_sizes)
- }
- }
-
- self.block_flow.base.intrinsic_inline_sizes = computation.finish()
+ // Proper calculation of intrinsic sizes in table layout requires access to the entire
+ // table, which we don't have yet. Defer to our parent.
}
/// Recursively (top-down) determines the actual inline-size of child contexts and fragments.
diff --git a/components/layout/table_wrapper.rs b/components/layout/table_wrapper.rs
index 271a6545d1d..0d785ce1ca3 100644
--- a/components/layout/table_wrapper.rs
+++ b/components/layout/table_wrapper.rs
@@ -31,7 +31,7 @@ use style::{ComputedValues, CSSFloat};
use style::computed_values::table_layout;
use sync::Arc;
-#[deriving(Encodable)]
+#[deriving(Encodable, Show)]
pub enum TableLayout {
FixedLayout,
AutoLayout
diff --git a/components/layout/wrapper.rs b/components/layout/wrapper.rs
index 9a70539c78b..97aa8dd189a 100644
--- a/components/layout/wrapper.rs
+++ b/components/layout/wrapper.rs
@@ -36,6 +36,7 @@ use incremental::RestyleDamage;
use util::{LayoutDataAccess, LayoutDataFlags, LayoutDataWrapper, OpaqueNodeMethods};
use util::{PrivateLayoutData};
+use cssparser::RGBA;
use gfx::display_list::OpaqueNode;
use script::dom::bindings::codegen::InheritTypes::{ElementCast, HTMLIFrameElementCast};
use script::dom::bindings::codegen::InheritTypes::{HTMLImageElementCast, HTMLInputElementCast};
@@ -56,11 +57,12 @@ use servo_msg::constellation_msg::{PipelineId, SubpageId};
use servo_util::str::{LengthOrPercentageOrAuto, is_whitespace};
use std::kinds::marker::ContravariantLifetime;
use std::mem;
+use string_cache::{Atom, Namespace};
use style::computed_values::{content, display, white_space};
-use style::{AnyNamespace, AttrSelector, IntegerAttribute, LengthAttribute};
-use style::{PropertyDeclarationBlock, SpecificNamespace, TElement, TElementAttributes, TNode};
+use style::{AnyNamespace, AttrSelector, BorderUnsignedIntegerAttribute, IntegerAttribute};
+use style::{LengthAttribute, PropertyDeclarationBlock, SimpleColorAttribute, SpecificNamespace};
+use style::{TElement, TElementAttributes, TNode, UnsignedIntegerAttribute};
use url::Url;
-use string_cache::{Atom, Namespace};
use std::cell::{Ref, RefMut};
@@ -580,6 +582,17 @@ impl<'le> TElement<'le> for LayoutElement<'le> {
}
}
}
+
+ #[inline]
+ fn has_nonzero_border(self) -> bool {
+ unsafe {
+ match self.element
+ .get_unsigned_integer_attribute_for_layout(BorderUnsignedIntegerAttribute) {
+ None | Some(0) => false,
+ _ => true,
+ }
+ }
+ }
}
impl<'le> TElementAttributes for LayoutElement<'le> {
@@ -594,6 +607,18 @@ impl<'le> TElementAttributes for LayoutElement<'le> {
self.element.get_integer_attribute_for_layout(integer_attribute)
}
}
+
+ fn get_unsigned_integer_attribute(self, attribute: UnsignedIntegerAttribute) -> Option<u32> {
+ unsafe {
+ self.element.get_unsigned_integer_attribute_for_layout(attribute)
+ }
+ }
+
+ fn get_simple_color_attribute(self, attribute: SimpleColorAttribute) -> Option<RGBA> {
+ unsafe {
+ self.element.get_simple_color_attribute_for_layout(attribute)
+ }
+ }
}
fn get_content(content_list: &content::T) -> String {
@@ -913,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.
/// This is a simple bitfield and fine to copy by value.
pub fn restyle_damage(self) -> RestyleDamage {
diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs
index afd329137c0..b877d022c2e 100644
--- a/components/script/dom/bindings/trace.rs
+++ b/components/script/dom/bindings/trace.rs
@@ -32,6 +32,7 @@ use dom::bindings::utils::{Reflectable, Reflector, WindowProxyHandler};
use dom::node::{Node, TrustedNodeAddress};
use collections::hash::{Hash, Hasher};
+use cssparser::RGBA;
use geom::rect::Rect;
use html5ever::tree_builder::QuirksMode;
use hyper::header::Headers;
@@ -48,7 +49,7 @@ use script_traits::UntrustedNodeAddress;
use servo_msg::compositor_msg::ScriptListener;
use servo_msg::constellation_msg::ConstellationChan;
use servo_util::smallvec::{SmallVec1, SmallVec};
-use servo_util::str::LengthOrPercentageOrAuto;
+use servo_util::str::{LengthOrPercentageOrAuto};
use std::cell::{Cell, RefCell};
use std::collections::HashMap;
use std::comm::{Receiver, Sender};
@@ -214,6 +215,7 @@ no_jsmanaged_fields!(LayoutChan)
no_jsmanaged_fields!(WindowProxyHandler)
no_jsmanaged_fields!(UntrustedNodeAddress)
no_jsmanaged_fields!(LengthOrPercentageOrAuto)
+no_jsmanaged_fields!(RGBA)
impl<'a> JSTraceable for &'a str {
#[inline]
diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs
index c4dc787ac26..4fdb8a9befd 100644
--- a/components/script/dom/document.rs
+++ b/components/script/dom/document.rs
@@ -61,6 +61,7 @@ use servo_util::namespace;
use servo_util::str::{DOMString, split_html_space_chars};
use html5ever::tree_builder::{QuirksMode, NoQuirks, LimitedQuirks, Quirks};
+use layout_interface::{LayoutChan, SetQuirksModeMsg};
use string_cache::{Atom, QualName};
use url::Url;
@@ -217,6 +218,15 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> {
fn set_quirks_mode(self, mode: QuirksMode) {
self.quirks_mode.set(mode);
+
+ match mode {
+ Quirks => {
+ let window = self.window.root();
+ let LayoutChan(ref layout_chan) = window.page().layout_chan;
+ layout_chan.send(SetQuirksModeMsg);
+ }
+ NoQuirks | LimitedQuirks => {}
+ }
}
fn set_last_modified(self, value: DOMString) {
diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs
index 1e55f9f73c2..69846d40543 100644
--- a/components/script/dom/element.rs
+++ b/components/script/dom/element.rs
@@ -15,8 +15,12 @@ use dom::bindings::codegen::Bindings::ElementBinding::ElementMethods;
use dom::bindings::codegen::Bindings::EventBinding::EventMethods;
use dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementMethods;
use dom::bindings::codegen::Bindings::NamedNodeMapBinding::NamedNodeMapMethods;
-use dom::bindings::codegen::InheritTypes::{ElementDerived, HTMLInputElementDerived, HTMLTableCellElementDerived};
-use dom::bindings::codegen::InheritTypes::{HTMLInputElementCast, NodeCast, EventTargetCast, ElementCast};
+use dom::bindings::codegen::InheritTypes::{ElementCast, ElementDerived, EventTargetCast};
+use dom::bindings::codegen::InheritTypes::{HTMLBodyElementDerived, HTMLInputElementCast};
+use dom::bindings::codegen::InheritTypes::{HTMLInputElementDerived, HTMLTableElementCast};
+use dom::bindings::codegen::InheritTypes::{HTMLTableElementDerived, HTMLTableCellElementDerived};
+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::{OptionalRootable, Root};
use dom::bindings::utils::{Reflectable, Reflector};
@@ -29,22 +33,28 @@ use dom::document::{Document, DocumentHelpers, LayoutDocumentHelpers};
use dom::domtokenlist::DOMTokenList;
use dom::event::Event;
use dom::eventtarget::{EventTarget, NodeTargetTypeId, EventTargetHelpers};
+use dom::htmlbodyelement::{HTMLBodyElement, HTMLBodyElementHelpers};
use dom::htmlcollection::HTMLCollection;
use dom::htmlinputelement::{HTMLInputElement, RawLayoutHTMLInputElementHelpers};
use dom::htmlserializer::serialize;
+use dom::htmltableelement::{HTMLTableElement, HTMLTableElementHelpers};
use dom::htmltablecellelement::{HTMLTableCellElement, HTMLTableCellElementHelpers};
-use dom::node::{CLICK_IN_PROGRESS, ElementNodeTypeId, Node, NodeHelpers, NodeIterator};
-use dom::node::{document_from_node, window_from_node, LayoutNodeHelpers, NodeStyleDamaged};
-use dom::node::{OtherNodeDamage};
+use dom::htmltablerowelement::{HTMLTableRowElement, HTMLTableRowElementHelpers};
+use dom::htmltablesectionelement::{HTMLTableSectionElement, HTMLTableSectionElementHelpers};
+use dom::node::{CLICK_IN_PROGRESS, ElementNodeTypeId, LayoutNodeHelpers, Node, NodeHelpers};
+use dom::node::{NodeIterator, NodeStyleDamaged, OtherNodeDamage, document_from_node};
+use dom::node::{window_from_node};
use dom::nodelist::NodeList;
use dom::virtualmethods::{VirtualMethods, vtable_for};
use devtools_traits::AttrInfo;
-use style::{IntegerAttribute, LengthAttribute, SizeIntegerAttribute, WidthLengthAttribute};
-use style::{matches, parse_selector_list_from_str};
-use style;
+use style::{mod, AuthorOrigin, BgColorSimpleColorAttribute, BorderUnsignedIntegerAttribute};
+use style::{ColSpanUnsignedIntegerAttribute, IntegerAttribute, LengthAttribute, ParserContext};
+use style::{SimpleColorAttribute, SizeIntegerAttribute, UnsignedIntegerAttribute};
+use style::{WidthLengthAttribute, matches};
use servo_util::namespace;
use servo_util::str::{DOMString, LengthOrPercentageOrAuto};
+use cssparser::RGBA;
use std::ascii::AsciiExt;
use std::cell::{Ref, RefMut};
use std::default::Default;
@@ -201,6 +211,10 @@ pub trait RawLayoutElementHelpers {
unsafe fn get_integer_attribute_for_layout(&self, integer_attribute: IntegerAttribute)
-> Option<i32>;
unsafe fn get_checked_state_for_layout(&self) -> bool;
+ unsafe fn get_unsigned_integer_attribute_for_layout(&self, attribute: UnsignedIntegerAttribute)
+ -> Option<u32>;
+ unsafe fn get_simple_color_attribute_for_layout(&self, attribute: SimpleColorAttribute)
+ -> Option<RGBA>;
fn local_name<'a>(&'a self) -> &'a Atom;
fn namespace<'a>(&'a self) -> &'a Namespace;
fn style_attribute<'a>(&'a self) -> &'a DOMRefCell<Option<style::PropertyDeclarationBlock>>;
@@ -285,11 +299,15 @@ impl RawLayoutElementHelpers for Element {
-> LengthOrPercentageOrAuto {
match length_attribute {
WidthLengthAttribute => {
- if !self.is_htmltablecellelement() {
- panic!("I'm not a table cell!")
+ if self.is_htmltableelement() {
+ let this: &HTMLTableElement = mem::transmute(self);
+ this.get_width()
+ } else if self.is_htmltablecellelement() {
+ let this: &HTMLTableCellElement = mem::transmute(self);
+ this.get_width()
+ } else {
+ panic!("I'm not a table or table cell!")
}
- let this: &HTMLTableCellElement = mem::transmute(self);
- this.get_width()
}
}
}
@@ -319,6 +337,61 @@ impl RawLayoutElementHelpers for Element {
this.get_checked_state_for_layout()
}
+ unsafe fn get_unsigned_integer_attribute_for_layout(&self,
+ attribute: UnsignedIntegerAttribute)
+ -> Option<u32> {
+ match attribute {
+ BorderUnsignedIntegerAttribute => {
+ if self.is_htmltableelement() {
+ let this: &HTMLTableElement = mem::transmute(self);
+ this.get_border()
+ } else {
+ // Don't panic since `:-servo-nonzero-border` can cause this to be called on
+ // arbitrary elements.
+ None
+ }
+ }
+ ColSpanUnsignedIntegerAttribute => {
+ if self.is_htmltablecellelement() {
+ let this: &HTMLTableCellElement = mem::transmute(self);
+ this.get_colspan()
+ } else {
+ // Don't panic since `display` can cause this to be called on arbitrary
+ // elements.
+ None
+ }
+ }
+ }
+ }
+
+ #[inline]
+ #[allow(unrooted_must_root)]
+ unsafe fn get_simple_color_attribute_for_layout(&self, attribute: SimpleColorAttribute)
+ -> Option<RGBA> {
+ match attribute {
+ BgColorSimpleColorAttribute => {
+ if self.is_htmlbodyelement() {
+ let this: &HTMLBodyElement = mem::transmute(self);
+ this.get_background_color()
+ } else if self.is_htmltableelement() {
+ let this: &HTMLTableElement = mem::transmute(self);
+ this.get_background_color()
+ } else if self.is_htmltablecellelement() {
+ let this: &HTMLTableCellElement = mem::transmute(self);
+ 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 {
+ None
+ }
+ }
+ }
+ }
+
// Getters used in components/layout/wrapper.rs
fn local_name<'a>(&'a self) -> &'a Atom {
@@ -947,7 +1020,10 @@ impl<'a> ElementMethods for JSRef<'a, Element> {
// http://dom.spec.whatwg.org/#dom-element-matches
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),
Ok(ref selectors) => {
let root: JSRef<Node> = NodeCast::from_ref(self);
@@ -1222,6 +1298,17 @@ impl<'a> style::TElement<'a> for JSRef<'a, Element> {
}
}
}
+ fn has_nonzero_border(self) -> bool {
+ match HTMLTableElementCast::to_ref(self) {
+ None => false,
+ Some(this) => {
+ match this.get_border() {
+ None | Some(0) => false,
+ Some(_) => true,
+ }
+ }
+ }
+ }
}
pub trait ActivationElementHelpers<'a> {
diff --git a/components/script/dom/htmlbodyelement.rs b/components/script/dom/htmlbodyelement.rs
index d088a6b8626..917b02913a7 100644
--- a/components/script/dom/htmlbodyelement.rs
+++ b/components/script/dom/htmlbodyelement.rs
@@ -2,11 +2,9 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-use dom::attr::Attr;
-use dom::attr::AttrHelpers;
+use dom::attr::{Attr, AttrHelpers};
use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
-use dom::bindings::codegen::Bindings::HTMLBodyElementBinding;
-use dom::bindings::codegen::Bindings::HTMLBodyElementBinding::HTMLBodyElementMethods;
+use dom::bindings::codegen::Bindings::HTMLBodyElementBinding::{mod, HTMLBodyElementMethods};
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
use dom::bindings::codegen::InheritTypes::EventTargetCast;
use dom::bindings::codegen::InheritTypes::{HTMLBodyElementDerived, HTMLElementCast};
@@ -19,11 +17,14 @@ use dom::htmlelement::HTMLElement;
use dom::node::{Node, ElementNodeTypeId, window_from_node};
use dom::virtualmethods::VirtualMethods;
-use servo_util::str::DOMString;
+use cssparser::RGBA;
+use servo_util::str::{mod, DOMString};
+use std::cell::Cell;
#[dom_struct]
pub struct HTMLBodyElement {
- htmlelement: HTMLElement
+ htmlelement: HTMLElement,
+ background_color: Cell<Option<RGBA>>,
}
impl HTMLBodyElementDerived for EventTarget {
@@ -33,14 +34,20 @@ impl HTMLBodyElementDerived for EventTarget {
}
impl HTMLBodyElement {
- fn new_inherited(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) -> HTMLBodyElement {
+ fn new_inherited(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>)
+ -> HTMLBodyElement {
HTMLBodyElement {
- htmlelement: HTMLElement::new_inherited(HTMLBodyElementTypeId, localName, prefix, document)
+ htmlelement: HTMLElement::new_inherited(HTMLBodyElementTypeId,
+ localName,
+ prefix,
+ document),
+ background_color: Cell::new(None),
}
}
#[allow(unrooted_must_root)]
- pub fn new(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) -> Temporary<HTMLBodyElement> {
+ pub fn new(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>)
+ -> Temporary<HTMLBodyElement> {
let element = HTMLBodyElement::new_inherited(localName, prefix, document);
Node::reflect_node(box element, document, HTMLBodyElementBinding::Wrap)
}
@@ -58,6 +65,16 @@ impl<'a> HTMLBodyElementMethods for JSRef<'a, HTMLBodyElement> {
}
}
+pub trait HTMLBodyElementHelpers {
+ fn get_background_color(&self) -> Option<RGBA>;
+}
+
+impl HTMLBodyElementHelpers for HTMLBodyElement {
+ fn get_background_color(&self) -> Option<RGBA> {
+ self.background_color.get()
+ }
+}
+
impl<'a> VirtualMethods for JSRef<'a, HTMLBodyElement> {
fn super_type<'a>(&'a self) -> Option<&'a VirtualMethods> {
let element: &JSRef<HTMLElement> = HTMLElementCast::from_borrowed_ref(self);
@@ -91,6 +108,25 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLBodyElement> {
name.slice_from(2),
attr.value().as_slice().to_string());
}
+
+ 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),
+ _ => {}
+ }
}
}
diff --git a/components/script/dom/htmltablecellelement.rs b/components/script/dom/htmltablecellelement.rs
index cee07540301..9ca84b0e70f 100644
--- a/components/script/dom/htmltablecellelement.rs
+++ b/components/script/dom/htmltablecellelement.rs
@@ -2,8 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-use dom::attr::Attr;
-use dom::attr::AttrHelpers;
+use dom::attr::{Attr, AttrHelpers};
use dom::bindings::codegen::InheritTypes::{HTMLElementCast, HTMLTableCellElementDerived};
use dom::bindings::js::JSRef;
use dom::bindings::utils::{Reflectable, Reflector};
@@ -15,13 +14,15 @@ use dom::htmlelement::HTMLElement;
use dom::node::ElementNodeTypeId;
use dom::virtualmethods::VirtualMethods;
-use servo_util::str::{AutoLpa, DOMString, LengthOrPercentageOrAuto};
-use servo_util::str;
+use cssparser::RGBA;
+use servo_util::str::{mod, AutoLpa, DOMString, LengthOrPercentageOrAuto};
use std::cell::Cell;
#[dom_struct]
pub struct HTMLTableCellElement {
htmlelement: HTMLElement,
+ background_color: Cell<Option<RGBA>>,
+ colspan: Cell<Option<u32>>,
width: Cell<LengthOrPercentageOrAuto>,
}
@@ -36,10 +37,16 @@ impl HTMLTableCellElementDerived for EventTarget {
}
impl HTMLTableCellElement {
- pub fn new_inherited(type_id: ElementTypeId, tag_name: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) -> HTMLTableCellElement {
+ pub fn new_inherited(type_id: ElementTypeId,
+ tag_name: DOMString,
+ prefix: Option<DOMString>,
+ document: JSRef<Document>)
+ -> HTMLTableCellElement {
HTMLTableCellElement {
htmlelement: HTMLElement::new_inherited(type_id, tag_name, prefix, document),
- width: Cell::new(AutoLpa)
+ background_color: Cell::new(None),
+ colspan: Cell::new(None),
+ width: Cell::new(AutoLpa),
}
}
@@ -50,10 +57,20 @@ impl HTMLTableCellElement {
}
pub trait HTMLTableCellElementHelpers {
+ fn get_background_color(&self) -> Option<RGBA>;
+ fn get_colspan(&self) -> Option<u32>;
fn get_width(&self) -> LengthOrPercentageOrAuto;
}
impl HTMLTableCellElementHelpers for HTMLTableCellElement {
+ fn get_background_color(&self) -> Option<RGBA> {
+ self.background_color.get()
+ }
+
+ fn get_colspan(&self) -> Option<u32> {
+ self.colspan.get()
+ }
+
fn get_width(&self) -> LengthOrPercentageOrAuto {
self.width.get()
}
@@ -72,6 +89,12 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLTableCellElement> {
}
match attr.local_name() {
+ &atom!("bgcolor") => {
+ self.background_color.set(str::parse_legacy_color(attr.value().as_slice()).ok())
+ }
+ &atom!("colspan") => {
+ self.colspan.set(str::parse_unsigned_integer(attr.value().as_slice().chars()));
+ }
&atom!("width") => self.width.set(str::parse_length(attr.value().as_slice())),
_ => ()
}
@@ -84,6 +107,8 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLTableCellElement> {
}
match attr.local_name() {
+ &atom!("bgcolor") => self.background_color.set(None),
+ &atom!("colspan") => self.colspan.set(None),
&atom!("width") => self.width.set(AutoLpa),
_ => ()
}
diff --git a/components/script/dom/htmltableelement.rs b/components/script/dom/htmltableelement.rs
index 7dddf98bdc4..d2c9e723b78 100644
--- a/components/script/dom/htmltableelement.rs
+++ b/components/script/dom/htmltableelement.rs
@@ -2,10 +2,12 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-use dom::bindings::codegen::Bindings::HTMLTableElementBinding;
+use dom::attr::{Attr, AttrHelpers};
use dom::bindings::codegen::Bindings::HTMLTableElementBinding::HTMLTableElementMethods;
-use dom::bindings::codegen::InheritTypes::{HTMLTableElementDerived, NodeCast, HTMLTableCaptionElementCast};
+use dom::bindings::codegen::Bindings::HTMLTableElementBinding;
use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
+use dom::bindings::codegen::InheritTypes::{HTMLElementCast, HTMLTableCaptionElementCast};
+use dom::bindings::codegen::InheritTypes::{HTMLTableElementDerived, NodeCast};
use dom::bindings::js::{JSRef, Temporary};
use dom::bindings::utils::{Reflectable, Reflector};
use dom::document::Document;
@@ -14,11 +16,18 @@ use dom::eventtarget::{EventTarget, NodeTargetTypeId};
use dom::htmlelement::HTMLElement;
use dom::htmltablecaptionelement::HTMLTableCaptionElement;
use dom::node::{Node, NodeHelpers, ElementNodeTypeId};
-use servo_util::str::DOMString;
+use dom::virtualmethods::VirtualMethods;
+
+use cssparser::RGBA;
+use servo_util::str::{mod, AutoLpa, DOMString, LengthOrPercentageOrAuto};
+use std::cell::Cell;
#[dom_struct]
pub struct HTMLTableElement {
htmlelement: HTMLElement,
+ background_color: Cell<Option<RGBA>>,
+ border: Cell<Option<u32>>,
+ width: Cell<LengthOrPercentageOrAuto>,
}
impl HTMLTableElementDerived for EventTarget {
@@ -28,14 +37,22 @@ impl HTMLTableElementDerived for EventTarget {
}
impl HTMLTableElement {
- fn new_inherited(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) -> HTMLTableElement {
+ fn new_inherited(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>)
+ -> HTMLTableElement {
HTMLTableElement {
- htmlelement: HTMLElement::new_inherited(HTMLTableElementTypeId, localName, prefix, document)
+ htmlelement: HTMLElement::new_inherited(HTMLTableElementTypeId,
+ localName,
+ prefix,
+ document),
+ background_color: Cell::new(None),
+ border: Cell::new(None),
+ width: Cell::new(AutoLpa),
}
}
#[allow(unrooted_must_root)]
- pub fn new(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) -> Temporary<HTMLTableElement> {
+ pub fn new(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>)
+ -> Temporary<HTMLTableElement> {
let element = HTMLTableElement::new_inherited(localName, prefix, document);
Node::reflect_node(box element, document, HTMLTableElementBinding::Wrap)
}
@@ -77,3 +94,66 @@ impl<'a> HTMLTableElementMethods for JSRef<'a, HTMLTableElement> {
});
}
}
+
+pub trait HTMLTableElementHelpers {
+ fn get_background_color(&self) -> Option<RGBA>;
+ fn get_border(&self) -> Option<u32>;
+ fn get_width(&self) -> LengthOrPercentageOrAuto;
+}
+
+impl HTMLTableElementHelpers for HTMLTableElement {
+ fn get_background_color(&self) -> Option<RGBA> {
+ self.background_color.get()
+ }
+
+ fn get_border(&self) -> Option<u32> {
+ self.border.get()
+ }
+
+ fn get_width(&self) -> LengthOrPercentageOrAuto {
+ self.width.get()
+ }
+}
+
+impl<'a> VirtualMethods for JSRef<'a, HTMLTableElement> {
+ 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())
+ }
+ &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!("width") => self.width.set(str::parse_length(attr.value().as_slice())),
+ _ => ()
+ }
+ }
+
+ 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),
+ &atom!("border") => self.border.set(None),
+ &atom!("width") => self.width.set(AutoLpa),
+ _ => ()
+ }
+ }
+}
+
diff --git a/components/script/dom/htmltablerowelement.rs b/components/script/dom/htmltablerowelement.rs
index 3edf4a0e64f..500c7d74472 100644
--- a/components/script/dom/htmltablerowelement.rs
+++ b/components/script/dom/htmltablerowelement.rs
@@ -2,8 +2,9 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+use dom::attr::{Attr, AttrHelpers};
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::utils::{Reflectable, Reflector};
use dom::document::Document;
@@ -11,11 +12,16 @@ use dom::element::HTMLTableRowElementTypeId;
use dom::eventtarget::{EventTarget, NodeTargetTypeId};
use dom::htmlelement::HTMLElement;
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]
pub struct HTMLTableRowElement {
htmlelement: HTMLElement,
+ background_color: Cell<Option<RGBA>>,
}
impl HTMLTableRowElementDerived for EventTarget {
@@ -25,9 +31,14 @@ impl HTMLTableRowElementDerived for EventTarget {
}
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 {
- 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()
}
}
+
+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),
+ _ => {}
+ }
+ }
+}
+
diff --git a/components/script/dom/htmltablesectionelement.rs b/components/script/dom/htmltablesectionelement.rs
index d10e4b82b44..b191e21bde0 100644
--- a/components/script/dom/htmltablesectionelement.rs
+++ b/components/script/dom/htmltablesectionelement.rs
@@ -2,8 +2,9 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+use dom::attr::{Attr, AttrHelpers};
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::utils::{Reflectable, Reflector};
use dom::document::Document;
@@ -11,11 +12,16 @@ use dom::element::HTMLTableSectionElementTypeId;
use dom::eventtarget::{EventTarget, NodeTargetTypeId};
use dom::htmlelement::HTMLElement;
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]
pub struct HTMLTableSectionElement {
htmlelement: HTMLElement,
+ background_color: Cell<Option<RGBA>>,
}
impl HTMLTableSectionElementDerived for EventTarget {
@@ -25,14 +31,20 @@ impl HTMLTableSectionElementDerived for EventTarget {
}
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 {
- 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)]
- 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);
Node::reflect_node(box element, document, HTMLTableSectionElementBinding::Wrap)
}
@@ -43,3 +55,47 @@ impl Reflectable for HTMLTableSectionElement {
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),
+ _ => {}
+ }
+ }
+}
+
diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs
index ecfccf8aefc..0871a266df3 100644
--- a/components/script/dom/node.rs
+++ b/components/script/dom/node.rs
@@ -50,7 +50,7 @@ use devtools_traits::NodeInfo;
use script_traits::UntrustedNodeAddress;
use servo_util::geometry::Au;
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::jsfriendapi;
@@ -60,8 +60,7 @@ use std::cell::{Cell, RefCell, Ref, RefMut};
use std::default::Default;
use std::iter::{FilterMap, Peekable};
use std::mem;
-use style;
-use style::ComputedValues;
+use style::{mod, ComputedValues};
use sync::Arc;
use uuid;
use string_cache::QualName;
@@ -741,7 +740,10 @@ impl<'a> NodeHelpers<'a> for JSRef<'a, Node> {
// http://dom.spec.whatwg.org/#dom-parentnode-queryselector
fn query_selector(self, selectors: DOMString) -> Fallible<Option<Temporary<Element>>> {
// 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.
Err(()) => return Err(Syntax),
// 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
/// Be careful not to do anything which may manipulate the DOM tree whilst iterating, otherwise
/// 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.
let nodes;
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.
Err(()) => return Err(Syntax),
// Step 3.
diff --git a/components/script/dom/virtualmethods.rs b/components/script/dom/virtualmethods.rs
index a90055bc50d..94a74124eca 100644
--- a/components/script/dom/virtualmethods.rs
+++ b/components/script/dom/virtualmethods.rs
@@ -22,7 +22,10 @@ use dom::bindings::codegen::InheritTypes::HTMLOptionElementCast;
use dom::bindings::codegen::InheritTypes::HTMLScriptElementCast;
use dom::bindings::codegen::InheritTypes::HTMLSelectElementCast;
use dom::bindings::codegen::InheritTypes::HTMLStyleElementCast;
+use dom::bindings::codegen::InheritTypes::HTMLTableElementCast;
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::HTMLTitleElementCast;
use dom::bindings::js::JSRef;
@@ -46,7 +49,10 @@ use dom::element::HTMLScriptElementTypeId;
use dom::element::HTMLSelectElementTypeId;
use dom::element::HTMLStyleElementTypeId;
use dom::element::HTMLTableDataCellElementTypeId;
+use dom::element::HTMLTableElementTypeId;
use dom::element::HTMLTableHeaderCellElementTypeId;
+use dom::element::HTMLTableRowElementTypeId;
+use dom::element::HTMLTableSectionElementTypeId;
use dom::element::HTMLTextAreaElementTypeId;
use dom::element::HTMLTitleElementTypeId;
use dom::event::Event;
@@ -67,7 +73,10 @@ use dom::htmloptionelement::HTMLOptionElement;
use dom::htmlscriptelement::HTMLScriptElement;
use dom::htmlselectelement::HTMLSelectElement;
use dom::htmlstyleelement::HTMLStyleElement;
+use dom::htmltableelement::HTMLTableElement;
use dom::htmltablecellelement::HTMLTableCellElement;
+use dom::htmltablerowelement::HTMLTableRowElement;
+use dom::htmltablesectionelement::HTMLTableSectionElement;
use dom::htmltextareaelement::HTMLTextAreaElement;
use dom::htmltitleelement::HTMLTitleElement;
use dom::node::{Node, NodeHelpers, ElementNodeTypeId, CloneChildrenFlag};
@@ -226,9 +235,25 @@ pub fn vtable_for<'a>(node: &'a JSRef<'a, Node>) -> &'a VirtualMethods + 'a {
let element: &'a JSRef<'a, HTMLStyleElement> = HTMLStyleElementCast::to_borrowed_ref(node).unwrap();
element as &'a VirtualMethods + 'a
}
+ ElementNodeTypeId(HTMLTableElementTypeId) => {
+ let element: &'a JSRef<'a, HTMLTableElement> =
+ HTMLTableElementCast::to_borrowed_ref(node).unwrap();
+ element as &'a VirtualMethods + 'a
+ }
ElementNodeTypeId(HTMLTableDataCellElementTypeId) |
ElementNodeTypeId(HTMLTableHeaderCellElementTypeId) => {
- let element: &'a JSRef<'a, HTMLTableCellElement> = HTMLTableCellElementCast::to_borrowed_ref(node).unwrap();
+ 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
}
ElementNodeTypeId(HTMLTextAreaElementTypeId) => {
diff --git a/components/script/layout_interface.rs b/components/script/layout_interface.rs
index c3afb15c057..0f3f4fbacbf 100644
--- a/components/script/layout_interface.rs
+++ b/components/script/layout_interface.rs
@@ -29,6 +29,9 @@ pub enum Msg {
/// Adds the given stylesheet to the document.
LoadStylesheetMsg(Url),
+ /// Puts a document into quirks mode, causing the quirks mode stylesheet to be loaded.
+ SetQuirksModeMsg,
+
/// Requests a reflow.
ReflowMsg(Box<Reflow>),
diff --git a/components/servo/Cargo.lock b/components/servo/Cargo.lock
index bb04bd528af..edede8178fc 100644
--- a/components/servo/Cargo.lock
+++ b/components/servo/Cargo.lock
@@ -121,7 +121,7 @@ dependencies = [
[[package]]
name = "cssparser"
version = "0.1.0"
-source = "git+https://github.com/servo/rust-cssparser#cbbfd66f794bd019bbdeaefc88b29eff455b62e5"
+source = "git+https://github.com/servo/rust-cssparser#3f98f1308b769b5d61efc6c133ac520df2b074ac"
dependencies = [
"encoding 0.2.0 (git+https://github.com/lifthrasiir/rust-encoding)",
]
@@ -410,6 +410,7 @@ dependencies = [
name = "layout"
version = "0.0.1"
dependencies = [
+ "cssparser 0.1.0 (git+https://github.com/servo/rust-cssparser)",
"encoding 0.2.0 (git+https://github.com/lifthrasiir/rust-encoding)",
"geom 0.1.0 (git+https://github.com/servo/rust-geom)",
"gfx 0.0.1",
@@ -672,6 +673,7 @@ dependencies = [
name = "util"
version = "0.0.1"
dependencies = [
+ "cssparser 0.1.0 (git+https://github.com/servo/rust-cssparser)",
"geom 0.1.0 (git+https://github.com/servo/rust-geom)",
"layers 0.1.0 (git+https://github.com/servo/rust-layers)",
"string_cache 0.0.0 (git+https://github.com/servo/string-cache)",
diff --git a/components/style/legacy.rs b/components/style/legacy.rs
index f99314667b8..6b22d70ca76 100644
--- a/components/style/legacy.rs
+++ b/components/style/legacy.rs
@@ -5,6 +5,17 @@
//! Legacy presentational attributes defined in the HTML5 specification: `<td width>`,
//! `<input size>`, and so forth.
+use node::{TElement, TElementAttributes, TNode};
+use properties::{BackgroundColorDeclaration, BorderBottomWidthDeclaration};
+use properties::{BorderLeftWidthDeclaration, BorderRightWidthDeclaration};
+use properties::{BorderTopWidthDeclaration, SpecifiedValue, WidthDeclaration, specified};
+use selector_matching::{DeclarationBlock, Stylist};
+
+use cssparser::RGBAColor;
+use servo_util::geometry::Au;
+use servo_util::smallvec::VecLike;
+use servo_util::str::{AutoLpa, LengthLpa, PercentageLpa};
+
/// Legacy presentational attributes that take a length as defined in HTML5 § 2.4.4.4.
pub enum LengthAttribute {
/// `<td width>`
@@ -17,3 +28,187 @@ pub enum IntegerAttribute {
SizeIntegerAttribute,
}
+/// Legacy presentational attributes that take a nonnegative integer as defined in HTML5 § 2.4.4.2.
+pub enum UnsignedIntegerAttribute {
+ /// `<td border>`
+ BorderUnsignedIntegerAttribute,
+ /// `<td colspan>`
+ ColSpanUnsignedIntegerAttribute,
+}
+
+/// Legacy presentational attributes that take a simple color as defined in HTML5 § 2.4.6.
+pub enum SimpleColorAttribute {
+ /// `<body bgcolor>`
+ BgColorSimpleColorAttribute,
+}
+
+/// Extension methods for `Stylist` that cause rules to be synthesized for legacy attributes.
+pub trait PresentationalHintSynthesis {
+ /// Synthesizes rules from various HTML attributes (mostly legacy junk from HTML4) that confer
+ /// *presentational hints* as defined in the HTML5 specification. This handles stuff like
+ /// `<body bgcolor>`, `<input size>`, `<td width>`, and so forth.
+ ///
+ /// NB: Beware! If you add an attribute to this list, be sure to add it to
+ /// `common_style_affecting_attributes` or `rare_style_affecting_attributes` as appropriate. If
+ /// you don't, you risk strange random nondeterministic failures due to false positives in
+ /// style sharing.
+ fn synthesize_presentational_hints_for_legacy_attributes<'a,E,N,V>(
+ &self,
+ node: &N,
+ matching_rules_list: &mut V,
+ shareable: &mut bool)
+ where E: TElement<'a> +
+ TElementAttributes,
+ N: TNode<'a,E>,
+ V: VecLike<DeclarationBlock>;
+ /// Synthesizes rules for the legacy `bgcolor` attribute.
+ fn synthesize_presentational_hint_for_legacy_background_color_attribute<'a,E,V>(
+ &self,
+ element: E,
+ matching_rules_list:
+ &mut V,
+ shareable: &mut bool)
+ where
+ E: TElement<'a> +
+ TElementAttributes,
+ V: VecLike<
+ DeclarationBlock>;
+ /// Synthesizes rules for the legacy `border` attribute.
+ fn synthesize_presentational_hint_for_legacy_border_attribute<'a,E,V>(
+ &self,
+ element: E,
+ matching_rules_list: &mut V,
+ shareable: &mut bool)
+ where
+ E: TElement<'a> +
+ TElementAttributes,
+ V: VecLike<DeclarationBlock>;
+}
+
+impl PresentationalHintSynthesis for Stylist {
+ fn synthesize_presentational_hints_for_legacy_attributes<'a,E,N,V>(
+ &self,
+ node: &N,
+ matching_rules_list: &mut V,
+ shareable: &mut bool)
+ where E: TElement<'a> +
+ TElementAttributes,
+ N: TNode<'a,E>,
+ V: VecLike<DeclarationBlock> {
+ let element = node.as_element();
+ match element.get_local_name() {
+ name if *name == atom!("td") => {
+ match element.get_length_attribute(WidthLengthAttribute) {
+ AutoLpa => {}
+ PercentageLpa(percentage) => {
+ let width_value = specified::LPA_Percentage(percentage);
+ matching_rules_list.vec_push(DeclarationBlock::from_declaration(
+ WidthDeclaration(SpecifiedValue(width_value))));
+ *shareable = false
+ }
+ LengthLpa(length) => {
+ let width_value = specified::LPA_Length(specified::Au_(length));
+ matching_rules_list.vec_push(DeclarationBlock::from_declaration(
+ WidthDeclaration(SpecifiedValue(width_value))));
+ *shareable = false
+ }
+ }
+ self.synthesize_presentational_hint_for_legacy_background_color_attribute(
+ element,
+ matching_rules_list,
+ shareable);
+ self.synthesize_presentational_hint_for_legacy_border_attribute(
+ element,
+ matching_rules_list,
+ shareable);
+ }
+ name if *name == atom!("table") => {
+ self.synthesize_presentational_hint_for_legacy_background_color_attribute(
+ element,
+ matching_rules_list,
+ shareable);
+ self.synthesize_presentational_hint_for_legacy_border_attribute(
+ element,
+ matching_rules_list,
+ shareable);
+ }
+ 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(
+ element,
+ matching_rules_list,
+ shareable);
+ }
+ name if *name == atom!("input") => {
+ match element.get_integer_attribute(SizeIntegerAttribute) {
+ Some(value) if value != 0 => {
+ // Per HTML 4.01 § 17.4, this value is in characters if `type` is `text` or
+ // `password` and in pixels otherwise.
+ //
+ // FIXME(pcwalton): More use of atoms, please!
+ let value = match element.get_attr(&ns!(""), &atom!("type")) {
+ Some("text") | Some("password") => {
+ specified::ServoCharacterWidth(value)
+ }
+ _ => specified::Au_(Au::from_px(value as int)),
+ };
+ matching_rules_list.vec_push(DeclarationBlock::from_declaration(
+ WidthDeclaration(SpecifiedValue(specified::LPA_Length(
+ value)))));
+ *shareable = false
+ }
+ Some(_) | None => {}
+ }
+ }
+ _ => {}
+ }
+ }
+
+ fn synthesize_presentational_hint_for_legacy_background_color_attribute<'a,E,V>(
+ &self,
+ element: E,
+ matching_rules_list:
+ &mut V,
+ shareable: &mut bool)
+ where
+ E: TElement<'a> +
+ TElementAttributes,
+ V: VecLike<
+ DeclarationBlock> {
+ match element.get_simple_color_attribute(BgColorSimpleColorAttribute) {
+ None => {}
+ Some(color) => {
+ matching_rules_list.vec_push(DeclarationBlock::from_declaration(
+ BackgroundColorDeclaration(SpecifiedValue(RGBAColor(color)))));
+ *shareable = false
+ }
+ }
+ }
+
+ fn synthesize_presentational_hint_for_legacy_border_attribute<'a,E,V>(
+ &self,
+ element: E,
+ matching_rules_list: &mut V,
+ shareable: &mut bool)
+ where
+ E: TElement<'a> +
+ TElementAttributes,
+ V: VecLike<DeclarationBlock> {
+ match element.get_unsigned_integer_attribute(BorderUnsignedIntegerAttribute) {
+ None => {}
+ Some(length) => {
+ let width_value = specified::Au_(Au::from_px(length as int));
+ matching_rules_list.vec_push(DeclarationBlock::from_declaration(
+ BorderTopWidthDeclaration(SpecifiedValue(width_value))));
+ matching_rules_list.vec_push(DeclarationBlock::from_declaration(
+ BorderLeftWidthDeclaration(SpecifiedValue(width_value))));
+ matching_rules_list.vec_push(DeclarationBlock::from_declaration(
+ BorderBottomWidthDeclaration(SpecifiedValue(width_value))));
+ matching_rules_list.vec_push(DeclarationBlock::from_declaration(
+ BorderRightWidthDeclaration(SpecifiedValue(width_value))));
+ *shareable = false
+ }
+ }
+ }
+}
+
diff --git a/components/style/lib.rs b/components/style/lib.rs
index e6e0e77f7a5..d28ba62b112 100644
--- a/components/style/lib.rs
+++ b/components/style/lib.rs
@@ -41,7 +41,8 @@ pub use selector_matching::{DeclarationBlock, CommonStyleAffectingAttributes};
pub use selector_matching::{CommonStyleAffectingAttributeInfo, CommonStyleAffectingAttributeMode};
pub use selector_matching::{AttrIsPresentMode, AttrIsEqualMode};
pub use selector_matching::{matches, matches_simple_selector, common_style_affecting_attributes};
-pub use selector_matching::{RECOMMENDED_SELECTOR_BLOOM_FILTER_SIZE,SELECTOR_WHITESPACE};
+pub use selector_matching::{rare_style_affecting_attributes};
+pub use selector_matching::{RECOMMENDED_SELECTOR_BLOOM_FILTER_SIZE, SELECTOR_WHITESPACE};
pub use properties::{cascade, cascade_anonymous, computed};
pub use properties::{PropertyDeclaration, ComputedValues, computed_values, style_structs};
pub use properties::{PropertyDeclarationBlock, parse_style_attribute}; // Style attributes
@@ -49,11 +50,14 @@ pub use properties::{CSSFloat, DeclaredValue, PropertyDeclarationParseResult};
pub use properties::{Angle, AngleOrCorner, AngleAoc, CornerAoc};
pub use properties::{Left, Right, Bottom, Top};
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::{SimpleSelector,LocalNameSelector};
+pub use selectors::{SimpleSelector, LocalNameSelector, parse_selector_list_from_str};
pub use cssparser::{Color, RGBA};
-pub use legacy::{IntegerAttribute, LengthAttribute, SizeIntegerAttribute, WidthLengthAttribute};
+pub use legacy::{BgColorSimpleColorAttribute, BorderUnsignedIntegerAttribute};
+pub use legacy::{ColSpanUnsignedIntegerAttribute, IntegerAttribute, LengthAttribute};
+pub use legacy::{SimpleColorAttribute, SizeIntegerAttribute, UnsignedIntegerAttribute};
+pub use legacy::{WidthLengthAttribute};
pub use font_face::{Source, LocalSource, UrlSource_};
mod stylesheets;
diff --git a/components/style/media_queries.rs b/components/style/media_queries.rs
index 33d5203064e..f014be0114d 100644
--- a/components/style/media_queries.rs
+++ b/components/style/media_queries.rs
@@ -8,12 +8,14 @@ use cssparser::ast::*;
use errors::{ErrorLoggerIterator, log_css_error};
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 parsing_utils::{BufferedIter, ParserIter};
use properties::common_types::*;
use properties::longhands;
use servo_util::geometry::ViewportPx;
+use stylesheets;
use url::Url;
pub struct MediaRule {
@@ -95,8 +97,11 @@ impl Device {
}
}
-pub fn parse_media_rule(rule: AtRule, parent_rules: &mut Vec<CSSRule>,
- namespaces: &NamespaceMap, base_url: &Url) {
+pub fn parse_media_rule(context: &ParserContext,
+ rule: AtRule,
+ parent_rules: &mut Vec<CSSRule>,
+ namespaces: &NamespaceMap,
+ base_url: &Url) {
let media_queries = parse_media_query_list(rule.prelude.as_slice());
let block = match rule.block {
Some(block) => block,
@@ -108,9 +113,17 @@ pub fn parse_media_rule(rule: AtRule, parent_rules: &mut Vec<CSSRule>,
let mut rules = vec!();
for rule in ErrorLoggerIterator(parse_rule_list(block.into_iter())) {
match rule {
- QualifiedRule_(rule) => parse_style_rule(rule, &mut rules, namespaces, base_url),
- AtRule_(rule) => parse_nested_at_rule(
- rule.name.as_slice().to_ascii_lower().as_slice(), rule, &mut rules, namespaces, base_url),
+ QualifiedRule_(rule) => {
+ stylesheets::parse_style_rule(context, 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 {
diff --git a/components/style/node.rs b/components/style/node.rs
index 5a765f2798d..a1c9a8071c4 100644
--- a/components/style/node.rs
+++ b/components/style/node.rs
@@ -5,7 +5,8 @@
//! Traits that nodes must implement. Breaks the otherwise-cyclic dependency between layout and
//! style.
-use legacy::{IntegerAttribute, LengthAttribute};
+use cssparser::RGBA;
+use legacy::{IntegerAttribute, LengthAttribute, SimpleColorAttribute, UnsignedIntegerAttribute};
use selectors::AttrSelector;
use servo_util::str::LengthOrPercentageOrAuto;
use string_cache::{Atom, Namespace};
@@ -47,6 +48,7 @@ pub trait TElement<'a> : Copy {
fn get_enabled_state(self) -> bool;
fn get_checked_state(self) -> bool;
fn has_class(self, name: &Atom) -> bool;
+ fn has_nonzero_border(self) -> bool;
// Ordinarily I wouldn't use callbacks like this, but the alternative is
// really messy, since there is a `JSRef` and a `RefCell` involved. Maybe
@@ -58,4 +60,6 @@ pub trait TElement<'a> : Copy {
pub trait TElementAttributes : Copy {
fn get_length_attribute(self, attribute: LengthAttribute) -> LengthOrPercentageOrAuto;
fn get_integer_attribute(self, attribute: IntegerAttribute) -> Option<i32>;
+ fn get_unsigned_integer_attribute(self, attribute: UnsignedIntegerAttribute) -> Option<u32>;
+ fn get_simple_color_attribute(self, attribute: SimpleColorAttribute) -> Option<RGBA>;
}
diff --git a/components/style/selector_matching.rs b/components/style/selector_matching.rs
index 7cd1fd1d232..5482745721f 100644
--- a/components/style/selector_matching.rs
+++ b/components/style/selector_matching.rs
@@ -11,21 +11,31 @@ use sync::Arc;
use url::Url;
use servo_util::bloom::BloomFilter;
-use servo_util::geometry::Au;
use servo_util::resource_files::read_resource_file;
use servo_util::smallvec::VecLike;
use servo_util::sort;
-use servo_util::str::{AutoLpa, LengthLpa, PercentageLpa};
use string_cache::Atom;
-use legacy::{SizeIntegerAttribute, WidthLengthAttribute};
+use legacy::PresentationalHintSynthesis;
use media_queries::Device;
use node::{TElement, TElementAttributes, TNode};
-use properties::{PropertyDeclaration, PropertyDeclarationBlock, SpecifiedValue, WidthDeclaration};
-use properties::{specified};
-use selectors::*;
+use properties::{PropertyDeclaration, PropertyDeclarationBlock};
+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};
+#[deriving(Clone, PartialEq)]
pub enum StylesheetOrigin {
UserAgentOrigin,
AuthorOrigin,
@@ -295,7 +305,6 @@ impl Stylist {
after_map: PerPseudoElementSelectorMap::new(),
rules_source_order: 0u,
};
- // FIXME: Add quirks-mode.css in quirks mode.
// FIXME: Add iso-8859-9.css when the document’s encoding is ISO-8859-8.
// FIXME: presentational-hints.css should be at author origin with zero specificity.
// (Does it make a difference?)
@@ -391,6 +400,15 @@ impl Stylist {
self.is_dirty |= is_dirty;
}
+ pub fn add_quirks_mode_stylesheet(&mut self) {
+ self.add_stylesheet(Stylesheet::from_bytes(
+ read_resource_file(["quirks-mode.css"]).unwrap().as_slice(),
+ Url::parse("chrome:///quirks-mode.css").unwrap(),
+ None,
+ None,
+ UserAgentOrigin))
+ }
+
pub fn add_stylesheet(&mut self, stylesheet: Stylesheet) {
self.stylesheets.push(stylesheet);
self.is_dirty = true;
@@ -477,62 +495,6 @@ impl Stylist {
shareable
}
-
- /// Synthesizes rules from various HTML attributes (mostly legacy junk from HTML4) that confer
- /// *presentational hints* as defined in the HTML5 specification. This handles stuff like
- /// `<body bgcolor>`, `<input size>`, `<td width>`, and so forth.
- fn synthesize_presentational_hints_for_legacy_attributes<'a,E,N,V>(
- &self,
- node: &N,
- matching_rules_list: &mut V,
- shareable: &mut bool)
- where E: TElement<'a> +
- TElementAttributes,
- N: TNode<'a,E>,
- V: VecLike<DeclarationBlock> {
- let element = node.as_element();
- match element.get_local_name() {
- name if *name == atom!("td") => {
- match element.get_length_attribute(WidthLengthAttribute) {
- AutoLpa => {}
- PercentageLpa(percentage) => {
- let width_value = specified::LPA_Percentage(percentage);
- matching_rules_list.vec_push(DeclarationBlock::from_declaration(
- WidthDeclaration(SpecifiedValue(width_value))));
- *shareable = false
- }
- LengthLpa(length) => {
- let width_value = specified::LPA_Length(specified::Au_(length));
- matching_rules_list.vec_push(DeclarationBlock::from_declaration(
- WidthDeclaration(SpecifiedValue(width_value))));
- *shareable = false
- }
- };
- }
- name if *name == atom!("input") => {
- match element.get_integer_attribute(SizeIntegerAttribute) {
- Some(value) if value != 0 => {
- // Per HTML 4.01 § 17.4, this value is in characters if `type` is `text` or
- // `password` and in pixels otherwise.
- //
- // FIXME(pcwalton): More use of atoms, please!
- let value = match element.get_attr(&ns!(""), &atom!("type")) {
- Some("text") | Some("password") => {
- specified::ServoCharacterWidth(value)
- }
- _ => specified::Au_(Au::from_px(value as int)),
- };
- matching_rules_list.vec_push(DeclarationBlock::from_declaration(
- WidthDeclaration(SpecifiedValue(specified::LPA_Length(
- value)))));
- *shareable = false
- }
- Some(_) | None => {}
- }
- }
- _ => {}
- }
- }
}
struct PerOriginSelectorMap {
@@ -858,6 +820,13 @@ pub fn common_style_affecting_attributes() -> [CommonStyleAffectingAttributeInfo
]
}
+/// Attributes that, if present, disable style sharing. All legacy HTML attributes must be in
+/// either this list or `common_style_affecting_attributes`. See the comment in
+/// `synthesize_presentational_hints_for_legacy_attributes`.
+pub fn rare_style_affecting_attributes() -> [Atom, ..3] {
+ [ atom!("bgcolor"), atom!("border"), atom!("colspan") ]
+}
+
/// Determines whether the given element matches the given single selector.
///
/// NB: If you add support for any new kinds of selectors to this routine, be sure to set
@@ -1052,6 +1021,12 @@ pub fn matches_simple_selector<'a,E,N>(selector: &SimpleSelector,
matches_generic_nth_child(element, 0, 1, true, true)
}
+ ServoNonzeroBorder => {
+ *shareable = false;
+ let elem = element.as_element();
+ elem.has_nonzero_border()
+ }
+
Negation(ref negated) => {
*shareable = false;
!negated.iter().all(|s| matches_simple_selector(s, element, shareable))
@@ -1204,12 +1179,16 @@ mod tests {
/// Each sublist of the result contains the Rules for one StyleRule.
fn get_mock_rules(css_selectors: &[&str]) -> Vec<Vec<Rule>> {
use namespaces::NamespaceMap;
- use selectors::parse_selector_list;
+ use selectors::{ParserContext, parse_selector_list};
+ use selector_matching::AuthorOrigin;
use cssparser::tokenize;
let namespaces = NamespaceMap::new();
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| {
Rule {
selector: s.compound_selectors.clone(),
diff --git a/components/style/selectors.rs b/components/style/selectors.rs
index da0e2b7b638..1d72a530383 100644
--- a/components/style/selectors.rs
+++ b/components/style/selectors.rs
@@ -9,10 +9,16 @@ use sync::Arc;
use cssparser::ast::*;
use cssparser::{tokenize, parse_nth};
+use selector_matching::{StylesheetOrigin, UserAgentOrigin};
use string_cache::{Atom, Namespace};
use namespaces::NamespaceMap;
+/// Ambient data used by the parser.
+pub struct ParserContext {
+ /// The origin of this stylesheet.
+ pub origin: StylesheetOrigin,
+}
#[deriving(PartialEq, Clone)]
pub struct Selector {
@@ -79,7 +85,8 @@ pub enum SimpleSelector {
NthLastOfType(i32, i32),
FirstOfType,
LastOfType,
- OnlyOfType
+ OnlyOfType,
+ ServoNonzeroBorder,
// ...
}
@@ -111,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.
pub struct SelectorList {
selectors: Vec<Selector>
@@ -127,70 +128,9 @@ pub fn get_selector_list_selectors<'a>(selector_list: &'a SelectorList) -> &'a [
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>;
-/// 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,
pseudo_element: &Option<PseudoElement>) -> u32 {
@@ -231,7 +171,7 @@ fn compute_specificity(mut selector: &CompoundSelector,
// | &Empty | &Lang(*)
| &NthChild(..) | &NthLastChild(..)
| &NthOfType(..) | &NthLastOfType(..)
- | &FirstOfType | &LastOfType | &OnlyOfType
+ | &FirstOfType | &LastOfType | &OnlyOfType | &ServoNonzeroBorder
=> specificity.class_like_selectors += 1,
&NamespaceSelector(..) => (),
&Negation(ref negated)
@@ -247,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
/// * `Ok(None)`: Not a type selector, could be something else. `iter` was not consumed.
@@ -309,67 +223,6 @@ enum SimpleSelectorParseResult {
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
/// * `Ok(None)`: Not a simple selector, could be something else. `iter` was not consumed.
@@ -489,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 })
+}
+
+/// 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))];
-fn parse_simple_pseudo_class(name: &str) -> Result<SimpleSelector, ()> {
+ 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() {
"any-link" => Ok(AnyLink),
"link" => Ok(Link),
@@ -506,27 +575,12 @@ fn parse_simple_pseudo_class(name: &str) -> Result<SimpleSelector, ()> {
"first-of-type" => Ok(FirstOfType),
"last-of-type" => Ok(LastOfType),
"only-of-type" => Ok(OnlyOfType),
-// "empty" => Ok(Empty),
- _ => 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) },
+ "-servo-nonzero-border" if context.origin == UserAgentOrigin => Ok(ServoNonzeroBorder),
+// "empty" => Ok(Empty),
_ => Err(())
}
}
-
fn parse_pseudo_element(name: String) -> Result<PseudoElement, ()> {
match name.as_slice().to_ascii_lower().as_slice() {
// All supported pseudo-elements
@@ -551,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
#[inline]
@@ -593,6 +632,7 @@ mod tests {
use sync::Arc;
use cssparser;
use namespaces::NamespaceMap;
+ use selector_matching::AuthorOrigin;
use string_cache::Atom;
use super::*;
@@ -601,7 +641,10 @@ mod tests {
}
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 {
diff --git a/components/style/stylesheets.rs b/components/style/stylesheets.rs
index efec013ee2d..824172af037 100644
--- a/components/style/stylesheets.rs
+++ b/components/style/stylesheets.rs
@@ -10,12 +10,11 @@ use encoding::EncodingRef;
use cssparser::{decode_stylesheet_bytes, tokenize, parse_stylesheet_rules, ToCss};
use cssparser::ast::*;
-use selectors;
+use selectors::{mod, ParserContext};
use properties;
use errors::{ErrorLoggerIterator, log_css_error};
use namespaces::{NamespaceMap, parse_namespace_rule};
-use media_queries::{Device, MediaRule, parse_media_rule};
-use media_queries;
+use media_queries::{mod, Device, MediaRule};
use font_face::{FontFaceRule, Source, parse_font_face_rule, iter_font_face_rules_inner};
use selector_matching::StylesheetOrigin;
@@ -53,9 +52,12 @@ impl Stylesheet {
Stylesheet::from_bytes(bytes.as_slice(), base_url, protocol_encoding_label, environment_encoding, origin)
}
- pub fn from_bytes(
- bytes: &[u8], base_url: Url, protocol_encoding_label: Option<&str>,
- environment_encoding: Option<EncodingRef>, origin: StylesheetOrigin) -> Stylesheet {
+ pub fn from_bytes(bytes: &[u8],
+ base_url: Url,
+ protocol_encoding_label: Option<&str>,
+ environment_encoding: Option<EncodingRef>,
+ origin: StylesheetOrigin)
+ -> Stylesheet {
// TODO: bytes.as_slice could be bytes.container_as_bytes()
let (string, _) = decode_stylesheet_bytes(
bytes.as_slice(), protocol_encoding_label, environment_encoding);
@@ -67,6 +69,11 @@ impl Stylesheet {
static STATE_IMPORTS: uint = 2;
static STATE_NAMESPACES: uint = 3;
static STATE_BODY: uint = 4;
+
+ let parser_context = ParserContext {
+ origin: origin,
+ };
+
let mut state: uint = STATE_CHARSET;
let mut rules = vec!();
@@ -77,7 +84,7 @@ impl Stylesheet {
match rule {
QualifiedRule_(rule) => {
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) => {
let lower_name = rule.name.as_slice().to_ascii_lower();
@@ -114,7 +121,12 @@ impl Stylesheet {
},
_ => {
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>,
- namespaces: &NamespaceMap, base_url: &Url) {
- let QualifiedRule { location, prelude, block} = rule;
+pub fn parse_style_rule(context: &ParserContext,
+ rule: QualifiedRule,
+ parent_rules: &mut Vec<CSSRule>,
+ namespaces: &NamespaceMap,
+ base_url: &Url) {
+ let QualifiedRule {
+ location,
+ prelude,
+ block
+ } = rule;
// FIXME: avoid doing this for valid selectors
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{
selectors: selectors,
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,
callback: |&StyleRule|) {
for rule in rules.iter() {
diff --git a/components/util/Cargo.toml b/components/util/Cargo.toml
index 29fc71d25a5..331fd2fb840 100644
--- a/components/util/Cargo.toml
+++ b/components/util/Cargo.toml
@@ -7,6 +7,9 @@ authors = ["The Servo Project Developers"]
name = "util"
path = "lib.rs"
+[dependencies.cssparser]
+git = "https://github.com/servo/rust-cssparser"
+
[dependencies.geom]
git = "https://github.com/servo/rust-geom"
diff --git a/components/util/lib.rs b/components/util/lib.rs
index f556dda6eb9..b0e4cff8c62 100644
--- a/components/util/lib.rs
+++ b/components/util/lib.rs
@@ -13,6 +13,7 @@ extern crate log;
extern crate alloc;
extern crate collections;
+extern crate cssparser;
extern crate geom;
extern crate getopts;
extern crate layers;
diff --git a/components/util/str.rs b/components/util/str.rs
index 28aedee4f30..7b2cfdf5107 100644
--- a/components/util/str.rs
+++ b/components/util/str.rs
@@ -4,6 +4,8 @@
use geometry::Au;
+use cssparser::{mod, RGBA, RGBAColor};
+use std::ascii::AsciiExt;
use std::from_str::FromStr;
use std::iter::Filter;
use std::str::{CharEq, CharSplits};
@@ -61,7 +63,8 @@ pub static HTML_SPACE_CHARACTERS: StaticCharVec = &[
'\u000d',
];
-pub fn split_html_space_chars<'a>(s: &'a str) -> Filter<'a, &'a str, CharSplits<'a, StaticCharVec>> {
+pub fn split_html_space_chars<'a>(s: &'a str)
+ -> Filter<'a, &'a str, CharSplits<'a, StaticCharVec>> {
s.split(HTML_SPACE_CHARACTERS).filter(|&split| !split.is_empty())
}
@@ -76,7 +79,6 @@ fn do_parse_integer<T: Iterator<char>>(input: T) -> Option<i64> {
}
}
-
let mut input = input.skip_while(|c| {
HTML_SPACE_CHARACTERS.iter().any(|s| s == c)
}).peekable();
@@ -184,6 +186,137 @@ pub fn parse_length(mut value: &str) -> LengthOrPercentageOrAuto {
}
}
+/// Parses a legacy color per HTML5 § 2.4.6. If unparseable, `Err` is returned.
+pub fn parse_legacy_color(mut input: &str) -> Result<RGBA,()> {
+ // Steps 1 and 2.
+ if input.len() == 0 {
+ return Err(())
+ }
+
+ // Step 3.
+ input = input.trim_left_chars(Whitespace).trim_right_chars(Whitespace);
+
+ // Step 4.
+ if input.eq_ignore_ascii_case("transparent") {
+ return Err(())
+ }
+
+ // Step 5.
+ match cssparser::parse_color_keyword(input) {
+ Ok(RGBAColor(rgba)) => return Ok(rgba),
+ _ => {}
+ }
+
+ // Step 6.
+ if input.len() == 4 {
+ match (input.as_bytes()[0],
+ hex(input.as_bytes()[1] as char),
+ hex(input.as_bytes()[2] as char),
+ hex(input.as_bytes()[3] as char)) {
+ (b'#', Ok(r), Ok(g), Ok(b)) => {
+ return Ok(RGBA {
+ red: (r as f32) * 17.0 / 255.0,
+ green: (g as f32) * 17.0 / 255.0,
+ blue: (b as f32) * 17.0 / 255.0,
+ alpha: 1.0,
+ })
+ }
+ _ => {}
+ }
+ }
+
+ // Step 7.
+ let mut new_input = String::new();
+ for ch in input.chars() {
+ if ch as u32 > 0xffff {
+ new_input.push_str("00")
+ } else {
+ new_input.push(ch)
+ }
+ }
+ let mut input = new_input.as_slice();
+
+ // Step 8.
+ for (char_count, (index, _)) in input.char_indices().enumerate() {
+ if char_count == 128 {
+ input = input.slice_to(index);
+ break
+ }
+ }
+
+ // Step 9.
+ if input.char_at(0) == '#' {
+ input = input.slice_from(1)
+ }
+
+ // Step 10.
+ let mut new_input = Vec::new();
+ for ch in input.chars() {
+ if hex(ch).is_ok() {
+ new_input.push(ch as u8)
+ } else {
+ new_input.push(b'0')
+ }
+ }
+ let mut input = new_input;
+
+ // Step 11.
+ while input.len() == 0 || (input.len() % 3) != 0 {
+ input.push(b'0')
+ }
+
+ // Step 12.
+ let mut length = input.len() / 3;
+ let (mut red, mut green, mut blue) = (input.slice_to(length),
+ input.slice(length, length * 2),
+ input.slice_from(length * 2));
+
+ // Step 13.
+ if length > 8 {
+ red = red.slice_from(length - 8);
+ green = green.slice_from(length - 8);
+ blue = blue.slice_from(length - 8);
+ length = 8
+ }
+
+ // Step 14.
+ while length > 2 && red[0] == b'0' && green[0] == b'0' && blue[0] == b'0' {
+ red = red.slice_from(1);
+ green = green.slice_from(1);
+ blue = blue.slice_from(1);
+ length -= 1
+ }
+
+ // Steps 15-20.
+ return Ok(RGBA {
+ red: hex_string(red).unwrap() as f32 / 255.0,
+ green: hex_string(green).unwrap() as f32 / 255.0,
+ blue: hex_string(blue).unwrap() as f32 / 255.0,
+ alpha: 1.0,
+ });
+
+ fn hex(ch: char) -> Result<u8,()> {
+ match ch {
+ '0'...'9' => Ok((ch as u8) - b'0'),
+ 'a'...'f' => Ok((ch as u8) - b'a' + 10),
+ 'A'...'F' => Ok((ch as u8) - b'A' + 10),
+ _ => 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)
+ }
+ }
+ }
+}
+
#[deriving(Clone, Eq, PartialEq, Hash, Show)]
pub struct LowercaseString {