mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
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
This commit is contained in:
commit
8e31e5f987
46 changed files with 1610 additions and 572 deletions
|
@ -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"
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
};
|
||||
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_sizes.minimum_length;
|
||||
total_inline_sizes.preferred_inline_size = total_inline_sizes.preferred_inline_size +
|
||||
parent_sizes.preferred;
|
||||
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.)
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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>) {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 style::computed_values::{content, display, white_space};
|
||||
use style::{AnyNamespace, AttrSelector, IntegerAttribute, LengthAttribute};
|
||||
use style::{PropertyDeclarationBlock, SpecificNamespace, TElement, TElementAttributes, TNode};
|
||||
use url::Url;
|
||||
use string_cache::{Atom, Namespace};
|
||||
use style::computed_values::{content, display, white_space};
|
||||
use style::{AnyNamespace, AttrSelector, BorderUnsignedIntegerAttribute, IntegerAttribute};
|
||||
use style::{LengthAttribute, PropertyDeclarationBlock, SimpleColorAttribute, SpecificNamespace};
|
||||
use style::{TElement, TElementAttributes, TNode, UnsignedIntegerAttribute};
|
||||
use url::Url;
|
||||
|
||||
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 {
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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),
|
||||
_ => ()
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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>),
|
||||
|
||||
|
|
4
components/servo/Cargo.lock
generated
4
components/servo/Cargo.lock
generated
|
@ -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)",
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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>;
|
||||
}
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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 })
|
||||
}
|
||||
|
||||
fn parse_simple_pseudo_class(name: &str) -> Result<SimpleSelector, ()> {
|
||||
/// Parse a comma-separated list of Selectors.
|
||||
/// aka Selector Group in http://www.w3.org/TR/css3-selectors/#grouping
|
||||
///
|
||||
/// Return the Selectors or None if there is an invalid selector.
|
||||
pub fn parse_selector_list<I>(context: &ParserContext, iter: I, namespaces: &NamespaceMap)
|
||||
-> Result<Vec<Selector>,()>
|
||||
where I: Iterator<ComponentValue> {
|
||||
let iter = &mut iter.peekable();
|
||||
let mut results = vec![try!(parse_selector(context, iter, namespaces))];
|
||||
|
||||
loop {
|
||||
skip_whitespace(iter);
|
||||
match iter.peek() {
|
||||
None => break, // EOF
|
||||
Some(&Comma) => {
|
||||
iter.next();
|
||||
}
|
||||
_ => return Err(()),
|
||||
}
|
||||
results.push(try!(parse_selector(context, iter, namespaces)));
|
||||
}
|
||||
Ok(results)
|
||||
}
|
||||
/// Build up a Selector.
|
||||
/// selector : simple_selector_sequence [ combinator simple_selector_sequence ]* ;
|
||||
///
|
||||
/// `Err` means invalid selector.
|
||||
fn parse_selector<I>(context: &ParserContext, iter: &mut Iter<I>, namespaces: &NamespaceMap)
|
||||
-> Result<Selector,()>
|
||||
where I: Iterator<ComponentValue> {
|
||||
let (first, mut pseudo_element) = try!(parse_simple_selectors(context, iter, namespaces));
|
||||
let mut compound = CompoundSelector{ simple_selectors: first, next: None };
|
||||
|
||||
while pseudo_element.is_none() {
|
||||
let any_whitespace = skip_whitespace(iter);
|
||||
let combinator = match iter.peek() {
|
||||
None => break, // EOF
|
||||
Some(&Comma) => break,
|
||||
Some(&Delim('>')) => { iter.next(); Child },
|
||||
Some(&Delim('+')) => { iter.next(); NextSibling },
|
||||
Some(&Delim('~')) => { iter.next(); LaterSibling },
|
||||
Some(_) => {
|
||||
if any_whitespace { Descendant }
|
||||
else { return Err(()) }
|
||||
}
|
||||
};
|
||||
let (simple_selectors, pseudo) = try!(parse_simple_selectors(context, iter, namespaces));
|
||||
compound = CompoundSelector {
|
||||
simple_selectors: simple_selectors,
|
||||
next: Some((box compound, combinator))
|
||||
};
|
||||
pseudo_element = pseudo;
|
||||
}
|
||||
Ok(Selector {
|
||||
specificity: compute_specificity(&compound, &pseudo_element),
|
||||
compound_selectors: Arc::new(compound),
|
||||
pseudo_element: pseudo_element,
|
||||
})
|
||||
}
|
||||
|
||||
/// Level 3: Parse **one** simple_selector
|
||||
fn parse_negation(context: &ParserContext,
|
||||
arguments: Vec<ComponentValue>,
|
||||
namespaces: &NamespaceMap)
|
||||
-> Result<SimpleSelector,()> {
|
||||
let iter = &mut arguments.into_iter().peekable();
|
||||
match try!(parse_type_selector(iter, namespaces)) {
|
||||
Some(type_selector) => Ok(Negation(type_selector)),
|
||||
None => {
|
||||
match try!(parse_one_simple_selector(context,
|
||||
iter,
|
||||
namespaces,
|
||||
/* inside_negation = */ true)) {
|
||||
Some(SimpleSelectorResult(simple_selector)) => {
|
||||
Ok(Negation(vec![simple_selector]))
|
||||
}
|
||||
_ => Err(())
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// simple_selector_sequence
|
||||
/// : [ type_selector | universal ] [ HASH | class | attrib | pseudo | negation ]*
|
||||
/// | [ HASH | class | attrib | pseudo | negation ]+
|
||||
///
|
||||
/// `Err(())` means invalid selector
|
||||
fn parse_simple_selectors<I>(context: &ParserContext,
|
||||
iter: &mut Iter<I>,
|
||||
namespaces: &NamespaceMap)
|
||||
-> Result<(Vec<SimpleSelector>, Option<PseudoElement>),()>
|
||||
where I: Iterator<ComponentValue> {
|
||||
let mut empty = true;
|
||||
let mut simple_selectors = match try!(parse_type_selector(iter, namespaces)) {
|
||||
None => vec![],
|
||||
Some(s) => { empty = false; s }
|
||||
};
|
||||
|
||||
let mut pseudo_element = None;
|
||||
loop {
|
||||
match try!(parse_one_simple_selector(context,
|
||||
iter,
|
||||
namespaces,
|
||||
/* inside_negation = */ false)) {
|
||||
None => break,
|
||||
Some(SimpleSelectorResult(s)) => { simple_selectors.push(s); empty = false },
|
||||
Some(PseudoElementResult(p)) => { pseudo_element = Some(p); empty = false; break },
|
||||
}
|
||||
}
|
||||
if empty {
|
||||
// An empty selector is invalid.
|
||||
Err(())
|
||||
} else {
|
||||
Ok((simple_selectors, pseudo_element))
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_functional_pseudo_class(context: &ParserContext,
|
||||
name: String,
|
||||
arguments: Vec<ComponentValue>,
|
||||
namespaces: &NamespaceMap,
|
||||
inside_negation: bool)
|
||||
-> Result<SimpleSelector,()> {
|
||||
match name.as_slice().to_ascii_lower().as_slice() {
|
||||
// "lang" => parse_lang(arguments),
|
||||
"nth-child" => parse_nth(arguments.as_slice()).map(|(a, b)| NthChild(a, b)),
|
||||
"nth-last-child" => parse_nth(arguments.as_slice()).map(|(a, b)| NthLastChild(a, b)),
|
||||
"nth-of-type" => parse_nth(arguments.as_slice()).map(|(a, b)| NthOfType(a, b)),
|
||||
"nth-last-of-type" => parse_nth(arguments.as_slice()).map(|(a, b)| NthLastOfType(a, b)),
|
||||
"not" => {
|
||||
if inside_negation {
|
||||
Err(())
|
||||
} else {
|
||||
parse_negation(context, arguments, namespaces)
|
||||
}
|
||||
}
|
||||
_ => Err(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse a simple selector other than a type selector.
|
||||
///
|
||||
/// * `Err(())`: Invalid selector, abort
|
||||
/// * `Ok(None)`: Not a simple selector, could be something else. `iter` was not consumed.
|
||||
/// * `Ok(Some(_))`: Parsed a simple selector or pseudo-element
|
||||
fn parse_one_simple_selector<I>(context: &ParserContext,
|
||||
iter: &mut Iter<I>,
|
||||
namespaces: &NamespaceMap,
|
||||
inside_negation: bool)
|
||||
-> Result<Option<SimpleSelectorParseResult>,()>
|
||||
where I: Iterator<ComponentValue> {
|
||||
match iter.peek() {
|
||||
Some(&IDHash(_)) => match iter.next() {
|
||||
Some(IDHash(id)) => Ok(Some(SimpleSelectorResult(
|
||||
IDSelector(Atom::from_slice(id.as_slice()))))),
|
||||
_ => panic!("Implementation error, this should not happen."),
|
||||
},
|
||||
Some(&Delim('.')) => {
|
||||
iter.next();
|
||||
match iter.next() {
|
||||
Some(Ident(class)) => Ok(Some(SimpleSelectorResult(
|
||||
ClassSelector(Atom::from_slice(class.as_slice()))))),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
Some(&SquareBracketBlock(_)) => match iter.next() {
|
||||
Some(SquareBracketBlock(content))
|
||||
=> Ok(Some(SimpleSelectorResult(try!(parse_attribute_selector(content, namespaces))))),
|
||||
_ => panic!("Implementation error, this should not happen."),
|
||||
},
|
||||
Some(&Colon) => {
|
||||
iter.next();
|
||||
match iter.next() {
|
||||
Some(Ident(name)) => match parse_simple_pseudo_class(context, name.as_slice()) {
|
||||
Err(()) => {
|
||||
match name.as_slice().to_ascii_lower().as_slice() {
|
||||
// Supported CSS 2.1 pseudo-elements only.
|
||||
// ** Do not add to this list! **
|
||||
"before" => Ok(Some(PseudoElementResult(Before))),
|
||||
"after" => Ok(Some(PseudoElementResult(After))),
|
||||
// "first-line" => PseudoElementResult(FirstLine),
|
||||
// "first-letter" => PseudoElementResult(FirstLetter),
|
||||
_ => Err(())
|
||||
}
|
||||
},
|
||||
Ok(result) => Ok(Some(SimpleSelectorResult(result))),
|
||||
},
|
||||
Some(Function(name, arguments))
|
||||
=> {
|
||||
Ok(Some(SimpleSelectorResult(try!(parse_functional_pseudo_class(
|
||||
context,
|
||||
name,
|
||||
arguments,
|
||||
namespaces,
|
||||
inside_negation)))))
|
||||
}
|
||||
Some(Colon) => {
|
||||
match iter.next() {
|
||||
Some(Ident(name))
|
||||
=> Ok(Some(PseudoElementResult(try!(parse_pseudo_element(name))))),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
_ => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_simple_pseudo_class(context: &ParserContext, name: &str) -> Result<SimpleSelector,()> {
|
||||
match name.to_ascii_lower().as_slice() {
|
||||
"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),
|
||||
"-servo-nonzero-border" if context.origin == UserAgentOrigin => Ok(ServoNonzeroBorder),
|
||||
// "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) },
|
||||
_ => 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 {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue