diff options
Diffstat (limited to 'components/layout')
-rw-r--r-- | components/layout/display_list_builder.rs | 4 | ||||
-rw-r--r-- | components/layout/fragment.rs | 112 | ||||
-rw-r--r-- | components/layout/text.rs | 81 | ||||
-rw-r--r-- | components/layout/webrender_helpers.rs | 2 | ||||
-rw-r--r-- | components/layout/wrapper.rs | 37 |
5 files changed, 88 insertions, 148 deletions
diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs index de76e2b20cc..495c22e7589 100644 --- a/components/layout/display_list_builder.rs +++ b/components/layout/display_list_builder.rs @@ -31,7 +31,7 @@ use gfx::display_list::{LineDisplayItem, OpaqueNode, SolidColorDisplayItem}; use gfx::display_list::{StackingContext, StackingContextId, StackingContextType}; use gfx::display_list::{TextDisplayItem, TextOrientation, WebRenderImageInfo}; use gfx::paint_thread::THREAD_TINT_COLORS; -use gfx::text::glyph::CharIndex; +use gfx::text::glyph::ByteIndex; use gfx_traits::{color, ScrollPolicy}; use inline::{FIRST_FRAGMENT_OF_ELEMENT, InlineFlow, LAST_FRAGMENT_OF_ELEMENT}; use ipc_channel::ipc::{self}; @@ -965,7 +965,7 @@ impl FragmentDisplayListBuilding for Fragment { Some(insertion_point_index) => insertion_point_index, None => return, }; - let range = Range::new(CharIndex(0), insertion_point_index); + let range = Range::new(ByteIndex(0), insertion_point_index); let advance = scanned_text_fragment_info.run.advance_for_range(&range); let insertion_point_bounds; diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs index 5612a322b95..ce9e55496c7 100644 --- a/components/layout/fragment.rs +++ b/components/layout/fragment.rs @@ -15,7 +15,7 @@ use flow::{self, Flow}; use flow_ref::{self, FlowRef}; use gfx; use gfx::display_list::{BLUR_INFLATION_FACTOR, FragmentType, OpaqueNode, StackingContextId}; -use gfx::text::glyph::CharIndex; +use gfx::text::glyph::ByteIndex; use gfx::text::text_run::{TextRun, TextRunSlice}; use gfx_traits::{LayerId, LayerType}; use incremental::{RECONSTRUCT_FLOW, RestyleDamage}; @@ -48,7 +48,6 @@ use text; use text::TextRunScanner; use url::Url; use util; -use util::str::slice_chars; use wrapper::{PseudoElementType, ThreadSafeLayoutElement, ThreadSafeLayoutNode}; /// Fragments (`struct Fragment`) are the leaves of the layout tree. They cannot position @@ -227,13 +226,8 @@ impl SpecificFragmentInfo { impl fmt::Debug for SpecificFragmentInfo { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - SpecificFragmentInfo::ScannedText(ref info) => { - write!(f, "{:?}", slice_chars(&*info.run.text, info.range.begin().get() as usize, - info.range.end().get() as usize)) - } - SpecificFragmentInfo::UnscannedText(ref info) => { - write!(f, "{:?}", info.text) - } + SpecificFragmentInfo::ScannedText(ref info) => write!(f, "{:?}", info.text()), + SpecificFragmentInfo::UnscannedText(ref info) => write!(f, "{:?}", info.text), _ => Ok(()) } } @@ -657,16 +651,16 @@ pub struct ScannedTextFragmentInfo { /// The intrinsic size of the text fragment. pub content_size: LogicalSize<Au>, - /// The position of the insertion point in characters, if any. - pub insertion_point: Option<CharIndex>, + /// The byte offset of the insertion point, if any. + pub insertion_point: Option<ByteIndex>, /// The range within the above text run that this represents. - pub range: Range<CharIndex>, + pub range: Range<ByteIndex>, /// The endpoint of the above range, including whitespace that was stripped out. This exists /// so that we can restore the range to its original value (before line breaking occurred) when /// performing incremental reflow. - pub range_end_including_stripped_whitespace: CharIndex, + pub range_end_including_stripped_whitespace: ByteIndex, pub flags: ScannedTextFlags, } @@ -685,9 +679,9 @@ bitflags! { impl ScannedTextFragmentInfo { /// Creates the information specific to a scanned text fragment from a range and a text run. pub fn new(run: Arc<TextRun>, - range: Range<CharIndex>, + range: Range<ByteIndex>, content_size: LogicalSize<Au>, - insertion_point: Option<CharIndex>, + insertion_point: Option<ByteIndex>, flags: ScannedTextFlags) -> ScannedTextFragmentInfo { ScannedTextFragmentInfo { @@ -700,6 +694,10 @@ impl ScannedTextFragmentInfo { } } + pub fn text(&self) -> &str { + &self.run.text[self.range.begin().to_usize() .. self.range.end().to_usize()] + } + pub fn requires_line_break_afterward_if_wrapping_on_newlines(&self) -> bool { self.flags.contains(REQUIRES_LINE_BREAK_AFTERWARD_IF_WRAPPING_ON_NEWLINES) } @@ -715,12 +713,12 @@ impl ScannedTextFragmentInfo { pub struct SplitInfo { // TODO(bjz): this should only need to be a single character index, but both values are // currently needed for splitting in the `inline::try_append_*` functions. - pub range: Range<CharIndex>, + pub range: Range<ByteIndex>, pub inline_size: Au, } impl SplitInfo { - fn new(range: Range<CharIndex>, info: &ScannedTextFragmentInfo) -> SplitInfo { + fn new(range: Range<ByteIndex>, info: &ScannedTextFragmentInfo) -> SplitInfo { let inline_size = info.run.advance_for_range(&range); SplitInfo { range: range, @@ -755,13 +753,13 @@ pub struct UnscannedTextFragmentInfo { pub text: Box<str>, /// The selected text range. An empty range represents the insertion point. - pub selection: Option<Range<CharIndex>>, + pub selection: Option<Range<ByteIndex>>, } impl UnscannedTextFragmentInfo { /// Creates a new instance of `UnscannedTextFragmentInfo` from the given text. #[inline] - pub fn new(text: String, selection: Option<Range<CharIndex>>) -> UnscannedTextFragmentInfo { + pub fn new(text: String, selection: Option<Range<ByteIndex>>) -> UnscannedTextFragmentInfo { UnscannedTextFragmentInfo { text: text.into_boxed_str(), selection: selection, @@ -1611,7 +1609,7 @@ impl Fragment { }; let mut remaining_inline_size = max_inline_size; - let mut inline_start_range = Range::new(text_fragment_info.range.begin(), CharIndex(0)); + let mut inline_start_range = Range::new(text_fragment_info.range.begin(), ByteIndex(0)); let mut inline_end_range = None; let mut overflowing = false; @@ -1651,7 +1649,7 @@ impl Fragment { // We're going to overflow the line. overflowing = true; inline_start_range = slice.text_run_range(); - remaining_range = Range::new(slice.text_run_range().end(), CharIndex(0)); + remaining_range = Range::new(slice.text_run_range().end(), ByteIndex(0)); remaining_range.extend_to(text_fragment_info.range.end()); } @@ -2322,32 +2320,20 @@ impl Fragment { match self.specific { SpecificFragmentInfo::ScannedText(ref mut scanned_text_fragment_info) => { - let mut leading_whitespace_character_count = 0; - { - let text = slice_chars( - &*scanned_text_fragment_info.run.text, - scanned_text_fragment_info.range.begin().to_usize(), - scanned_text_fragment_info.range.end().to_usize()); - for character in text.chars() { - if util::str::char_is_whitespace(character) { - leading_whitespace_character_count += 1 - } else { - break - } - } - } + let leading_whitespace_byte_count = scanned_text_fragment_info.text() + .find(|c| !util::str::char_is_whitespace(c)) + .unwrap_or(scanned_text_fragment_info.text().len()); + let whitespace_len = ByteIndex(leading_whitespace_byte_count as isize); let whitespace_range = Range::new(scanned_text_fragment_info.range.begin(), - CharIndex(leading_whitespace_character_count)); + whitespace_len); let text_bounds = scanned_text_fragment_info.run.metrics_for_range(&whitespace_range).bounding_box; self.border_box.size.inline = self.border_box.size.inline - text_bounds.size.width; scanned_text_fragment_info.content_size.inline = scanned_text_fragment_info.content_size.inline - text_bounds.size.width; - scanned_text_fragment_info.range.adjust_by( - CharIndex(leading_whitespace_character_count), - -CharIndex(leading_whitespace_character_count)); + scanned_text_fragment_info.range.adjust_by(whitespace_len, -whitespace_len); WhitespaceStrippingResult::RetainFragment } @@ -2388,43 +2374,29 @@ impl Fragment { match self.specific { SpecificFragmentInfo::ScannedText(ref mut scanned_text_fragment_info) => { - // FIXME(pcwalton): Is there a more clever (i.e. faster) way to do this? - debug!("stripping trailing whitespace: range={:?}, len={}", - scanned_text_fragment_info.range, - scanned_text_fragment_info.run.text.chars().count()); - let mut trailing_whitespace_character_count = 0; - let text_bounds; - { - let text = slice_chars(&*scanned_text_fragment_info.run.text, - scanned_text_fragment_info.range.begin().to_usize(), - scanned_text_fragment_info.range.end().to_usize()); - for ch in text.chars().rev() { - if util::str::char_is_whitespace(ch) { - trailing_whitespace_character_count += 1 - } else { - break - } + let mut trailing_whitespace_start_byte = 0; + for (i, c) in scanned_text_fragment_info.text().char_indices().rev() { + if !util::str::char_is_whitespace(c) { + trailing_whitespace_start_byte = i + c.len_utf8(); + break; } - - let whitespace_range = - Range::new(scanned_text_fragment_info.range.end() - - CharIndex(trailing_whitespace_character_count), - CharIndex(trailing_whitespace_character_count)); - text_bounds = scanned_text_fragment_info.run - .metrics_for_range(&whitespace_range) - .bounding_box; - self.border_box.size.inline = self.border_box.size.inline - - text_bounds.size.width; } + let whitespace_start = ByteIndex(trailing_whitespace_start_byte as isize); + let whitespace_len = scanned_text_fragment_info.range.length() - whitespace_start; + let whitespace_range = Range::new(whitespace_start, whitespace_len); + + // FIXME: This may be unnecessary because these metrics will be recomputed in + // LineBreaker::strip_trailing_whitespace_from_pending_line_if_necessary + let text_bounds = scanned_text_fragment_info.run + .metrics_for_range(&whitespace_range) + .bounding_box; + self.border_box.size.inline = self.border_box.size.inline - + text_bounds.size.width; scanned_text_fragment_info.content_size.inline = scanned_text_fragment_info.content_size.inline - text_bounds.size.width; - if trailing_whitespace_character_count != 0 { - scanned_text_fragment_info.range.extend_by( - CharIndex(-trailing_whitespace_character_count)); - } - + scanned_text_fragment_info.range.extend_by(-whitespace_len); WhitespaceStrippingResult::RetainFragment } SpecificFragmentInfo::UnscannedText(ref mut unscanned_text_fragment_info) => { diff --git a/components/layout/text.rs b/components/layout/text.rs index 22fbeb9ad1a..b7729608ce7 100644 --- a/components/layout/text.rs +++ b/components/layout/text.rs @@ -12,7 +12,7 @@ use fragment::{ScannedTextFragmentInfo, SELECTED, SpecificFragmentInfo, Unscanne use gfx::font::{DISABLE_KERNING_SHAPING_FLAG, FontMetrics, IGNORE_LIGATURES_SHAPING_FLAG}; use gfx::font::{RTL_FLAG, RunMetrics, ShapingFlags, ShapingOptions}; use gfx::font_context::FontContext; -use gfx::text::glyph::CharIndex; +use gfx::text::glyph::ByteIndex; use gfx::text::text_run::TextRun; use gfx::text::util::{self, CompressionMode}; use inline::{FIRST_FRAGMENT_OF_ELEMENT, InlineFragments, LAST_FRAGMENT_OF_ELEMENT}; @@ -174,7 +174,7 @@ impl TextRunScanner { for (fragment_index, in_fragment) in self.clump.iter().enumerate() { debug!(" flushing {:?}", in_fragment); - let mut mapping = RunMapping::new(&run_info_list[..], &run_info, fragment_index); + let mut mapping = RunMapping::new(&run_info_list[..], fragment_index); let text; let selection; match in_fragment.specific { @@ -188,13 +188,13 @@ impl TextRunScanner { Some(range) if range.is_empty() => { // `range` is the range within the current fragment. To get the range // within the text run, offset it by the length of the preceding fragments. - Some(range.begin() + CharIndex(run_info.character_length as isize)) + Some(range.begin() + ByteIndex(run_info.text.len() as isize)) } _ => None }; let (mut start_position, mut end_position) = (0, 0); - for (char_index, character) in text.chars().enumerate() { + for (byte_index, character) in text.char_indices() { // Search for the first font in this font group that contains a glyph for this // character. let mut font_index = 0; @@ -226,7 +226,7 @@ impl TextRunScanner { } let selected = match selection { - Some(range) => range.contains(CharIndex(char_index as isize)), + Some(range) => range.contains(ByteIndex(byte_index as isize)), None => false }; @@ -251,7 +251,6 @@ impl TextRunScanner { run_info = RunInfo::new(); } mapping = RunMapping::new(&run_info_list[..], - &run_info, fragment_index); } run_info.font_index = font_index; @@ -343,11 +342,14 @@ impl TextRunScanner { let mut mapping = mappings.next().unwrap(); let scanned_run = runs[mapping.text_run_index].clone(); + let mut byte_range = Range::new(ByteIndex(mapping.byte_range.begin() as isize), + ByteIndex(mapping.byte_range.length() as isize)); + let requires_line_break_afterward_if_wrapping_on_newlines = !mapping.byte_range.is_empty() && scanned_run.run.text.char_at_reverse(mapping.byte_range.end()) == '\n'; if requires_line_break_afterward_if_wrapping_on_newlines { - mapping.char_range.extend_by(CharIndex(-1)); + byte_range.extend_by(ByteIndex(-1)); // Trim the '\n' } let text_size = old_fragment.border_box.size; @@ -368,12 +370,12 @@ impl TextRunScanner { let mut new_text_fragment_info = box ScannedTextFragmentInfo::new( scanned_run.run, - mapping.char_range, + byte_range, text_size, insertion_point, flags); - let new_metrics = new_text_fragment_info.run.metrics_for_range(&mapping.char_range); + let new_metrics = new_text_fragment_info.run.metrics_for_range(&byte_range); let writing_mode = old_fragment.style.writing_mode; let bounding_box_size = bounding_box_for_run_metrics(&new_metrics, writing_mode); new_text_fragment_info.content_size = bounding_box_size; @@ -490,7 +492,7 @@ fn split_first_fragment_at_newline_if_necessary(fragments: &mut LinkedList<Fragm unscanned_text_fragment_info.text[..(position + 1)].to_owned(); unscanned_text_fragment_info.text = unscanned_text_fragment_info.text[(position + 1)..].to_owned().into_boxed_str(); - let offset = CharIndex(string_before.char_indices().count() as isize); + let offset = ByteIndex(string_before.len() as isize); match unscanned_text_fragment_info.selection { Some(ref mut selection) if selection.begin() >= offset => { // Selection is entirely in the second fragment. @@ -500,7 +502,7 @@ fn split_first_fragment_at_newline_if_necessary(fragments: &mut LinkedList<Fragm Some(ref mut selection) if selection.end() > offset => { // Selection is split across two fragments. selection_before = Some(Range::new(selection.begin(), offset)); - *selection = Range::new(CharIndex(0), selection.end() - offset); + *selection = Range::new(ByteIndex(0), selection.end() - offset); } _ => { // Selection is entirely in the first fragment. @@ -523,11 +525,9 @@ struct RunInfo { /// The text that will go in this text run. text: String, /// The insertion point in this text run, if applicable. - insertion_point: Option<CharIndex>, + insertion_point: Option<ByteIndex>, /// The index of the applicable font in the font group. font_index: usize, - /// A cached copy of the number of Unicode characters in the text run. - character_length: usize, /// The bidirection embedding level of this text run. bidi_level: u8, /// The Unicode script property of this text run. @@ -540,7 +540,6 @@ impl RunInfo { text: String::new(), insertion_point: None, font_index: 0, - character_length: 0, bidi_level: 0, script: Script::Common, } @@ -552,9 +551,9 @@ impl RunInfo { /// of this text run. fn flush(mut self, list: &mut Vec<RunInfo>, - insertion_point: &mut Option<CharIndex>) { + insertion_point: &mut Option<ByteIndex>) { if let Some(idx) = *insertion_point { - let char_len = CharIndex(self.character_length as isize); + let char_len = ByteIndex(self.text.len() as isize); if idx <= char_len { // The insertion point is in this text run. self.insertion_point = insertion_point.take() @@ -571,8 +570,6 @@ impl RunInfo { /// for it. #[derive(Copy, Clone, Debug)] struct RunMapping { - /// The range of characters within the text fragment. - char_range: Range<CharIndex>, /// The range of byte indices within the text fragment. byte_range: Range<usize>, /// The index of the unscanned text fragment that this mapping corresponds to. @@ -585,13 +582,10 @@ struct RunMapping { impl RunMapping { /// Given the current set of text runs, creates a run mapping for the next fragment. - /// `run_info_list` describes the set of runs we've seen already, and `current_run_info` - /// describes the run we just finished processing. - fn new(run_info_list: &[RunInfo], current_run_info: &RunInfo, fragment_index: usize) + /// `run_info_list` describes the set of runs we've seen already. + fn new(run_info_list: &[RunInfo], fragment_index: usize) -> RunMapping { RunMapping { - char_range: Range::new(CharIndex(current_run_info.character_length as isize), - CharIndex(0)), byte_range: Range::new(0, 0), old_fragment_index: fragment_index, text_run_index: run_info_list.len(), @@ -620,26 +614,21 @@ impl RunMapping { // Account for `text-transform`. (Confusingly, this is not handled in "text // transformation" above, but we follow Gecko in the naming.) let is_first_run = *start_position == 0; - let character_count = apply_style_transform_if_necessary(&mut run_info.text, - old_byte_length, - text_transform, - *last_whitespace, - is_first_run); - - run_info.character_length = run_info.character_length + character_count; + apply_style_transform_if_necessary(&mut run_info.text, old_byte_length, text_transform, + *last_whitespace, is_first_run); *start_position = end_position; + let new_byte_length = run_info.text.len(); + let is_empty = new_byte_length == old_byte_length; + // Don't save mappings that contain only discarded characters. // (But keep ones that contained no characters to begin with, since they might have been // generated by an empty flow to draw its borders/padding/insertion point.) - let is_empty = character_count == 0; if is_empty && !was_empty { return; } - let new_byte_length = run_info.text.len(); self.byte_range = Range::new(old_byte_length, new_byte_length - old_byte_length); - self.char_range.extend_by(CharIndex(character_count as isize)); mappings.push(self) } @@ -648,10 +637,10 @@ impl RunMapping { /// NOTE: We treat the range as inclusive at both ends, since the insertion point can lie /// before the first character *or* after the last character, and should be drawn even if the /// text is empty. - fn contains_insertion_point(&self, insertion_point: Option<CharIndex>) -> bool { - match insertion_point { + fn contains_insertion_point(&self, insertion_point: Option<ByteIndex>) -> bool { + match insertion_point.map(ByteIndex::to_usize) { None => false, - Some(idx) => self.char_range.begin() <= idx && idx <= self.char_range.end() + Some(idx) => self.byte_range.begin() <= idx && idx <= self.byte_range.end() } } } @@ -666,39 +655,29 @@ fn apply_style_transform_if_necessary(string: &mut String, first_character_position: usize, text_transform: text_transform::T, last_whitespace: bool, - is_first_run: bool) - -> usize { + is_first_run: bool) { match text_transform { - text_transform::T::none => string[first_character_position..].chars().count(), + text_transform::T::none => {} text_transform::T::uppercase => { let original = string[first_character_position..].to_owned(); string.truncate(first_character_position); - let mut count = 0; for ch in original.chars().flat_map(|ch| ch.to_uppercase()) { string.push(ch); - count += 1; } - count } text_transform::T::lowercase => { let original = string[first_character_position..].to_owned(); string.truncate(first_character_position); - let mut count = 0; for ch in original.chars().flat_map(|ch| ch.to_lowercase()) { string.push(ch); - count += 1; } - count } text_transform::T::capitalize => { let original = string[first_character_position..].to_owned(); string.truncate(first_character_position); let mut capitalize_next_letter = is_first_run || last_whitespace; - let mut count = 0; for character in original.chars() { - count += 1; - // FIXME(#4311, pcwalton): Should be the CSS/Unicode notion of a *typographic // letter unit*, not an *alphabetic* character: // @@ -716,8 +695,6 @@ fn apply_style_transform_if_necessary(string: &mut String, capitalize_next_letter = true } } - - count } } } @@ -725,7 +702,7 @@ fn apply_style_transform_if_necessary(string: &mut String, #[derive(Clone)] struct ScannedTextRun { run: Arc<TextRun>, - insertion_point: Option<CharIndex>, + insertion_point: Option<ByteIndex>, } /// Can a character with script `b` continue a text run with script `a`? diff --git a/components/layout/webrender_helpers.rs b/components/layout/webrender_helpers.rs index c3b28f8ecc1..053ecf87f50 100644 --- a/components/layout/webrender_helpers.rs +++ b/components/layout/webrender_helpers.rs @@ -392,7 +392,7 @@ impl WebRenderDisplayItemConverter for DisplayItem { let mut glyphs = vec!(); for slice in item.text_run.natural_word_slices_in_visual_order(&item.range) { - for glyph in slice.glyphs.iter_glyphs_for_char_range(&slice.range) { + for glyph in slice.glyphs.iter_glyphs_for_byte_range(&slice.range) { let glyph_advance = glyph.advance(); let glyph_offset = glyph.offset().unwrap_or(Point2D::zero()); let glyph = webrender_traits::GlyphInstance { diff --git a/components/layout/wrapper.rs b/components/layout/wrapper.rs index 00c77631707..86c370c0a83 100644 --- a/components/layout/wrapper.rs +++ b/components/layout/wrapper.rs @@ -33,7 +33,7 @@ use core::nonzero::NonZero; use data::{LayoutDataFlags, PrivateLayoutData}; use gfx::display_list::OpaqueNode; -use gfx::text::glyph::CharIndex; +use gfx::text::glyph::ByteIndex; use gfx_traits::{LayerId, LayerType}; use incremental::RestyleDamage; use msg::constellation_msg::PipelineId; @@ -74,7 +74,7 @@ use style::restyle_hints::ElementSnapshot; use style::selector_impl::{NonTSPseudoClass, PseudoElement, ServoSelectorImpl}; use style::servo::PrivateStyleData; use url::Url; -use util::str::{is_whitespace, search_index}; +use util::str::is_whitespace; pub type NonOpaqueStyleAndLayoutData = *mut RefCell<PrivateLayoutData>; @@ -838,7 +838,7 @@ pub trait ThreadSafeLayoutNode : Clone + Copy + Sized + PartialEq { fn text_content(&self) -> TextContent; /// If the insertion point is within this node, returns it. Otherwise, returns `None`. - fn selection(&self) -> Option<Range<CharIndex>>; + fn selection(&self) -> Option<Range<ByteIndex>>; /// If this is an image element, returns its URL. If this is not an image element, fails. /// @@ -1077,27 +1077,18 @@ impl<'ln> ThreadSafeLayoutNode for ServoThreadSafeLayoutNode<'ln> { panic!("not text!") } - fn selection(&self) -> Option<Range<CharIndex>> { - let this = unsafe { - self.get_jsmanaged() - }; + fn selection(&self) -> Option<Range<ByteIndex>> { + let this = unsafe { self.get_jsmanaged() }; - if let Some(area) = this.downcast::<HTMLTextAreaElement>() { - if let Some(selection) = unsafe { area.get_absolute_selection_for_layout() } { - let text = unsafe { area.get_value_for_layout() }; - let begin_byte = selection.begin(); - let begin = search_index(begin_byte, text.char_indices()); - let length = search_index(selection.length(), text[begin_byte..].char_indices()); - return Some(Range::new(CharIndex(begin), CharIndex(length))); - } - } - if let Some(input) = this.downcast::<HTMLInputElement>() { - if let Some(selection) = unsafe { input.selection_for_layout() } { - return Some(Range::new(CharIndex(selection.begin()), - CharIndex(selection.length()))); - } - } - None + let selection = if let Some(area) = this.downcast::<HTMLTextAreaElement>() { + unsafe { area.selection_for_layout() } + } else if let Some(input) = this.downcast::<HTMLInputElement>() { + unsafe { input.selection_for_layout() } + } else { + return None; + }; + selection.map(|range| Range::new(ByteIndex(range.start as isize), + ByteIndex(range.len() as isize))) } fn image_url(&self) -> Option<Url> { |