From 08cac68d5a481c3c612fff1e9a87612871f95f5f Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Mon, 29 Sep 2014 02:14:21 -0400 Subject: [PATCH 1/8] Check if the immediate node is an element before looking at ancestors when clicking, and flush pending layout changes after dispatching the event. --- components/script/script_task.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/components/script/script_task.rs b/components/script/script_task.rs index 88c84ef5b74..a8c7901bc77 100644 --- a/components/script/script_task.rs +++ b/components/script/script_task.rs @@ -928,9 +928,14 @@ impl ScriptTask { let temp_node = node::from_untrusted_node_address( - self.js_runtime.deref().ptr, node_address); + self.js_runtime.deref().ptr, node_address).root(); + + let maybe_node = if !temp_node.is_element() { + temp_node.ancestors().find(|node| node.is_element()) + } else { + Some(*temp_node) + }; - let maybe_node = temp_node.root().ancestors().find(|node| node.is_element()); match maybe_node { Some(node) => { debug!("clicked on {:s}", node.debug_str()); @@ -945,6 +950,8 @@ impl ScriptTask { true, true).root(); let eventtarget: JSRef = EventTargetCast::from_ref(node); let _ = eventtarget.dispatch_event_with_target(None, *event); + + window.flush_layout(ReflowForDisplay); } None => {} } From 9b20d6e7d296a0eec6ba925fee740fa8a09018f3 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Mon, 29 Sep 2014 02:15:43 -0400 Subject: [PATCH 2/8] Add average advance width to the font metrics structure, using the X glyph as a best guess. --- components/gfx/font.rs | 1 + components/gfx/platform/freetype/font.rs | 6 ++++++ components/gfx/platform/macos/font.rs | 9 ++++++++- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/components/gfx/font.rs b/components/gfx/font.rs index b3f85edf823..b3e30b100fd 100644 --- a/components/gfx/font.rs +++ b/components/gfx/font.rs @@ -78,6 +78,7 @@ pub struct FontMetrics { pub ascent: Au, pub descent: Au, pub max_advance: Au, + pub average_advance: Au, pub line_gap: Au, } diff --git a/components/gfx/platform/freetype/font.rs b/components/gfx/platform/freetype/font.rs index 5bc2ce0abe4..4d78afd8c46 100644 --- a/components/gfx/platform/freetype/font.rs +++ b/components/gfx/platform/freetype/font.rs @@ -235,6 +235,11 @@ impl FontHandleMethods for FontHandle { } } + let average_advance = self.glyph_index('x') + .and_then(|idx| self.glyph_h_advance(idx)) + .map(|advance| self.font_units_to_au(advance)) + .unwrap_or(max_advance_width); + let metrics = FontMetrics { underline_size: underline_size, underline_offset: underline_offset, @@ -246,6 +251,7 @@ impl FontHandleMethods for FontHandle { ascent: ascent, descent: -descent, // linux font's seem to use the opposite sign from mac max_advance: max_advance, + average_advance: average_advance, line_gap: height, }; diff --git a/components/gfx/platform/macos/font.rs b/components/gfx/platform/macos/font.rs index b025b70566c..ad3d5bd1b29 100644 --- a/components/gfx/platform/macos/font.rs +++ b/components/gfx/platform/macos/font.rs @@ -153,6 +153,12 @@ impl FontHandleMethods for FontHandle { let scale = px_to_pt(self.ctfont.pt_size() as f64) / (ascent + descent); let line_gap = (ascent + descent + leading + 0.5).floor(); + let max_advance_width = Au::from_pt(bounding_rect.size.width as f64); + let average_advance = self.glyph_index('x') + .and_then(|idx| self.glyph_h_advance(idx)) + .map(|advance| Au::from_frac_px(advance)) + .unwrap_or(max_advance_width); + let metrics = FontMetrics { underline_size: Au::from_pt(self.ctfont.underline_thickness() as f64), // TODO(Issue #201): underline metrics are not reliable. Have to pull out of font table @@ -168,7 +174,8 @@ impl FontHandleMethods for FontHandle { em_size: em_size, ascent: Au::from_pt(ascent * scale), descent: Au::from_pt(descent * scale), - max_advance: Au::from_pt(bounding_rect.size.width as f64), + max_advance: max_advance_width, + average_advance: average_advance, line_gap: Au::from_frac_px(line_gap), }; debug!("Font metrics (@{:f} pt): {:?}", self.ctfont.pt_size() as f64, metrics); From f70bb685037399fa16cd9a54695ef7d8c512f732 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Mon, 29 Sep 2014 02:59:39 -0400 Subject: [PATCH 3/8] Implement simple layout for text, button, radio, and checkbox inputs. Implement simple interaction for checkboxes and radio buttons. --- components/layout/block.rs | 4 +- components/layout/construct.rs | 33 ++- components/layout/fragment.rs | 66 ++++- components/layout/inline.rs | 4 +- components/layout/table.rs | 5 +- components/layout/wrapper.rs | 52 +++- components/script/dom/htmlinputelement.rs | 275 +++++++++++++++++- .../dom/webidls/HTMLInputElement.webidl | 10 +- components/style/user-agent.css | 6 + tests/html/test-inputs.html | 17 ++ 10 files changed, 422 insertions(+), 50 deletions(-) create mode 100644 tests/html/test-inputs.html diff --git a/components/layout/block.rs b/components/layout/block.rs index 23a2381e754..40d5a89244f 100644 --- a/components/layout/block.rs +++ b/components/layout/block.rs @@ -1516,7 +1516,7 @@ impl Flow for BlockFlow { /// any fragments it is responsible for flowing. /// /// 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 mut flags = self.base.flags; @@ -1572,7 +1572,7 @@ 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(); + 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, diff --git a/components/layout/construct.rs b/components/layout/construct.rs index 755f39582f9..24d85b7fece 100644 --- a/components/layout/construct.rs +++ b/components/layout/construct.rs @@ -27,12 +27,12 @@ use flow::{Flow, ImmutableFlowUtils, MutableOwnedFlowUtils}; use flow::{Descendants, AbsDescendants}; use flow; use flow_ref::FlowRef; -use fragment::{InlineBlockFragment, InlineBlockFragmentInfo}; +use fragment::{InlineBlockFragment, InlineBlockFragmentInfo, InputFragment}; use fragment::{Fragment, GenericFragment, IframeFragment, IframeFragmentInfo}; use fragment::{ImageFragment, ImageFragmentInfo, SpecificFragmentInfo, TableFragment}; use fragment::{TableCellFragment, TableColumnFragment, TableColumnFragmentInfo}; -use fragment::{TableRowFragment, TableWrapperFragment, UnscannedTextFragment}; -use fragment::{UnscannedTextFragmentInfo}; +use fragment::{TableRowFragment, TableWrapperFragment, UnscannedTextFragment, InputRadioButton}; +use fragment::{UnscannedTextFragmentInfo, InputCheckbox, InputButton, InputText, InputFile}; use inline::{InlineFragments, InlineFlow}; use parallel; use table_wrapper::TableWrapperFlow; @@ -49,7 +49,7 @@ use wrapper::{Before, After, Normal}; use gfx::display_list::OpaqueNode; 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::{HTMLTableElementTypeId, HTMLTableHeaderCellElementTypeId}; 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. /// /// 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) -> SpecificFragmentInfo { match node.type_id() { + Some(ElementNodeTypeId(HTMLIFrameElementTypeId)) => { + IframeFragment(IframeFragmentInfo::new(node)) + } Some(ElementNodeTypeId(HTMLImageElementTypeId)) => { self.build_fragment_info_for_image(node, node.image_url()) } - Some(ElementNodeTypeId(HTMLIFrameElementTypeId)) => { - IframeFragment(IframeFragmentInfo::new(node)) + Some(ElementNodeTypeId(HTMLInputElementTypeId)) => { + self.build_fragment_info_for_input(node) } Some(ElementNodeTypeId(HTMLObjectElementTypeId)) => { 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 // 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 mut fragment = Fragment::new_from_specific_info(node, fragment_info); inline_fragment_accumulator.fragments.push(&mut fragment); diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs index 59c256022bb..010d26f98e0 100644 --- a/components/layout/fragment.rs +++ b/components/layout/fragment.rs @@ -128,10 +128,11 @@ impl> Encodable for Fragment { /// Info specific to the kind of fragment. Keep this enum small. #[deriving(Clone)] pub enum SpecificFragmentInfo { - InlineBlockFragment(InlineBlockFragmentInfo), GenericFragment, - ImageFragment(ImageFragmentInfo), IframeFragment(IframeFragmentInfo), + ImageFragment(ImageFragmentInfo), + InlineBlockFragment(InlineBlockFragmentInfo), + InputFragment(InputFragmentInfo), ScannedTextFragment(ScannedTextFragmentInfo), TableFragment, 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 { + 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. #[deriving(Clone)] pub struct ImageFragmentInfo { @@ -499,7 +532,8 @@ impl Fragment { /// replaced elements. fn style_specified_intrinsic_inline_size(&self) -> IntrinsicISizes { 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), TableWrapperFragment => (true, false), TableRowFragment => (false, false), @@ -1129,7 +1163,7 @@ impl Fragment { text_fragment)) } 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 // should have a real `SERVO_DEBUG` system. 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. - 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(); match self.specific { @@ -1214,6 +1248,12 @@ 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); @@ -1260,7 +1300,7 @@ impl Fragment { pub fn content_inline_size(&self) -> Au { match self.specific { GenericFragment | IframeFragment(_) | TableFragment | TableCellFragment | TableRowFragment | - TableWrapperFragment | InlineBlockFragment(_) => Au(0), + TableWrapperFragment | InlineBlockFragment(_) | InputFragment(_) => Au(0), ImageFragment(ref image_fragment_info) => { image_fragment_info.computed_inline_size() } @@ -1278,7 +1318,8 @@ impl Fragment { pub fn content_block_size(&self, layout_context: &LayoutContext) -> Au { match self.specific { GenericFragment | IframeFragment(_) | TableFragment | TableCellFragment | - TableRowFragment | TableWrapperFragment | InlineBlockFragment(_) => Au(0), + TableRowFragment | TableWrapperFragment | InlineBlockFragment(_) | + InputFragment(_) => Au(0), ImageFragment(ref image_fragment_info) => { image_fragment_info.computed_block_size() } @@ -1312,7 +1353,7 @@ impl Fragment { -> Option<(SplitInfo, Option, Arc> /* TODO(bjz): remove */)> { match self.specific { GenericFragment | IframeFragment(_) | ImageFragment(_) | TableFragment | TableCellFragment | - TableRowFragment | TableWrapperFragment => 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(_) => fail!("Inline blocks do not get split"), @@ -1353,7 +1394,7 @@ impl Fragment { -> Option<(Option, Option, Arc> /* TODO(bjz): remove */)> { match self.specific { GenericFragment | IframeFragment(_) | ImageFragment(_) | TableFragment | TableCellFragment | - TableRowFragment | TableWrapperFragment | InlineBlockFragment(_) => None, + TableRowFragment | TableWrapperFragment | InlineBlockFragment(_) | InputFragment(_) => None, TableColumnFragment(_) => fail!("Table column fragments do not have inline_size"), UnscannedTextFragment(_) => fail!("Unscanned text fragments should have been scanned by now!"), ScannedTextFragment(ref text_fragment_info) => { @@ -1457,7 +1498,7 @@ impl Fragment { container_inline_size: Au) { match self.specific { GenericFragment | IframeFragment(_) | TableFragment | TableCellFragment | - TableRowFragment | TableWrapperFragment => 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!") @@ -1540,7 +1581,7 @@ impl Fragment { pub fn assign_replaced_block_size_if_necessary(&mut self) { match self.specific { GenericFragment | IframeFragment(_) | TableFragment | TableCellFragment | - TableRowFragment | TableWrapperFragment => 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!") @@ -1682,7 +1723,7 @@ impl Fragment { InlineBlockFragment(_) | TableWrapperFragment => false, GenericFragment | IframeFragment(_) | ImageFragment(_) | ScannedTextFragment(_) | TableFragment | TableCellFragment | TableColumnFragment(_) | TableRowFragment | - UnscannedTextFragment(_) => true, + UnscannedTextFragment(_) | InputFragment(_) => true, } } } @@ -1703,6 +1744,7 @@ impl fmt::Show for Fragment { TableWrapperFragment => "TableWrapperFragment", UnscannedTextFragment(_) => "UnscannedTextFragment", InlineBlockFragment(_) => "InlineBlockFragment", + InputFragment(_) => "InputFragment", })); try!(write!(f, "bp {}", self.border_padding)); try!(write!(f, " ")); diff --git a/components/layout/inline.rs b/components/layout/inline.rs index a683c8574c9..d937a6cef53 100644 --- a/components/layout/inline.rs +++ b/components/layout/inline.rs @@ -955,7 +955,7 @@ impl Flow for InlineFlow { 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 writing_mode = self.base.writing_mode; @@ -968,7 +968,7 @@ impl Flow for InlineFlow { debug!("Flow: measuring {}", *fragment); 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, fragment_intrinsic_inline_sizes.minimum_inline_size); diff --git a/components/layout/table.rs b/components/layout/table.rs index d48b845bc98..92e069f1c5c 100644 --- a/components/layout/table.rs +++ b/components/layout/table.rs @@ -171,7 +171,7 @@ 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, _: &LayoutContext) { + fn bubble_inline_sizes(&mut self, layout_context: &LayoutContext) { let _scope = layout_debug_scope!("table::bubble_inline_sizes {:s}", 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.preferred_inline_size = max(min_inline_size, pref_inline_size); diff --git a/components/layout/wrapper.rs b/components/layout/wrapper.rs index 18fd74fca9b..5c634ab3caf 100644 --- a/components/layout/wrapper.rs +++ b/components/layout/wrapper.rs @@ -36,13 +36,14 @@ use css::node_style::StyledNode; 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::js::JS; use script::dom::element::{Element, HTMLAreaElementTypeId, HTMLAnchorElementTypeId}; use script::dom::element::{HTMLLinkElementTypeId, LayoutElementHelpers, RawLayoutElementHelpers}; use script::dom::htmliframeelement::HTMLIFrameElement; use script::dom::htmlimageelement::{HTMLImageElement, LayoutHTMLImageElementHelpers}; +use script::dom::htmlinputelement::{HTMLInputElement, LayoutHTMLInputElementHelpers}; use script::dom::node::{DocumentNodeTypeId, ElementNodeTypeId, Node, NodeTypeId}; use script::dom::node::{LayoutNodeHelpers, RawLayoutNodeHelpers, SharedLayoutData, TextNodeTypeId}; use script::dom::text::Text; @@ -184,11 +185,15 @@ impl<'ln> TLayoutNode for LayoutNode<'ln> { fn text(&self) -> String { unsafe { - if !self.get().is_text() { + if self.get().is_text() { + let text: JS = self.get_jsmanaged().transmute_copy(); + (*text.unsafe_get()).characterdata.data.deref().borrow().clone() + } else if self.get().is_htmlinputelement() { + let input: JS = self.get_jsmanaged().transmute_copy(); + input.get_value_for_layout() + } else { fail!("not text!") } - let text: JS = 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) } } - - unsafe { - if !self.get().is_text() { - fail!("not text!") - } - let text: JS = self.get_jsmanaged().transmute_copy(); - (*text.unsafe_get()).characterdata.data.deref().borrow().clone() - } + self.node.text() } } @@ -732,6 +730,36 @@ impl<'ln> ThreadSafeLayoutNode<'ln> { _ => false } } + + pub fn get_input_checked(&self) -> bool { + unsafe { + if !self.get().is_htmlinputelement() { + fail!("not an input element!") + } + let input: JS = 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 = 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 = self.get_jsmanaged().transmute_copy(); + input.get_size_for_layout() + } + } } pub struct ThreadSafeLayoutNodeChildrenIterator<'a> { diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs index cc4c85a0791..251a0dd0439 100644 --- a/components/script/dom/htmlinputelement.rs +++ b/components/script/dom/htmlinputelement.rs @@ -2,26 +2,55 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +use dom::bindings::codegen::Bindings::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::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::js::{JSRef, Temporary}; +use dom::bindings::js::{JS, JSRef, Temporary, OptionalRootable, ResultRootable}; 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::event::Event; use dom::eventtarget::{EventTarget, NodeTargetTypeId}; 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 servo_util::str::DOMString; +use servo_util::str::{DOMString, parse_unsigned_integer}; 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] #[must_root] pub struct HTMLInputElement { pub htmlelement: HTMLElement, + input_type: Cell, + checked: Cell, + uncommitted_value: RefCell>, + value: RefCell>, + size: Cell, } impl HTMLInputElementDerived for EventTarget { @@ -30,10 +59,17 @@ impl HTMLInputElementDerived for EventTarget { } } +static DEFAULT_INPUT_SIZE: u32 = 20; + impl HTMLInputElement { fn new_inherited(localName: DOMString, document: JSRef) -> 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 { + #[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) -> Option { + mem::transmute::<&RefCell>, &Option>(&(*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> { // http://www.whatwg.org/html/#dom-fe-disabled make_bool_getter!(Disabled) @@ -53,6 +136,104 @@ impl<'a> HTMLInputElementMethods for JSRef<'a, HTMLInputElement> { let elem: JSRef = ElementCast::from_ref(self); 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 = 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 = 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 = 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 = 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; + fn update_checked_state(self, checked: bool); +} + +fn broadcast_radio_checked(broadcaster: JSRef, 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 = 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 { + //TODO: determine form owner + let elem: JSRef = 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> { @@ -72,7 +253,41 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLInputElement> { "disabled" => { node.set_disabled_state(true); 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_enabled_state(true); 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(); } } + + fn handle_event(&self, event: JSRef) { + 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 { diff --git a/components/script/dom/webidls/HTMLInputElement.webidl b/components/script/dom/webidls/HTMLInputElement.webidl index 1caa9137e0b..a418f50928c 100644 --- a/components/script/dom/webidls/HTMLInputElement.webidl +++ b/components/script/dom/webidls/HTMLInputElement.webidl @@ -10,7 +10,7 @@ interface HTMLInputElement : HTMLElement { // attribute DOMString autocomplete; // attribute boolean autofocus; // attribute boolean defaultChecked; - // attribute boolean checked; + attribute boolean checked; // attribute DOMString dirName; attribute boolean disabled; //readonly attribute HTMLFormElement? form; @@ -29,17 +29,17 @@ interface HTMLInputElement : HTMLElement { // attribute DOMString min; // attribute long minLength; // attribute boolean multiple; - // attribute DOMString name; + attribute DOMString name; // attribute DOMString pattern; // attribute DOMString placeholder; // attribute boolean readOnly; // attribute boolean required; - // attribute unsigned long size; + attribute unsigned long size; // attribute DOMString src; // attribute DOMString step; - // attribute DOMString type; + // attribute DOMString type; //XXXjdm need binaryName // attribute DOMString defaultValue; - //[TreatNullAs=EmptyString] attribute DOMString value; + [TreatNullAs=EmptyString] attribute DOMString value; // attribute Date? valueAsDate; // attribute unrestricted double valueAsNumber; // attribute double valueLow; diff --git a/components/style/user-agent.css b/components/style/user-agent.css index c2719a130f3..a9def733ef2 100644 --- a/components/style/user-agent.css +++ b/components/style/user-agent.css @@ -116,4 +116,10 @@ area:link, link:link { color: blue } script { 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="checkbox"], +input[type="radio"] { font-family: monospace !important; border: none !important; background: transparent; } diff --git a/tests/html/test-inputs.html b/tests/html/test-inputs.html new file mode 100644 index 00000000000..0c94ed87d94 --- /dev/null +++ b/tests/html/test-inputs.html @@ -0,0 +1,17 @@ + +
+
+
+
+
+
+
+
group 1 +
+
+
+
group 2 +
+
+
From 8112859d556477f4ec42dbc58538371826edd468 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Mon, 29 Sep 2014 14:14:18 -0400 Subject: [PATCH 4/8] Disallow toggling radio buttons. Use generated content for checkboxes and radio buttons. Switching to the glyph 0 for the average advance width. --- components/gfx/platform/freetype/font.rs | 2 +- components/gfx/platform/macos/font.rs | 2 +- components/layout/construct.rs | 4 ++-- components/layout/fragment.rs | 4 ++-- components/layout/wrapper.rs | 10 ---------- components/script/dom/htmlinputelement.rs | 24 ++++------------------- components/style/user-agent.css | 5 +++++ 7 files changed, 15 insertions(+), 36 deletions(-) diff --git a/components/gfx/platform/freetype/font.rs b/components/gfx/platform/freetype/font.rs index 4d78afd8c46..dfe649a0ca7 100644 --- a/components/gfx/platform/freetype/font.rs +++ b/components/gfx/platform/freetype/font.rs @@ -235,7 +235,7 @@ impl FontHandleMethods for FontHandle { } } - let average_advance = self.glyph_index('x') + let average_advance = self.glyph_index('0') .and_then(|idx| self.glyph_h_advance(idx)) .map(|advance| self.font_units_to_au(advance)) .unwrap_or(max_advance_width); diff --git a/components/gfx/platform/macos/font.rs b/components/gfx/platform/macos/font.rs index ad3d5bd1b29..dd48e797cd1 100644 --- a/components/gfx/platform/macos/font.rs +++ b/components/gfx/platform/macos/font.rs @@ -154,7 +154,7 @@ impl FontHandleMethods for FontHandle { let line_gap = (ascent + descent + leading + 0.5).floor(); let max_advance_width = Au::from_pt(bounding_rect.size.width as f64); - let average_advance = self.glyph_index('x') + let average_advance = self.glyph_index('0') .and_then(|idx| self.glyph_h_advance(idx)) .map(|advance| Au::from_frac_px(advance)) .unwrap_or(max_advance_width); diff --git a/components/layout/construct.rs b/components/layout/construct.rs index 24d85b7fece..2897f04fa43 100644 --- a/components/layout/construct.rs +++ b/components/layout/construct.rs @@ -226,10 +226,10 @@ impl<'a> FlowConstructor<'a> { // 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("checkbox") => InputCheckbox, Some("button") | Some("submit") | Some("reset") => InputButton(node.get_input_value().len() as u32), - Some("radio") => InputRadioButton(node.get_input_checked()), + Some("radio") => InputRadioButton, Some("file") => InputFile(node.get_input_size()), _ => InputText(node.get_input_size()), }; diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs index 010d26f98e0..0be128ccb27 100644 --- a/components/layout/fragment.rs +++ b/components/layout/fragment.rs @@ -161,8 +161,8 @@ impl InlineBlockFragmentInfo { pub enum InputFragmentInfo { InputButton(u32), InputText(u32), - InputCheckbox(bool), - InputRadioButton(bool), + InputCheckbox, + InputRadioButton, InputFile(u32), } diff --git a/components/layout/wrapper.rs b/components/layout/wrapper.rs index 5c634ab3caf..eb1be0b09de 100644 --- a/components/layout/wrapper.rs +++ b/components/layout/wrapper.rs @@ -731,16 +731,6 @@ impl<'ln> ThreadSafeLayoutNode<'ln> { } } - pub fn get_input_checked(&self) -> bool { - unsafe { - if !self.get().is_htmlinputelement() { - fail!("not an input element!") - } - let input: JS = self.get_jsmanaged().transmute_copy(); - input.get_checked_for_layout() - } - } - pub fn get_input_value(&self) -> String { unsafe { if !self.get().is_htmlinputelement() { diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs index 251a0dd0439..8c50c6519a0 100644 --- a/components/script/dom/htmlinputelement.rs +++ b/components/script/dom/htmlinputelement.rs @@ -81,17 +81,11 @@ 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 { - #[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) -> Option { @@ -99,16 +93,7 @@ impl LayoutHTMLInputElementHelpers for JS { } 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(), + InputCheckbox | InputRadio => "".to_string(), InputFile | InputImage => "".to_string(), InputButton(ref default) => get_raw_value(self) .or_else(|| default.map(|v| v.to_string())) @@ -117,7 +102,7 @@ impl LayoutHTMLInputElementHelpers for JS { let raw = get_raw_value(self).unwrap_or_else(|| "".to_string()); String::from_char(raw.len(), '*') } - _ => get_raw_value(self).unwrap_or("".to_string()), + _ => get_raw_value(self).unwrap_or_else(|| "".to_string()), } } @@ -369,9 +354,8 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLInputElement> { if "click" == event.Type().as_slice() && !event.DefaultPrevented() { match self.input_type.get() { - InputCheckbox | InputRadio => { - self.SetChecked(!self.checked.get()); - } + InputCheckbox => self.SetChecked(!self.checked.get()), + InputRadio => self.SetChecked(true), _ => {} } } diff --git a/components/style/user-agent.css b/components/style/user-agent.css index a9def733ef2..4ca752f344d 100644 --- a/components/style/user-agent.css +++ b/components/style/user-agent.css @@ -123,3 +123,8 @@ input[type="reset"] { background: lightgrey; border-top: solid 1px #EEEEEE; input[type="hidden"] { display: none !important } input[type="checkbox"], input[type="radio"] { font-family: monospace !important; border: none !important; background: transparent; } + +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: "(●)"; } From ca5d4633c23f73187f76f546dd9fa04cd1769bde Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Mon, 29 Sep 2014 14:23:02 -0400 Subject: [PATCH 5/8] Disallow wrapping on input elements. --- components/style/user-agent.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/style/user-agent.css b/components/style/user-agent.css index 4ca752f344d..b1f7c6f10cf 100644 --- a/components/style/user-agent.css +++ b/components/style/user-agent.css @@ -116,7 +116,7 @@ area:link, link:link { color: blue } script { 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 { 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; white-space: nowrap; } 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; } From 5794da904eb758ebb1f143b172dae515379fad43 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Mon, 29 Sep 2014 16:13:12 -0400 Subject: [PATCH 6/8] Remove unneeded input type information from layout. --- components/layout/construct.rs | 16 ++++++++-------- components/layout/fragment.rs | 26 +++++--------------------- 2 files changed, 13 insertions(+), 29 deletions(-) diff --git a/components/layout/construct.rs b/components/layout/construct.rs index 2897f04fa43..818fa1602e6 100644 --- a/components/layout/construct.rs +++ b/components/layout/construct.rs @@ -31,8 +31,8 @@ use fragment::{InlineBlockFragment, InlineBlockFragmentInfo, InputFragment}; use fragment::{Fragment, GenericFragment, IframeFragment, IframeFragmentInfo}; use fragment::{ImageFragment, ImageFragmentInfo, SpecificFragmentInfo, TableFragment}; use fragment::{TableCellFragment, TableColumnFragment, TableColumnFragmentInfo}; -use fragment::{TableRowFragment, TableWrapperFragment, UnscannedTextFragment, InputRadioButton}; -use fragment::{UnscannedTextFragmentInfo, InputCheckbox, InputButton, InputText, InputFile}; +use fragment::{TableRowFragment, TableWrapperFragment, UnscannedTextFragment}; +use fragment::{UnscannedTextFragmentInfo, InputFragmentInfo}; use inline::{InlineFragments, InlineFlow}; use parallel; use table_wrapper::TableWrapperFlow; @@ -226,14 +226,14 @@ impl<'a> FlowConstructor<'a> { // value? definitely for string comparisons. let elem = node.as_element(); let data = match elem.get_attr(&ns!(""), "type") { - Some("checkbox") => InputCheckbox, + Some("checkbox") | Some("radio") => None, Some("button") | Some("submit") | Some("reset") => - InputButton(node.get_input_value().len() as u32), - Some("radio") => InputRadioButton, - Some("file") => InputFile(node.get_input_size()), - _ => InputText(node.get_input_size()), + Some(node.get_input_value().len() as u32), + Some("file") => Some(node.get_input_size()), + _ => Some(node.get_input_size()), }; - InputFragment(data) + data.map(|size| InputFragment(InputFragmentInfo { size: size })) + .unwrap_or(GenericFragment) } /// Builds specific `Fragment` info for the given node. diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs index 0be128ccb27..7c2ef5c4a05 100644 --- a/components/layout/fragment.rs +++ b/components/layout/fragment.rs @@ -158,33 +158,17 @@ impl InlineBlockFragmentInfo { /// A fragment that represents a displayable form element #[deriving(Clone)] -pub enum InputFragmentInfo { - InputButton(u32), - InputText(u32), - InputCheckbox, - InputRadioButton, - InputFile(u32), +pub struct InputFragmentInfo { + pub size: u32, } impl InputFragmentInfo { - fn size(&self) -> Option { - 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); + 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) - } + // https://html.spec.whatwg.org/#converting-a-character-width-to-pixels + metrics.average_advance * (self.size as i32 - 1) + metrics.max_advance } } From 7401cd0aeb195235323179028438d3fb5fa1c74e Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Wed, 1 Oct 2014 15:02:39 -0400 Subject: [PATCH 7/8] Split a long line. --- components/script/dom/htmlinputelement.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs index 8c50c6519a0..3bb57dfdb0e 100644 --- a/components/script/dom/htmlinputelement.rs +++ b/components/script/dom/htmlinputelement.rs @@ -243,7 +243,8 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLInputElement> { self.update_checked_state(true); } "size" => { - self.size.set(parse_unsigned_integer(value.as_slice().chars()).unwrap_or(DEFAULT_INPUT_SIZE)); + let parsed = parse_unsigned_integer(value.as_slice().chars()); + self.size.set(parsed.unwrap_or(DEFAULT_INPUT_SIZE)); self.force_relayout(); } "type" => { From be41c203ac1dbc362f445169c7a5f2a48bebad58 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Wed, 1 Oct 2014 18:43:24 -0400 Subject: [PATCH 8/8] Fix linux build error. --- components/gfx/platform/freetype/font.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/gfx/platform/freetype/font.rs b/components/gfx/platform/freetype/font.rs index dfe649a0ca7..b121e869865 100644 --- a/components/gfx/platform/freetype/font.rs +++ b/components/gfx/platform/freetype/font.rs @@ -238,7 +238,7 @@ impl FontHandleMethods for FontHandle { let average_advance = self.glyph_index('0') .and_then(|idx| self.glyph_h_advance(idx)) .map(|advance| self.font_units_to_au(advance)) - .unwrap_or(max_advance_width); + .unwrap_or(max_advance); let metrics = FontMetrics { underline_size: underline_size,