diff --git a/components/gfx/display_list/mod.rs b/components/gfx/display_list/mod.rs index 86da27a0e2d..23efeaf9445 100644 --- a/components/gfx/display_list/mod.rs +++ b/components/gfx/display_list/mod.rs @@ -14,27 +14,22 @@ //! They are therefore not exactly analogous to constructs like Skia pictures, which consist of //! low-level drawing primitives. -use euclid::{Transform3D, Vector2D, TypedRect, SideOffsets2D}; +use euclid::{Vector2D, TypedRect, SideOffsets2D}; use euclid::num::{One, Zero}; use gfx_traits::{self, StackingContextId}; use gfx_traits::print_tree::PrintTree; -use ipc_channel::ipc::IpcSharedMemory; use msg::constellation_msg::PipelineId; use net_traits::image::base::{Image, PixelFormat}; -use range::Range; use servo_geometry::MaxRect; use std::cmp::Ordering; use std::collections::HashMap; use std::f32; use std::fmt; -use std::sync::Arc; -use text::TextRun; -use text::glyph::ByteIndex; use webrender_api::{BorderRadius, BorderWidths, BoxShadowClipMode, ClipMode, ColorF}; use webrender_api::{ComplexClipRegion, ExtendMode, ExternalScrollId, FilterOp, FontInstanceKey}; use webrender_api::{GlyphInstance, GradientStop, ImageBorder, ImageKey, ImageRendering}; -use webrender_api::{LayoutPoint, LayoutRect, LayoutSize, LayoutVector2D, LineStyle, LocalClip}; -use webrender_api::{MixBlendMode, NormalBorder, ScrollPolicy, ScrollSensitivity}; +use webrender_api::{LayoutPoint, LayoutRect, LayoutSize, LayoutTransform, LayoutVector2D, LineStyle}; +use webrender_api::{LocalClip, MixBlendMode, NormalBorder, ScrollPolicy, ScrollSensitivity}; use webrender_api::{StickyOffsetBounds, TransformStyle}; pub use style::dom::OpaqueNode; @@ -104,25 +99,6 @@ impl DisplayList { } } - // Returns the text index within a node for the point of interest. - pub fn text_index(&self, node: OpaqueNode, point_in_item: LayoutPoint) -> Option { - for item in &self.list { - match item { - &DisplayItem::Text(ref text) => { - let base = item.base(); - if base.metadata.node == node { - let point = point_in_item + item.base().bounds.origin.to_vector(); - let offset = point - text.baseline_origin; - return Some(text.text_run.range_index_of_advance(&text.range, offset.x)); - } - }, - _ => {}, - } - } - - None - } - pub fn print(&self) { let mut print_tree = PrintTree::new("Display List".to_owned()); self.print_with_tree(&mut print_tree); @@ -209,13 +185,13 @@ pub struct StackingContext { pub mix_blend_mode: MixBlendMode, /// A transform to be applied to this stacking context. - pub transform: Option>, + pub transform: Option, /// The transform style of this stacking context. pub transform_style: TransformStyle, /// The perspective matrix to be applied to children. - pub perspective: Option>, + pub perspective: Option, /// The scroll policy of this layer. pub scroll_policy: ScrollPolicy, @@ -234,9 +210,9 @@ impl StackingContext { z_index: i32, filters: Vec, mix_blend_mode: MixBlendMode, - transform: Option>, + transform: Option, transform_style: TransformStyle, - perspective: Option>, + perspective: Option, scroll_policy: ScrollPolicy, parent_clipping_and_scrolling: ClippingAndScrolling) -> StackingContext { @@ -651,16 +627,6 @@ pub struct SolidColorDisplayItem { pub struct TextDisplayItem { /// Fields common to all display items. pub base: BaseDisplayItem, - - /// The text run. - #[ignore_malloc_size_of = "Because it is non-owning"] - pub text_run: Arc, - - /// The range of text within the text run. - pub range: Range, - - /// The position of the start of the baseline of this text. - pub baseline_origin: LayoutPoint, /// A collection of (non-whitespace) glyphs to be displayed. pub glyphs: Vec, /// Reference to the font to be used. @@ -683,9 +649,6 @@ pub struct ImageDisplayItem { pub webrender_image: WebRenderImageInfo, - #[ignore_malloc_size_of = "Because it is non-owning"] - pub image_data: Option>, - /// The dimensions to which the image display item should be stretched. If this is smaller than /// the bounds of this display item, then the image will be repeated in the appropriate /// direction to tile the entire bounds. @@ -1028,7 +991,7 @@ pub trait SimpleMatrixDetection { fn is_identity_or_simple_translation(&self) -> bool; } -impl SimpleMatrixDetection for Transform3D { +impl SimpleMatrixDetection for LayoutTransform { #[inline] fn is_identity_or_simple_translation(&self) -> bool { let (_0, _1) = (Zero::zero(), One::one()); diff --git a/components/gfx/text/text_run.rs b/components/gfx/text/text_run.rs index a85c8aababd..62126ab838a 100644 --- a/components/gfx/text/text_run.rs +++ b/components/gfx/text/text_run.rs @@ -333,10 +333,10 @@ impl<'a> TextRun { } /// Returns the index in the range of the first glyph advancing over given advance - pub fn range_index_of_advance(&self, range: &Range, advance: f32) -> usize { + pub fn range_index_of_advance(&self, range: &Range, advance: Au) -> usize { // TODO(Issue #199): alter advance direction for RTL // TODO(Issue #98): using inter-char and inter-word spacing settings when measuring text - let mut remaining = Au::from_f32_px(advance); + let mut remaining = advance; self.natural_word_slices_in_range(range) .map(|slice| { let (slice_index, slice_advance) = diff --git a/components/layout/display_list/background.rs b/components/layout/display_list/background.rs index 6019e20f9fe..dd9567ee833 100644 --- a/components/layout/display_list/background.rs +++ b/components/layout/display_list/background.rs @@ -2,7 +2,10 @@ * 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/. */ -//! Calculations for CSS images and CSS backgrounds. +//! Calculations for CSS images, backgrounds and borders. +//! +//! * [CSS Images Module Level 3](https://drafts.csswg.org/css-images-3/) +//! * [CSS Backgrounds and Borders Module Level 3](https://drafts.csswg.org/css-backgrounds-3/) #![deny(unsafe_code)] @@ -10,12 +13,16 @@ use app_units::Au; use display_list::ToLayout; -use euclid::{Point2D, Size2D, Vector2D}; -use gfx::display_list; -use model::MaybeAuto; +use euclid::{Point2D, Rect, SideOffsets2D, Size2D, Vector2D}; +use gfx::display_list::{self, BorderDetails, WebRenderImageInfo}; +use model::{self, MaybeAuto}; +use style::computed_values::background_attachment::single_value::T as BackgroundAttachment; +use style::computed_values::background_clip::single_value::T as BackgroundClip; +use style::computed_values::background_origin::single_value::T as BackgroundOrigin; +use style::properties::style_structs::{self, Background}; use style::values::computed::{Angle, GradientItem}; -use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto, Percentage}; -use style::values::computed::Position; +use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto}; +use style::values::computed::{NumberOrPercentage, Percentage, Position}; use style::values::computed::image::{EndingShape, LineDirection}; use style::values::generics::background::BackgroundSize; use style::values::generics::image::{Circle, Ellipse, ShapeExtent}; @@ -23,7 +30,8 @@ use style::values::generics::image::EndingShape as GenericEndingShape; use style::values::generics::image::GradientItem as GenericGradientItem; use style::values::specified::background::BackgroundRepeatKeyword; use style::values::specified::position::{X, Y}; -use webrender_api::{ExtendMode, GradientStop}; +use webrender_api::{BorderRadius, BorderSide, BorderStyle, ColorF, ExtendMode, ImageBorder}; +use webrender_api::{GradientStop, LayoutSize, NinePatchDescriptor, NormalBorder}; /// A helper data structure for gradients. #[derive(Clone, Copy)] @@ -34,9 +42,51 @@ struct StopRun { stop_count: usize, } +/// Placment information for both image and gradient backgrounds. +#[derive(Clone, Copy, Debug)] +pub struct BackgroundPlacement { + /// Rendering bounds. The background will start in the uppper-left corner + /// and fill the whole area. + pub bounds: Rect, + /// Background tile size. Some backgrounds are repeated. These are the + /// dimensions of a single image of the background. + pub tile_size: Size2D, + /// Spacing between tiles. Some backgrounds are not repeated seamless + /// but have seams between them like tiles in real life. + pub tile_spacing: Size2D, + /// A clip area. While the background is rendered according to all the + /// measures above it is only shown within these bounds. + pub css_clip: Rect, + /// Whether or not the background is fixed to the viewport. + pub fixed: bool, +} + +trait ResolvePercentage { + fn resolve(&self, length: u32) -> u32; +} + +impl ResolvePercentage for NumberOrPercentage { + fn resolve(&self, length: u32) -> u32 { + match *self { + NumberOrPercentage::Percentage(p) => (p.0 * length as f32).round() as u32, + NumberOrPercentage::Number(n) => n.round() as u32, + } + } +} + +/// Access element at index modulo the array length. +/// +/// Obviously it does not work with empty arrays. +/// +/// This is used for multiple layered background images. +/// See: https://drafts.csswg.org/css-backgrounds-3/#layering +pub fn get_cyclic(arr: &[T], index: usize) -> &T { + &arr[index % arr.len()] +} + /// For a given area and an image compute how big the /// image should be displayed on the background. -pub fn compute_background_image_size( +fn compute_background_image_size( bg_size: BackgroundSize, bounds_size: Size2D, intrinsic_size: Option>, @@ -99,6 +149,82 @@ pub fn compute_background_image_size( } } +/// Determines where to place an element background image or gradient. +/// +/// Photos have their resolution as intrinsic size while gradients have +/// no intrinsic size. +pub fn compute_background_placement( + bg: &Background, + viewport_size: Size2D, + absolute_bounds: Rect, + intrinsic_size: Option>, + border: SideOffsets2D, + border_padding: SideOffsets2D, + index: usize, +) -> BackgroundPlacement { + let bg_attachment = *get_cyclic(&bg.background_attachment.0, index); + let bg_clip = *get_cyclic(&bg.background_clip.0, index); + let bg_origin = *get_cyclic(&bg.background_origin.0, index); + let bg_position_x = get_cyclic(&bg.background_position_x.0, index); + let bg_position_y = get_cyclic(&bg.background_position_y.0, index); + let bg_repeat = get_cyclic(&bg.background_repeat.0, index); + let bg_size = *get_cyclic(&bg.background_size.0, index); + + let css_clip = match bg_clip { + BackgroundClip::BorderBox => absolute_bounds, + BackgroundClip::PaddingBox => absolute_bounds.inner_rect(border), + BackgroundClip::ContentBox => absolute_bounds.inner_rect(border_padding), + }; + + let mut fixed = false; + let mut bounds = match bg_attachment { + BackgroundAttachment::Scroll => match bg_origin { + BackgroundOrigin::BorderBox => absolute_bounds, + BackgroundOrigin::PaddingBox => absolute_bounds.inner_rect(border), + BackgroundOrigin::ContentBox => absolute_bounds.inner_rect(border_padding), + }, + BackgroundAttachment::Fixed => { + fixed = true; + Rect::new(Point2D::origin(), viewport_size) + }, + }; + + let mut tile_size = compute_background_image_size(bg_size, bounds.size, intrinsic_size); + + let mut tile_spacing = Size2D::zero(); + let own_position = bounds.size - tile_size; + let pos_x = bg_position_x.to_used_value(own_position.width); + let pos_y = bg_position_y.to_used_value(own_position.height); + tile_image_axis( + bg_repeat.0, + &mut bounds.origin.x, + &mut bounds.size.width, + &mut tile_size.width, + &mut tile_spacing.width, + pos_x, + css_clip.origin.x, + css_clip.size.width, + ); + tile_image_axis( + bg_repeat.1, + &mut bounds.origin.y, + &mut bounds.size.height, + &mut tile_size.height, + &mut tile_spacing.height, + pos_y, + css_clip.origin.y, + css_clip.size.height, + ); + + BackgroundPlacement { + bounds, + tile_size, + tile_spacing, + css_clip, + fixed, + } +} + fn tile_image_round( position: &mut Au, size: &mut Au, @@ -175,7 +301,7 @@ fn tile_image(position: &mut Au, size: &mut Au, absolute_anchor_origin: Au, imag /// For either the x or the y axis ajust various values to account for tiling. /// /// This is done separately for both axes because the repeat keywords may differ. -pub fn tile_image_axis( +fn tile_image_axis( repeat: BackgroundRepeatKeyword, position: &mut Au, size: &mut Au, @@ -322,8 +448,7 @@ fn convert_gradient_stops(gradient_items: &[GradientItem], total_length: Au) -> } // Step 3: Evenly space stops without position. - // Note: Remove the + 2 if fix_gradient_stops is changed. - let mut stops = Vec::with_capacity(stop_items.len() + 2); + let mut stops = Vec::with_capacity(stop_items.len()); let mut stop_run = None; for (i, stop) in stop_items.iter().enumerate() { let offset = match stop.position { @@ -427,14 +552,7 @@ pub fn convert_linear_gradient( // This is the length of the gradient line. let length = Au::from_f32_px((delta.x.to_f32_px() * 2.0).hypot(delta.y.to_f32_px() * 2.0)); - let mut stops = convert_gradient_stops(stops, length); - - // Only clamped gradients need to be fixed because in repeating gradients - // there is no "first" or "last" stop because they repeat infinitly in - // both directions, so the rendering is always correct. - if !repeating { - fix_gradient_stops(&mut stops); - } + let stops = convert_gradient_stops(stops, length); let center = Point2D::new(size.width / 2, size.height / 2); @@ -473,12 +591,7 @@ pub fn convert_radial_gradient( }, }; - let mut stops = convert_gradient_stops(stops, radius.width); - // Repeating gradients have no last stops that can be ignored. So - // fixup is not necessary but may actually break the gradient. - if !repeating { - fix_gradient_stops(&mut stops); - } + let stops = convert_gradient_stops(stops, radius.width); display_list::RadialGradient { center: center.to_layout(), @@ -488,38 +601,6 @@ pub fn convert_radial_gradient( } } -#[inline] -/// Duplicate the first and last stops if necessary. -/// -/// Explanation by pyfisch: -/// If the last stop is at the same position as the previous stop the -/// last color is ignored by webrender. This differs from the spec -/// (I think so). The implementations of Chrome and Firefox seem -/// to have the same problem but work fine if the position of the last -/// stop is smaller than 100%. (Otherwise they ignore the last stop.) -/// -/// Similarly the first stop is duplicated if it is not placed -/// at the start of the virtual gradient ray. -fn fix_gradient_stops(stops: &mut Vec) { - if stops.first().unwrap().offset > 0.0 { - let color = stops.first().unwrap().color; - stops.insert( - 0, - GradientStop { - offset: 0.0, - color: color, - }, - ) - } - if stops.last().unwrap().offset < 1.0 { - let color = stops.last().unwrap().color; - stops.push(GradientStop { - offset: 1.0, - color: color, - }) - } -} - /// Returns the the distance to the nearest or farthest corner depending on the comperator. fn get_distance_to_corner(size: &Size2D, center: &Point2D, cmp: F) -> Au where @@ -555,3 +636,151 @@ fn position_to_offset(position: LengthOrPercentage, total_length: Au) -> f32 { }, } } + +fn scale_border_radii(radii: BorderRadius, factor: f32) -> BorderRadius { + BorderRadius { + top_left: radii.top_left * factor, + top_right: radii.top_right * factor, + bottom_left: radii.bottom_left * factor, + bottom_right: radii.bottom_right * factor, + } +} + +fn handle_overlapping_radii(size: LayoutSize, radii: BorderRadius) -> BorderRadius { + // No two corners' border radii may add up to more than the length of the edge + // between them. To prevent that, all radii are scaled down uniformly. + fn scale_factor(radius_a: f32, radius_b: f32, edge_length: f32) -> f32 { + let required = radius_a + radius_b; + + if required <= edge_length { + 1.0 + } else { + edge_length / required + } + } + + let top_factor = scale_factor(radii.top_left.width, radii.top_right.width, size.width); + let bottom_factor = scale_factor( + radii.bottom_left.width, + radii.bottom_right.width, + size.width, + ); + let left_factor = scale_factor(radii.top_left.height, radii.bottom_left.height, size.height); + let right_factor = scale_factor( + radii.top_right.height, + radii.bottom_right.height, + size.height, + ); + let min_factor = top_factor + .min(bottom_factor) + .min(left_factor) + .min(right_factor); + if min_factor < 1.0 { + scale_border_radii(radii, min_factor) + } else { + radii + } +} + +pub fn build_border_radius( + abs_bounds: &Rect, + border_style: &style_structs::Border, +) -> BorderRadius { + // TODO(cgaebel): Support border radii even in the case of multiple border widths. + // This is an extension of supporting elliptical radii. For now, all percentage + // radii will be relative to the width. + + handle_overlapping_radii( + abs_bounds.size.to_layout(), + BorderRadius { + top_left: model::specified_border_radius( + border_style.border_top_left_radius, + abs_bounds.size, + ).to_layout(), + top_right: model::specified_border_radius( + border_style.border_top_right_radius, + abs_bounds.size, + ).to_layout(), + bottom_right: model::specified_border_radius( + border_style.border_bottom_right_radius, + abs_bounds.size, + ).to_layout(), + bottom_left: model::specified_border_radius( + border_style.border_bottom_left_radius, + abs_bounds.size, + ).to_layout(), + }, + ) +} + +/// Creates a four-sided border with uniform color, width and corner radius. +pub fn simple_normal_border(color: ColorF, style: BorderStyle) -> NormalBorder { + let side = BorderSide { color, style }; + NormalBorder { + left: side, + right: side, + top: side, + bottom: side, + radius: BorderRadius::zero(), + } +} + +/// Calculates radii for the inner side. +/// +/// Radii usually describe the outer side of a border but for the lines to look nice +/// the inner radii need to be smaller depending on the line width. +/// +/// This is used to determine clipping areas. +pub fn calculate_inner_border_radii( + mut radii: BorderRadius, + offsets: SideOffsets2D, +) -> BorderRadius { + fn inner_length(x: f32, offset: Au) -> f32 { + 0.0_f32.max(x - offset.to_f32_px()) + } + radii.top_left.width = inner_length(radii.top_left.width, offsets.left); + radii.bottom_left.width = inner_length(radii.bottom_left.width, offsets.left); + + radii.top_right.width = inner_length(radii.top_right.width, offsets.right); + radii.bottom_right.width = inner_length(radii.bottom_right.width, offsets.right); + + radii.top_left.height = inner_length(radii.top_left.height, offsets.top); + radii.top_right.height = inner_length(radii.top_right.height, offsets.top); + + radii.bottom_left.height = inner_length(radii.bottom_left.height, offsets.bottom); + radii.bottom_right.height = inner_length(radii.bottom_right.height, offsets.bottom); + radii +} + +/// Given an image and a border style constructs a border image. +/// +/// See: https://drafts.csswg.org/css-backgrounds-3/#border-images +pub fn build_image_border_details( + webrender_image: WebRenderImageInfo, + border_style_struct: &style_structs::Border, +) -> Option { + let corners = &border_style_struct.border_image_slice.offsets; + let border_image_repeat = &border_style_struct.border_image_repeat; + if let Some(image_key) = webrender_image.key { + Some(BorderDetails::Image(ImageBorder { + image_key: image_key, + patch: NinePatchDescriptor { + width: webrender_image.width, + height: webrender_image.height, + slice: SideOffsets2D::new( + corners.0.resolve(webrender_image.height), + corners.1.resolve(webrender_image.width), + corners.2.resolve(webrender_image.height), + corners.3.resolve(webrender_image.width), + ), + }, + fill: border_style_struct.border_image_slice.fill, + // TODO(gw): Support border-image-outset + outset: SideOffsets2D::zero(), + repeat_horizontal: border_image_repeat.0.to_layout(), + repeat_vertical: border_image_repeat.1.to_layout(), + })) + } else { + None + } +} diff --git a/components/layout/display_list/builder.rs b/components/layout/display_list/builder.rs index d57c663ecd5..ec660607251 100644 --- a/components/layout/display_list/builder.rs +++ b/components/layout/display_list/builder.rs @@ -15,9 +15,11 @@ use block::{BlockFlow, BlockStackingContextType}; use canvas_traits::canvas::{CanvasMsg, FromLayoutMsg}; use context::LayoutContext; use display_list::ToLayout; -use display_list::background::{compute_background_image_size, tile_image_axis}; +use display_list::background::{build_border_radius, build_image_border_details}; +use display_list::background::{calculate_inner_border_radii, compute_background_placement}; use display_list::background::{convert_linear_gradient, convert_radial_gradient}; -use euclid::{Point2D, Rect, SideOffsets2D, Size2D, Transform3D, TypedSize2D, Vector2D, rect}; +use display_list::background::{get_cyclic, simple_normal_border}; +use euclid::{rect, Point2D, Rect, SideOffsets2D, Size2D, TypedSize2D, Vector2D}; use flex::FlexFlow; use flow::{BaseFlow, Flow, FlowFlags}; use flow_ref::FlowRef; @@ -41,7 +43,7 @@ use gfx_traits::{combine_id_with_fragment_type, FragmentType, StackingContextId} use inline::{InlineFlow, InlineFragmentNodeFlags}; use ipc_channel::ipc; use list_item::ListItemFlow; -use model::{self, MaybeAuto}; +use model::MaybeAuto; use msg::constellation_msg::{BrowsingContextId, PipelineId}; use net_traits::image::base::PixelFormat; use net_traits::image_cache::UsePlaceholder; @@ -52,20 +54,17 @@ use std::default::Default; use std::f32; use std::mem; use std::sync::Arc; -use style::computed_values::background_attachment::single_value::T as BackgroundAttachment; use style::computed_values::background_clip::single_value::T as BackgroundClip; -use style::computed_values::background_origin::single_value::T as BackgroundOrigin; use style::computed_values::border_style::T as BorderStyle; use style::computed_values::overflow_x::T as StyleOverflow; use style::computed_values::pointer_events::T as PointerEvents; use style::computed_values::position::T as StylePosition; use style::computed_values::visibility::T as Visibility; use style::logical_geometry::{LogicalMargin, LogicalPoint, LogicalRect}; -use style::properties::ComputedValues; -use style::properties::style_structs; +use style::properties::{ComputedValues, style_structs}; use style::servo::restyle_damage::ServoRestyleDamage; use style::values::{Either, RGBA}; -use style::values::computed::{Gradient, NumberOrPercentage}; +use style::values::computed::Gradient; use style::values::computed::effects::SimpleShadow; use style::values::computed::pointing::Cursor; use style::values::generics::background::BackgroundSize; @@ -75,23 +74,10 @@ use style_traits::ToCss; use style_traits::cursor::CursorKind; use table_cell::CollapsedBordersForCell; use webrender_api::{self, BorderRadius, BorderSide, BoxShadowClipMode, ClipMode, ColorF}; -use webrender_api::{ComplexClipRegion, ExternalScrollId, FilterOp, GlyphInstance, ImageBorder}; -use webrender_api::{ImageRendering, LayoutRect, LayoutSize, LayoutVector2D, LineStyle, LocalClip}; -use webrender_api::{NinePatchDescriptor, NormalBorder, ScrollPolicy, ScrollSensitivity}; -use webrender_api::StickyOffsetBounds; - -trait ResolvePercentage { - fn resolve(&self, length: u32) -> u32; -} - -impl ResolvePercentage for NumberOrPercentage { - fn resolve(&self, length: u32) -> u32 { - match *self { - NumberOrPercentage::Percentage(p) => (p.0 * length as f32).round() as u32, - NumberOrPercentage::Number(n) => n.round() as u32, - } - } -} +use webrender_api::{ComplexClipRegion, ExternalScrollId, FilterOp, GlyphInstance}; +use webrender_api::{ImageRendering, LayoutRect, LayoutSize, LayoutTransform, LayoutVector2D}; +use webrender_api::{LineStyle, LocalClip, NormalBorder, ScrollPolicy}; +use webrender_api::{ScrollSensitivity, StickyOffsetBounds}; fn establishes_containing_block_for_absolute( flags: StackingContextCollectionFlags, @@ -167,10 +153,6 @@ static THREAD_TINT_COLORS: [ColorF; 8] = [ }, ]; -fn get_cyclic(arr: &[T], index: usize) -> &T { - &arr[index % arr.len()] -} - pub struct InlineNodeBorderInfo { is_first_fragment_of_element: bool, is_last_fragment_of_element: bool, @@ -332,6 +314,9 @@ pub struct DisplayListBuildState<'a> { /// Vector containing iframe sizes, used to inform the constellation about /// new iframe sizes pub iframe_sizes: Vec<(BrowsingContextId, TypedSize2D)>, + + /// Stores text runs to answer text queries used to place a cursor inside text. + pub indexable_text: IndexableText, } impl<'a> DisplayListBuildState<'a> { @@ -350,6 +335,7 @@ impl<'a> DisplayListBuildState<'a> { current_stacking_context_id: StackingContextId::root(), current_clipping_and_scrolling: root_clip_indices, iframe_sizes: Vec::new(), + indexable_text: IndexableText::default(), } } @@ -543,19 +529,6 @@ pub trait FragmentDisplayListBuilding { absolute_bounds: &Rect, ); - /// Determines where to place an element background image or gradient. - /// - /// Photos have their resolution as intrinsic size while gradients have - /// no intrinsic size. - fn compute_background_placement( - &self, - state: &mut DisplayListBuildState, - style: &ComputedValues, - absolute_bounds: Rect, - intrinsic_size: Option>, - index: usize, - ) -> BackgroundPlacement; - /// Adds the display items necessary to paint a webrender image of this fragment to the /// appropriate section of the display list. fn build_display_list_for_webrender_image( @@ -728,82 +701,6 @@ pub trait FragmentDisplayListBuilding { fn fragment_type(&self) -> FragmentType; } -fn scale_border_radii(radii: BorderRadius, factor: f32) -> BorderRadius { - BorderRadius { - top_left: radii.top_left * factor, - top_right: radii.top_right * factor, - bottom_left: radii.bottom_left * factor, - bottom_right: radii.bottom_right * factor, - } -} - -fn handle_overlapping_radii(size: LayoutSize, radii: BorderRadius) -> BorderRadius { - // No two corners' border radii may add up to more than the length of the edge - // between them. To prevent that, all radii are scaled down uniformly. - fn scale_factor(radius_a: f32, radius_b: f32, edge_length: f32) -> f32 { - let required = radius_a + radius_b; - - if required <= edge_length { - 1.0 - } else { - edge_length / required - } - } - - let top_factor = scale_factor(radii.top_left.width, radii.top_right.width, size.width); - let bottom_factor = scale_factor( - radii.bottom_left.width, - radii.bottom_right.width, - size.width, - ); - let left_factor = scale_factor(radii.top_left.height, radii.bottom_left.height, size.height); - let right_factor = scale_factor( - radii.top_right.height, - radii.bottom_right.height, - size.height, - ); - let min_factor = top_factor - .min(bottom_factor) - .min(left_factor) - .min(right_factor); - if min_factor < 1.0 { - scale_border_radii(radii, min_factor) - } else { - radii - } -} - -fn build_border_radius( - abs_bounds: &Rect, - border_style: &style_structs::Border, -) -> BorderRadius { - // TODO(cgaebel): Support border radii even in the case of multiple border widths. - // This is an extension of supporting elliptical radii. For now, all percentage - // radii will be relative to the width. - - handle_overlapping_radii( - abs_bounds.size.to_layout(), - BorderRadius { - top_left: model::specified_border_radius( - border_style.border_top_left_radius, - abs_bounds.size, - ).to_layout(), - top_right: model::specified_border_radius( - border_style.border_top_right_radius, - abs_bounds.size, - ).to_layout(), - bottom_right: model::specified_border_radius( - border_style.border_bottom_right_radius, - abs_bounds.size, - ).to_layout(), - bottom_left: model::specified_border_radius( - border_style.border_bottom_left_radius, - abs_bounds.size, - ).to_layout(), - }, - ) -} - /// Get the border radius for the rectangle inside of a rounded border. This is useful /// for building the clip for the content inside the border. fn build_border_radius_for_inner_rect( @@ -822,97 +719,6 @@ fn build_border_radius_for_inner_rect( calculate_inner_border_radii(radii, border_widths) } -fn simple_normal_border(color: ColorF, style: webrender_api::BorderStyle) -> NormalBorder { - let side = BorderSide { color, style }; - NormalBorder { - left: side, - right: side, - top: side, - bottom: side, - radius: webrender_api::BorderRadius::zero(), - } -} - -fn calculate_inner_border_radii( - mut radii: BorderRadius, - offsets: SideOffsets2D, -) -> BorderRadius { - fn inner_length(x: f32, offset: Au) -> f32 { - 0.0_f32.max(x - offset.to_f32_px()) - } - radii.top_left.width = inner_length(radii.top_left.width, offsets.left); - radii.bottom_left.width = inner_length(radii.bottom_left.width, offsets.left); - - radii.top_right.width = inner_length(radii.top_right.width, offsets.right); - radii.bottom_right.width = inner_length(radii.bottom_right.width, offsets.right); - - radii.top_left.height = inner_length(radii.top_left.height, offsets.top); - radii.top_right.height = inner_length(radii.top_right.height, offsets.top); - - radii.bottom_left.height = inner_length(radii.bottom_left.height, offsets.bottom); - radii.bottom_right.height = inner_length(radii.bottom_right.height, offsets.bottom); - radii -} - -fn build_image_border_details( - webrender_image: WebRenderImageInfo, - border_style_struct: &style_structs::Border, -) -> Option { - let corners = &border_style_struct.border_image_slice.offsets; - let border_image_repeat = &border_style_struct.border_image_repeat; - if let Some(image_key) = webrender_image.key { - Some(BorderDetails::Image(ImageBorder { - image_key: image_key, - patch: NinePatchDescriptor { - width: webrender_image.width, - height: webrender_image.height, - slice: SideOffsets2D::new( - corners.0.resolve(webrender_image.height), - corners.1.resolve(webrender_image.width), - corners.2.resolve(webrender_image.height), - corners.3.resolve(webrender_image.width), - ), - }, - fill: border_style_struct.border_image_slice.fill, - // TODO(gw): Support border-image-outset - outset: SideOffsets2D::zero(), - repeat_horizontal: border_image_repeat.0.to_layout(), - repeat_vertical: border_image_repeat.1.to_layout(), - })) - } else { - None - } -} - -fn convert_text_run_to_glyphs( - text_run: Arc, - range: Range, - mut origin: Point2D, -) -> Vec { - let mut glyphs = vec![]; - - for slice in text_run.natural_word_slices_in_visual_order(&range) { - for glyph in slice.glyphs.iter_glyphs_for_byte_range(&slice.range) { - let glyph_advance = if glyph.char_is_space() { - glyph.advance() + text_run.extra_word_spacing - } else { - glyph.advance() - }; - if !slice.glyphs.is_whitespace() { - let glyph_offset = glyph.offset().unwrap_or(Point2D::zero()); - let point = origin + glyph_offset.to_vector(); - let glyph = GlyphInstance { - index: glyph.id(), - point: point.to_layout(), - }; - glyphs.push(glyph); - } - origin.x += glyph_advance; - } - } - return glyphs; -} - impl FragmentDisplayListBuilding for Fragment { fn collect_stacking_contexts_for_blocklike_fragment( &mut self, @@ -975,15 +781,13 @@ impl FragmentDisplayListBuilding for Fragment { let mut bounds = *absolute_bounds; // This is the clip for the color (which is the last element in the bg array) - let color_clip = get_cyclic( - &background.background_clip.0, - background.background_image.0.len() - 1, - ); + // Background clips are never empty. + let color_clip = &background.background_clip.0.last().unwrap(); // Adjust the clipping region as necessary to account for `border-radius`. let mut border_radii = build_border_radius(absolute_bounds, style.get_border()); - match *color_clip { + match **color_clip { BackgroundClip::BorderBox => {}, BackgroundClip::PaddingBox => { let border = style.logical_border_width().to_physical(style.writing_mode); @@ -1095,83 +899,6 @@ impl FragmentDisplayListBuilding for Fragment { } } - fn compute_background_placement( - &self, - state: &mut DisplayListBuildState, - style: &ComputedValues, - absolute_bounds: Rect, - intrinsic_size: Option>, - index: usize, - ) -> BackgroundPlacement { - let bg = style.get_background(); - let bg_attachment = *get_cyclic(&bg.background_attachment.0, index); - let bg_clip = *get_cyclic(&bg.background_clip.0, index); - let bg_origin = *get_cyclic(&bg.background_origin.0, index); - let bg_position_x = get_cyclic(&bg.background_position_x.0, index); - let bg_position_y = get_cyclic(&bg.background_position_y.0, index); - let bg_repeat = get_cyclic(&bg.background_repeat.0, index); - let bg_size = *get_cyclic(&bg.background_size.0, index); - - let css_clip = match bg_clip { - BackgroundClip::BorderBox => absolute_bounds, - BackgroundClip::PaddingBox => absolute_bounds - .inner_rect(style.logical_border_width().to_physical(style.writing_mode)), - BackgroundClip::ContentBox => { - absolute_bounds.inner_rect(self.border_padding.to_physical(style.writing_mode)) - }, - }; - - let mut bounds = match bg_attachment { - BackgroundAttachment::Scroll => match bg_origin { - BackgroundOrigin::BorderBox => absolute_bounds, - BackgroundOrigin::PaddingBox => absolute_bounds - .inner_rect(style.logical_border_width().to_physical(style.writing_mode)), - BackgroundOrigin::ContentBox => { - absolute_bounds.inner_rect(self.border_padding.to_physical(style.writing_mode)) - }, - }, - BackgroundAttachment::Fixed => Rect::new( - Point2D::origin(), - // Get current viewport - state.layout_context.shared_context().viewport_size(), - ), - }; - - let mut tile_size = compute_background_image_size(bg_size, bounds.size, intrinsic_size); - - let mut tile_spacing = Size2D::zero(); - let own_position = bounds.size - tile_size; - let pos_x = bg_position_x.to_used_value(own_position.width); - let pos_y = bg_position_y.to_used_value(own_position.height); - tile_image_axis( - bg_repeat.0, - &mut bounds.origin.x, - &mut bounds.size.width, - &mut tile_size.width, - &mut tile_spacing.width, - pos_x, - css_clip.origin.x, - css_clip.size.width, - ); - tile_image_axis( - bg_repeat.1, - &mut bounds.origin.y, - &mut bounds.size.height, - &mut tile_size.height, - &mut tile_spacing.height, - pos_y, - css_clip.origin.y, - css_clip.size.height, - ); - - BackgroundPlacement { - bounds, - tile_size, - tile_spacing, - css_clip, - } - } - fn build_display_list_for_webrender_image( &self, state: &mut DisplayListBuildState, @@ -1187,8 +914,15 @@ impl FragmentDisplayListBuilding for Fragment { Au::from_px(webrender_image.width as i32), Au::from_px(webrender_image.height as i32), ); - let placement = - self.compute_background_placement(state, style, absolute_bounds, Some(image), index); + let placement = compute_background_placement( + style.get_background(), + state.layout_context.shared_context().viewport_size(), + absolute_bounds, + Some(image), + style.logical_border_width().to_physical(style.writing_mode), + self.border_padding.to_physical(style.writing_mode), + index, + ); // Create the image display item. let base = state.create_base_display_item( @@ -1203,7 +937,6 @@ impl FragmentDisplayListBuilding for Fragment { state.add_display_item(DisplayItem::Image(Box::new(ImageDisplayItem { base: base, webrender_image: webrender_image, - image_data: None, stretch_size: placement.tile_size.to_layout(), tile_spacing: placement.tile_spacing.to_layout(), image_rendering: style.get_inheritedbox().image_rendering.to_layout(), @@ -1280,8 +1013,15 @@ impl FragmentDisplayListBuilding for Fragment { style: &ComputedValues, index: usize, ) { - let placement = - self.compute_background_placement(state, style, absolute_bounds, None, index); + let placement = compute_background_placement( + style.get_background(), + state.layout_context.shared_context().viewport_size(), + absolute_bounds, + None, + style.logical_border_width().to_physical(style.writing_mode), + self.border_padding.to_physical(style.writing_mode), + index, + ); let base = state.create_base_display_item( &placement.bounds, @@ -2000,7 +1740,6 @@ impl FragmentDisplayListBuilding for Fragment { state.add_display_item(DisplayItem::Image(Box::new(ImageDisplayItem { base: base, webrender_image: WebRenderImageInfo::from_image(image), - image_data: Some(Arc::new(image.bytes.clone())), stretch_size: stacking_relative_content_box.size.to_layout(), tile_spacing: LayoutSize::zero(), image_rendering: self.style.get_inheritedbox().image_rendering.to_layout(), @@ -2041,7 +1780,6 @@ impl FragmentDisplayListBuilding for Fragment { format: format, key: Some(image_key), }, - image_data: None, stretch_size: stacking_relative_content_box.size.to_layout(), tile_spacing: LayoutSize::zero(), image_rendering: ImageRendering::Auto, @@ -2178,7 +1916,7 @@ impl FragmentDisplayListBuilding for Fragment { // Create display items for text decorations. let text_decorations = self.style().get_inheritedtext().text_decorations_in_effect; - let stacking_relative_content_box = LogicalRect::from_physical( + let logical_stacking_relative_content_box = LogicalRect::from_physical( self.style.writing_mode, *stacking_relative_content_box, container_size, @@ -2186,9 +1924,10 @@ impl FragmentDisplayListBuilding for Fragment { // Underline if text_decorations.underline { - let mut stacking_relative_box = stacking_relative_content_box; - stacking_relative_box.start.b = - stacking_relative_content_box.start.b + metrics.ascent - metrics.underline_offset; + let mut stacking_relative_box = logical_stacking_relative_content_box; + stacking_relative_box.start.b = logical_stacking_relative_content_box.start.b + + metrics.ascent - + metrics.underline_offset; stacking_relative_box.size.block = metrics.underline_size; self.build_display_list_for_text_decoration( state, @@ -2200,7 +1939,7 @@ impl FragmentDisplayListBuilding for Fragment { // Overline if text_decorations.overline { - let mut stacking_relative_box = stacking_relative_content_box; + let mut stacking_relative_box = logical_stacking_relative_content_box; stacking_relative_box.size.block = metrics.underline_size; self.build_display_list_for_text_decoration( state, @@ -2217,11 +1956,16 @@ impl FragmentDisplayListBuilding for Fragment { baseline_origin, ); if !glyphs.is_empty() { - state.add_display_item(DisplayItem::Text(Box::new(TextDisplayItem { - base: base.clone(), + let indexable_text = IndexableTextItem { + origin: stacking_relative_content_box.origin, text_run: text_fragment.run.clone(), range: text_fragment.range, - baseline_origin: baseline_origin.to_layout(), + baseline_origin, + }; + state.indexable_text.insert(self.node, indexable_text); + + state.add_display_item(DisplayItem::Text(Box::new(TextDisplayItem { + base: base.clone(), glyphs: glyphs, font_key: text_fragment.run.font_key, text_color: text_color.to_layout(), @@ -2233,7 +1977,7 @@ impl FragmentDisplayListBuilding for Fragment { // Line-Through if text_decorations.line_through { - let mut stacking_relative_box = stacking_relative_content_box; + let mut stacking_relative_box = logical_stacking_relative_content_box; stacking_relative_box.start.b = stacking_relative_box.start.b + metrics.ascent - metrics.strikeout_offset; stacking_relative_box.size.block = metrics.strikeout_size; @@ -2464,7 +2208,7 @@ impl BlockFlowDisplayListBuilding for BlockFlow { let perspective = self.fragment .perspective_matrix(&border_box) - .unwrap_or_else(Transform3D::identity); + .unwrap_or(LayoutTransform::identity()); let transform = transform.pre_mul(&perspective).inverse(); let origin = &border_box.origin; @@ -2766,10 +2510,8 @@ impl BlockFlowDisplayListBuilding for BlockFlow { let content_size = self.base.overflow.scroll.origin + self.base.overflow.scroll.size; let content_size = Size2D::new(content_size.x, content_size.y); - let external_id = ExternalScrollId( - self.fragment.unique_id(), - state.pipeline_id.to_webrender() - ); + let external_id = + ExternalScrollId(self.fragment.unique_id(), state.pipeline_id.to_webrender()); let new_clip_scroll_index = state.add_clip_scroll_node(ClipScrollNode { parent_index: self.clipping_and_scrolling().scrolling, clip: clip, @@ -3228,18 +2970,67 @@ pub enum BorderPaintingMode<'a> { Hidden, } -#[derive(Clone, Copy, Debug)] -pub struct BackgroundPlacement { - /// Rendering bounds. The background will start in the uppper-left corner - /// and fill the whole area. - bounds: Rect, - /// Background tile size. Some backgrounds are repeated. These are the - /// dimensions of a single image of the background. - tile_size: Size2D, - /// Spacing between tiles. Some backgrounds are not repeated seamless - /// but have seams between them like tiles in real life. - tile_spacing: Size2D, - /// A clip area. While the background is rendered according to all the - /// measures above it is only shown within these bounds. - css_clip: Rect, +fn convert_text_run_to_glyphs( + text_run: Arc, + range: Range, + mut origin: Point2D, +) -> Vec { + let mut glyphs = vec![]; + + for slice in text_run.natural_word_slices_in_visual_order(&range) { + for glyph in slice.glyphs.iter_glyphs_for_byte_range(&slice.range) { + let glyph_advance = if glyph.char_is_space() { + glyph.advance() + text_run.extra_word_spacing + } else { + glyph.advance() + }; + if !slice.glyphs.is_whitespace() { + let glyph_offset = glyph.offset().unwrap_or(Point2D::zero()); + let point = origin + glyph_offset.to_vector(); + let glyph = GlyphInstance { + index: glyph.id(), + point: point.to_layout(), + }; + glyphs.push(glyph); + } + origin.x += glyph_advance; + } + } + return glyphs; +} + +pub struct IndexableTextItem { + /// The placement of the text item on the plane. + pub origin: Point2D, + /// The text run. + pub text_run: Arc, + /// The range of text within the text run. + pub range: Range, + /// The position of the start of the baseline of this text. + pub baseline_origin: Point2D, +} + +#[derive(Default)] +pub struct IndexableText { + inner: FnvHashMap>, +} + +impl IndexableText { + fn insert(&mut self, node: OpaqueNode, item: IndexableTextItem) { + let entries = self.inner.entry(node).or_insert(Vec::new()); + entries.push(item); + } + + pub fn get(&self, node: OpaqueNode) -> Option<&[IndexableTextItem]> { + self.inner.get(&node).map(|x| x.as_slice()) + } + + // Returns the text index within a node for the point of interest. + pub fn text_index(&self, node: OpaqueNode, point_in_item: Point2D) -> Option { + let item = self.inner.get(&node)?; + // TODO(#20020): access all elements + let point = point_in_item + item[0].origin.to_vector(); + let offset = point - item[0].baseline_origin; + Some(item[0].text_run.range_index_of_advance(&item[0].range, offset.x)) + } } diff --git a/components/layout/display_list/mod.rs b/components/layout/display_list/mod.rs index c5f1aa2aa71..917c39687b2 100644 --- a/components/layout/display_list/mod.rs +++ b/components/layout/display_list/mod.rs @@ -6,6 +6,7 @@ pub use self::builder::BlockFlowDisplayListBuilding; pub use self::builder::BorderPaintingMode; pub use self::builder::DisplayListBuildState; pub use self::builder::FlexFlowDisplayListBuilding; +pub use self::builder::IndexableText; pub use self::builder::InlineFlowDisplayListBuilding; pub use self::builder::ListItemFlowDisplayListBuilding; pub use self::builder::StackingContextCollectionFlags; diff --git a/components/layout/display_list/webrender_helpers.rs b/components/layout/display_list/webrender_helpers.rs index 3c9a4b5dd3b..f657a628e45 100644 --- a/components/layout/display_list/webrender_helpers.rs +++ b/components/layout/display_list/webrender_helpers.rs @@ -11,8 +11,7 @@ use gfx::display_list::{BorderDetails, ClipScrollNode}; use gfx::display_list::{ClipScrollNodeIndex, ClipScrollNodeType, DisplayItem}; use gfx::display_list::{DisplayList, StackingContextType}; use msg::constellation_msg::PipelineId; -use webrender_api::{self, ClipAndScrollInfo, ClipId}; -use webrender_api::{DisplayListBuilder, LayoutTransform}; +use webrender_api::{self, ClipAndScrollInfo, ClipId, DisplayListBuilder}; pub trait WebRenderDisplayListConverter { fn convert_to_webrender(&self, pipeline_id: PipelineId) -> DisplayListBuilder; @@ -224,19 +223,12 @@ impl WebRenderDisplayItemConverter for DisplayItem { let stacking_context = &item.stacking_context; debug_assert_eq!(stacking_context.context_type, StackingContextType::Real); - let transform = stacking_context - .transform - .map(|transform| LayoutTransform::from_untyped(&transform).into()); - let perspective = stacking_context - .perspective - .map(|perspective| LayoutTransform::from_untyped(&perspective)); - builder.push_stacking_context( &webrender_api::LayoutPrimitiveInfo::new(stacking_context.bounds), stacking_context.scroll_policy, - transform, + stacking_context.transform.map(Into::into), stacking_context.transform_style, - perspective, + stacking_context.perspective, stacking_context.mix_blend_mode, stacking_context.filters.clone(), ); diff --git a/components/layout/flow.rs b/components/layout/flow.rs index 399a21087c4..40a1b90972f 100644 --- a/components/layout/flow.rs +++ b/components/layout/flow.rs @@ -29,7 +29,7 @@ use app_units::Au; use block::{BlockFlow, FormattingContextType}; use context::LayoutContext; use display_list::{DisplayListBuildState, StackingContextCollectionState}; -use euclid::{Transform3D, Point2D, Vector2D, Rect, Size2D}; +use euclid::{Point2D, Vector2D, Rect, Size2D}; use flex::FlexFlow; use floats::{Floats, SpeculatedFloatPlacement}; use flow_list::{FlowList, FlowListIterator, MutFlowListIterator}; @@ -67,6 +67,7 @@ use table_colgroup::TableColGroupFlow; use table_row::TableRowFlow; use table_rowgroup::TableRowGroupFlow; use table_wrapper::TableWrapperFlow; +use webrender_api::LayoutTransform; /// This marker trait indicates that a type is a struct with `#[repr(C)]` whose first field /// is of type `BaseFlow` or some type that also implements this trait. @@ -335,8 +336,8 @@ pub trait Flow: HasBaseFlow + fmt::Debug + Sync + Send + 'static { let transform_2d = self.as_block() .fragment .transform_matrix(&position) - .unwrap_or(Transform3D::identity()) - .to_2d(); + .unwrap_or(LayoutTransform::identity()) + .to_2d().to_untyped(); let transformed_overflow = Overflow { paint: f32_rect_to_au_rect(transform_2d.transform_rect( &au_rect_to_f32_rect(overflow.paint))), diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs index 2ee3dbf0058..48764b625df 100644 --- a/components/layout/fragment.rs +++ b/components/layout/fragment.rs @@ -10,7 +10,8 @@ use ServoArc; use app_units::Au; use canvas_traits::canvas::CanvasMsg; use context::{LayoutContext, with_thread_local_font_context}; -use euclid::{Transform3D, Point2D, Vector2D, Rect, Size2D}; +use display_list::ToLayout; +use euclid::{Point2D, Vector2D, Rect, Size2D}; use floats::ClearType; use flow::{GetBaseFlow, ImmutableFlowUtils}; use flow_ref::FlowRef; @@ -65,7 +66,7 @@ use style::values::generics::box_::VerticalAlign; use style::values::generics::transform; use text; use text::TextRunScanner; -use webrender_api; +use webrender_api::{self, LayoutTransform}; use wrapper::ThreadSafeLayoutNodeHelpers; // From gfxFontConstants.h in Firefox. @@ -2867,9 +2868,10 @@ impl Fragment { } /// Returns the 4D matrix representing this fragment's transform. - pub fn transform_matrix(&self, stacking_relative_border_box: &Rect) -> Option> { + pub fn transform_matrix(&self, stacking_relative_border_box: &Rect) -> Option { let list = &self.style.get_box().transform; - let transform = list.to_transform_3d_matrix(Some(stacking_relative_border_box)).ok()?.0; + let transform = LayoutTransform::from_untyped( + &list.to_transform_3d_matrix(Some(stacking_relative_border_box)).ok()?.0); let transform_origin = &self.style.get_box().transform_origin; let transform_origin_x = @@ -2882,38 +2884,42 @@ impl Fragment { .to_f32_px(); let transform_origin_z = transform_origin.depth.px(); - let pre_transform = Transform3D::create_translation(transform_origin_x, - transform_origin_y, - transform_origin_z); - let post_transform = Transform3D::create_translation(-transform_origin_x, - -transform_origin_y, - -transform_origin_z); + let pre_transform = LayoutTransform::create_translation( + transform_origin_x, + transform_origin_y, + transform_origin_z); + let post_transform = LayoutTransform::create_translation( + -transform_origin_x, + -transform_origin_y, + -transform_origin_z); Some(pre_transform.pre_mul(&transform).pre_mul(&post_transform)) } /// Returns the 4D matrix representing this fragment's perspective. - pub fn perspective_matrix(&self, stacking_relative_border_box: &Rect) -> Option> { + pub fn perspective_matrix(&self, stacking_relative_border_box: &Rect) -> Option { match self.style().get_box().perspective { Either::First(length) => { let perspective_origin = self.style().get_box().perspective_origin; let perspective_origin = Point2D::new( perspective_origin.horizontal - .to_used_value(stacking_relative_border_box.size.width) - .to_f32_px(), + .to_used_value(stacking_relative_border_box.size.width), perspective_origin.vertical .to_used_value(stacking_relative_border_box.size.height) - .to_f32_px()); + ).to_layout(); - let pre_transform = Transform3D::create_translation(perspective_origin.x, - perspective_origin.y, - 0.0); - let post_transform = Transform3D::create_translation(-perspective_origin.x, - -perspective_origin.y, - 0.0); + let pre_transform = LayoutTransform::create_translation( + perspective_origin.x, + perspective_origin.y, + 0.0); + let post_transform = LayoutTransform::create_translation( + -perspective_origin.x, + -perspective_origin.y, + 0.0); - let perspective_matrix = transform::create_perspective_matrix(length.px()); + let perspective_matrix = LayoutTransform::from_untyped( + &transform::create_perspective_matrix(length.px())); Some(pre_transform.pre_mul(&perspective_matrix).pre_mul(&post_transform)) } diff --git a/components/layout/query.rs b/components/layout/query.rs index bea54d43364..025953b374e 100644 --- a/components/layout/query.rs +++ b/components/layout/query.rs @@ -7,10 +7,11 @@ use app_units::Au; use construct::ConstructionResult; use context::LayoutContext; +use display_list::IndexableText; use euclid::{Point2D, Vector2D, Rect, Size2D}; use flow::{Flow, GetBaseFlow}; use fragment::{Fragment, FragmentBorderBoxIterator, SpecificFragmentInfo}; -use gfx::display_list::{DisplayItem, DisplayList, OpaqueNode, ScrollOffsetMap}; +use gfx::display_list::{DisplayList, OpaqueNode, ScrollOffsetMap}; use inline::InlineFragmentNodeFlags; use ipc_channel::ipc::IpcSender; use msg::constellation_msg::PipelineId; @@ -26,7 +27,6 @@ use script_traits::LayoutMsg as ConstellationMsg; use script_traits::UntrustedNodeAddress; use sequential; use std::cmp::{min, max}; -use std::collections::HashMap; use std::ops::Deref; use std::sync::{Arc, Mutex}; use style::computed_values::display::T as Display; @@ -51,6 +51,8 @@ pub struct LayoutThreadData { /// The root stacking context. pub display_list: Option>, + pub indexable_text: IndexableText, + /// A queued response for the union of the content boxes of a node. pub content_box_response: Option>, @@ -885,16 +887,11 @@ enum InnerTextItem { // https://html.spec.whatwg.org/multipage/#the-innertext-idl-attribute pub fn process_element_inner_text_query(node: N, - display_list: &Option>) -> String { - if !display_list.is_some() { - warn!("We should have a display list at this point. Cannot get inner text"); - return String::new(); - } - + indexable_text: &IndexableText) -> String { // Step 1. let mut results = Vec::new(); // Step 2. - inner_text_collection_steps(node, display_list.as_ref().unwrap(), &mut results); + inner_text_collection_steps(node, indexable_text, &mut results); let mut max_req_line_break_count = 0; let mut inner_text = Vec::new(); for item in results { @@ -933,18 +930,8 @@ pub fn process_element_inner_text_query(node: N, // https://html.spec.whatwg.org/multipage/#inner-text-collection-steps #[allow(unsafe_code)] fn inner_text_collection_steps(node: N, - display_list: &Arc, + indexable_text: &IndexableText, results: &mut Vec) { - // Extracts the text nodes from the display list to avoid traversing it - // for each child node. - let mut text = HashMap::new(); - for item in &display_list.as_ref().list { - if let &DisplayItem::Text(ref text_content) = item { - let entries = text.entry(&item.base().metadata.node).or_insert(Vec::new()); - entries.push(&text_content.text_run.text); - } - } - let mut items = Vec::new(); for child in node.traverse_preorder() { let node = match child.type_id() { @@ -983,9 +970,9 @@ fn inner_text_collection_steps(node: N, match child.type_id() { LayoutNodeType::Text => { // Step 4. - if let Some(text_content) = text.get(&child.opaque()) { + if let Some(text_content) = indexable_text.get(child.opaque()) { for content in text_content { - items.push(InnerTextItem::Text(content.to_string())); + items.push(InnerTextItem::Text(content.text_run.text.to_string())); } } }, diff --git a/components/layout/sequential.rs b/components/layout/sequential.rs index 4270929c5e7..d9fca6c904f 100644 --- a/components/layout/sequential.rs +++ b/components/layout/sequential.rs @@ -17,6 +17,7 @@ use servo_config::opts; use style::servo::restyle_damage::ServoRestyleDamage; use traversal::{AssignBSizes, AssignISizes, BubbleISizes, BuildDisplayList}; use traversal::{InorderFlowTraversal, PostorderFlowTraversal, PreorderFlowTraversal}; +use webrender_api::LayoutPoint; pub fn resolve_generated_content(root: &mut Flow, layout_context: &LayoutContext) { ResolveGeneratedContent::new(&layout_context).traverse(root, 0); @@ -98,7 +99,7 @@ pub fn iterate_through_flow_tree_fragment_border_boxes(root: &mut Flow, iterator if let Some(matrix) = kid.as_block() .fragment .transform_matrix(&relative_position) { - let transform_matrix = matrix.transform_point2d(&Point2D::zero()); + let transform_matrix = matrix.transform_point2d(&LayoutPoint::zero()); stacking_context_position = stacking_context_position + Vector2D::new(Au::from_f32_px(transform_matrix.x), Au::from_f32_px(transform_matrix.y)) diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs index a5c3e03293f..4e55daaab6a 100644 --- a/components/layout_thread/lib.rs +++ b/components/layout_thread/lib.rs @@ -68,7 +68,7 @@ use layout::context::LayoutContext; use layout::context::RegisteredPainter; use layout::context::RegisteredPainters; use layout::context::malloc_size_of_persistent_local_context; -use layout::display_list::ToLayout; +use layout::display_list::{IndexableText, ToLayout}; use layout::display_list::WebRenderDisplayListConverter; use layout::flow::{Flow, GetBaseFlow, ImmutableFlowUtils, MutableOwnedFlowUtils}; use layout::flow_ref::FlowRef; @@ -515,6 +515,7 @@ impl LayoutThread { LayoutThreadData { constellation_chan: constellation_chan, display_list: None, + indexable_text: IndexableText::default(), content_box_response: None, content_boxes_response: Vec::new(), client_rect_response: Rect::zero(), @@ -1002,6 +1003,9 @@ impl LayoutThread { } } + rw_data.indexable_text = std::mem::replace( + &mut build_state.indexable_text, + IndexableText::default()); rw_data.display_list = Some(Arc::new(build_state.to_display_list())); } } @@ -1366,10 +1370,7 @@ impl LayoutThread { Au::from_f32_px(point_in_node.y) ); rw_data.text_index_response = TextIndexResponse( - rw_data.display_list - .as_ref() - .expect("Tried to hit test with no display list") - .text_index(opaque_node, point_in_node.to_layout()) + rw_data.indexable_text.text_index(opaque_node, point_in_node) ); }, ReflowGoal::NodeGeometryQuery(node) => { @@ -1426,7 +1427,7 @@ impl LayoutThread { ReflowGoal::ElementInnerTextQuery(node) => { let node = unsafe { ServoLayoutNode::new(&node) }; rw_data.element_inner_text_response = - process_element_inner_text_query(node, &rw_data.display_list); + process_element_inner_text_query(node, &rw_data.indexable_text); }, ReflowGoal::Full | ReflowGoal::TickAnimations => {} } diff --git a/tests/unit/metrics/paint_time.rs b/tests/unit/metrics/paint_time.rs index 9e6adfa1580..79a40799dd9 100644 --- a/tests/unit/metrics/paint_time.rs +++ b/tests/unit/metrics/paint_time.rs @@ -126,7 +126,6 @@ fn test_first_contentful_paint_setter() { format: PixelFormat::RGB8, key: None, }, - image_data: None, stretch_size: LayoutSize::zero(), tile_spacing: LayoutSize::zero(), image_rendering: ImageRendering::Auto,