зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
03432319a7
Коммит
0aeca90f2d
|
@ -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; }
|
||||
|
|
Загрузка…
Ссылка в новой задаче