mirror of
https://github.com/servo/servo.git
synced 2025-07-24 15:50:21 +01:00
Implement simple layout for text, button, radio, and checkbox inputs. Implement simple interaction for checkboxes and radio buttons.
This commit is contained in:
parent
9b20d6e7d2
commit
f70bb68503
10 changed files with 422 additions and 50 deletions
|
@ -1516,7 +1516,7 @@ impl Flow for BlockFlow {
|
||||||
/// any fragments it is responsible for flowing.
|
/// any fragments it is responsible for flowing.
|
||||||
///
|
///
|
||||||
/// TODO(pcwalton): Inline blocks.
|
/// TODO(pcwalton): Inline blocks.
|
||||||
fn bubble_inline_sizes(&mut self, _: &LayoutContext) {
|
fn bubble_inline_sizes(&mut self, layout_context: &LayoutContext) {
|
||||||
let _scope = layout_debug_scope!("block::bubble_inline_sizes {:s}", self.base.debug_id());
|
let _scope = layout_debug_scope!("block::bubble_inline_sizes {:s}", self.base.debug_id());
|
||||||
|
|
||||||
let mut flags = self.base.flags;
|
let mut flags = self.base.flags;
|
||||||
|
@ -1572,7 +1572,7 @@ impl Flow for BlockFlow {
|
||||||
max(intrinsic_inline_sizes.preferred_inline_size,
|
max(intrinsic_inline_sizes.preferred_inline_size,
|
||||||
left_float_width + right_float_width);
|
left_float_width + right_float_width);
|
||||||
|
|
||||||
let fragment_intrinsic_inline_sizes = self.fragment.intrinsic_inline_sizes();
|
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,
|
intrinsic_inline_sizes.minimum_inline_size = max(intrinsic_inline_sizes.minimum_inline_size,
|
||||||
fragment_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,
|
intrinsic_inline_sizes.preferred_inline_size = max(intrinsic_inline_sizes.preferred_inline_size,
|
||||||
|
|
|
@ -27,12 +27,12 @@ use flow::{Flow, ImmutableFlowUtils, MutableOwnedFlowUtils};
|
||||||
use flow::{Descendants, AbsDescendants};
|
use flow::{Descendants, AbsDescendants};
|
||||||
use flow;
|
use flow;
|
||||||
use flow_ref::FlowRef;
|
use flow_ref::FlowRef;
|
||||||
use fragment::{InlineBlockFragment, InlineBlockFragmentInfo};
|
use fragment::{InlineBlockFragment, InlineBlockFragmentInfo, InputFragment};
|
||||||
use fragment::{Fragment, GenericFragment, IframeFragment, IframeFragmentInfo};
|
use fragment::{Fragment, GenericFragment, IframeFragment, IframeFragmentInfo};
|
||||||
use fragment::{ImageFragment, ImageFragmentInfo, SpecificFragmentInfo, TableFragment};
|
use fragment::{ImageFragment, ImageFragmentInfo, SpecificFragmentInfo, TableFragment};
|
||||||
use fragment::{TableCellFragment, TableColumnFragment, TableColumnFragmentInfo};
|
use fragment::{TableCellFragment, TableColumnFragment, TableColumnFragmentInfo};
|
||||||
use fragment::{TableRowFragment, TableWrapperFragment, UnscannedTextFragment};
|
use fragment::{TableRowFragment, TableWrapperFragment, UnscannedTextFragment, InputRadioButton};
|
||||||
use fragment::{UnscannedTextFragmentInfo};
|
use fragment::{UnscannedTextFragmentInfo, InputCheckbox, InputButton, InputText, InputFile};
|
||||||
use inline::{InlineFragments, InlineFlow};
|
use inline::{InlineFragments, InlineFlow};
|
||||||
use parallel;
|
use parallel;
|
||||||
use table_wrapper::TableWrapperFlow;
|
use table_wrapper::TableWrapperFlow;
|
||||||
|
@ -49,7 +49,7 @@ use wrapper::{Before, After, Normal};
|
||||||
|
|
||||||
use gfx::display_list::OpaqueNode;
|
use gfx::display_list::OpaqueNode;
|
||||||
use script::dom::element::{HTMLIFrameElementTypeId, HTMLImageElementTypeId};
|
use script::dom::element::{HTMLIFrameElementTypeId, HTMLImageElementTypeId};
|
||||||
use script::dom::element::{HTMLObjectElementTypeId};
|
use script::dom::element::{HTMLObjectElementTypeId, HTMLInputElementTypeId};
|
||||||
use script::dom::element::{HTMLTableColElementTypeId, HTMLTableDataCellElementTypeId};
|
use script::dom::element::{HTMLTableColElementTypeId, HTMLTableDataCellElementTypeId};
|
||||||
use script::dom::element::{HTMLTableElementTypeId, HTMLTableHeaderCellElementTypeId};
|
use script::dom::element::{HTMLTableElementTypeId, HTMLTableHeaderCellElementTypeId};
|
||||||
use script::dom::element::{HTMLTableRowElementTypeId, HTMLTableSectionElementTypeId};
|
use script::dom::element::{HTMLTableRowElementTypeId, HTMLTableSectionElementTypeId};
|
||||||
|
@ -221,6 +221,21 @@ 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!(""), "type") {
|
||||||
|
Some("checkbox") => InputCheckbox(node.get_input_checked()),
|
||||||
|
Some("button") | Some("submit") | Some("reset") =>
|
||||||
|
InputButton(node.get_input_value().len() as u32),
|
||||||
|
Some("radio") => InputRadioButton(node.get_input_checked()),
|
||||||
|
Some("file") => InputFile(node.get_input_size()),
|
||||||
|
_ => InputText(node.get_input_size()),
|
||||||
|
};
|
||||||
|
InputFragment(data)
|
||||||
|
}
|
||||||
|
|
||||||
/// Builds specific `Fragment` info for the given node.
|
/// Builds specific `Fragment` info for the given node.
|
||||||
///
|
///
|
||||||
/// This does *not* construct the text for generated content (but, for generated content with
|
/// This does *not* construct the text for generated content (but, for generated content with
|
||||||
|
@ -230,11 +245,14 @@ impl<'a> FlowConstructor<'a> {
|
||||||
pub fn build_specific_fragment_info_for_node(&mut self, node: &ThreadSafeLayoutNode)
|
pub fn build_specific_fragment_info_for_node(&mut self, node: &ThreadSafeLayoutNode)
|
||||||
-> SpecificFragmentInfo {
|
-> SpecificFragmentInfo {
|
||||||
match node.type_id() {
|
match node.type_id() {
|
||||||
|
Some(ElementNodeTypeId(HTMLIFrameElementTypeId)) => {
|
||||||
|
IframeFragment(IframeFragmentInfo::new(node))
|
||||||
|
}
|
||||||
Some(ElementNodeTypeId(HTMLImageElementTypeId)) => {
|
Some(ElementNodeTypeId(HTMLImageElementTypeId)) => {
|
||||||
self.build_fragment_info_for_image(node, node.image_url())
|
self.build_fragment_info_for_image(node, node.image_url())
|
||||||
}
|
}
|
||||||
Some(ElementNodeTypeId(HTMLIFrameElementTypeId)) => {
|
Some(ElementNodeTypeId(HTMLInputElementTypeId)) => {
|
||||||
IframeFragment(IframeFragmentInfo::new(node))
|
self.build_fragment_info_for_input(node)
|
||||||
}
|
}
|
||||||
Some(ElementNodeTypeId(HTMLObjectElementTypeId)) => {
|
Some(ElementNodeTypeId(HTMLObjectElementTypeId)) => {
|
||||||
let data = node.get_object_data();
|
let data = node.get_object_data();
|
||||||
|
@ -445,7 +463,8 @@ impl<'a> FlowConstructor<'a> {
|
||||||
|
|
||||||
// Special case: If this is generated content, then we need to initialize the accumulator
|
// Special case: If this is generated content, then we need to initialize the accumulator
|
||||||
// with the fragment corresponding to that content.
|
// with the fragment corresponding to that content.
|
||||||
if node.get_pseudo_element_type() != Normal {
|
if node.get_pseudo_element_type() != Normal ||
|
||||||
|
node.type_id() == Some(ElementNodeTypeId(HTMLInputElementTypeId)) {
|
||||||
let fragment_info = UnscannedTextFragment(UnscannedTextFragmentInfo::new(node));
|
let fragment_info = UnscannedTextFragment(UnscannedTextFragmentInfo::new(node));
|
||||||
let mut fragment = Fragment::new_from_specific_info(node, fragment_info);
|
let mut fragment = Fragment::new_from_specific_info(node, fragment_info);
|
||||||
inline_fragment_accumulator.fragments.push(&mut fragment);
|
inline_fragment_accumulator.fragments.push(&mut fragment);
|
||||||
|
|
|
@ -128,10 +128,11 @@ impl<E, S: Encoder<E>> Encodable<S, E> for Fragment {
|
||||||
/// Info specific to the kind of fragment. Keep this enum small.
|
/// Info specific to the kind of fragment. Keep this enum small.
|
||||||
#[deriving(Clone)]
|
#[deriving(Clone)]
|
||||||
pub enum SpecificFragmentInfo {
|
pub enum SpecificFragmentInfo {
|
||||||
InlineBlockFragment(InlineBlockFragmentInfo),
|
|
||||||
GenericFragment,
|
GenericFragment,
|
||||||
ImageFragment(ImageFragmentInfo),
|
|
||||||
IframeFragment(IframeFragmentInfo),
|
IframeFragment(IframeFragmentInfo),
|
||||||
|
ImageFragment(ImageFragmentInfo),
|
||||||
|
InlineBlockFragment(InlineBlockFragmentInfo),
|
||||||
|
InputFragment(InputFragmentInfo),
|
||||||
ScannedTextFragment(ScannedTextFragmentInfo),
|
ScannedTextFragment(ScannedTextFragmentInfo),
|
||||||
TableFragment,
|
TableFragment,
|
||||||
TableCellFragment,
|
TableCellFragment,
|
||||||
|
@ -155,6 +156,38 @@ impl InlineBlockFragmentInfo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A fragment that represents a displayable form element
|
||||||
|
#[deriving(Clone)]
|
||||||
|
pub enum InputFragmentInfo {
|
||||||
|
InputButton(u32),
|
||||||
|
InputText(u32),
|
||||||
|
InputCheckbox(bool),
|
||||||
|
InputRadioButton(bool),
|
||||||
|
InputFile(u32),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InputFragmentInfo {
|
||||||
|
fn size(&self) -> Option<u32> {
|
||||||
|
match self {
|
||||||
|
&InputText(size) | &InputFile(size) | &InputButton(size) => Some(size),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the original inline-size of the input.
|
||||||
|
fn input_inline_size(&self, font_style: &FontStyle, layout_context: &LayoutContext) -> Au {
|
||||||
|
match self.size() {
|
||||||
|
Some(size) => {
|
||||||
|
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 * (size as i32 - 1) + metrics.max_advance
|
||||||
|
}
|
||||||
|
None => Au::from_px(10)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A fragment that represents a replaced content image and its accompanying borders, shadows, etc.
|
/// A fragment that represents a replaced content image and its accompanying borders, shadows, etc.
|
||||||
#[deriving(Clone)]
|
#[deriving(Clone)]
|
||||||
pub struct ImageFragmentInfo {
|
pub struct ImageFragmentInfo {
|
||||||
|
@ -499,7 +532,8 @@ impl Fragment {
|
||||||
/// replaced elements.
|
/// replaced elements.
|
||||||
fn style_specified_intrinsic_inline_size(&self) -> IntrinsicISizes {
|
fn style_specified_intrinsic_inline_size(&self) -> IntrinsicISizes {
|
||||||
let (use_margins, use_padding) = match self.specific {
|
let (use_margins, use_padding) = match self.specific {
|
||||||
GenericFragment | IframeFragment(_) | ImageFragment(_) | InlineBlockFragment(_) => (true, true),
|
GenericFragment | IframeFragment(_) | ImageFragment(_) | InlineBlockFragment(_) |
|
||||||
|
InputFragment(_) => (true, true),
|
||||||
TableFragment | TableCellFragment => (false, true),
|
TableFragment | TableCellFragment => (false, true),
|
||||||
TableWrapperFragment => (true, false),
|
TableWrapperFragment => (true, false),
|
||||||
TableRowFragment => (false, false),
|
TableRowFragment => (false, false),
|
||||||
|
@ -1129,7 +1163,7 @@ impl Fragment {
|
||||||
text_fragment))
|
text_fragment))
|
||||||
}
|
}
|
||||||
GenericFragment | IframeFragment(..) | TableFragment | TableCellFragment |
|
GenericFragment | IframeFragment(..) | TableFragment | TableCellFragment |
|
||||||
TableRowFragment | TableWrapperFragment | InlineBlockFragment(_) => {
|
TableRowFragment | TableWrapperFragment | InlineBlockFragment(_) | InputFragment(_) => {
|
||||||
// FIXME(pcwalton): This is a bit of an abuse of the logging infrastructure. We
|
// FIXME(pcwalton): This is a bit of an abuse of the logging infrastructure. We
|
||||||
// should have a real `SERVO_DEBUG` system.
|
// should have a real `SERVO_DEBUG` system.
|
||||||
debug!("{:?}", self.build_debug_borders_around_fragment(display_list, flow_origin))
|
debug!("{:?}", self.build_debug_borders_around_fragment(display_list, flow_origin))
|
||||||
|
@ -1193,7 +1227,7 @@ impl Fragment {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the intrinsic inline-sizes of this fragment.
|
/// Returns the intrinsic inline-sizes of this fragment.
|
||||||
pub fn intrinsic_inline_sizes(&mut self) -> IntrinsicISizes {
|
pub fn intrinsic_inline_sizes(&mut self, layout_context: &LayoutContext) -> IntrinsicISizes {
|
||||||
let mut result = self.style_specified_intrinsic_inline_size();
|
let mut result = self.style_specified_intrinsic_inline_size();
|
||||||
|
|
||||||
match self.specific {
|
match self.specific {
|
||||||
|
@ -1214,6 +1248,12 @@ impl Fragment {
|
||||||
result.preferred_inline_size = max(result.preferred_inline_size,
|
result.preferred_inline_size = max(result.preferred_inline_size,
|
||||||
image_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) => {
|
ScannedTextFragment(ref text_fragment_info) => {
|
||||||
let range = &text_fragment_info.range;
|
let range = &text_fragment_info.range;
|
||||||
let min_line_inline_size = text_fragment_info.run.min_width_for_range(range);
|
let min_line_inline_size = text_fragment_info.run.min_width_for_range(range);
|
||||||
|
@ -1260,7 +1300,7 @@ impl Fragment {
|
||||||
pub fn content_inline_size(&self) -> Au {
|
pub fn content_inline_size(&self) -> Au {
|
||||||
match self.specific {
|
match self.specific {
|
||||||
GenericFragment | IframeFragment(_) | TableFragment | TableCellFragment | TableRowFragment |
|
GenericFragment | IframeFragment(_) | TableFragment | TableCellFragment | TableRowFragment |
|
||||||
TableWrapperFragment | InlineBlockFragment(_) => Au(0),
|
TableWrapperFragment | InlineBlockFragment(_) | InputFragment(_) => Au(0),
|
||||||
ImageFragment(ref image_fragment_info) => {
|
ImageFragment(ref image_fragment_info) => {
|
||||||
image_fragment_info.computed_inline_size()
|
image_fragment_info.computed_inline_size()
|
||||||
}
|
}
|
||||||
|
@ -1278,7 +1318,8 @@ impl Fragment {
|
||||||
pub fn content_block_size(&self, layout_context: &LayoutContext) -> Au {
|
pub fn content_block_size(&self, layout_context: &LayoutContext) -> Au {
|
||||||
match self.specific {
|
match self.specific {
|
||||||
GenericFragment | IframeFragment(_) | TableFragment | TableCellFragment |
|
GenericFragment | IframeFragment(_) | TableFragment | TableCellFragment |
|
||||||
TableRowFragment | TableWrapperFragment | InlineBlockFragment(_) => Au(0),
|
TableRowFragment | TableWrapperFragment | InlineBlockFragment(_) |
|
||||||
|
InputFragment(_) => Au(0),
|
||||||
ImageFragment(ref image_fragment_info) => {
|
ImageFragment(ref image_fragment_info) => {
|
||||||
image_fragment_info.computed_block_size()
|
image_fragment_info.computed_block_size()
|
||||||
}
|
}
|
||||||
|
@ -1312,7 +1353,7 @@ impl Fragment {
|
||||||
-> Option<(SplitInfo, Option<SplitInfo>, Arc<Box<TextRun>> /* TODO(bjz): remove */)> {
|
-> Option<(SplitInfo, Option<SplitInfo>, Arc<Box<TextRun>> /* TODO(bjz): remove */)> {
|
||||||
match self.specific {
|
match self.specific {
|
||||||
GenericFragment | IframeFragment(_) | ImageFragment(_) | TableFragment | TableCellFragment |
|
GenericFragment | IframeFragment(_) | ImageFragment(_) | TableFragment | TableCellFragment |
|
||||||
TableRowFragment | TableWrapperFragment => None,
|
TableRowFragment | TableWrapperFragment | InputFragment(_) => None,
|
||||||
TableColumnFragment(_) => fail!("Table column fragments do not need to split"),
|
TableColumnFragment(_) => fail!("Table column fragments do not need to split"),
|
||||||
UnscannedTextFragment(_) => fail!("Unscanned text fragments should have been scanned by now!"),
|
UnscannedTextFragment(_) => fail!("Unscanned text fragments should have been scanned by now!"),
|
||||||
InlineBlockFragment(_) => fail!("Inline blocks do not get split"),
|
InlineBlockFragment(_) => fail!("Inline blocks do not get split"),
|
||||||
|
@ -1353,7 +1394,7 @@ impl Fragment {
|
||||||
-> Option<(Option<SplitInfo>, Option<SplitInfo>, Arc<Box<TextRun>> /* TODO(bjz): remove */)> {
|
-> Option<(Option<SplitInfo>, Option<SplitInfo>, Arc<Box<TextRun>> /* TODO(bjz): remove */)> {
|
||||||
match self.specific {
|
match self.specific {
|
||||||
GenericFragment | IframeFragment(_) | ImageFragment(_) | TableFragment | TableCellFragment |
|
GenericFragment | IframeFragment(_) | ImageFragment(_) | TableFragment | TableCellFragment |
|
||||||
TableRowFragment | TableWrapperFragment | InlineBlockFragment(_) => None,
|
TableRowFragment | TableWrapperFragment | InlineBlockFragment(_) | InputFragment(_) => None,
|
||||||
TableColumnFragment(_) => fail!("Table column fragments do not have inline_size"),
|
TableColumnFragment(_) => fail!("Table column fragments do not have inline_size"),
|
||||||
UnscannedTextFragment(_) => fail!("Unscanned text fragments should have been scanned by now!"),
|
UnscannedTextFragment(_) => fail!("Unscanned text fragments should have been scanned by now!"),
|
||||||
ScannedTextFragment(ref text_fragment_info) => {
|
ScannedTextFragment(ref text_fragment_info) => {
|
||||||
|
@ -1457,7 +1498,7 @@ impl Fragment {
|
||||||
container_inline_size: Au) {
|
container_inline_size: Au) {
|
||||||
match self.specific {
|
match self.specific {
|
||||||
GenericFragment | IframeFragment(_) | TableFragment | TableCellFragment |
|
GenericFragment | IframeFragment(_) | TableFragment | TableCellFragment |
|
||||||
TableRowFragment | TableWrapperFragment => return,
|
TableRowFragment | TableWrapperFragment | InputFragment(_) => return,
|
||||||
TableColumnFragment(_) => fail!("Table column fragments do not have inline_size"),
|
TableColumnFragment(_) => fail!("Table column fragments do not have inline_size"),
|
||||||
UnscannedTextFragment(_) => {
|
UnscannedTextFragment(_) => {
|
||||||
fail!("Unscanned text fragments should have been scanned by now!")
|
fail!("Unscanned text fragments should have been scanned by now!")
|
||||||
|
@ -1540,7 +1581,7 @@ impl Fragment {
|
||||||
pub fn assign_replaced_block_size_if_necessary(&mut self) {
|
pub fn assign_replaced_block_size_if_necessary(&mut self) {
|
||||||
match self.specific {
|
match self.specific {
|
||||||
GenericFragment | IframeFragment(_) | TableFragment | TableCellFragment |
|
GenericFragment | IframeFragment(_) | TableFragment | TableCellFragment |
|
||||||
TableRowFragment | TableWrapperFragment => return,
|
TableRowFragment | TableWrapperFragment | InputFragment(_) => return,
|
||||||
TableColumnFragment(_) => fail!("Table column fragments do not have block_size"),
|
TableColumnFragment(_) => fail!("Table column fragments do not have block_size"),
|
||||||
UnscannedTextFragment(_) => {
|
UnscannedTextFragment(_) => {
|
||||||
fail!("Unscanned text fragments should have been scanned by now!")
|
fail!("Unscanned text fragments should have been scanned by now!")
|
||||||
|
@ -1682,7 +1723,7 @@ impl Fragment {
|
||||||
InlineBlockFragment(_) | TableWrapperFragment => false,
|
InlineBlockFragment(_) | TableWrapperFragment => false,
|
||||||
GenericFragment | IframeFragment(_) | ImageFragment(_) | ScannedTextFragment(_) |
|
GenericFragment | IframeFragment(_) | ImageFragment(_) | ScannedTextFragment(_) |
|
||||||
TableFragment | TableCellFragment | TableColumnFragment(_) | TableRowFragment |
|
TableFragment | TableCellFragment | TableColumnFragment(_) | TableRowFragment |
|
||||||
UnscannedTextFragment(_) => true,
|
UnscannedTextFragment(_) | InputFragment(_) => true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1703,6 +1744,7 @@ impl fmt::Show for Fragment {
|
||||||
TableWrapperFragment => "TableWrapperFragment",
|
TableWrapperFragment => "TableWrapperFragment",
|
||||||
UnscannedTextFragment(_) => "UnscannedTextFragment",
|
UnscannedTextFragment(_) => "UnscannedTextFragment",
|
||||||
InlineBlockFragment(_) => "InlineBlockFragment",
|
InlineBlockFragment(_) => "InlineBlockFragment",
|
||||||
|
InputFragment(_) => "InputFragment",
|
||||||
}));
|
}));
|
||||||
try!(write!(f, "bp {}", self.border_padding));
|
try!(write!(f, "bp {}", self.border_padding));
|
||||||
try!(write!(f, " "));
|
try!(write!(f, " "));
|
||||||
|
|
|
@ -955,7 +955,7 @@ impl Flow for InlineFlow {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bubble_inline_sizes(&mut self, _: &LayoutContext) {
|
fn bubble_inline_sizes(&mut self, layout_context: &LayoutContext) {
|
||||||
let _scope = layout_debug_scope!("inline::bubble_inline_sizes {:s}", self.base.debug_id());
|
let _scope = layout_debug_scope!("inline::bubble_inline_sizes {:s}", self.base.debug_id());
|
||||||
|
|
||||||
let writing_mode = self.base.writing_mode;
|
let writing_mode = self.base.writing_mode;
|
||||||
|
@ -968,7 +968,7 @@ impl Flow for InlineFlow {
|
||||||
debug!("Flow: measuring {}", *fragment);
|
debug!("Flow: measuring {}", *fragment);
|
||||||
|
|
||||||
let fragment_intrinsic_inline_sizes =
|
let fragment_intrinsic_inline_sizes =
|
||||||
fragment.intrinsic_inline_sizes();
|
fragment.intrinsic_inline_sizes(layout_context);
|
||||||
intrinsic_inline_sizes.minimum_inline_size = max(
|
intrinsic_inline_sizes.minimum_inline_size = max(
|
||||||
intrinsic_inline_sizes.minimum_inline_size,
|
intrinsic_inline_sizes.minimum_inline_size,
|
||||||
fragment_intrinsic_inline_sizes.minimum_inline_size);
|
fragment_intrinsic_inline_sizes.minimum_inline_size);
|
||||||
|
|
|
@ -171,7 +171,7 @@ impl Flow for TableFlow {
|
||||||
/// table layout calculation.
|
/// table layout calculation.
|
||||||
/// The maximum min/pref inline-sizes of each column are set from the rows for the automatic
|
/// The maximum min/pref inline-sizes of each column are set from the rows for the automatic
|
||||||
/// table layout calculation.
|
/// table layout calculation.
|
||||||
fn bubble_inline_sizes(&mut self, _: &LayoutContext) {
|
fn bubble_inline_sizes(&mut self, layout_context: &LayoutContext) {
|
||||||
let _scope = layout_debug_scope!("table::bubble_inline_sizes {:s}",
|
let _scope = layout_debug_scope!("table::bubble_inline_sizes {:s}",
|
||||||
self.block_flow.base.debug_id());
|
self.block_flow.base.debug_id());
|
||||||
|
|
||||||
|
@ -239,7 +239,8 @@ impl Flow for TableFlow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let fragment_intrinsic_inline_sizes = self.block_flow.fragment.intrinsic_inline_sizes();
|
let fragment_intrinsic_inline_sizes =
|
||||||
|
self.block_flow.fragment.intrinsic_inline_sizes(layout_context);
|
||||||
self.block_flow.base.intrinsic_inline_sizes.minimum_inline_size = min_inline_size;
|
self.block_flow.base.intrinsic_inline_sizes.minimum_inline_size = min_inline_size;
|
||||||
self.block_flow.base.intrinsic_inline_sizes.preferred_inline_size =
|
self.block_flow.base.intrinsic_inline_sizes.preferred_inline_size =
|
||||||
max(min_inline_size, pref_inline_size);
|
max(min_inline_size, pref_inline_size);
|
||||||
|
|
|
@ -36,13 +36,14 @@
|
||||||
use css::node_style::StyledNode;
|
use css::node_style::StyledNode;
|
||||||
use util::{LayoutDataAccess, LayoutDataWrapper, PrivateLayoutData};
|
use util::{LayoutDataAccess, LayoutDataWrapper, PrivateLayoutData};
|
||||||
|
|
||||||
use script::dom::bindings::codegen::InheritTypes::{HTMLIFrameElementDerived};
|
use script::dom::bindings::codegen::InheritTypes::{HTMLIFrameElementDerived, HTMLInputElementDerived};
|
||||||
use script::dom::bindings::codegen::InheritTypes::{HTMLImageElementDerived, TextDerived};
|
use script::dom::bindings::codegen::InheritTypes::{HTMLImageElementDerived, TextDerived};
|
||||||
use script::dom::bindings::js::JS;
|
use script::dom::bindings::js::JS;
|
||||||
use script::dom::element::{Element, HTMLAreaElementTypeId, HTMLAnchorElementTypeId};
|
use script::dom::element::{Element, HTMLAreaElementTypeId, HTMLAnchorElementTypeId};
|
||||||
use script::dom::element::{HTMLLinkElementTypeId, LayoutElementHelpers, RawLayoutElementHelpers};
|
use script::dom::element::{HTMLLinkElementTypeId, LayoutElementHelpers, RawLayoutElementHelpers};
|
||||||
use script::dom::htmliframeelement::HTMLIFrameElement;
|
use script::dom::htmliframeelement::HTMLIFrameElement;
|
||||||
use script::dom::htmlimageelement::{HTMLImageElement, LayoutHTMLImageElementHelpers};
|
use script::dom::htmlimageelement::{HTMLImageElement, LayoutHTMLImageElementHelpers};
|
||||||
|
use script::dom::htmlinputelement::{HTMLInputElement, LayoutHTMLInputElementHelpers};
|
||||||
use script::dom::node::{DocumentNodeTypeId, ElementNodeTypeId, Node, NodeTypeId};
|
use script::dom::node::{DocumentNodeTypeId, ElementNodeTypeId, Node, NodeTypeId};
|
||||||
use script::dom::node::{LayoutNodeHelpers, RawLayoutNodeHelpers, SharedLayoutData, TextNodeTypeId};
|
use script::dom::node::{LayoutNodeHelpers, RawLayoutNodeHelpers, SharedLayoutData, TextNodeTypeId};
|
||||||
use script::dom::text::Text;
|
use script::dom::text::Text;
|
||||||
|
@ -184,11 +185,15 @@ impl<'ln> TLayoutNode for LayoutNode<'ln> {
|
||||||
|
|
||||||
fn text(&self) -> String {
|
fn text(&self) -> String {
|
||||||
unsafe {
|
unsafe {
|
||||||
if !self.get().is_text() {
|
if self.get().is_text() {
|
||||||
|
let text: JS<Text> = self.get_jsmanaged().transmute_copy();
|
||||||
|
(*text.unsafe_get()).characterdata.data.deref().borrow().clone()
|
||||||
|
} else if self.get().is_htmlinputelement() {
|
||||||
|
let input: JS<HTMLInputElement> = self.get_jsmanaged().transmute_copy();
|
||||||
|
input.get_value_for_layout()
|
||||||
|
} else {
|
||||||
fail!("not text!")
|
fail!("not text!")
|
||||||
}
|
}
|
||||||
let text: JS<Text> = self.get_jsmanaged().transmute_copy();
|
|
||||||
(*text.unsafe_get()).characterdata.data.deref().borrow().clone()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -567,14 +572,7 @@ impl<'ln> TLayoutNode for ThreadSafeLayoutNode<'ln> {
|
||||||
return get_content(&after_style.get_box().content)
|
return get_content(&after_style.get_box().content)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.node.text()
|
||||||
unsafe {
|
|
||||||
if !self.get().is_text() {
|
|
||||||
fail!("not text!")
|
|
||||||
}
|
|
||||||
let text: JS<Text> = self.get_jsmanaged().transmute_copy();
|
|
||||||
(*text.unsafe_get()).characterdata.data.deref().borrow().clone()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -732,6 +730,36 @@ impl<'ln> ThreadSafeLayoutNode<'ln> {
|
||||||
_ => false
|
_ => false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_input_checked(&self) -> bool {
|
||||||
|
unsafe {
|
||||||
|
if !self.get().is_htmlinputelement() {
|
||||||
|
fail!("not an input element!")
|
||||||
|
}
|
||||||
|
let input: JS<HTMLInputElement> = self.get_jsmanaged().transmute_copy();
|
||||||
|
input.get_checked_for_layout()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_input_value(&self) -> String {
|
||||||
|
unsafe {
|
||||||
|
if !self.get().is_htmlinputelement() {
|
||||||
|
fail!("not an input element!")
|
||||||
|
}
|
||||||
|
let input: JS<HTMLInputElement> = self.get_jsmanaged().transmute_copy();
|
||||||
|
input.get_value_for_layout()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_input_size(&self) -> u32 {
|
||||||
|
unsafe {
|
||||||
|
if !self.get().is_htmlinputelement() {
|
||||||
|
fail!("not an input element!")
|
||||||
|
}
|
||||||
|
let input: JS<HTMLInputElement> = self.get_jsmanaged().transmute_copy();
|
||||||
|
input.get_size_for_layout()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ThreadSafeLayoutNodeChildrenIterator<'a> {
|
pub struct ThreadSafeLayoutNodeChildrenIterator<'a> {
|
||||||
|
|
|
@ -2,26 +2,55 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
|
||||||
|
use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
|
||||||
|
use dom::bindings::codegen::Bindings::EventBinding::EventMethods;
|
||||||
use dom::bindings::codegen::Bindings::HTMLInputElementBinding;
|
use dom::bindings::codegen::Bindings::HTMLInputElementBinding;
|
||||||
use dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementMethods;
|
use dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementMethods;
|
||||||
use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast, NodeCast};
|
use dom::bindings::codegen::Bindings::NodeListBinding::NodeListMethods;
|
||||||
|
use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast, HTMLInputElementCast, NodeCast};
|
||||||
use dom::bindings::codegen::InheritTypes::{HTMLInputElementDerived, HTMLFieldSetElementDerived};
|
use dom::bindings::codegen::InheritTypes::{HTMLInputElementDerived, HTMLFieldSetElementDerived};
|
||||||
use dom::bindings::js::{JSRef, Temporary};
|
use dom::bindings::js::{JS, JSRef, Temporary, OptionalRootable, ResultRootable};
|
||||||
use dom::bindings::utils::{Reflectable, Reflector};
|
use dom::bindings::utils::{Reflectable, Reflector};
|
||||||
use dom::document::Document;
|
use dom::attr::{AttrHelpers};
|
||||||
|
use dom::document::{Document, DocumentHelpers};
|
||||||
use dom::element::{AttributeHandlers, Element, HTMLInputElementTypeId};
|
use dom::element::{AttributeHandlers, Element, HTMLInputElementTypeId};
|
||||||
|
use dom::event::Event;
|
||||||
use dom::eventtarget::{EventTarget, NodeTargetTypeId};
|
use dom::eventtarget::{EventTarget, NodeTargetTypeId};
|
||||||
use dom::htmlelement::HTMLElement;
|
use dom::htmlelement::HTMLElement;
|
||||||
use dom::node::{DisabledStateHelpers, Node, NodeHelpers, ElementNodeTypeId};
|
use dom::node::{DisabledStateHelpers, Node, NodeHelpers, ElementNodeTypeId, document_from_node};
|
||||||
use dom::virtualmethods::VirtualMethods;
|
use dom::virtualmethods::VirtualMethods;
|
||||||
|
|
||||||
use servo_util::str::DOMString;
|
use servo_util::str::{DOMString, parse_unsigned_integer};
|
||||||
use string_cache::Atom;
|
use string_cache::Atom;
|
||||||
|
|
||||||
|
use std::cell::{Cell, RefCell};
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
|
static DEFAULT_SUBMIT_VALUE: &'static str = "Submit";
|
||||||
|
static DEFAULT_RESET_VALUE: &'static str = "Reset";
|
||||||
|
|
||||||
|
#[jstraceable]
|
||||||
|
#[deriving(PartialEq)]
|
||||||
|
enum InputType {
|
||||||
|
InputButton(Option<&'static str>),
|
||||||
|
InputText,
|
||||||
|
InputFile,
|
||||||
|
InputImage,
|
||||||
|
InputCheckbox,
|
||||||
|
InputRadio,
|
||||||
|
InputPassword
|
||||||
|
}
|
||||||
|
|
||||||
#[jstraceable]
|
#[jstraceable]
|
||||||
#[must_root]
|
#[must_root]
|
||||||
pub struct HTMLInputElement {
|
pub struct HTMLInputElement {
|
||||||
pub htmlelement: HTMLElement,
|
pub htmlelement: HTMLElement,
|
||||||
|
input_type: Cell<InputType>,
|
||||||
|
checked: Cell<bool>,
|
||||||
|
uncommitted_value: RefCell<Option<String>>,
|
||||||
|
value: RefCell<Option<String>>,
|
||||||
|
size: Cell<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HTMLInputElementDerived for EventTarget {
|
impl HTMLInputElementDerived for EventTarget {
|
||||||
|
@ -30,10 +59,17 @@ impl HTMLInputElementDerived for EventTarget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static DEFAULT_INPUT_SIZE: u32 = 20;
|
||||||
|
|
||||||
impl HTMLInputElement {
|
impl HTMLInputElement {
|
||||||
fn new_inherited(localName: DOMString, document: JSRef<Document>) -> HTMLInputElement {
|
fn new_inherited(localName: DOMString, document: JSRef<Document>) -> HTMLInputElement {
|
||||||
HTMLInputElement {
|
HTMLInputElement {
|
||||||
htmlelement: HTMLElement::new_inherited(HTMLInputElementTypeId, localName, document)
|
htmlelement: HTMLElement::new_inherited(HTMLInputElementTypeId, localName, document),
|
||||||
|
input_type: Cell::new(InputText),
|
||||||
|
checked: Cell::new(false),
|
||||||
|
uncommitted_value: RefCell::new(None),
|
||||||
|
value: RefCell::new(None),
|
||||||
|
size: Cell::new(DEFAULT_INPUT_SIZE),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,6 +80,53 @@ impl HTMLInputElement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait LayoutHTMLInputElementHelpers {
|
||||||
|
unsafe fn get_checked_for_layout(&self) -> bool;
|
||||||
|
unsafe fn get_value_for_layout(&self) -> String;
|
||||||
|
unsafe fn get_size_for_layout(&self) -> u32;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LayoutHTMLInputElementHelpers for JS<HTMLInputElement> {
|
||||||
|
#[allow(unrooted_must_root)]
|
||||||
|
unsafe fn get_checked_for_layout(&self) -> bool {
|
||||||
|
(*self.unsafe_get()).checked.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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()
|
||||||
|
}
|
||||||
|
|
||||||
|
match (*self.unsafe_get()).input_type.get() {
|
||||||
|
InputCheckbox => if self.get_checked_for_layout() {
|
||||||
|
"[X]"
|
||||||
|
} else {
|
||||||
|
"[ ]"
|
||||||
|
}.to_string(),
|
||||||
|
InputRadio => if self.get_checked_for_layout() {
|
||||||
|
"(*)"
|
||||||
|
} else {
|
||||||
|
"( )"
|
||||||
|
}.to_string(),
|
||||||
|
InputFile | InputImage => "".to_string(),
|
||||||
|
InputButton(ref default) => get_raw_value(self)
|
||||||
|
.or_else(|| default.map(|v| v.to_string()))
|
||||||
|
.unwrap_or_else(|| "".to_string()),
|
||||||
|
InputPassword => {
|
||||||
|
let raw = get_raw_value(self).unwrap_or_else(|| "".to_string());
|
||||||
|
String::from_char(raw.len(), '*')
|
||||||
|
}
|
||||||
|
_ => get_raw_value(self).unwrap_or("".to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unrooted_must_root)]
|
||||||
|
unsafe fn get_size_for_layout(&self) -> u32 {
|
||||||
|
(*self.unsafe_get()).size.get()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> HTMLInputElementMethods for JSRef<'a, HTMLInputElement> {
|
impl<'a> HTMLInputElementMethods for JSRef<'a, HTMLInputElement> {
|
||||||
// http://www.whatwg.org/html/#dom-fe-disabled
|
// http://www.whatwg.org/html/#dom-fe-disabled
|
||||||
make_bool_getter!(Disabled)
|
make_bool_getter!(Disabled)
|
||||||
|
@ -53,6 +136,104 @@ impl<'a> HTMLInputElementMethods for JSRef<'a, HTMLInputElement> {
|
||||||
let elem: JSRef<Element> = ElementCast::from_ref(self);
|
let elem: JSRef<Element> = ElementCast::from_ref(self);
|
||||||
elem.set_bool_attribute("disabled", disabled)
|
elem.set_bool_attribute("disabled", disabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/forms.html#dom-input-checked
|
||||||
|
make_bool_getter!(Checked)
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/forms.html#dom-input-checked
|
||||||
|
fn SetChecked(self, checked: bool) {
|
||||||
|
let elem: JSRef<Element> = ElementCast::from_ref(self);
|
||||||
|
elem.set_bool_attribute("checked", checked)
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/forms.html#dom-input-size
|
||||||
|
make_uint_getter!(Size)
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/forms.html#dom-input-size
|
||||||
|
fn SetSize(self, size: u32) {
|
||||||
|
let elem: JSRef<Element> = ElementCast::from_ref(self);
|
||||||
|
elem.set_uint_attribute("size", size)
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/forms.html#dom-input-value
|
||||||
|
make_getter!(Value)
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/forms.html#dom-input-value
|
||||||
|
fn SetValue(self, value: DOMString) {
|
||||||
|
let elem: JSRef<Element> = ElementCast::from_ref(self);
|
||||||
|
elem.set_string_attribute("value", value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/forms.html#attr-fe-name
|
||||||
|
make_getter!(Name)
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/forms.html#attr-fe-name
|
||||||
|
fn SetName(self, name: DOMString) {
|
||||||
|
let elem: JSRef<Element> = ElementCast::from_ref(self);
|
||||||
|
elem.set_string_attribute("name", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 broadcast_radio_checked(broadcaster: JSRef<HTMLInputElement>, group: Option<&str>) {
|
||||||
|
//TODO: if not in document, use root ancestor instead of document
|
||||||
|
let doc = document_from_node(broadcaster).root();
|
||||||
|
let radios = doc.QuerySelectorAll("input[type=\"radio\"]".to_string()).unwrap().root();
|
||||||
|
let mut i = 0;
|
||||||
|
while i < radios.Length() {
|
||||||
|
let node = radios.Item(i).unwrap().root();
|
||||||
|
let radio: JSRef<HTMLInputElement> = HTMLInputElementCast::to_ref(*node).unwrap();
|
||||||
|
if radio != broadcaster {
|
||||||
|
//TODO: determine form owner
|
||||||
|
let other_group = radio.get_radio_group();
|
||||||
|
//TODO: ensure compatibility caseless match (https://html.spec.whatwg.org/multipage/infrastructure.html#compatibility-caseless)
|
||||||
|
let group_matches = other_group.as_ref().map(|group| group.as_slice()) == group.as_ref().map(|&group| &*group);
|
||||||
|
if group_matches && radio.Checked() {
|
||||||
|
radio.SetChecked(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> HTMLInputElementHelpers for JSRef<'a, HTMLInputElement> {
|
||||||
|
fn force_relayout(self) {
|
||||||
|
let doc = document_from_node(self).root();
|
||||||
|
doc.content_changed()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn radio_group_updated(self, group: Option<&str>) {
|
||||||
|
if self.Checked() {
|
||||||
|
broadcast_radio_checked(self, group);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/forms.html#radio-button-group
|
||||||
|
fn get_radio_group(self) -> Option<String> {
|
||||||
|
//TODO: determine form owner
|
||||||
|
let elem: JSRef<Element> = ElementCast::from_ref(self);
|
||||||
|
elem.get_attribute(ns!(""), "name")
|
||||||
|
.root()
|
||||||
|
.map(|name| name.Value())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_checked_state(self, checked: bool) {
|
||||||
|
self.checked.set(checked);
|
||||||
|
if self.input_type.get() == InputRadio && checked {
|
||||||
|
broadcast_radio_checked(self,
|
||||||
|
self.get_radio_group()
|
||||||
|
.as_ref()
|
||||||
|
.map(|group| group.as_slice()));
|
||||||
|
}
|
||||||
|
//TODO: dispatch change event
|
||||||
|
self.force_relayout();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> VirtualMethods for JSRef<'a, HTMLInputElement> {
|
impl<'a> VirtualMethods for JSRef<'a, HTMLInputElement> {
|
||||||
|
@ -72,7 +253,41 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLInputElement> {
|
||||||
"disabled" => {
|
"disabled" => {
|
||||||
node.set_disabled_state(true);
|
node.set_disabled_state(true);
|
||||||
node.set_enabled_state(false);
|
node.set_enabled_state(false);
|
||||||
},
|
}
|
||||||
|
"checked" => {
|
||||||
|
self.update_checked_state(true);
|
||||||
|
}
|
||||||
|
"size" => {
|
||||||
|
self.size.set(parse_unsigned_integer(value.as_slice().chars()).unwrap_or(DEFAULT_INPUT_SIZE));
|
||||||
|
self.force_relayout();
|
||||||
|
}
|
||||||
|
"type" => {
|
||||||
|
self.input_type.set(match value.as_slice() {
|
||||||
|
"button" => InputButton(None),
|
||||||
|
"submit" => InputButton(Some(DEFAULT_SUBMIT_VALUE)),
|
||||||
|
"reset" => InputButton(Some(DEFAULT_RESET_VALUE)),
|
||||||
|
"file" => InputFile,
|
||||||
|
"radio" => InputRadio,
|
||||||
|
"checkbox" => InputCheckbox,
|
||||||
|
"password" => InputPassword,
|
||||||
|
_ => InputText,
|
||||||
|
});
|
||||||
|
if self.input_type.get() == InputRadio {
|
||||||
|
self.radio_group_updated(self.get_radio_group()
|
||||||
|
.as_ref()
|
||||||
|
.map(|group| group.as_slice()));
|
||||||
|
}
|
||||||
|
self.force_relayout();
|
||||||
|
}
|
||||||
|
"value" => {
|
||||||
|
*self.value.borrow_mut() = Some(value);
|
||||||
|
self.force_relayout();
|
||||||
|
}
|
||||||
|
"name" => {
|
||||||
|
if self.input_type.get() == InputRadio {
|
||||||
|
self.radio_group_updated(Some(value.as_slice()));
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => ()
|
_ => ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -89,7 +304,33 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLInputElement> {
|
||||||
node.set_disabled_state(false);
|
node.set_disabled_state(false);
|
||||||
node.set_enabled_state(true);
|
node.set_enabled_state(true);
|
||||||
node.check_ancestors_disabled_state_for_form_control();
|
node.check_ancestors_disabled_state_for_form_control();
|
||||||
},
|
}
|
||||||
|
"checked" => {
|
||||||
|
self.update_checked_state(false);
|
||||||
|
}
|
||||||
|
"size" => {
|
||||||
|
self.size.set(DEFAULT_INPUT_SIZE);
|
||||||
|
self.force_relayout();
|
||||||
|
}
|
||||||
|
"type" => {
|
||||||
|
if self.input_type.get() == InputRadio {
|
||||||
|
broadcast_radio_checked(*self,
|
||||||
|
self.get_radio_group()
|
||||||
|
.as_ref()
|
||||||
|
.map(|group| group.as_slice()));
|
||||||
|
}
|
||||||
|
self.input_type.set(InputText);
|
||||||
|
self.force_relayout();
|
||||||
|
}
|
||||||
|
"value" => {
|
||||||
|
*self.value.borrow_mut() = None;
|
||||||
|
self.force_relayout();
|
||||||
|
}
|
||||||
|
"name" => {
|
||||||
|
if self.input_type.get() == InputRadio {
|
||||||
|
self.radio_group_updated(None);
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => ()
|
_ => ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -117,6 +358,24 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLInputElement> {
|
||||||
node.check_disabled_attribute();
|
node.check_disabled_attribute();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_event(&self, event: JSRef<Event>) {
|
||||||
|
match self.super_type() {
|
||||||
|
Some(s) => {
|
||||||
|
s.handle_event(event);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
if "click" == event.Type().as_slice() && !event.DefaultPrevented() {
|
||||||
|
match self.input_type.get() {
|
||||||
|
InputCheckbox | InputRadio => {
|
||||||
|
self.SetChecked(!self.checked.get());
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Reflectable for HTMLInputElement {
|
impl Reflectable for HTMLInputElement {
|
||||||
|
|
|
@ -10,7 +10,7 @@ interface HTMLInputElement : HTMLElement {
|
||||||
// attribute DOMString autocomplete;
|
// attribute DOMString autocomplete;
|
||||||
// attribute boolean autofocus;
|
// attribute boolean autofocus;
|
||||||
// attribute boolean defaultChecked;
|
// attribute boolean defaultChecked;
|
||||||
// attribute boolean checked;
|
attribute boolean checked;
|
||||||
// attribute DOMString dirName;
|
// attribute DOMString dirName;
|
||||||
attribute boolean disabled;
|
attribute boolean disabled;
|
||||||
//readonly attribute HTMLFormElement? form;
|
//readonly attribute HTMLFormElement? form;
|
||||||
|
@ -29,17 +29,17 @@ interface HTMLInputElement : HTMLElement {
|
||||||
// attribute DOMString min;
|
// attribute DOMString min;
|
||||||
// attribute long minLength;
|
// attribute long minLength;
|
||||||
// attribute boolean multiple;
|
// attribute boolean multiple;
|
||||||
// attribute DOMString name;
|
attribute DOMString name;
|
||||||
// attribute DOMString pattern;
|
// attribute DOMString pattern;
|
||||||
// attribute DOMString placeholder;
|
// attribute DOMString placeholder;
|
||||||
// attribute boolean readOnly;
|
// attribute boolean readOnly;
|
||||||
// attribute boolean required;
|
// attribute boolean required;
|
||||||
// attribute unsigned long size;
|
attribute unsigned long size;
|
||||||
// attribute DOMString src;
|
// attribute DOMString src;
|
||||||
// attribute DOMString step;
|
// attribute DOMString step;
|
||||||
// attribute DOMString type;
|
// attribute DOMString type; //XXXjdm need binaryName
|
||||||
// attribute DOMString defaultValue;
|
// attribute DOMString defaultValue;
|
||||||
//[TreatNullAs=EmptyString] attribute DOMString value;
|
[TreatNullAs=EmptyString] attribute DOMString value;
|
||||||
// attribute Date? valueAsDate;
|
// attribute Date? valueAsDate;
|
||||||
// attribute unrestricted double valueAsNumber;
|
// attribute unrestricted double valueAsNumber;
|
||||||
// attribute double valueLow;
|
// attribute double valueLow;
|
||||||
|
|
|
@ -116,4 +116,10 @@ area:link,
|
||||||
link:link { color: blue }
|
link:link { color: blue }
|
||||||
script { display: none }
|
script { display: none }
|
||||||
style { display: none }
|
style { display: none }
|
||||||
|
input { background: white; min-height: 1.0em; max-height: 1.0em; padding: 0em; padding-left: 0.25em; padding-right: 0.25em; border: solid lightgrey 1px; color: black; }
|
||||||
|
input[type="button"],
|
||||||
|
input[type="submit"],
|
||||||
|
input[type="reset"] { background: lightgrey; border-top: solid 1px #EEEEEE; border-left: solid 1px #CCCCCC; border-right: solid 1px #999999; border-bottom: solid 1px #999999; text-align: center; vertical-align: middle; color: black; }
|
||||||
input[type="hidden"] { display: none !important }
|
input[type="hidden"] { display: none !important }
|
||||||
|
input[type="checkbox"],
|
||||||
|
input[type="radio"] { font-family: monospace !important; border: none !important; background: transparent; }
|
||||||
|
|
17
tests/html/test-inputs.html
Normal file
17
tests/html/test-inputs.html
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<style>
|
||||||
|
</style>
|
||||||
|
<div><input type="checkbox"></div>
|
||||||
|
<div><input type="text" size="30" value="placeholder"></div>
|
||||||
|
<div><input type="text" size="10" value="whefghijklmnopqrstuvwxyzabcdefg"></div>
|
||||||
|
<div><input type="text" value=""><div>
|
||||||
|
<div><input type="submit"><input type="reset"><div>
|
||||||
|
<div><input type="checkbox"></div>
|
||||||
|
<div><input type="checkbox" checked></div>
|
||||||
|
<div>group 1
|
||||||
|
<div><input type="radio"></div>
|
||||||
|
<div><input type="radio" checked></div>
|
||||||
|
</div>
|
||||||
|
<div>group 2
|
||||||
|
<div><input type="radio" name="a" checked></div>
|
||||||
|
<div><input type="radio" name="a"></div>
|
||||||
|
</div>
|
Loading…
Add table
Add a link
Reference in a new issue