servo: Merge #3622 - layout: Introduce support for legacy presentational attributes to selector matching, and use it for `<input size>` and `<td width>` (from pcwalton:html4ever); r=jdm

This implements a general framework for legacy presentational attributes
to the DOM and style calculation, so that adding more of them later will
be straightforward.

Source-Repo: https://github.com/servo/servo
Source-Revision: 0aeecfc41d5f0c637960fcddf87cc2db3e5efeea
This commit is contained in:
Patrick Walton 2014-10-14 14:06:36 -06:00
Родитель 03432319a7
Коммит 0aeca90f2d
26 изменённых файлов: 511 добавлений и 214 удалений

Просмотреть файл

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

Просмотреть файл

@ -30,10 +30,9 @@ use flow_ref::FlowRef;
use fragment::{Fragment, GenericFragment, IframeFragment, IframeFragmentInfo, ImageFragment};
use fragment::{ImageFragmentInfo, InlineAbsoluteHypotheticalFragment};
use fragment::{InlineAbsoluteHypotheticalFragmentInfo, InlineBlockFragment};
use fragment::{InlineBlockFragmentInfo, InputFragment, InputFragmentInfo, SpecificFragmentInfo};
use fragment::{TableCellFragment, TableColumnFragment, TableColumnFragmentInfo, TableFragment};
use fragment::{TableRowFragment, TableWrapperFragment, UnscannedTextFragment};
use fragment::{UnscannedTextFragmentInfo};
use fragment::{InlineBlockFragmentInfo, InputFragment, SpecificFragmentInfo, TableCellFragment};
use fragment::{TableColumnFragment, TableColumnFragmentInfo, TableFragment, TableRowFragment};
use fragment::{TableWrapperFragment, UnscannedTextFragment, UnscannedTextFragmentInfo};
use inline::{InlineFragments, InlineFlow};
use parallel;
use table_wrapper::TableWrapperFlow;
@ -222,21 +221,6 @@ impl<'a> FlowConstructor<'a> {
}
}
fn build_fragment_info_for_input(&mut self, node: &ThreadSafeLayoutNode) -> SpecificFragmentInfo {
//FIXME: would it make more sense to use HTMLInputElement::input_type instead of the raw
// value? definitely for string comparisons.
let elem = node.as_element();
let data = match elem.get_attr(&ns!(""), &atom!("type")) {
Some("checkbox") | Some("radio") => None,
Some("button") | Some("submit") | Some("reset") =>
Some(node.get_input_value().len() as u32),
Some("file") => Some(node.get_input_size()),
_ => Some(node.get_input_size()),
};
data.map(|size| InputFragment(InputFragmentInfo { size: size }))
.unwrap_or(GenericFragment)
}
/// Builds specific `Fragment` info for the given node.
///
/// This does *not* construct the text for generated content (but, for generated content with
@ -253,7 +237,7 @@ impl<'a> FlowConstructor<'a> {
self.build_fragment_info_for_image(node, node.image_url())
}
Some(ElementNodeTypeId(HTMLInputElementTypeId)) => {
self.build_fragment_info_for_input(node)
InputFragment
}
Some(ElementNodeTypeId(HTMLObjectElementTypeId)) => {
let data = node.get_object_data();
@ -1216,7 +1200,7 @@ impl FlowConstructionUtils for FlowRef {
/// This must not be public because only the layout constructor can do this.
fn finish(&mut self, context: &LayoutContext) {
if !context.shared.opts.bubble_inline_sizes_separately {
self.get_mut().bubble_inline_sizes(context)
self.get_mut().bubble_inline_sizes()
}
}
}

Просмотреть файл

@ -188,7 +188,7 @@ pub trait Flow: fmt::Show + ToString + Sync {
/// called on this flow, all child flows have had their minimum and preferred inline-sizes set.
/// This function must decide minimum/preferred inline-sizes based on its children's inline-
/// sizes and the dimensions of any boxes it is responsible for flowing.
fn bubble_inline_sizes(&mut self, _ctx: &LayoutContext) {
fn bubble_inline_sizes(&mut self) {
fail!("bubble_inline_sizes not yet implemented")
}

Просмотреть файл

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

Просмотреть файл

@ -914,7 +914,7 @@ impl Flow for InlineFlow {
self
}
fn bubble_inline_sizes(&mut self, layout_context: &LayoutContext) {
fn bubble_inline_sizes(&mut self) {
let _scope = layout_debug_scope!("inline::bubble_inline_sizes {:s}", self.base.debug_id());
let writing_mode = self.base.writing_mode;
@ -927,7 +927,7 @@ impl Flow for InlineFlow {
debug!("Flow: measuring {}", *fragment);
let fragment_intrinsic_inline_sizes =
fragment.intrinsic_inline_sizes(layout_context);
fragment.intrinsic_inline_sizes();
intrinsic_inline_sizes.minimum_inline_size = max(
intrinsic_inline_sizes.minimum_inline_size,
fragment_intrinsic_inline_sizes.minimum_inline_size);

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

@ -53,7 +53,7 @@ impl Flow for TableColGroupFlow {
self
}
fn bubble_inline_sizes(&mut self, _: &LayoutContext) {
fn bubble_inline_sizes(&mut self) {
let _scope = layout_debug_scope!("table_colgroup::bubble_inline_sizes {:s}",
self.base.debug_id());

Просмотреть файл

@ -171,7 +171,7 @@ impl Flow for TableRowFlow {
/// responsible for flowing.
/// Min/pref inline-sizes set by this function are used in automatic table layout calculation.
/// The specified column inline-sizes of children cells are used in fixed table layout calculation.
fn bubble_inline_sizes(&mut self, _: &LayoutContext) {
fn bubble_inline_sizes(&mut self) {
let _scope = layout_debug_scope!("table_row::bubble_inline_sizes {:s}",
self.block_flow.base.debug_id());

Просмотреть файл

@ -132,14 +132,16 @@ impl Flow for TableRowGroupFlow {
&self.col_pref_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.
/// 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.
/// Those are used in fixed table layout calculation
fn bubble_inline_sizes(&mut self, _: &LayoutContext) {
///
/// 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 {:s}",
self.block_flow.base.debug_id());

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

@ -53,6 +53,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 layout_interface::{LayoutRPC, LayoutChan};
use dom::bindings::utils::WindowProxyHandler;
@ -234,3 +235,6 @@ impl JSTraceable for Box<LayoutRPC+'static> {
// Do nothing
}
}
untraceable!(LengthOrPercentageOrAuto)

Просмотреть файл

@ -11,7 +11,8 @@ use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
use dom::bindings::codegen::Bindings::ElementBinding;
use dom::bindings::codegen::Bindings::ElementBinding::ElementMethods;
use dom::bindings::codegen::Bindings::NamedNodeMapBinding::NamedNodeMapMethods;
use dom::bindings::codegen::InheritTypes::{ElementDerived, NodeCast};
use dom::bindings::codegen::InheritTypes::{ElementDerived, HTMLInputElementDerived};
use dom::bindings::codegen::InheritTypes::{HTMLTableCellElementDerived, NodeCast};
use dom::bindings::js::{MutNullableJS, JS, JSRef, Temporary, TemporaryPushable};
use dom::bindings::js::{OptionalSettable, OptionalRootable, Root};
use dom::bindings::utils::{Reflectable, Reflector};
@ -23,16 +24,19 @@ use dom::document::{Document, DocumentHelpers};
use dom::domtokenlist::DOMTokenList;
use dom::eventtarget::{EventTarget, NodeTargetTypeId};
use dom::htmlcollection::HTMLCollection;
use dom::htmlinputelement::{HTMLInputElement, LayoutHTMLInputElementHelpers};
use dom::htmlserializer::serialize;
use dom::htmltablecellelement::{HTMLTableCellElement, HTMLTableCellElementHelpers};
use dom::node::{ElementNodeTypeId, Node, NodeHelpers, NodeIterator, document_from_node};
use dom::node::{window_from_node, LayoutNodeHelpers};
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 servo_util::namespace;
use servo_util::str::DOMString;
use servo_util::str::{DOMString, LengthOrPercentageOrAuto};
use std::ascii::StrAsciiExt;
use std::cell::{Ref, RefMut, RefCell};
@ -211,6 +215,10 @@ pub trait RawLayoutElementHelpers {
unsafe fn get_attr_atom_for_layout(&self, namespace: &Namespace, name: &Atom) -> Option<Atom>;
unsafe fn has_class_for_layout(&self, name: &Atom) -> bool;
unsafe fn get_classes_for_layout(&self) -> Option<&'static [Atom]>;
unsafe fn get_length_attribute_for_layout(&self, length_attribute: LengthAttribute)
-> LengthOrPercentageOrAuto;
unsafe fn get_integer_attribute_for_layout(&self, integer_attribute: IntegerAttribute)
-> Option<i32>;
}
impl RawLayoutElementHelpers for Element {
@ -288,6 +296,36 @@ impl RawLayoutElementHelpers for Element {
(*attr).value_tokens_forever()
})
}
#[inline]
#[allow(unrooted_must_root)]
unsafe fn get_length_attribute_for_layout(&self, length_attribute: LengthAttribute)
-> LengthOrPercentageOrAuto {
match length_attribute {
WidthLengthAttribute => {
if !self.is_htmltablecellelement() {
fail!("I'm not a table cell!")
}
let this: &HTMLTableCellElement = mem::transmute(self);
this.get_width()
}
}
}
#[inline]
#[allow(unrooted_must_root)]
unsafe fn get_integer_attribute_for_layout(&self, integer_attribute: IntegerAttribute)
-> Option<i32> {
match integer_attribute {
SizeIntegerAttribute => {
if !self.is_htmlinputelement() {
fail!("I'm not a form input!")
}
let this: &HTMLInputElement = mem::transmute(self);
Some(this.get_size_for_layout() as i32)
}
}
}
}
pub trait LayoutElementHelpers {
@ -1133,3 +1171,4 @@ impl<'a> style::TElement<'a> for JSRef<'a, Element> {
}
}
}

Просмотреть файл

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

Просмотреть файл

@ -2,21 +2,28 @@
* 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::InheritTypes::HTMLTableCellElementDerived;
use dom::bindings::codegen::InheritTypes::{HTMLElementCast, HTMLTableCellElementDerived};
use dom::bindings::js::JSRef;
use dom::bindings::utils::{Reflectable, Reflector};
use dom::document::Document;
use dom::element::{ElementTypeId, HTMLTableDataCellElementTypeId, HTMLTableHeaderCellElementTypeId};
use dom::element::{ElementTypeId, HTMLTableDataCellElementTypeId};
use dom::element::{HTMLTableHeaderCellElementTypeId};
use dom::eventtarget::{EventTarget, NodeTargetTypeId};
use dom::htmlelement::HTMLElement;
use dom::node::ElementNodeTypeId;
use servo_util::str::DOMString;
use dom::virtualmethods::VirtualMethods;
use servo_util::str::{AutoLpa, DOMString, LengthOrPercentageOrAuto};
use servo_util::str;
use std::cell::Cell;
use string_cache::Atom;
#[jstraceable]
#[must_root]
#[privatize]
pub struct HTMLTableCellElement {
htmlelement: HTMLElement,
width: Cell<LengthOrPercentageOrAuto>,
}
impl HTMLTableCellElementDerived for EventTarget {
@ -32,16 +39,58 @@ impl HTMLTableCellElementDerived for EventTarget {
impl 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)
htmlelement: HTMLElement::new_inherited(type_id, tag_name, prefix, document),
width: Cell::new(AutoLpa)
}
}
#[inline]
pub fn htmlelement<'a>(&'a self) -> &'a HTMLElement {
pub fn htmlelement(&self) -> &HTMLElement {
&self.htmlelement
}
}
pub trait HTMLTableCellElementHelpers {
fn get_width(&self) -> LengthOrPercentageOrAuto;
}
impl HTMLTableCellElementHelpers for HTMLTableCellElement {
fn get_width(&self) -> LengthOrPercentageOrAuto {
self.width.get()
}
}
impl<'a> VirtualMethods for JSRef<'a, HTMLTableCellElement> {
fn super_type<'a>(&'a self) -> Option<&'a VirtualMethods> {
let htmlelement: &JSRef<HTMLElement> = HTMLElementCast::from_borrowed_ref(self);
Some(htmlelement as &VirtualMethods)
}
fn after_set_attr(&self, name: &Atom, value: DOMString) {
match self.super_type() {
Some(ref s) => s.after_set_attr(name, value.clone()),
_ => {}
}
match name.as_slice() {
"width" => self.width.set(str::parse_length(value.as_slice())),
_ => {}
}
}
fn before_remove_attr(&self, name: &Atom, value: DOMString) {
match self.super_type() {
Some(ref s) => s.before_remove_attr(name, value),
_ => {}
}
match name.as_slice() {
"width" => self.width.set(AutoLpa),
_ => {}
}
}
}
impl Reflectable for HTMLTableCellElement {
fn reflector<'a>(&'a self) -> &'a Reflector {
self.htmlelement.reflector()

Просмотреть файл

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

Просмотреть файл

@ -0,0 +1,19 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//! Legacy presentational attributes defined in the HTML5 specification: `<td width>`,
//! `<input size>`, and so forth.
/// Legacy presentational attributes that take a length as defined in HTML5 § 2.4.4.4.
pub enum LengthAttribute {
/// `<td width>`
WidthLengthAttribute,
}
/// Legacy presentational attributes that take an integer as defined in HTML5 § 2.4.4.2.
pub enum IntegerAttribute {
/// `<input size>`
SizeIntegerAttribute,
}

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

@ -24,6 +24,13 @@ pub mod specified {
Au_(Au), // application units
Em(CSSFloat),
Ex(CSSFloat),
/// HTML5 "character width", as defined in HTML5 § 14.5.4.
///
/// This cannot be specified by the user directly and is only generated by
/// `Stylist::synthesize_rules_for_legacy_attributes()`.
ServoCharacterWidth(i32),
// XXX uncomment when supported:
// Ch(CSSFloat),
// Rem(CSSFloat),
@ -245,6 +252,15 @@ pub mod computed {
let x_height = 0.5; // TODO: find that from the font
reference_font_size.scale_by(value * x_height)
},
specified::ServoCharacterWidth(value) => {
// This applies the *converting a character width to pixels* algorithm as specified
// in HTML5 § 14.5.4.
//
// TODO(pcwalton): Find these from the font.
let average_advance = reference_font_size.scale_by(0.5);
let max_advance = reference_font_size;
average_advance.scale_by(value as CSSFloat - 1.0) + max_advance
}
}
}

Просмотреть файл

@ -11,14 +11,18 @@ 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 media_queries::{Device, Screen};
use node::{TElement, TNode};
use properties::{PropertyDeclaration, PropertyDeclarationBlock};
use node::{TElement, TElementAttributes, TNode};
use properties::{PropertyDeclaration, PropertyDeclarationBlock, SpecifiedValue, WidthDeclaration};
use properties::{specified};
use selectors::*;
use stylesheets::{Stylesheet, iter_stylesheet_style_rules};
@ -80,15 +84,14 @@ impl SelectorMap {
///
/// Extract matching rules as per node's ID, classes, tag name, etc..
/// Sort the Rules at the end to maintain cascading order.
fn get_all_matching_rules<'a,
E:TElement<'a>,
N:TNode<'a, E>,
V:VecLike<DeclarationBlock>>(
&self,
node: &N,
parent_bf: &Option<Box<BloomFilter>>,
matching_rules_list: &mut V,
shareable: &mut bool) {
fn get_all_matching_rules<'a,E,N,V>(&self,
node: &N,
parent_bf: &Option<Box<BloomFilter>>,
matching_rules_list: &mut V,
shareable: &mut bool)
where E: TElement<'a> + TElementAttributes,
N: TNode<'a,E>,
V: VecLike<DeclarationBlock> {
if self.empty {
return
}
@ -143,34 +146,36 @@ impl SelectorMap {
}
}
fn get_matching_rules_from_hash<'a,
E:TElement<'a>,
N:TNode<'a, E>,
V:VecLike<DeclarationBlock>>(
node: &N,
parent_bf: &Option<Box<BloomFilter>>,
hash: &HashMap<Atom, Vec<Rule>>,
key: &Atom,
matching_rules: &mut V,
shareable: &mut bool) {
fn get_matching_rules_from_hash<'a,E,N,V>(node: &N,
parent_bf: &Option<Box<BloomFilter>>,
hash: &HashMap<Atom, Vec<Rule>>,
key: &Atom,
matching_rules: &mut V,
shareable: &mut bool)
where E: TElement<'a> + TElementAttributes,
N: TNode<'a,E>,
V: VecLike<DeclarationBlock> {
match hash.find(key) {
Some(rules) => {
SelectorMap::get_matching_rules(node, parent_bf, rules.as_slice(), matching_rules, shareable)
SelectorMap::get_matching_rules(node,
parent_bf,
rules.as_slice(),
matching_rules,
shareable)
}
None => {}
}
}
/// Adds rules in `rules` that match `node` to the `matching_rules` list.
fn get_matching_rules<'a,
E:TElement<'a>,
N:TNode<'a, E>,
V:VecLike<DeclarationBlock>>(
node: &N,
parent_bf: &Option<Box<BloomFilter>>,
rules: &[Rule],
matching_rules: &mut V,
shareable: &mut bool) {
fn get_matching_rules<'a,E,N,V>(node: &N,
parent_bf: &Option<Box<BloomFilter>>,
rules: &[Rule],
matching_rules: &mut V,
shareable: &mut bool)
where E: TElement<'a> + TElementAttributes,
N: TNode<'a,E>,
V: VecLike<DeclarationBlock> {
for rule in rules.iter() {
if matches_compound_selector(&*rule.selector, node, parent_bf, shareable) {
matching_rules.vec_push(rule.declarations.clone());
@ -348,17 +353,17 @@ impl Stylist {
/// The returned boolean indicates whether the style is *shareable*; that is, whether the
/// matched selectors are simple enough to allow the matching logic to be reduced to the logic
/// in `css::matching::PrivateMatchMethods::candidate_element_allows_for_style_sharing`.
pub fn push_applicable_declarations<'a,
E:TElement<'a>,
N:TNode<'a, E>,
V:VecLike<DeclarationBlock>>(
pub fn push_applicable_declarations<'a,E,N,V>(
&self,
element: &N,
parent_bf: &Option<Box<BloomFilter>>,
style_attribute: Option<&PropertyDeclarationBlock>,
pseudo_element: Option<PseudoElement>,
applicable_declarations: &mut V)
-> bool {
-> bool
where E: TElement<'a> + TElementAttributes,
N: TNode<'a,E>,
V: VecLike<DeclarationBlock> {
assert!(element.is_element());
assert!(style_attribute.is_none() || pseudo_element.is_none(),
"Style attributes do not apply to pseudo-elements");
@ -371,33 +376,46 @@ impl Stylist {
let mut shareable = true;
// Step 1: Normal rules.
// Step 1: Virtual rules that are synthesized from legacy HTML attributes.
self.synthesize_presentational_hints_for_legacy_attributes(element,
applicable_declarations,
&mut shareable);
// Step 2: Normal rules.
map.user_agent.normal.get_all_matching_rules(element,
parent_bf,
applicable_declarations,
&mut shareable);
map.user.normal.get_all_matching_rules(element, parent_bf, applicable_declarations, &mut shareable);
map.author.normal.get_all_matching_rules(element, parent_bf, applicable_declarations, &mut shareable);
map.user.normal.get_all_matching_rules(element,
parent_bf,
applicable_declarations,
&mut shareable);
map.author.normal.get_all_matching_rules(element,
parent_bf,
applicable_declarations,
&mut shareable);
// Step 2: Normal style attributes.
// Step 3: Normal style attributes.
style_attribute.map(|sa| {
shareable = false;
applicable_declarations.vec_push(DeclarationBlock::from_declarations(sa.normal.clone()))
applicable_declarations.vec_push(DeclarationBlock::from_declarations(sa.normal
.clone()))
});
// Step 3: Author-supplied `!important` rules.
// Step 4: Author-supplied `!important` rules.
map.author.important.get_all_matching_rules(element,
parent_bf,
applicable_declarations,
&mut shareable);
// Step 4: `!important` style attributes.
// Step 5: `!important` style attributes.
style_attribute.map(|sa| {
shareable = false;
applicable_declarations.vec_push(DeclarationBlock::from_declarations(sa.important.clone()))
applicable_declarations.vec_push(DeclarationBlock::from_declarations(sa.important
.clone()))
});
// Step 5: User and UA `!important` rules.
// Step 6: User and UA `!important` rules.
map.user.important.get_all_matching_rules(element,
parent_bf,
applicable_declarations,
@ -409,6 +427,62 @@ 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 {
@ -470,14 +544,20 @@ impl DeclarationBlock {
specificity: 0,
}
}
/// A convenience function to create a declaration block from a single declaration. This is
/// primarily used in `synthesize_rules_for_legacy_attributes`.
#[inline]
pub fn from_declaration(rule: PropertyDeclaration) -> DeclarationBlock {
DeclarationBlock::from_declarations(Arc::new(vec![rule]))
}
}
pub fn matches<'a,E,N>(
selector_list: &SelectorList,
element: &N,
parent_bf: &Option<Box<BloomFilter>>)
-> bool
where E: TElement<'a>, N: TNode<'a,E> {
pub fn matches<'a,E,N>(selector_list: &SelectorList,
element: &N,
parent_bf: &Option<Box<BloomFilter>>)
-> bool
where E: TElement<'a>, N: TNode<'a,E> {
get_selector_list_selectors(selector_list).iter().any(|selector|
selector.pseudo_element.is_none() &&
matches_compound_selector(&*selector.compound_selectors, element, parent_bf, &mut false))
@ -489,14 +569,12 @@ pub fn matches<'a,E,N>(
/// `shareable` to false unless you are willing to update the style sharing logic. Otherwise things
/// will almost certainly break as nodes will start mistakenly sharing styles. (See the code in
/// `main/css/matching.rs`.)
fn matches_compound_selector<'a,
E:TElement<'a>,
N:TNode<'a, E>>(
selector: &CompoundSelector,
element: &N,
parent_bf: &Option<Box<BloomFilter>>,
shareable: &mut bool)
-> bool {
fn matches_compound_selector<'a,E,N>(selector: &CompoundSelector,
element: &N,
parent_bf: &Option<Box<BloomFilter>>,
shareable: &mut bool)
-> bool
where E: TElement<'a>, N: TNode<'a,E> {
match matches_compound_selector_internal(selector, element, parent_bf, shareable) {
Matched => true,
_ => false
@ -555,13 +633,12 @@ enum SelectorMatchingResult {
/// Quickly figures out whether or not the compound selector is worth doing more
/// work on. If the simple selectors don't match, or there's a child selector
/// that does not appear in the bloom parent bloom filter, we can exit early.
fn can_fast_reject<'a,E,N>(
mut selector: &CompoundSelector,
element: &N,
parent_bf: &Option<Box<BloomFilter>>,
shareable: &mut bool)
-> Option<SelectorMatchingResult>
where E: TElement<'a>, N: TNode<'a,E> {
fn can_fast_reject<'a,E,N>(mut selector: &CompoundSelector,
element: &N,
parent_bf: &Option<Box<BloomFilter>>,
shareable: &mut bool)
-> Option<SelectorMatchingResult>
where E: TElement<'a>, N: TNode<'a,E> {
if !selector.simple_selectors.iter().all(|simple_selector| {
matches_simple_selector(simple_selector, element, shareable) }) {
return Some(NotMatchedAndRestartFromClosestLaterSibling);
@ -617,14 +694,12 @@ fn can_fast_reject<'a,E,N>(
return None;
}
fn matches_compound_selector_internal<'a,
E:TElement<'a>,
N:TNode<'a, E>>(
selector: &CompoundSelector,
element: &N,
parent_bf: &Option<Box<BloomFilter>>,
shareable: &mut bool)
-> SelectorMatchingResult {
fn matches_compound_selector_internal<'a,E,N>(selector: &CompoundSelector,
element: &N,
parent_bf: &Option<Box<BloomFilter>>,
shareable: &mut bool)
-> SelectorMatchingResult
where E: TElement<'a>, N: TNode<'a,E> {
match can_fast_reject(selector, element, parent_bf, shareable) {
None => {},
Some(result) => return result,
@ -693,12 +768,11 @@ fn matches_compound_selector_internal<'a,
/// will almost certainly break as nodes will start mistakenly sharing styles. (See the code in
/// `main/css/matching.rs`.)
#[inline]
pub fn matches_simple_selector<'a,E,N>(
selector: &SimpleSelector,
element: &N,
shareable: &mut bool)
-> bool
where E:TElement<'a>, N:TNode<'a,E> {
pub fn matches_simple_selector<'a,E,N>(selector: &SimpleSelector,
element: &N,
shareable: &mut bool)
-> bool
where E: TElement<'a>, N: TNode<'a,E> {
match *selector {
LocalNameSelector(LocalName { ref name, ref lower_name }) => {
let name = if element.is_html_element_in_html_document() { lower_name } else { name };
@ -877,14 +951,13 @@ fn url_is_visited(_url: &str) -> bool {
}
#[inline]
fn matches_generic_nth_child<'a,E,N>(
element: &N,
a: i32,
b: i32,
is_of_type: bool,
is_from_end: bool)
-> bool
where E: TElement<'a>, N: TNode<'a,E> {
fn matches_generic_nth_child<'a,E,N>(element: &N,
a: i32,
b: i32,
is_of_type: bool,
is_from_end: bool)
-> bool
where E: TElement<'a>, N: TNode<'a,E> {
let mut node = element.clone();
// fail if we can't find a parent or if the node is the root element
// of the document (Cf. Selectors Level 3)
@ -933,7 +1006,7 @@ fn matches_generic_nth_child<'a,E,N>(
}
#[inline]
fn matches_root<'a, E:TElement<'a>,N:TNode<'a, E>>(element: &N) -> bool {
fn matches_root<'a,E,N>(element: &N) -> bool where E: TElement<'a>, N: TNode<'a,E> {
match element.parent_node() {
Some(parent) => parent.is_document(),
None => false
@ -941,7 +1014,7 @@ fn matches_root<'a, E:TElement<'a>,N:TNode<'a, E>>(element: &N) -> bool {
}
#[inline]
fn matches_first_child<'a, E:TElement<'a>,N:TNode<'a, E>>(element: &N) -> bool {
fn matches_first_child<'a,E,N>(element: &N) -> bool where E: TElement<'a>, N: TNode<'a,E> {
let mut node = element.clone();
loop {
match node.prev_sibling() {
@ -963,7 +1036,7 @@ fn matches_first_child<'a, E:TElement<'a>,N:TNode<'a, E>>(element: &N) -> bool {
}
#[inline]
fn matches_last_child<'a, E:TElement<'a>,N:TNode<'a, E>>(element: &N) -> bool {
fn matches_last_child<'a,E,N>(element: &N) -> bool where E: TElement<'a>, N: TNode<'a,E> {
let mut node = element.clone();
loop {
match node.next_sibling() {
@ -1081,3 +1154,4 @@ mod tests {
assert!(selector_map.class_hash.find(&Atom::from_slice("foo")).is_none());
}
}

Просмотреть файл

@ -2,8 +2,11 @@
* 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 geometry::Au;
use std::from_str::FromStr;
use std::iter::Filter;
use std::str::CharSplits;
use std::str::{CharEq, CharSplits};
pub type DOMString = String;
pub type StaticCharVec = &'static [char];
@ -24,11 +27,26 @@ pub fn null_str_as_empty_ref<'a>(s: &'a Option<DOMString>) -> &'a str {
}
}
/// Whitespace as defined by HTML5 § 2.4.1.
struct Whitespace;
impl CharEq for Whitespace {
#[inline]
fn matches(&mut self, ch: char) -> bool {
match ch {
' ' | '\t' | '\x0a' | '\x0c' | '\x0d' => true,
_ => false,
}
}
#[inline]
fn only_ascii(&self) -> bool {
true
}
}
pub fn is_whitespace(s: &str) -> bool {
s.chars().all(|c| match c {
'\u0020' | '\u0009' | '\u000D' | '\u000A' => true,
_ => false
})
s.chars().all(|c| Whitespace.matches(c))
}
/// A "space character" according to:
@ -108,3 +126,61 @@ pub fn parse_unsigned_integer<T: Iterator<char>>(input: T) -> Option<u32> {
result.to_u32()
})
}
pub enum LengthOrPercentageOrAuto {
AutoLpa,
PercentageLpa(f64),
LengthLpa(Au),
}
/// Parses a length per HTML5 § 2.4.4.4. If unparseable, `AutoLpa` is returned.
pub fn parse_length(mut value: &str) -> LengthOrPercentageOrAuto {
value = value.trim_left_chars(Whitespace);
if value.len() == 0 {
return AutoLpa
}
if value.starts_with("+") {
value = value.slice_from(1)
}
value = value.trim_left_chars('0');
if value.len() == 0 {
return AutoLpa
}
let mut end_index = value.len();
let (mut found_full_stop, mut found_percent) = (false, false);
for (i, ch) in value.chars().enumerate() {
match ch {
'0'..'9' => continue,
'%' => {
found_percent = true;
end_index = i;
break
}
'.' if !found_full_stop => {
found_full_stop = true;
continue
}
_ => {
end_index = i;
break
}
}
}
value = value.slice_to(end_index);
if found_percent {
let result: Option<f64> = FromStr::from_str(value);
match result {
Some(number) => return PercentageLpa((number as f64) / 100.0),
None => return AutoLpa,
}
}
match FromStr::from_str(value) {
Some(number) => LengthLpa(Au::from_px(number)),
None => AutoLpa,
}
}

Просмотреть файл

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