1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5 //! Builds display lists from flows and fragments.
6 //!
7 //! Other browser engines sometimes call this "painting", but it is more accurately called display
8 //! list building, as the actual painting does not happen here—only deciding *what* we're going to
9 //! paint.
10
11 #![deny(unsafe_code)]
12
13 use app_units::{Au, AU_PER_PX};
14 use block::BlockFlow;
15 use canvas_traits::canvas::{CanvasMsg, FromLayoutMsg};
16 use context::LayoutContext;
17 use display_list::ToLayout;
18 use display_list::background::{build_border_radius, build_image_border_details};
19 use display_list::background::{calculate_inner_border_radii, compute_background_placement};
20 use display_list::background::{convert_linear_gradient, convert_radial_gradient};
21 use display_list::background::{get_cyclic, simple_normal_border};
22 use euclid::{rect, Point2D, Rect, SideOffsets2D, Size2D, TypedSize2D, Vector2D};
23 use flex::FlexFlow;
24 use flow::{BaseFlow, Flow, FlowFlags};
25 use flow_ref::FlowRef;
26 use fnv::FnvHashMap;
27 use fragment::{CanvasFragmentSource, CoordinateSystem, Fragment, ScannedTextFragmentInfo};
28 use fragment::SpecificFragmentInfo;
29 use gfx::display_list;
30 use gfx::display_list::{BaseDisplayItem, BorderDetails, BorderDisplayItem, BLUR_INFLATION_FACTOR};
31 use gfx::display_list::{BoxShadowDisplayItem, ClipScrollNode};
32 use gfx::display_list::{ClipScrollNodeIndex, ClipScrollNodeType, ClippingAndScrolling};
33 use gfx::display_list::{ClippingRegion, DisplayItem, DisplayItemMetadata, DisplayList};
34 use gfx::display_list::{DisplayListSection, GradientDisplayItem, IframeDisplayItem};
35 use gfx::display_list::{ImageDisplayItem, LineDisplayItem, OpaqueNode};
36 use gfx::display_list::{PopAllTextShadowsDisplayItem, PushTextShadowDisplayItem};
37 use gfx::display_list::{RadialGradientDisplayItem, SolidColorDisplayItem, StackingContext};
38 use gfx::display_list::{StackingContextType, StickyFrameData, TextDisplayItem, TextOrientation};
39 use gfx::display_list::WebRenderImageInfo;
40 use gfx::text::TextRun;
41 use gfx::text::glyph::ByteIndex;
42 use gfx_traits::{combine_id_with_fragment_type, FragmentType, StackingContextId};
43 use inline::{InlineFlow, InlineFragmentNodeFlags};
44 use ipc_channel::ipc;
45 use list_item::ListItemFlow;
46 use model::MaybeAuto;
47 use msg::constellation_msg::{BrowsingContextId, PipelineId};
48 use net_traits::image::base::PixelFormat;
49 use net_traits::image_cache::UsePlaceholder;
50 use range::Range;
51 use servo_config::opts;
52 use servo_geometry::MaxRect;
53 use std::default::Default;
54 use std::f32;
55 use std::mem;
56 use std::sync::Arc;
57 use style::computed_values::background_clip::single_value::T as BackgroundClip;
58 use style::computed_values::border_style::T as BorderStyle;
59 use style::computed_values::overflow_x::T as StyleOverflow;
60 use style::computed_values::pointer_events::T as PointerEvents;
61 use style::computed_values::position::T as StylePosition;
62 use style::computed_values::visibility::T as Visibility;
63 use style::logical_geometry::{LogicalMargin, LogicalPoint, LogicalRect};
64 use style::properties::{ComputedValues, style_structs};
65 use style::servo::restyle_damage::ServoRestyleDamage;
66 use style::values::{Either, RGBA};
67 use style::values::computed::Gradient;
68 use style::values::computed::effects::SimpleShadow;
69 use style::values::computed::pointing::Cursor;
70 use style::values::generics::background::BackgroundSize;
71 use style::values::generics::image::{GradientKind, Image, PaintWorklet};
72 use style_traits::CSSPixel;
73 use style_traits::ToCss;
74 use style_traits::cursor::CursorKind;
75 use table_cell::CollapsedBordersForCell;
76 use webrender_api::{self, BorderRadius, BorderSide, BoxShadowClipMode, ColorF, ExternalScrollId};
77 use webrender_api::{FilterOp, GlyphInstance, ImageRendering, LayoutRect, LayoutSize};
78 use webrender_api::{LayoutTransform, LayoutVector2D, LineStyle, LocalClip, NormalBorder};
79 use webrender_api::{ScrollPolicy, ScrollSensitivity, StickyOffsetBounds};
80
establishes_containing_block_for_absolute( flags: StackingContextCollectionFlags, positioning: StylePosition, ) -> bool81 fn establishes_containing_block_for_absolute(
82 flags: StackingContextCollectionFlags,
83 positioning: StylePosition,
84 ) -> bool {
85 !flags.contains(StackingContextCollectionFlags::NEVER_CREATES_CONTAINING_BLOCK) &&
86 StylePosition::Static != positioning
87 }
88
89 trait RgbColor {
rgb(r: u8, g: u8, b: u8) -> Self90 fn rgb(r: u8, g: u8, b: u8) -> Self;
91 }
92
93 impl RgbColor for ColorF {
rgb(r: u8, g: u8, b: u8) -> Self94 fn rgb(r: u8, g: u8, b: u8) -> Self {
95 ColorF {
96 r: (r as f32) / (255.0 as f32),
97 g: (g as f32) / (255.0 as f32),
98 b: (b as f32) / (255.0 as f32),
99 a: 1.0 as f32,
100 }
101 }
102 }
103
104 static THREAD_TINT_COLORS: [ColorF; 8] = [
105 ColorF {
106 r: 6.0 / 255.0,
107 g: 153.0 / 255.0,
108 b: 198.0 / 255.0,
109 a: 0.7,
110 },
111 ColorF {
112 r: 255.0 / 255.0,
113 g: 212.0 / 255.0,
114 b: 83.0 / 255.0,
115 a: 0.7,
116 },
117 ColorF {
118 r: 116.0 / 255.0,
119 g: 29.0 / 255.0,
120 b: 109.0 / 255.0,
121 a: 0.7,
122 },
123 ColorF {
124 r: 204.0 / 255.0,
125 g: 158.0 / 255.0,
126 b: 199.0 / 255.0,
127 a: 0.7,
128 },
129 ColorF {
130 r: 242.0 / 255.0,
131 g: 46.0 / 255.0,
132 b: 121.0 / 255.0,
133 a: 0.7,
134 },
135 ColorF {
136 r: 116.0 / 255.0,
137 g: 203.0 / 255.0,
138 b: 196.0 / 255.0,
139 a: 0.7,
140 },
141 ColorF {
142 r: 255.0 / 255.0,
143 g: 249.0 / 255.0,
144 b: 201.0 / 255.0,
145 a: 0.7,
146 },
147 ColorF {
148 r: 137.0 / 255.0,
149 g: 196.0 / 255.0,
150 b: 78.0 / 255.0,
151 a: 0.7,
152 },
153 ];
154
155 pub struct InlineNodeBorderInfo {
156 is_first_fragment_of_element: bool,
157 is_last_fragment_of_element: bool,
158 }
159
160 #[derive(Debug)]
161 struct StackingContextInfo {
162 children: Vec<StackingContext>,
163 clip_scroll_nodes: Vec<ClipScrollNodeIndex>,
164 real_stacking_context_id: StackingContextId,
165 }
166
167 impl StackingContextInfo {
new(real_stacking_context_id: StackingContextId) -> StackingContextInfo168 fn new(real_stacking_context_id: StackingContextId) -> StackingContextInfo {
169 StackingContextInfo {
170 children: Vec::new(),
171 clip_scroll_nodes: Vec::new(),
172 real_stacking_context_id,
173 }
174 }
175
take_children(&mut self) -> Vec<StackingContext>176 fn take_children(&mut self) -> Vec<StackingContext> {
177 mem::replace(&mut self.children, Vec::new())
178 }
179 }
180
181 pub struct StackingContextCollectionState {
182 /// The PipelineId of this stacking context collection.
183 pub pipeline_id: PipelineId,
184
185 /// The root of the StackingContext tree.
186 pub root_stacking_context: StackingContext,
187
188 /// StackingContext and ClipScrollNode children for each StackingContext.
189 stacking_context_info: FnvHashMap<StackingContextId, StackingContextInfo>,
190
191 pub clip_scroll_nodes: Vec<ClipScrollNode>,
192
193 /// The current stacking context id, used to keep track of state when building.
194 /// recursively building and processing the display list.
195 pub current_stacking_context_id: StackingContextId,
196
197 /// The current stacking real context id, which doesn't include pseudo-stacking contexts.
198 pub current_real_stacking_context_id: StackingContextId,
199
200 /// The next stacking context id that we will assign to a stacking context.
201 pub next_stacking_context_id: StackingContextId,
202
203 /// The current clip and scroll info, used to keep track of state when
204 /// recursively building and processing the display list.
205 pub current_clipping_and_scrolling: ClippingAndScrolling,
206
207 /// The clip and scroll info of the first ancestor which defines a containing block.
208 /// This is necessary because absolutely positioned items should be clipped
209 /// by their containing block's scroll root.
210 pub containing_block_clipping_and_scrolling: ClippingAndScrolling,
211
212 /// A stack of clips used to cull display list entries that are outside the
213 /// rendered region.
214 pub clip_stack: Vec<Rect<Au>>,
215
216 /// A stack of clips used to cull display list entries that are outside the
217 /// rendered region, but only collected at containing block boundaries.
218 pub containing_block_clip_stack: Vec<Rect<Au>>,
219
220 /// The flow parent's content box, used to calculate sticky constraints.
221 parent_stacking_relative_content_box: Rect<Au>,
222 }
223
224 impl StackingContextCollectionState {
new(pipeline_id: PipelineId) -> StackingContextCollectionState225 pub fn new(pipeline_id: PipelineId) -> StackingContextCollectionState {
226 let root_clip_indices = ClippingAndScrolling::simple(ClipScrollNodeIndex(0));
227
228 // This is just a dummy node to take up a slot in the array. WebRender
229 // takes care of adding this root node and it can be ignored during DL conversion.
230 let root_node = ClipScrollNode {
231 parent_index: ClipScrollNodeIndex(0),
232 clip: ClippingRegion::from_rect(LayoutRect::zero()),
233 content_rect: LayoutRect::zero(),
234 node_type: ClipScrollNodeType::ScrollFrame(
235 ScrollSensitivity::ScriptAndInputEvents,
236 pipeline_id.root_scroll_id(),
237 ),
238 };
239
240 let mut stacking_context_info = FnvHashMap::default();
241 stacking_context_info.insert(
242 StackingContextId::root(),
243 StackingContextInfo::new(StackingContextId::root())
244 );
245
246 StackingContextCollectionState {
247 pipeline_id: pipeline_id,
248 root_stacking_context: StackingContext::root(),
249 stacking_context_info,
250 clip_scroll_nodes: vec![root_node],
251 current_stacking_context_id: StackingContextId::root(),
252 current_real_stacking_context_id: StackingContextId::root(),
253 next_stacking_context_id: StackingContextId::root().next(),
254 current_clipping_and_scrolling: root_clip_indices,
255 containing_block_clipping_and_scrolling: root_clip_indices,
256 clip_stack: Vec::new(),
257 containing_block_clip_stack: Vec::new(),
258 parent_stacking_relative_content_box: Rect::zero(),
259 }
260 }
261
allocate_stacking_context_info( &mut self, stacking_context_type: StackingContextType ) -> StackingContextId262 fn allocate_stacking_context_info(
263 &mut self,
264 stacking_context_type: StackingContextType
265 ) -> StackingContextId {
266 let next_stacking_context_id = self.next_stacking_context_id.next();
267 let allocated_id =
268 mem::replace(&mut self.next_stacking_context_id, next_stacking_context_id);
269
270 let real_stacking_context_id = match stacking_context_type {
271 StackingContextType::Real => allocated_id,
272 _ => self.current_real_stacking_context_id,
273 };
274
275 self.stacking_context_info.insert(
276 allocated_id,
277 StackingContextInfo::new(real_stacking_context_id)
278 );
279
280 allocated_id
281 }
282
add_stacking_context( &mut self, parent_id: StackingContextId, stacking_context: StackingContext, )283 fn add_stacking_context(
284 &mut self,
285 parent_id: StackingContextId,
286 stacking_context: StackingContext,
287 ) {
288 self.stacking_context_info.get_mut(&parent_id).unwrap().children.push(stacking_context);
289 }
290
add_clip_scroll_node(&mut self, clip_scroll_node: ClipScrollNode) -> ClipScrollNodeIndex291 fn add_clip_scroll_node(&mut self, clip_scroll_node: ClipScrollNode) -> ClipScrollNodeIndex {
292 // We want the scroll root to be defined before any possible item that could use it,
293 // so we make sure that it is added to the beginning of the parent "real" (non-pseudo)
294 // stacking context. This ensures that item reordering will not result in an item using
295 // the scroll root before it is defined.
296 self.clip_scroll_nodes.push(clip_scroll_node);
297 let index = ClipScrollNodeIndex(self.clip_scroll_nodes.len() - 1);
298 self.stacking_context_info
299 .get_mut(&self.current_real_stacking_context_id)
300 .unwrap()
301 .clip_scroll_nodes
302 .push(index);
303 index
304 }
305 }
306
307 pub struct DisplayListBuildState<'a> {
308 /// A LayoutContext reference important for creating WebRender images.
309 pub layout_context: &'a LayoutContext<'a>,
310
311 /// The root of the StackingContext tree.
312 pub root_stacking_context: StackingContext,
313
314 /// StackingContext and ClipScrollNode children for each StackingContext.
315 stacking_context_info: FnvHashMap<StackingContextId, StackingContextInfo>,
316
317 /// A vector of ClipScrollNodes which will be given ids during WebRender DL conversion.
318 pub clip_scroll_nodes: Vec<ClipScrollNode>,
319
320 /// The items in this display list.
321 pub items: FnvHashMap<StackingContextId, Vec<DisplayItem>>,
322
323 /// Whether or not we are processing an element that establishes scrolling overflow. Used
324 /// to determine what ClipScrollNode to place backgrounds and borders into.
325 pub processing_scrolling_overflow_element: bool,
326
327 /// The current stacking context id, used to keep track of state when building.
328 /// recursively building and processing the display list.
329 pub current_stacking_context_id: StackingContextId,
330
331 /// The current clip and scroll info, used to keep track of state when
332 /// recursively building and processing the display list.
333 pub current_clipping_and_scrolling: ClippingAndScrolling,
334
335 /// Vector containing iframe sizes, used to inform the constellation about
336 /// new iframe sizes
337 pub iframe_sizes: Vec<(BrowsingContextId, TypedSize2D<f32, CSSPixel>)>,
338
339 /// Stores text runs to answer text queries used to place a cursor inside text.
340 pub indexable_text: IndexableText,
341 }
342
343 impl<'a> DisplayListBuildState<'a> {
new( layout_context: &'a LayoutContext, state: StackingContextCollectionState, ) -> DisplayListBuildState<'a>344 pub fn new(
345 layout_context: &'a LayoutContext,
346 state: StackingContextCollectionState,
347 ) -> DisplayListBuildState<'a> {
348 let root_clip_indices = ClippingAndScrolling::simple(ClipScrollNodeIndex(0));
349 DisplayListBuildState {
350 layout_context: layout_context,
351 root_stacking_context: state.root_stacking_context,
352 items: FnvHashMap::default(),
353 stacking_context_info: state.stacking_context_info,
354 clip_scroll_nodes: state.clip_scroll_nodes,
355 processing_scrolling_overflow_element: false,
356 current_stacking_context_id: StackingContextId::root(),
357 current_clipping_and_scrolling: root_clip_indices,
358 iframe_sizes: Vec::new(),
359 indexable_text: IndexableText::default(),
360 }
361 }
362
add_display_item(&mut self, display_item: DisplayItem)363 fn add_display_item(&mut self, display_item: DisplayItem) {
364 let items = self.items
365 .entry(display_item.stacking_context_id())
366 .or_insert(Vec::new());
367 items.push(display_item);
368 }
369
parent_clip_scroll_node_index(&self, index: ClipScrollNodeIndex) -> ClipScrollNodeIndex370 fn parent_clip_scroll_node_index(&self, index: ClipScrollNodeIndex) -> ClipScrollNodeIndex {
371 if index.is_root_scroll_node() {
372 return index;
373 }
374
375 self.clip_scroll_nodes[index.0].parent_index
376 }
377
is_background_or_border_of_clip_scroll_node(&self, section: DisplayListSection) -> bool378 fn is_background_or_border_of_clip_scroll_node(&self, section: DisplayListSection) -> bool {
379 (section == DisplayListSection::BackgroundAndBorders ||
380 section == DisplayListSection::BlockBackgroundsAndBorders) &&
381 self.processing_scrolling_overflow_element
382 }
383
create_base_display_item( &self, bounds: &Rect<Au>, clip: LocalClip, node: OpaqueNode, cursor: Option<CursorKind>, section: DisplayListSection, ) -> BaseDisplayItem384 fn create_base_display_item(
385 &self,
386 bounds: &Rect<Au>,
387 clip: LocalClip,
388 node: OpaqueNode,
389 cursor: Option<CursorKind>,
390 section: DisplayListSection,
391 ) -> BaseDisplayItem {
392 let clipping_and_scrolling = if self.is_background_or_border_of_clip_scroll_node(section) {
393 ClippingAndScrolling::simple(self.parent_clip_scroll_node_index(
394 self.current_clipping_and_scrolling.scrolling,
395 ))
396 } else {
397 self.current_clipping_and_scrolling
398 };
399
400 BaseDisplayItem::new(
401 bounds.to_layout(),
402 DisplayItemMetadata {
403 node,
404 // Store cursor id in display list.
405 pointing: cursor.map(|x| x as u16),
406 },
407 clip,
408 section,
409 self.current_stacking_context_id,
410 clipping_and_scrolling,
411 )
412 }
413
add_late_clip_node(&mut self, rect: LayoutRect, radii: BorderRadius) -> ClipScrollNodeIndex414 fn add_late_clip_node(&mut self, rect: LayoutRect, radii: BorderRadius) -> ClipScrollNodeIndex {
415 let mut clip = ClippingRegion::from_rect(rect);
416 clip.intersect_with_rounded_rect(rect, radii);
417
418 let node = ClipScrollNode {
419 parent_index: self.current_clipping_and_scrolling.scrolling,
420 clip,
421 content_rect: LayoutRect::zero(), // content_rect isn't important for clips.
422 node_type: ClipScrollNodeType::Clip,
423 };
424
425 // We want the scroll root to be defined before any possible item that could use it,
426 // so we make sure that it is added to the beginning of the parent "real" (non-pseudo)
427 // stacking context. This ensures that item reordering will not result in an item using
428 // the scroll root before it is defined.
429 self.clip_scroll_nodes.push(node);
430 let index = ClipScrollNodeIndex(self.clip_scroll_nodes.len() - 1);
431 let real_stacking_context_id =
432 self.stacking_context_info[&self.current_stacking_context_id].real_stacking_context_id;
433 self.stacking_context_info
434 .get_mut(&real_stacking_context_id)
435 .unwrap()
436 .clip_scroll_nodes
437 .push(index);
438
439 index
440 }
441
to_display_list(mut self) -> DisplayList442 pub fn to_display_list(mut self) -> DisplayList {
443 let mut list = Vec::new();
444 let root_context = mem::replace(&mut self.root_stacking_context, StackingContext::root());
445
446 self.to_display_list_for_stacking_context(&mut list, root_context);
447
448 DisplayList {
449 list: list,
450 clip_scroll_nodes: self.clip_scroll_nodes,
451 }
452 }
453
to_display_list_for_stacking_context( &mut self, list: &mut Vec<DisplayItem>, stacking_context: StackingContext, )454 fn to_display_list_for_stacking_context(
455 &mut self,
456 list: &mut Vec<DisplayItem>,
457 stacking_context: StackingContext,
458 ) {
459 let mut child_items = self.items
460 .remove(&stacking_context.id)
461 .unwrap_or(Vec::new());
462 child_items.sort_by(|a, b| a.base().section.cmp(&b.base().section));
463 child_items.reverse();
464
465 let mut info = self.stacking_context_info.remove(&stacking_context.id).unwrap();
466
467 info.children.sort();
468
469 if stacking_context.context_type != StackingContextType::Real {
470 list.extend(
471 info.clip_scroll_nodes
472 .into_iter()
473 .map(|index| index.to_define_item()),
474 );
475 self.to_display_list_for_items(list, child_items, info.children);
476 } else {
477 let (push_item, pop_item) = stacking_context.to_display_list_items();
478 list.push(push_item);
479 list.extend(
480 info.clip_scroll_nodes
481 .into_iter()
482 .map(|index| index.to_define_item()),
483 );
484 self.to_display_list_for_items(list, child_items, info.children);
485 list.push(pop_item);
486 }
487 }
488
to_display_list_for_items( &mut self, list: &mut Vec<DisplayItem>, mut child_items: Vec<DisplayItem>, child_stacking_contexts: Vec<StackingContext>, )489 fn to_display_list_for_items(
490 &mut self,
491 list: &mut Vec<DisplayItem>,
492 mut child_items: Vec<DisplayItem>,
493 child_stacking_contexts: Vec<StackingContext>,
494 ) {
495 // Properly order display items that make up a stacking context. "Steps" here
496 // refer to the steps in CSS 2.1 Appendix E.
497 // Steps 1 and 2: Borders and background for the root.
498 while child_items.last().map_or(false, |child| {
499 child.section() == DisplayListSection::BackgroundAndBorders
500 }) {
501 list.push(child_items.pop().unwrap());
502 }
503
504 // Step 3: Positioned descendants with negative z-indices.
505 let mut child_stacking_contexts = child_stacking_contexts.into_iter().peekable();
506 while child_stacking_contexts
507 .peek()
508 .map_or(false, |child| child.z_index < 0)
509 {
510 let context = child_stacking_contexts.next().unwrap();
511 self.to_display_list_for_stacking_context(list, context);
512 }
513
514 // Step 4: Block backgrounds and borders.
515 while child_items.last().map_or(false, |child| {
516 child.section() == DisplayListSection::BlockBackgroundsAndBorders
517 }) {
518 list.push(child_items.pop().unwrap());
519 }
520
521 // Step 5: Floats.
522 while child_stacking_contexts.peek().map_or(false, |child| {
523 child.context_type == StackingContextType::PseudoFloat
524 }) {
525 let context = child_stacking_contexts.next().unwrap();
526 self.to_display_list_for_stacking_context(list, context);
527 }
528
529 // Step 6 & 7: Content and inlines that generate stacking contexts.
530 while child_items.last().map_or(false, |child| {
531 child.section() == DisplayListSection::Content
532 }) {
533 list.push(child_items.pop().unwrap());
534 }
535
536 // Step 8 & 9: Positioned descendants with nonnegative, numeric z-indices.
537 for child in child_stacking_contexts {
538 self.to_display_list_for_stacking_context(list, child);
539 }
540
541 // Step 10: Outlines.
542 for item in child_items.drain(..) {
543 list.push(item);
544 }
545 }
546 }
547
548 /// The logical width of an insertion point: at the moment, a one-pixel-wide line.
549 const INSERTION_POINT_LOGICAL_WIDTH: Au = Au(1 * AU_PER_PX);
550
551 pub trait FragmentDisplayListBuilding {
collect_stacking_contexts_for_blocklike_fragment( &mut self, state: &mut StackingContextCollectionState, ) -> bool552 fn collect_stacking_contexts_for_blocklike_fragment(
553 &mut self,
554 state: &mut StackingContextCollectionState,
555 ) -> bool;
556
557 /// Adds the display items necessary to paint the background of this fragment to the display
558 /// list if necessary.
build_display_list_for_background_if_applicable( &self, state: &mut DisplayListBuildState, style: &ComputedValues, display_list_section: DisplayListSection, absolute_bounds: &Rect<Au>, )559 fn build_display_list_for_background_if_applicable(
560 &self,
561 state: &mut DisplayListBuildState,
562 style: &ComputedValues,
563 display_list_section: DisplayListSection,
564 absolute_bounds: &Rect<Au>,
565 );
566
567 /// Same as build_display_list_for_background_if_applicable, but lets you
568 /// override the actual background used
build_display_list_for_background_if_applicable_with_background( &self, state: &mut DisplayListBuildState, style: &ComputedValues, background: &style_structs::Background, background_color: RGBA, display_list_section: DisplayListSection, absolute_bounds: &Rect<Au>, )569 fn build_display_list_for_background_if_applicable_with_background(
570 &self,
571 state: &mut DisplayListBuildState,
572 style: &ComputedValues,
573 background: &style_structs::Background,
574 background_color: RGBA,
575 display_list_section: DisplayListSection,
576 absolute_bounds: &Rect<Au>,
577 );
578
579 /// Adds the display items necessary to paint a webrender image of this fragment to the
580 /// appropriate section of the display list.
build_display_list_for_webrender_image( &self, state: &mut DisplayListBuildState, style: &ComputedValues, display_list_section: DisplayListSection, absolute_bounds: Rect<Au>, webrender_image: WebRenderImageInfo, index: usize, )581 fn build_display_list_for_webrender_image(
582 &self,
583 state: &mut DisplayListBuildState,
584 style: &ComputedValues,
585 display_list_section: DisplayListSection,
586 absolute_bounds: Rect<Au>,
587 webrender_image: WebRenderImageInfo,
588 index: usize,
589 );
590
591 /// Calculates the webrender image for a paint worklet.
592 /// Returns None if the worklet is not registered.
593 /// If the worklet has missing image URLs, it passes them to the image cache for loading.
get_webrender_image_for_paint_worklet( &self, state: &mut DisplayListBuildState, style: &ComputedValues, paint_worklet: &PaintWorklet, size: Size2D<Au>, ) -> Option<WebRenderImageInfo>594 fn get_webrender_image_for_paint_worklet(
595 &self,
596 state: &mut DisplayListBuildState,
597 style: &ComputedValues,
598 paint_worklet: &PaintWorklet,
599 size: Size2D<Au>,
600 ) -> Option<WebRenderImageInfo>;
601
602 /// Adds the display items necessary to paint the background linear gradient of this fragment
603 /// to the appropriate section of the display list.
build_display_list_for_background_gradient( &self, state: &mut DisplayListBuildState, display_list_section: DisplayListSection, absolute_bounds: Rect<Au>, gradient: &Gradient, style: &ComputedValues, index: usize, )604 fn build_display_list_for_background_gradient(
605 &self,
606 state: &mut DisplayListBuildState,
607 display_list_section: DisplayListSection,
608 absolute_bounds: Rect<Au>,
609 gradient: &Gradient,
610 style: &ComputedValues,
611 index: usize,
612 );
613
614 /// Adds the display items necessary to paint the borders of this fragment to a display list if
615 /// necessary.
build_display_list_for_borders_if_applicable( &self, state: &mut DisplayListBuildState, style: &ComputedValues, inline_node_info: Option<InlineNodeBorderInfo>, border_painting_mode: BorderPaintingMode, bounds: &Rect<Au>, display_list_section: DisplayListSection, clip: &Rect<Au>, )616 fn build_display_list_for_borders_if_applicable(
617 &self,
618 state: &mut DisplayListBuildState,
619 style: &ComputedValues,
620 inline_node_info: Option<InlineNodeBorderInfo>,
621 border_painting_mode: BorderPaintingMode,
622 bounds: &Rect<Au>,
623 display_list_section: DisplayListSection,
624 clip: &Rect<Au>,
625 );
626
627 /// Adds the display items necessary to paint the outline of this fragment to the display list
628 /// if necessary.
build_display_list_for_outline_if_applicable( &self, state: &mut DisplayListBuildState, style: &ComputedValues, bounds: &Rect<Au>, clip: &Rect<Au>, )629 fn build_display_list_for_outline_if_applicable(
630 &self,
631 state: &mut DisplayListBuildState,
632 style: &ComputedValues,
633 bounds: &Rect<Au>,
634 clip: &Rect<Au>,
635 );
636
637 /// Adds the display items necessary to paint the box shadow of this fragment to the display
638 /// list if necessary.
build_display_list_for_box_shadow_if_applicable( &self, state: &mut DisplayListBuildState, style: &ComputedValues, display_list_section: DisplayListSection, absolute_bounds: &Rect<Au>, clip: &Rect<Au>, )639 fn build_display_list_for_box_shadow_if_applicable(
640 &self,
641 state: &mut DisplayListBuildState,
642 style: &ComputedValues,
643 display_list_section: DisplayListSection,
644 absolute_bounds: &Rect<Au>,
645 clip: &Rect<Au>,
646 );
647
648 /// Adds display items necessary to draw debug boxes around a scanned text fragment.
build_debug_borders_around_text_fragments( &self, state: &mut DisplayListBuildState, style: &ComputedValues, stacking_relative_border_box: &Rect<Au>, stacking_relative_content_box: &Rect<Au>, text_fragment: &ScannedTextFragmentInfo, clip: &Rect<Au>, )649 fn build_debug_borders_around_text_fragments(
650 &self,
651 state: &mut DisplayListBuildState,
652 style: &ComputedValues,
653 stacking_relative_border_box: &Rect<Au>,
654 stacking_relative_content_box: &Rect<Au>,
655 text_fragment: &ScannedTextFragmentInfo,
656 clip: &Rect<Au>,
657 );
658
659 /// Adds display items necessary to draw debug boxes around this fragment.
build_debug_borders_around_fragment( &self, state: &mut DisplayListBuildState, stacking_relative_border_box: &Rect<Au>, clip: &Rect<Au>, )660 fn build_debug_borders_around_fragment(
661 &self,
662 state: &mut DisplayListBuildState,
663 stacking_relative_border_box: &Rect<Au>,
664 clip: &Rect<Au>,
665 );
666
667 /// Adds the display items for this fragment to the given display list.
668 ///
669 /// Arguments:
670 ///
671 /// * `state`: The display building state, including the display list currently
672 /// under construction and other metadata useful for constructing it.
673 /// * `dirty`: The dirty rectangle in the coordinate system of the owning flow.
674 /// * `clip`: The region to clip the display items to.
build_display_list( &mut self, state: &mut DisplayListBuildState, stacking_relative_border_box: Rect<Au>, border_painting_mode: BorderPaintingMode, display_list_section: DisplayListSection, clip: &Rect<Au>, )675 fn build_display_list(
676 &mut self,
677 state: &mut DisplayListBuildState,
678 stacking_relative_border_box: Rect<Au>,
679 border_painting_mode: BorderPaintingMode,
680 display_list_section: DisplayListSection,
681 clip: &Rect<Au>,
682 );
683
684 /// build_display_list, but don't update the restyle damage
685 ///
686 /// Must be paired with a self.restyle_damage.remove(REPAINT) somewhere
build_display_list_no_damage( &self, state: &mut DisplayListBuildState, stacking_relative_border_box: Rect<Au>, border_painting_mode: BorderPaintingMode, display_list_section: DisplayListSection, clip: &Rect<Au>, )687 fn build_display_list_no_damage(
688 &self,
689 state: &mut DisplayListBuildState,
690 stacking_relative_border_box: Rect<Au>,
691 border_painting_mode: BorderPaintingMode,
692 display_list_section: DisplayListSection,
693 clip: &Rect<Au>,
694 );
695
696 /// Builds the display items necessary to paint the selection and/or caret for this fragment,
697 /// if any.
build_display_items_for_selection_if_necessary( &self, state: &mut DisplayListBuildState, stacking_relative_border_box: &Rect<Au>, display_list_section: DisplayListSection, clip: &Rect<Au>, )698 fn build_display_items_for_selection_if_necessary(
699 &self,
700 state: &mut DisplayListBuildState,
701 stacking_relative_border_box: &Rect<Au>,
702 display_list_section: DisplayListSection,
703 clip: &Rect<Au>,
704 );
705
706 /// Creates the text display item for one text fragment. This can be called multiple times for
707 /// one fragment if there are text shadows.
708 ///
709 /// `text_shadow` will be `Some` if this is rendering a shadow.
build_display_list_for_text_fragment( &self, state: &mut DisplayListBuildState, text_fragment: &ScannedTextFragmentInfo, stacking_relative_content_box: &Rect<Au>, text_shadows: &[SimpleShadow], clip: &Rect<Au>, )710 fn build_display_list_for_text_fragment(
711 &self,
712 state: &mut DisplayListBuildState,
713 text_fragment: &ScannedTextFragmentInfo,
714 stacking_relative_content_box: &Rect<Au>,
715 text_shadows: &[SimpleShadow],
716 clip: &Rect<Au>,
717 );
718
719 /// Creates the display item for a text decoration: underline, overline, or line-through.
build_display_list_for_text_decoration( &self, state: &mut DisplayListBuildState, color: &RGBA, stacking_relative_box: &LogicalRect<Au>, clip: &Rect<Au>, )720 fn build_display_list_for_text_decoration(
721 &self,
722 state: &mut DisplayListBuildState,
723 color: &RGBA,
724 stacking_relative_box: &LogicalRect<Au>,
725 clip: &Rect<Au>,
726 );
727
728 /// A helper method that `build_display_list` calls to create per-fragment-type display items.
build_fragment_type_specific_display_items( &self, state: &mut DisplayListBuildState, stacking_relative_border_box: &Rect<Au>, clip: &Rect<Au>, )729 fn build_fragment_type_specific_display_items(
730 &self,
731 state: &mut DisplayListBuildState,
732 stacking_relative_border_box: &Rect<Au>,
733 clip: &Rect<Au>,
734 );
735
736 /// Creates a stacking context for associated fragment.
create_stacking_context( &self, id: StackingContextId, base_flow: &BaseFlow, scroll_policy: ScrollPolicy, context_type: StackingContextType, parent_clipping_and_scrolling: ClippingAndScrolling, ) -> StackingContext737 fn create_stacking_context(
738 &self,
739 id: StackingContextId,
740 base_flow: &BaseFlow,
741 scroll_policy: ScrollPolicy,
742 context_type: StackingContextType,
743 parent_clipping_and_scrolling: ClippingAndScrolling,
744 ) -> StackingContext;
745
unique_id(&self) -> u64746 fn unique_id(&self) -> u64;
747
fragment_type(&self) -> FragmentType748 fn fragment_type(&self) -> FragmentType;
749 }
750
751 /// Get the border radius for the rectangle inside of a rounded border. This is useful
752 /// for building the clip for the content inside the border.
build_border_radius_for_inner_rect( outer_rect: &Rect<Au>, style: &ComputedValues, ) -> BorderRadius753 fn build_border_radius_for_inner_rect(
754 outer_rect: &Rect<Au>,
755 style: &ComputedValues,
756 ) -> BorderRadius {
757 let radii = build_border_radius(&outer_rect, style.get_border());
758 if radii.is_zero() {
759 return radii;
760 }
761
762 // Since we are going to using the inner rectangle (outer rectangle minus
763 // border width), we need to adjust to border radius so that we are smaller
764 // rectangle with the same border curve.
765 let border_widths = style.logical_border_width().to_physical(style.writing_mode);
766 calculate_inner_border_radii(radii, border_widths)
767 }
768
769 impl FragmentDisplayListBuilding for Fragment {
collect_stacking_contexts_for_blocklike_fragment( &mut self, state: &mut StackingContextCollectionState, ) -> bool770 fn collect_stacking_contexts_for_blocklike_fragment(
771 &mut self,
772 state: &mut StackingContextCollectionState,
773 ) -> bool {
774 match self.specific {
775 SpecificFragmentInfo::InlineBlock(ref mut block_flow) => {
776 let block_flow = FlowRef::deref_mut(&mut block_flow.flow_ref);
777 block_flow.collect_stacking_contexts(state);
778 true
779 },
780 SpecificFragmentInfo::InlineAbsoluteHypothetical(ref mut block_flow) => {
781 let block_flow = FlowRef::deref_mut(&mut block_flow.flow_ref);
782 block_flow.collect_stacking_contexts(state);
783 true
784 },
785 SpecificFragmentInfo::InlineAbsolute(ref mut block_flow) => {
786 let block_flow = FlowRef::deref_mut(&mut block_flow.flow_ref);
787 block_flow.collect_stacking_contexts(state);
788 true
789 },
790 // FIXME: In the future, if #15144 is fixed we can remove this case. See #18510.
791 SpecificFragmentInfo::TruncatedFragment(ref mut info) => info.full
792 .collect_stacking_contexts_for_blocklike_fragment(state),
793 _ => false,
794 }
795 }
796
build_display_list_for_background_if_applicable( &self, state: &mut DisplayListBuildState, style: &ComputedValues, display_list_section: DisplayListSection, absolute_bounds: &Rect<Au>, )797 fn build_display_list_for_background_if_applicable(
798 &self,
799 state: &mut DisplayListBuildState,
800 style: &ComputedValues,
801 display_list_section: DisplayListSection,
802 absolute_bounds: &Rect<Au>,
803 ) {
804 let background = style.get_background();
805 let background_color = style.resolve_color(background.background_color);
806 // XXXManishearth the below method should ideally use an iterator over
807 // backgrounds
808 self.build_display_list_for_background_if_applicable_with_background(
809 state, style, background, background_color, display_list_section, absolute_bounds)
810 }
811
build_display_list_for_background_if_applicable_with_background( &self, state: &mut DisplayListBuildState, style: &ComputedValues, background: &style_structs::Background, background_color: RGBA, display_list_section: DisplayListSection, absolute_bounds: &Rect<Au>, )812 fn build_display_list_for_background_if_applicable_with_background(
813 &self,
814 state: &mut DisplayListBuildState,
815 style: &ComputedValues,
816 background: &style_structs::Background,
817 background_color: RGBA,
818 display_list_section: DisplayListSection,
819 absolute_bounds: &Rect<Au>,
820 ) {
821 // FIXME: This causes a lot of background colors to be displayed when they are clearly not
822 // needed. We could use display list optimization to clean this up, but it still seems
823 // inefficient. What we really want is something like "nearest ancestor element that
824 // doesn't have a fragment".
825
826 // 'background-clip' determines the area within which the background is painted.
827 // http://dev.w3.org/csswg/css-backgrounds-3/#the-background-clip
828 let mut bounds = *absolute_bounds;
829
830 // This is the clip for the color (which is the last element in the bg array)
831 // Background clips are never empty.
832 let color_clip = &background.background_clip.0.last().unwrap();
833
834 // Adjust the clipping region as necessary to account for `border-radius`.
835 let mut border_radii = build_border_radius(absolute_bounds, style.get_border());
836
837 match **color_clip {
838 BackgroundClip::BorderBox => {},
839 BackgroundClip::PaddingBox => {
840 let border = style.logical_border_width().to_physical(style.writing_mode);
841 bounds = bounds.inner_rect(border);
842 border_radii = calculate_inner_border_radii(border_radii, border);
843 },
844 BackgroundClip::ContentBox => {
845 let border_padding = self.border_padding.to_physical(style.writing_mode);
846 bounds = bounds.inner_rect(border_padding);
847 border_radii = calculate_inner_border_radii(border_radii, border_padding);
848 },
849 }
850
851 let previous_clipping_and_scrolling = state.current_clipping_and_scrolling;
852 if !border_radii.is_zero() {
853 let clip_id = state.add_late_clip_node(bounds.to_layout(), border_radii);
854 state.current_clipping_and_scrolling = ClippingAndScrolling::simple(clip_id);
855 }
856
857 let base = state.create_base_display_item(
858 &bounds,
859 LocalClip::Rect(bounds.to_layout()),
860 self.node,
861 style.get_cursor(CursorKind::Default),
862 display_list_section,
863 );
864 state.add_display_item(DisplayItem::SolidColor(Box::new(SolidColorDisplayItem {
865 base: base,
866 color: background_color.to_layout(),
867 })));
868
869 state.current_clipping_and_scrolling = previous_clipping_and_scrolling;
870
871 // The background image is painted on top of the background color.
872 // Implements background image, per spec:
873 // http://www.w3.org/TR/CSS21/colors.html#background
874 let background = style.get_background();
875 for (i, background_image) in background.background_image.0.iter().enumerate().rev() {
876 match *background_image {
877 Either::First(_) => {},
878 Either::Second(Image::Gradient(ref gradient)) => {
879 self.build_display_list_for_background_gradient(
880 state,
881 display_list_section,
882 *absolute_bounds,
883 gradient,
884 style,
885 i,
886 );
887 },
888 Either::Second(Image::Url(ref image_url)) => {
889 if let Some(url) = image_url.url() {
890 let webrender_image = state.layout_context.get_webrender_image_for_url(
891 self.node,
892 url.clone(),
893 UsePlaceholder::No,
894 );
895 if let Some(webrender_image) = webrender_image {
896 self.build_display_list_for_webrender_image(
897 state,
898 style,
899 display_list_section,
900 *absolute_bounds,
901 webrender_image,
902 i,
903 );
904 }
905 }
906 },
907 Either::Second(Image::PaintWorklet(ref paint_worklet)) => {
908 let bounding_box = self.border_box - style.logical_border_width();
909 let bounding_box_size = bounding_box.size.to_physical(style.writing_mode);
910 let background_size =
911 get_cyclic(&style.get_background().background_size.0, i).clone();
912 let size = match background_size {
913 BackgroundSize::Explicit { width, height } => Size2D::new(
914 MaybeAuto::from_style(width, bounding_box_size.width)
915 .specified_or_default(bounding_box_size.width),
916 MaybeAuto::from_style(height, bounding_box_size.height)
917 .specified_or_default(bounding_box_size.height),
918 ),
919 _ => bounding_box_size,
920 };
921 let webrender_image = self.get_webrender_image_for_paint_worklet(
922 state,
923 style,
924 paint_worklet,
925 size,
926 );
927 if let Some(webrender_image) = webrender_image {
928 self.build_display_list_for_webrender_image(
929 state,
930 style,
931 display_list_section,
932 *absolute_bounds,
933 webrender_image,
934 i,
935 );
936 }
937 },
938 Either::Second(Image::Rect(_)) => {
939 // TODO: Implement `-moz-image-rect`
940 },
941 Either::Second(Image::Element(_)) => {
942 // TODO: Implement `-moz-element`
943 },
944 }
945 }
946 }
947
build_display_list_for_webrender_image( &self, state: &mut DisplayListBuildState, style: &ComputedValues, display_list_section: DisplayListSection, absolute_bounds: Rect<Au>, webrender_image: WebRenderImageInfo, index: usize, )948 fn build_display_list_for_webrender_image(
949 &self,
950 state: &mut DisplayListBuildState,
951 style: &ComputedValues,
952 display_list_section: DisplayListSection,
953 absolute_bounds: Rect<Au>,
954 webrender_image: WebRenderImageInfo,
955 index: usize,
956 ) {
957 debug!("(building display list) building background image");
958
959 let image = Size2D::new(
960 Au::from_px(webrender_image.width as i32),
961 Au::from_px(webrender_image.height as i32),
962 );
963 let placement = compute_background_placement(
964 style.get_background(),
965 state.layout_context.shared_context().viewport_size(),
966 absolute_bounds,
967 Some(image),
968 style.logical_border_width().to_physical(style.writing_mode),
969 self.border_padding.to_physical(style.writing_mode),
970 index,
971 );
972
973 // Create the image display item.
974 let base = state.create_base_display_item(
975 &placement.bounds,
976 LocalClip::Rect(placement.css_clip.to_layout()),
977 self.node,
978 style.get_cursor(CursorKind::Default),
979 display_list_section,
980 );
981
982 debug!("(building display list) adding background image.");
983 state.add_display_item(DisplayItem::Image(Box::new(ImageDisplayItem {
984 base: base,
985 webrender_image: webrender_image,
986 stretch_size: placement.tile_size.to_layout(),
987 tile_spacing: placement.tile_spacing.to_layout(),
988 image_rendering: style.get_inheritedbox().image_rendering.to_layout(),
989 })));
990 }
991
get_webrender_image_for_paint_worklet( &self, state: &mut DisplayListBuildState, style: &ComputedValues, paint_worklet: &PaintWorklet, size_in_au: Size2D<Au>, ) -> Option<WebRenderImageInfo>992 fn get_webrender_image_for_paint_worklet(
993 &self,
994 state: &mut DisplayListBuildState,
995 style: &ComputedValues,
996 paint_worklet: &PaintWorklet,
997 size_in_au: Size2D<Au>,
998 ) -> Option<WebRenderImageInfo> {
999 let device_pixel_ratio = state.layout_context.style_context.device_pixel_ratio();
1000 let size_in_px =
1001 TypedSize2D::new(size_in_au.width.to_f32_px(), size_in_au.height.to_f32_px());
1002
1003 // TODO: less copying.
1004 let name = paint_worklet.name.clone();
1005 let arguments = paint_worklet
1006 .arguments
1007 .iter()
1008 .map(|argument| argument.to_css_string())
1009 .collect();
1010
1011 let draw_result = match state.layout_context.registered_painters.get(&name) {
1012 Some(painter) => {
1013 debug!(
1014 "Drawing a paint image {}({},{}).",
1015 name, size_in_px.width, size_in_px.height
1016 );
1017 let properties = painter
1018 .properties()
1019 .iter()
1020 .filter_map(|(name, id)| id.as_shorthand().err().map(|id| (name, id)))
1021 .map(|(name, id)| (name.clone(), style.computed_value_to_string(id)))
1022 .collect();
1023 painter.draw_a_paint_image(size_in_px, device_pixel_ratio, properties, arguments)
1024 },
1025 None => {
1026 debug!("Worklet {} called before registration.", name);
1027 return None;
1028 },
1029 };
1030
1031 if let Ok(draw_result) = draw_result {
1032 let webrender_image = WebRenderImageInfo {
1033 width: draw_result.width,
1034 height: draw_result.height,
1035 format: draw_result.format,
1036 key: draw_result.image_key,
1037 };
1038
1039 for url in draw_result.missing_image_urls.into_iter() {
1040 debug!("Requesting missing image URL {}.", url);
1041 state.layout_context.get_webrender_image_for_url(
1042 self.node,
1043 url,
1044 UsePlaceholder::No,
1045 );
1046 }
1047 Some(webrender_image)
1048 } else {
1049 None
1050 }
1051 }
1052
build_display_list_for_background_gradient( &self, state: &mut DisplayListBuildState, display_list_section: DisplayListSection, absolute_bounds: Rect<Au>, gradient: &Gradient, style: &ComputedValues, index: usize, )1053 fn build_display_list_for_background_gradient(
1054 &self,
1055 state: &mut DisplayListBuildState,
1056 display_list_section: DisplayListSection,
1057 absolute_bounds: Rect<Au>,
1058 gradient: &Gradient,
1059 style: &ComputedValues,
1060 index: usize,
1061 ) {
1062 let placement = compute_background_placement(
1063 style.get_background(),
1064 state.layout_context.shared_context().viewport_size(),
1065 absolute_bounds,
1066 None,
1067 style.logical_border_width().to_physical(style.writing_mode),
1068 self.border_padding.to_physical(style.writing_mode),
1069 index,
1070 );
1071
1072 let base = state.create_base_display_item(
1073 &placement.bounds,
1074 LocalClip::Rect(placement.css_clip.to_layout()),
1075 self.node,
1076 style.get_cursor(CursorKind::Default),
1077 display_list_section,
1078 );
1079
1080 let display_item = match gradient.kind {
1081 GradientKind::Linear(angle_or_corner) => {
1082 let gradient = convert_linear_gradient(
1083 placement.tile_size,
1084 &gradient.items[..],
1085 angle_or_corner,
1086 gradient.repeating,
1087 );
1088 DisplayItem::Gradient(Box::new(GradientDisplayItem {
1089 base: base,
1090 gradient: gradient,
1091 tile: placement.tile_size.to_layout(),
1092 tile_spacing: placement.tile_spacing.to_layout(),
1093 }))
1094 },
1095 GradientKind::Radial(shape, center, _angle) => {
1096 let gradient = convert_radial_gradient(
1097 placement.tile_size,
1098 &gradient.items[..],
1099 shape,
1100 center,
1101 gradient.repeating,
1102 );
1103 DisplayItem::RadialGradient(Box::new(RadialGradientDisplayItem {
1104 base: base,
1105 gradient: gradient,
1106 tile: placement.tile_size.to_layout(),
1107 tile_spacing: placement.tile_spacing.to_layout(),
1108 }))
1109 },
1110 };
1111 state.add_display_item(display_item);
1112 }
1113
build_display_list_for_box_shadow_if_applicable( &self, state: &mut DisplayListBuildState, style: &ComputedValues, display_list_section: DisplayListSection, absolute_bounds: &Rect<Au>, clip: &Rect<Au>, )1114 fn build_display_list_for_box_shadow_if_applicable(
1115 &self,
1116 state: &mut DisplayListBuildState,
1117 style: &ComputedValues,
1118 display_list_section: DisplayListSection,
1119 absolute_bounds: &Rect<Au>,
1120 clip: &Rect<Au>,
1121 ) {
1122 // NB: According to CSS-BACKGROUNDS, box shadows render in *reverse* order (front to back).
1123 for box_shadow in style.get_effects().box_shadow.0.iter().rev() {
1124 let bounds = shadow_bounds(
1125 &absolute_bounds.translate(&Vector2D::new(
1126 Au::from(box_shadow.base.horizontal),
1127 Au::from(box_shadow.base.vertical),
1128 )),
1129 Au::from(box_shadow.base.blur),
1130 Au::from(box_shadow.spread),
1131 );
1132
1133 let base = state.create_base_display_item(
1134 &bounds,
1135 LocalClip::from(clip.to_layout()),
1136 self.node,
1137 style.get_cursor(CursorKind::Default),
1138 display_list_section,
1139 );
1140 let border_radius = build_border_radius(absolute_bounds, style.get_border());
1141 state.add_display_item(DisplayItem::BoxShadow(Box::new(BoxShadowDisplayItem {
1142 base: base,
1143 box_bounds: absolute_bounds.to_layout(),
1144 color: box_shadow
1145 .base
1146 .color
1147 .unwrap_or(style.get_color().color)
1148 .to_layout(),
1149 offset: LayoutVector2D::new(
1150 box_shadow.base.horizontal.px(),
1151 box_shadow.base.vertical.px(),
1152 ),
1153 blur_radius: box_shadow.base.blur.px(),
1154 spread_radius: box_shadow.spread.px(),
1155 border_radius: border_radius,
1156 clip_mode: if box_shadow.inset {
1157 BoxShadowClipMode::Inset
1158 } else {
1159 BoxShadowClipMode::Outset
1160 },
1161 })));
1162 }
1163 }
1164
build_display_list_for_borders_if_applicable( &self, state: &mut DisplayListBuildState, style: &ComputedValues, inline_info: Option<InlineNodeBorderInfo>, border_painting_mode: BorderPaintingMode, bounds: &Rect<Au>, display_list_section: DisplayListSection, clip: &Rect<Au>, )1165 fn build_display_list_for_borders_if_applicable(
1166 &self,
1167 state: &mut DisplayListBuildState,
1168 style: &ComputedValues,
1169 inline_info: Option<InlineNodeBorderInfo>,
1170 border_painting_mode: BorderPaintingMode,
1171 bounds: &Rect<Au>,
1172 display_list_section: DisplayListSection,
1173 clip: &Rect<Au>,
1174 ) {
1175 let mut border = style.logical_border_width();
1176
1177 if let Some(inline_info) = inline_info {
1178 modify_border_width_for_inline_sides(&mut border, inline_info);
1179 }
1180
1181 match border_painting_mode {
1182 BorderPaintingMode::Separate => {},
1183 BorderPaintingMode::Collapse(collapsed_borders) => {
1184 collapsed_borders.adjust_border_widths_for_painting(&mut border)
1185 },
1186 BorderPaintingMode::Hidden => return,
1187 }
1188 if border.is_zero() {
1189 // TODO: check if image-border-outset is zero
1190 return;
1191 }
1192
1193 let border_style_struct = style.get_border();
1194 let mut colors = SideOffsets2D::new(
1195 border_style_struct.border_top_color,
1196 border_style_struct.border_right_color,
1197 border_style_struct.border_bottom_color,
1198 border_style_struct.border_left_color,
1199 );
1200 let mut border_style = SideOffsets2D::new(
1201 border_style_struct.border_top_style,
1202 border_style_struct.border_right_style,
1203 border_style_struct.border_bottom_style,
1204 border_style_struct.border_left_style,
1205 );
1206
1207 if let BorderPaintingMode::Collapse(collapsed_borders) = border_painting_mode {
1208 collapsed_borders.adjust_border_colors_and_styles_for_painting(
1209 &mut colors,
1210 &mut border_style,
1211 style.writing_mode,
1212 );
1213 }
1214
1215 // If this border collapses, then we draw outside the boundaries we were given.
1216 let mut bounds = *bounds;
1217 if let BorderPaintingMode::Collapse(collapsed_borders) = border_painting_mode {
1218 collapsed_borders.adjust_border_bounds_for_painting(&mut bounds, style.writing_mode)
1219 }
1220
1221 // Append the border to the display list.
1222 let base = state.create_base_display_item(
1223 &bounds,
1224 LocalClip::from(clip.to_layout()),
1225 self.node,
1226 style.get_cursor(CursorKind::Default),
1227 display_list_section,
1228 );
1229
1230 let border_radius = build_border_radius(&bounds, border_style_struct);
1231
1232 let details = match border_style_struct.border_image_source {
1233 Either::First(_) => Some(BorderDetails::Normal(NormalBorder {
1234 left: BorderSide {
1235 color: style.resolve_color(colors.left).to_layout(),
1236 style: border_style.left.to_layout(),
1237 },
1238 right: BorderSide {
1239 color: style.resolve_color(colors.right).to_layout(),
1240 style: border_style.right.to_layout(),
1241 },
1242 top: BorderSide {
1243 color: style.resolve_color(colors.top).to_layout(),
1244 style: border_style.top.to_layout(),
1245 },
1246 bottom: BorderSide {
1247 color: style.resolve_color(colors.bottom).to_layout(),
1248 style: border_style.bottom.to_layout(),
1249 },
1250 radius: border_radius,
1251 })),
1252 Either::Second(Image::Gradient(ref gradient)) => {
1253 Some(match gradient.kind {
1254 GradientKind::Linear(angle_or_corner) => {
1255 BorderDetails::Gradient(display_list::GradientBorder {
1256 gradient: convert_linear_gradient(
1257 bounds.size,
1258 &gradient.items[..],
1259 angle_or_corner,
1260 gradient.repeating,
1261 ),
1262 // TODO(gw): Support border-image-outset
1263 outset: SideOffsets2D::zero(),
1264 })
1265 },
1266 GradientKind::Radial(shape, center, _angle) => {
1267 BorderDetails::RadialGradient(display_list::RadialGradientBorder {
1268 gradient: convert_radial_gradient(
1269 bounds.size,
1270 &gradient.items[..],
1271 shape,
1272 center,
1273 gradient.repeating,
1274 ),
1275 // TODO(gw): Support border-image-outset
1276 outset: SideOffsets2D::zero(),
1277 })
1278 },
1279 })
1280 },
1281 Either::Second(Image::PaintWorklet(ref paint_worklet)) => {
1282 // TODO: this size should be increased by border-image-outset
1283 let size = self.border_box.size.to_physical(style.writing_mode);
1284 self.get_webrender_image_for_paint_worklet(state, style, paint_worklet, size)
1285 .and_then(|image| build_image_border_details(image, border_style_struct))
1286 },
1287 Either::Second(Image::Rect(..)) => {
1288 // TODO: Handle border-image with `-moz-image-rect`.
1289 None
1290 },
1291 Either::Second(Image::Element(..)) => {
1292 // TODO: Handle border-image with `-moz-element`.
1293 None
1294 },
1295 Either::Second(Image::Url(ref image_url)) => image_url
1296 .url()
1297 .and_then(|url| {
1298 state.layout_context.get_webrender_image_for_url(
1299 self.node,
1300 url.clone(),
1301 UsePlaceholder::No,
1302 )
1303 })
1304 .and_then(|image| build_image_border_details(image, border_style_struct)),
1305 };
1306 if let Some(details) = details {
1307 state.add_display_item(DisplayItem::Border(Box::new(BorderDisplayItem {
1308 base,
1309 border_widths: border.to_physical(style.writing_mode).to_layout(),
1310 details,
1311 })));
1312 }
1313 }
1314
build_display_list_for_outline_if_applicable( &self, state: &mut DisplayListBuildState, style: &ComputedValues, bounds: &Rect<Au>, clip: &Rect<Au>, )1315 fn build_display_list_for_outline_if_applicable(
1316 &self,
1317 state: &mut DisplayListBuildState,
1318 style: &ComputedValues,
1319 bounds: &Rect<Au>,
1320 clip: &Rect<Au>,
1321 ) {
1322 use style::values::specified::outline::OutlineStyle;
1323
1324 let width = Au::from(style.get_outline().outline_width);
1325 if width == Au(0) {
1326 return;
1327 }
1328
1329 let outline_style = match style.get_outline().outline_style {
1330 OutlineStyle::Auto => BorderStyle::Solid,
1331 OutlineStyle::Other(BorderStyle::None) => return,
1332 OutlineStyle::Other(border_style) => border_style,
1333 };
1334
1335 // Outlines are not accounted for in the dimensions of the border box, so adjust the
1336 // absolute bounds.
1337 let mut bounds = *bounds;
1338 let offset = width + Au::from(style.get_outline().outline_offset);
1339 bounds = bounds.inflate(offset, offset);
1340
1341 // Append the outline to the display list.
1342 let color = style
1343 .resolve_color(style.get_outline().outline_color)
1344 .to_layout();
1345 let base = state.create_base_display_item(
1346 &bounds,
1347 LocalClip::from(clip.to_layout()),
1348 self.node,
1349 style.get_cursor(CursorKind::Default),
1350 DisplayListSection::Outlines,
1351 );
1352 state.add_display_item(DisplayItem::Border(Box::new(BorderDisplayItem {
1353 base: base,
1354 border_widths: SideOffsets2D::new_all_same(width).to_layout(),
1355 details: BorderDetails::Normal(simple_normal_border(color, outline_style.to_layout())),
1356 })));
1357 }
1358
build_debug_borders_around_text_fragments( &self, state: &mut DisplayListBuildState, style: &ComputedValues, stacking_relative_border_box: &Rect<Au>, stacking_relative_content_box: &Rect<Au>, text_fragment: &ScannedTextFragmentInfo, clip: &Rect<Au>, )1359 fn build_debug_borders_around_text_fragments(
1360 &self,
1361 state: &mut DisplayListBuildState,
1362 style: &ComputedValues,
1363 stacking_relative_border_box: &Rect<Au>,
1364 stacking_relative_content_box: &Rect<Au>,
1365 text_fragment: &ScannedTextFragmentInfo,
1366 clip: &Rect<Au>,
1367 ) {
1368 // FIXME(pcwalton, #2795): Get the real container size.
1369 let container_size = Size2D::zero();
1370
1371 // Compute the text fragment bounds and draw a border surrounding them.
1372 let base = state.create_base_display_item(
1373 stacking_relative_border_box,
1374 LocalClip::from(clip.to_layout()),
1375 self.node,
1376 style.get_cursor(CursorKind::Default),
1377 DisplayListSection::Content,
1378 );
1379 state.add_display_item(DisplayItem::Border(Box::new(BorderDisplayItem {
1380 base: base,
1381 border_widths: SideOffsets2D::new_all_same(Au::from_px(1)).to_layout(),
1382 details: BorderDetails::Normal(simple_normal_border(
1383 ColorF::rgb(0, 0, 200),
1384 webrender_api::BorderStyle::Solid,
1385 )),
1386 })));
1387
1388 // Draw a rectangle representing the baselines.
1389 let mut baseline = LogicalRect::from_physical(
1390 self.style.writing_mode,
1391 *stacking_relative_content_box,
1392 container_size,
1393 );
1394 baseline.start.b = baseline.start.b + text_fragment.run.ascent();
1395 baseline.size.block = Au(0);
1396 let baseline = baseline.to_physical(self.style.writing_mode, container_size);
1397
1398 let base = state.create_base_display_item(
1399 &baseline,
1400 LocalClip::from(clip.to_layout()),
1401 self.node,
1402 style.get_cursor(CursorKind::Default),
1403 DisplayListSection::Content,
1404 );
1405 state.add_display_item(DisplayItem::Line(Box::new(LineDisplayItem {
1406 base: base,
1407 color: ColorF::rgb(0, 200, 0),
1408 style: LineStyle::Dashed,
1409 })));
1410 }
1411
build_debug_borders_around_fragment( &self, state: &mut DisplayListBuildState, stacking_relative_border_box: &Rect<Au>, clip: &Rect<Au>, )1412 fn build_debug_borders_around_fragment(
1413 &self,
1414 state: &mut DisplayListBuildState,
1415 stacking_relative_border_box: &Rect<Au>,
1416 clip: &Rect<Au>,
1417 ) {
1418 // This prints a debug border around the border of this fragment.
1419 let base = state.create_base_display_item(
1420 stacking_relative_border_box,
1421 LocalClip::from(clip.to_layout()),
1422 self.node,
1423 self.style.get_cursor(CursorKind::Default),
1424 DisplayListSection::Content,
1425 );
1426 state.add_display_item(DisplayItem::Border(Box::new(BorderDisplayItem {
1427 base: base,
1428 border_widths: SideOffsets2D::new_all_same(Au::from_px(1)).to_layout(),
1429 details: BorderDetails::Normal(simple_normal_border(
1430 ColorF::rgb(0, 0, 200),
1431 webrender_api::BorderStyle::Solid,
1432 )),
1433 })));
1434 }
1435
build_display_items_for_selection_if_necessary( &self, state: &mut DisplayListBuildState, stacking_relative_border_box: &Rect<Au>, display_list_section: DisplayListSection, clip: &Rect<Au>, )1436 fn build_display_items_for_selection_if_necessary(
1437 &self,
1438 state: &mut DisplayListBuildState,
1439 stacking_relative_border_box: &Rect<Au>,
1440 display_list_section: DisplayListSection,
1441 clip: &Rect<Au>,
1442 ) {
1443 let scanned_text_fragment_info = match self.specific {
1444 SpecificFragmentInfo::ScannedText(ref scanned_text_fragment_info) => {
1445 scanned_text_fragment_info
1446 },
1447 _ => return,
1448 };
1449
1450 // Draw a highlighted background if the text is selected.
1451 //
1452 // TODO: Allow non-text fragments to be selected too.
1453 if scanned_text_fragment_info.selected() {
1454 let style = self.selected_style();
1455 let background_color = style.resolve_color(style.get_background().background_color);
1456 let base = state.create_base_display_item(
1457 stacking_relative_border_box,
1458 LocalClip::from(clip.to_layout()),
1459 self.node,
1460 self.style.get_cursor(CursorKind::Default),
1461 display_list_section,
1462 );
1463 state.add_display_item(DisplayItem::SolidColor(Box::new(SolidColorDisplayItem {
1464 base: base,
1465 color: background_color.to_layout(),
1466 })));
1467 }
1468
1469 // Draw a caret at the insertion point.
1470 let insertion_point_index = match scanned_text_fragment_info.insertion_point {
1471 Some(insertion_point_index) => insertion_point_index,
1472 None => return,
1473 };
1474 let range = Range::new(
1475 scanned_text_fragment_info.range.begin(),
1476 insertion_point_index - scanned_text_fragment_info.range.begin(),
1477 );
1478 let advance = scanned_text_fragment_info.run.advance_for_range(&range);
1479
1480 let insertion_point_bounds;
1481 let cursor;
1482 if !self.style.writing_mode.is_vertical() {
1483 insertion_point_bounds = rect(
1484 stacking_relative_border_box.origin.x + advance,
1485 stacking_relative_border_box.origin.y,
1486 INSERTION_POINT_LOGICAL_WIDTH,
1487 stacking_relative_border_box.size.height,
1488 );
1489 cursor = CursorKind::Text;
1490 } else {
1491 insertion_point_bounds = rect(
1492 stacking_relative_border_box.origin.x,
1493 stacking_relative_border_box.origin.y + advance,
1494 stacking_relative_border_box.size.width,
1495 INSERTION_POINT_LOGICAL_WIDTH,
1496 );
1497 cursor = CursorKind::VerticalText;
1498 };
1499
1500 let base = state.create_base_display_item(
1501 &insertion_point_bounds,
1502 LocalClip::from(clip.to_layout()),
1503 self.node,
1504 self.style.get_cursor(cursor),
1505 display_list_section,
1506 );
1507 state.add_display_item(DisplayItem::SolidColor(Box::new(SolidColorDisplayItem {
1508 base: base,
1509 color: self.style().get_color().color.to_layout(),
1510 })));
1511 }
1512
build_display_list( &mut self, state: &mut DisplayListBuildState, stacking_relative_border_box: Rect<Au>, border_painting_mode: BorderPaintingMode, display_list_section: DisplayListSection, clip: &Rect<Au>, )1513 fn build_display_list(
1514 &mut self,
1515 state: &mut DisplayListBuildState,
1516 stacking_relative_border_box: Rect<Au>,
1517 border_painting_mode: BorderPaintingMode,
1518 display_list_section: DisplayListSection,
1519 clip: &Rect<Au>,
1520 ) {
1521 self.restyle_damage.remove(ServoRestyleDamage::REPAINT);
1522 self.build_display_list_no_damage(state, stacking_relative_border_box,
1523 border_painting_mode, display_list_section, clip)
1524 }
1525
build_display_list_no_damage( &self, state: &mut DisplayListBuildState, stacking_relative_border_box: Rect<Au>, border_painting_mode: BorderPaintingMode, display_list_section: DisplayListSection, clip: &Rect<Au>, )1526 fn build_display_list_no_damage(
1527 &self,
1528 state: &mut DisplayListBuildState,
1529 stacking_relative_border_box: Rect<Au>,
1530 border_painting_mode: BorderPaintingMode,
1531 display_list_section: DisplayListSection,
1532 clip: &Rect<Au>,
1533 ) {
1534 if self.style().get_inheritedbox().visibility != Visibility::Visible {
1535 return;
1536 }
1537
1538 debug!(
1539 "Fragment::build_display_list at rel={:?}, abs={:?}: {:?}",
1540 self.border_box, stacking_relative_border_box, self
1541 );
1542
1543 // Check the clip rect. If there's nothing to render at all, don't even construct display
1544 // list items.
1545 let empty_rect = !clip.intersects(&stacking_relative_border_box);
1546 if self.is_primary_fragment() && !empty_rect {
1547 // Add shadows, background, borders, and outlines, if applicable.
1548 if let Some(ref inline_context) = self.inline_context {
1549 for node in inline_context.nodes.iter().rev() {
1550 self.build_display_list_for_background_if_applicable(
1551 state,
1552 &*node.style,
1553 display_list_section,
1554 &stacking_relative_border_box,
1555 );
1556
1557 self.build_display_list_for_box_shadow_if_applicable(
1558 state,
1559 &*node.style,
1560 display_list_section,
1561 &stacking_relative_border_box,
1562 clip,
1563 );
1564
1565 self.build_display_list_for_borders_if_applicable(
1566 state,
1567 &*node.style,
1568 Some(InlineNodeBorderInfo {
1569 is_first_fragment_of_element: node.flags
1570 .contains(InlineFragmentNodeFlags::FIRST_FRAGMENT_OF_ELEMENT),
1571 is_last_fragment_of_element: node.flags
1572 .contains(InlineFragmentNodeFlags::LAST_FRAGMENT_OF_ELEMENT),
1573 }),
1574 border_painting_mode,
1575 &stacking_relative_border_box,
1576 display_list_section,
1577 clip,
1578 );
1579
1580 // FIXME(emilio): Why does outline not do the same width
1581 // fixup as border?
1582 self.build_display_list_for_outline_if_applicable(
1583 state,
1584 &*node.style,
1585 &stacking_relative_border_box,
1586 clip,
1587 );
1588 }
1589 }
1590
1591 if !self.is_scanned_text_fragment() {
1592 self.build_display_list_for_background_if_applicable(
1593 state,
1594 &*self.style,
1595 display_list_section,
1596 &stacking_relative_border_box,
1597 );
1598
1599 self.build_display_list_for_box_shadow_if_applicable(
1600 state,
1601 &*self.style,
1602 display_list_section,
1603 &stacking_relative_border_box,
1604 clip,
1605 );
1606
1607 self.build_display_list_for_borders_if_applicable(
1608 state,
1609 &*self.style,
1610 /* inline_node_info = */ None,
1611 border_painting_mode,
1612 &stacking_relative_border_box,
1613 display_list_section,
1614 clip,
1615 );
1616
1617 self.build_display_list_for_outline_if_applicable(
1618 state,
1619 &*self.style,
1620 &stacking_relative_border_box,
1621 clip,
1622 );
1623 }
1624 }
1625
1626 if self.is_primary_fragment() {
1627 // Paint the selection point if necessary. Even an empty text fragment may have an
1628 // insertion point, so we do this even if `empty_rect` is true.
1629 self.build_display_items_for_selection_if_necessary(
1630 state,
1631 &stacking_relative_border_box,
1632 display_list_section,
1633 clip,
1634 );
1635 }
1636
1637 if empty_rect {
1638 return;
1639 }
1640
1641 debug!("Fragment::build_display_list: intersected. Adding display item...");
1642
1643 // Create special per-fragment-type display items.
1644 self.build_fragment_type_specific_display_items(state, &stacking_relative_border_box, clip);
1645
1646 if opts::get().show_debug_fragment_borders {
1647 self.build_debug_borders_around_fragment(state, &stacking_relative_border_box, clip)
1648 }
1649 }
1650
build_fragment_type_specific_display_items( &self, state: &mut DisplayListBuildState, stacking_relative_border_box: &Rect<Au>, clip: &Rect<Au>, )1651 fn build_fragment_type_specific_display_items(
1652 &self,
1653 state: &mut DisplayListBuildState,
1654 stacking_relative_border_box: &Rect<Au>,
1655 clip: &Rect<Au>,
1656 ) {
1657 let previous_clipping_and_scrolling = state.current_clipping_and_scrolling;
1658
1659 // Compute the context box position relative to the parent stacking context.
1660 let stacking_relative_content_box =
1661 self.stacking_relative_content_box(stacking_relative_border_box);
1662
1663 let create_base_display_item = |state: &mut DisplayListBuildState| {
1664 let layout_rect = stacking_relative_border_box.to_layout();
1665
1666 // Adjust the clipping region as necessary to account for `border-radius`.
1667 let radii =
1668 build_border_radius_for_inner_rect(&stacking_relative_border_box, &self.style);
1669
1670 if !radii.is_zero() {
1671 let clip_id = state.add_late_clip_node(layout_rect, radii);
1672 state.current_clipping_and_scrolling = ClippingAndScrolling::simple(clip_id);
1673 }
1674
1675 state.create_base_display_item(
1676 &stacking_relative_content_box,
1677 LocalClip::Rect(layout_rect),
1678 self.node,
1679 self.style.get_cursor(CursorKind::Default),
1680 DisplayListSection::Content,
1681 )
1682 };
1683
1684 match self.specific {
1685 SpecificFragmentInfo::TruncatedFragment(ref truncated_fragment)
1686 if truncated_fragment.text_info.is_some() =>
1687 {
1688 let text_fragment = truncated_fragment.text_info.as_ref().unwrap();
1689 // Create the main text display item.
1690 self.build_display_list_for_text_fragment(
1691 state,
1692 &text_fragment,
1693 &stacking_relative_content_box,
1694 &self.style.get_inheritedtext().text_shadow.0,
1695 clip,
1696 );
1697
1698 if opts::get().show_debug_fragment_borders {
1699 self.build_debug_borders_around_text_fragments(
1700 state,
1701 self.style(),
1702 stacking_relative_border_box,
1703 &stacking_relative_content_box,
1704 &text_fragment,
1705 clip,
1706 );
1707 }
1708 }
1709 SpecificFragmentInfo::ScannedText(ref text_fragment) => {
1710 // Create the main text display item.
1711 self.build_display_list_for_text_fragment(
1712 state,
1713 &text_fragment,
1714 &stacking_relative_content_box,
1715 &self.style.get_inheritedtext().text_shadow.0,
1716 clip,
1717 );
1718
1719 if opts::get().show_debug_fragment_borders {
1720 self.build_debug_borders_around_text_fragments(
1721 state,
1722 self.style(),
1723 stacking_relative_border_box,
1724 &stacking_relative_content_box,
1725 &text_fragment,
1726 clip,
1727 );
1728 }
1729 },
1730 SpecificFragmentInfo::Generic |
1731 SpecificFragmentInfo::GeneratedContent(..) |
1732 SpecificFragmentInfo::Table |
1733 SpecificFragmentInfo::TableCell |
1734 SpecificFragmentInfo::TableRow |
1735 SpecificFragmentInfo::TableWrapper |
1736 SpecificFragmentInfo::Multicol |
1737 SpecificFragmentInfo::MulticolColumn |
1738 SpecificFragmentInfo::InlineBlock(_) |
1739 SpecificFragmentInfo::InlineAbsoluteHypothetical(_) |
1740 SpecificFragmentInfo::InlineAbsolute(_) |
1741 SpecificFragmentInfo::TruncatedFragment(_) |
1742 SpecificFragmentInfo::Svg(_) => {
1743 if opts::get().show_debug_fragment_borders {
1744 self.build_debug_borders_around_fragment(
1745 state,
1746 stacking_relative_border_box,
1747 clip,
1748 );
1749 }
1750 },
1751 SpecificFragmentInfo::Iframe(ref fragment_info) => {
1752 if !stacking_relative_content_box.is_empty() {
1753 let browsing_context_id = match fragment_info.browsing_context_id {
1754 Some(browsing_context_id) => browsing_context_id,
1755 None => return warn!("No browsing context id for iframe."),
1756 };
1757 let pipeline_id = match fragment_info.pipeline_id {
1758 Some(pipeline_id) => pipeline_id,
1759 None => return warn!("No pipeline id for iframe {}.", browsing_context_id),
1760 };
1761
1762 let base = create_base_display_item(state);
1763 let item = DisplayItem::Iframe(Box::new(IframeDisplayItem {
1764 base,
1765 iframe: pipeline_id,
1766 }));
1767
1768 let size = Size2D::new(item.bounds().size.width, item.bounds().size.height);
1769 state
1770 .iframe_sizes
1771 .push((browsing_context_id, TypedSize2D::from_untyped(&size)));
1772
1773 state.add_display_item(item);
1774 }
1775 },
1776 SpecificFragmentInfo::Image(ref image_fragment) => {
1777 // Place the image into the display list.
1778 if let Some(ref image) = image_fragment.image {
1779 let base = create_base_display_item(state);
1780 state.add_display_item(DisplayItem::Image(Box::new(ImageDisplayItem {
1781 base,
1782 webrender_image: WebRenderImageInfo::from_image(image),
1783 stretch_size: stacking_relative_content_box.size.to_layout(),
1784 tile_spacing: LayoutSize::zero(),
1785 image_rendering: self.style.get_inheritedbox().image_rendering.to_layout(),
1786 })));
1787 }
1788 },
1789 SpecificFragmentInfo::Canvas(ref canvas_fragment_info) => {
1790 let computed_width = canvas_fragment_info.dom_width.to_px();
1791 let computed_height = canvas_fragment_info.dom_height.to_px();
1792
1793 let (image_key, format) = match canvas_fragment_info.source {
1794 CanvasFragmentSource::WebGL(image_key) => (image_key, PixelFormat::BGRA8),
1795 CanvasFragmentSource::Image(ref ipc_renderer) => match *ipc_renderer {
1796 Some(ref ipc_renderer) => {
1797 let ipc_renderer = ipc_renderer.lock().unwrap();
1798 let (sender, receiver) = ipc::channel().unwrap();
1799 ipc_renderer
1800 .send(CanvasMsg::FromLayout(FromLayoutMsg::SendData(sender)))
1801 .unwrap();
1802 (receiver.recv().unwrap().image_key, PixelFormat::BGRA8)
1803 },
1804 None => return,
1805 },
1806 };
1807
1808 let base = create_base_display_item(state);
1809 let display_item = DisplayItem::Image(Box::new(ImageDisplayItem {
1810 base,
1811 webrender_image: WebRenderImageInfo {
1812 width: computed_width as u32,
1813 height: computed_height as u32,
1814 format: format,
1815 key: Some(image_key),
1816 },
1817 stretch_size: stacking_relative_content_box.size.to_layout(),
1818 tile_spacing: LayoutSize::zero(),
1819 image_rendering: ImageRendering::Auto,
1820 }));
1821
1822 state.add_display_item(display_item);
1823 },
1824 SpecificFragmentInfo::UnscannedText(_) => {
1825 panic!("Shouldn't see unscanned fragments here.")
1826 },
1827 SpecificFragmentInfo::TableColumn(_) => {
1828 panic!("Shouldn't see table column fragments here.")
1829 },
1830 }
1831
1832 state.current_clipping_and_scrolling = previous_clipping_and_scrolling;
1833 }
1834
create_stacking_context( &self, id: StackingContextId, base_flow: &BaseFlow, scroll_policy: ScrollPolicy, context_type: StackingContextType, parent_clipping_and_scrolling: ClippingAndScrolling, ) -> StackingContext1835 fn create_stacking_context(
1836 &self,
1837 id: StackingContextId,
1838 base_flow: &BaseFlow,
1839 scroll_policy: ScrollPolicy,
1840 context_type: StackingContextType,
1841 parent_clipping_and_scrolling: ClippingAndScrolling,
1842 ) -> StackingContext {
1843 let border_box = self.stacking_relative_border_box(
1844 &base_flow.stacking_relative_position,
1845 &base_flow
1846 .early_absolute_position_info
1847 .relative_containing_block_size,
1848 base_flow
1849 .early_absolute_position_info
1850 .relative_containing_block_mode,
1851 CoordinateSystem::Parent,
1852 );
1853 // First, compute the offset of our border box (including relative positioning)
1854 // from our flow origin, since that is what `BaseFlow::overflow` is relative to.
1855 let border_box_offset = border_box
1856 .translate(&-base_flow.stacking_relative_position)
1857 .origin;
1858 // Then, using that, compute our overflow region relative to our border box.
1859 let overflow = base_flow
1860 .overflow
1861 .paint
1862 .translate(&-border_box_offset.to_vector());
1863
1864 // Create the filter pipeline.
1865 let effects = self.style().get_effects();
1866 let mut filters: Vec<FilterOp> = effects.filter.0.iter().map(ToLayout::to_layout).collect();
1867 if effects.opacity != 1.0 {
1868 filters.push(FilterOp::Opacity(effects.opacity.into(), effects.opacity));
1869 }
1870
1871 StackingContext::new(
1872 id,
1873 context_type,
1874 border_box.to_layout(),
1875 overflow.to_layout(),
1876 self.effective_z_index(),
1877 filters,
1878 self.style().get_effects().mix_blend_mode.to_layout(),
1879 self.transform_matrix(&border_box),
1880 self.style().get_used_transform_style().to_layout(),
1881 self.perspective_matrix(&border_box),
1882 scroll_policy,
1883 parent_clipping_and_scrolling,
1884 )
1885 }
1886
build_display_list_for_text_fragment( &self, state: &mut DisplayListBuildState, text_fragment: &ScannedTextFragmentInfo, stacking_relative_content_box: &Rect<Au>, text_shadows: &[SimpleShadow], clip: &Rect<Au>, )1887 fn build_display_list_for_text_fragment(
1888 &self,
1889 state: &mut DisplayListBuildState,
1890 text_fragment: &ScannedTextFragmentInfo,
1891 stacking_relative_content_box: &Rect<Au>,
1892 text_shadows: &[SimpleShadow],
1893 clip: &Rect<Au>,
1894 ) {
1895 // NB: The order for painting text components (CSS Text Decoration Module Level 3) is:
1896 // shadows, underline, overline, text, text-emphasis, and then line-through.
1897
1898 // TODO(emilio): Allow changing more properties by ::selection
1899 // Paint the text with the color as described in its styling.
1900 let text_color = if text_fragment.selected() {
1901 self.selected_style().get_color().color
1902 } else {
1903 self.style().get_color().color
1904 };
1905
1906 // Determine the orientation and cursor to use.
1907 let (_orientation, cursor) = if self.style.writing_mode.is_vertical() {
1908 // TODO: Distinguish between 'sideways-lr' and 'sideways-rl' writing modes in CSS
1909 // Writing Modes Level 4.
1910 (TextOrientation::SidewaysRight, CursorKind::VerticalText)
1911 } else {
1912 (TextOrientation::Upright, CursorKind::Text)
1913 };
1914
1915 // Compute location of the baseline.
1916 //
1917 // FIXME(pcwalton): Get the real container size.
1918 let container_size = Size2D::zero();
1919 let metrics = &text_fragment.run.font_metrics;
1920 let baseline_origin = stacking_relative_content_box.origin +
1921 LogicalPoint::new(self.style.writing_mode, Au(0), metrics.ascent)
1922 .to_physical(self.style.writing_mode, container_size)
1923 .to_vector();
1924
1925 // Base item for all text/shadows
1926 let base = state.create_base_display_item(
1927 &stacking_relative_content_box,
1928 LocalClip::from(clip.to_layout()),
1929 self.node,
1930 self.style().get_cursor(cursor),
1931 DisplayListSection::Content,
1932 );
1933
1934 // NB: According to CSS-BACKGROUNDS, text shadows render in *reverse* order (front
1935 // to back).
1936
1937 // Shadows
1938 for shadow in text_shadows.iter().rev() {
1939 state.add_display_item(DisplayItem::PushTextShadow(Box::new(
1940 PushTextShadowDisplayItem {
1941 base: base.clone(),
1942 blur_radius: shadow.blur.px(),
1943 offset: LayoutVector2D::new(shadow.horizontal.px(), shadow.vertical.px()),
1944 color: shadow
1945 .color
1946 .unwrap_or(self.style().get_color().color)
1947 .to_layout(),
1948 },
1949 )));
1950 }
1951
1952 // Create display items for text decorations.
1953 let text_decorations = self.style().get_inheritedtext().text_decorations_in_effect;
1954
1955 let logical_stacking_relative_content_box = LogicalRect::from_physical(
1956 self.style.writing_mode,
1957 *stacking_relative_content_box,
1958 container_size,
1959 );
1960
1961 // Underline
1962 if text_decorations.underline {
1963 let mut stacking_relative_box = logical_stacking_relative_content_box;
1964 stacking_relative_box.start.b = logical_stacking_relative_content_box.start.b +
1965 metrics.ascent -
1966 metrics.underline_offset;
1967 stacking_relative_box.size.block = metrics.underline_size;
1968 self.build_display_list_for_text_decoration(
1969 state,
1970 &text_color,
1971 &stacking_relative_box,
1972 clip,
1973 );
1974 }
1975
1976 // Overline
1977 if text_decorations.overline {
1978 let mut stacking_relative_box = logical_stacking_relative_content_box;
1979 stacking_relative_box.size.block = metrics.underline_size;
1980 self.build_display_list_for_text_decoration(
1981 state,
1982 &text_color,
1983 &stacking_relative_box,
1984 clip,
1985 );
1986 }
1987
1988 // Text
1989 let glyphs = convert_text_run_to_glyphs(
1990 text_fragment.run.clone(),
1991 text_fragment.range,
1992 baseline_origin,
1993 );
1994 if !glyphs.is_empty() {
1995 let indexable_text = IndexableTextItem {
1996 origin: stacking_relative_content_box.origin,
1997 text_run: text_fragment.run.clone(),
1998 range: text_fragment.range,
1999 baseline_origin,
2000 };
2001 state.indexable_text.insert(self.node, indexable_text);
2002
2003 state.add_display_item(DisplayItem::Text(Box::new(TextDisplayItem {
2004 base: base.clone(),
2005 glyphs: glyphs,
2006 font_key: text_fragment.run.font_key,
2007 text_color: text_color.to_layout(),
2008 })));
2009 }
2010
2011 // TODO(#17715): emit text-emphasis marks here.
2012 // (just push another TextDisplayItem?)
2013
2014 // Line-Through
2015 if text_decorations.line_through {
2016 let mut stacking_relative_box = logical_stacking_relative_content_box;
2017 stacking_relative_box.start.b =
2018 stacking_relative_box.start.b + metrics.ascent - metrics.strikeout_offset;
2019 stacking_relative_box.size.block = metrics.strikeout_size;
2020 self.build_display_list_for_text_decoration(
2021 state,
2022 &text_color,
2023 &stacking_relative_box,
2024 clip,
2025 );
2026 }
2027
2028 // Pop all the PushTextShadows
2029 if !text_shadows.is_empty() {
2030 state.add_display_item(DisplayItem::PopAllTextShadows(Box::new(
2031 PopAllTextShadowsDisplayItem { base: base.clone() },
2032 )));
2033 }
2034 }
2035
build_display_list_for_text_decoration( &self, state: &mut DisplayListBuildState, color: &RGBA, stacking_relative_box: &LogicalRect<Au>, clip: &Rect<Au>, )2036 fn build_display_list_for_text_decoration(
2037 &self,
2038 state: &mut DisplayListBuildState,
2039 color: &RGBA,
2040 stacking_relative_box: &LogicalRect<Au>,
2041 clip: &Rect<Au>,
2042 ) {
2043 // FIXME(pcwalton, #2795): Get the real container size.
2044 let container_size = Size2D::zero();
2045 let stacking_relative_box =
2046 stacking_relative_box.to_physical(self.style.writing_mode, container_size);
2047 let base = state.create_base_display_item(
2048 &stacking_relative_box,
2049 LocalClip::from(clip.to_layout()),
2050 self.node,
2051 self.style.get_cursor(CursorKind::Default),
2052 DisplayListSection::Content,
2053 );
2054
2055 state.add_display_item(DisplayItem::Line(Box::new(LineDisplayItem {
2056 base: base,
2057 color: color.to_layout(),
2058 style: LineStyle::Solid,
2059 })));
2060 }
2061
unique_id(&self) -> u642062 fn unique_id(&self) -> u64 {
2063 let fragment_type = self.fragment_type();
2064 let id = self.node.id() as usize;
2065 combine_id_with_fragment_type(id, fragment_type) as u64
2066 }
2067
fragment_type(&self) -> FragmentType2068 fn fragment_type(&self) -> FragmentType {
2069 self.pseudo.fragment_type()
2070 }
2071 }
2072
2073 bitflags! {
2074 pub struct StackingContextCollectionFlags: u8 {
2075 /// This flow never establishes a containing block.
2076 const NEVER_CREATES_CONTAINING_BLOCK = 0b001;
2077 /// This flow never creates a ClipScrollNode.
2078 const NEVER_CREATES_CLIP_SCROLL_NODE = 0b010;
2079 /// This flow never creates a stacking context.
2080 const NEVER_CREATES_STACKING_CONTEXT = 0b100;
2081 }
2082 }
2083
2084 pub trait BlockFlowDisplayListBuilding {
collect_stacking_contexts_for_block( &mut self, state: &mut StackingContextCollectionState, flags: StackingContextCollectionFlags, )2085 fn collect_stacking_contexts_for_block(
2086 &mut self,
2087 state: &mut StackingContextCollectionState,
2088 flags: StackingContextCollectionFlags,
2089 );
2090
transform_clip_to_coordinate_space( &mut self, state: &mut StackingContextCollectionState, preserved_state: &mut SavedStackingContextCollectionState, )2091 fn transform_clip_to_coordinate_space(
2092 &mut self,
2093 state: &mut StackingContextCollectionState,
2094 preserved_state: &mut SavedStackingContextCollectionState,
2095 );
setup_clipping_for_block( &mut self, state: &mut StackingContextCollectionState, preserved_state: &mut SavedStackingContextCollectionState, stacking_context_type: Option<StackingContextType>, flags: StackingContextCollectionFlags, ) -> ClippingAndScrolling2096 fn setup_clipping_for_block(
2097 &mut self,
2098 state: &mut StackingContextCollectionState,
2099 preserved_state: &mut SavedStackingContextCollectionState,
2100 stacking_context_type: Option<StackingContextType>,
2101 flags: StackingContextCollectionFlags,
2102 ) -> ClippingAndScrolling;
setup_clip_scroll_node_for_position( &mut self, state: &mut StackingContextCollectionState, border_box: &Rect<Au>, )2103 fn setup_clip_scroll_node_for_position(
2104 &mut self,
2105 state: &mut StackingContextCollectionState,
2106 border_box: &Rect<Au>,
2107 );
setup_clip_scroll_node_for_overflow( &mut self, state: &mut StackingContextCollectionState, border_box: &Rect<Au>, )2108 fn setup_clip_scroll_node_for_overflow(
2109 &mut self,
2110 state: &mut StackingContextCollectionState,
2111 border_box: &Rect<Au>,
2112 );
setup_clip_scroll_node_for_css_clip( &mut self, state: &mut StackingContextCollectionState, preserved_state: &mut SavedStackingContextCollectionState, stacking_relative_border_box: &Rect<Au>, )2113 fn setup_clip_scroll_node_for_css_clip(
2114 &mut self,
2115 state: &mut StackingContextCollectionState,
2116 preserved_state: &mut SavedStackingContextCollectionState,
2117 stacking_relative_border_box: &Rect<Au>,
2118 );
create_pseudo_stacking_context_for_block( &mut self, stacking_context_type: StackingContextType, parent_stacking_context_id: StackingContextId, parent_clip_and_scroll_info: ClippingAndScrolling, state: &mut StackingContextCollectionState, )2119 fn create_pseudo_stacking_context_for_block(
2120 &mut self,
2121 stacking_context_type: StackingContextType,
2122 parent_stacking_context_id: StackingContextId,
2123 parent_clip_and_scroll_info: ClippingAndScrolling,
2124 state: &mut StackingContextCollectionState,
2125 );
create_real_stacking_context_for_block( &mut self, parent_stacking_context_id: StackingContextId, parent_clipping_and_scrolling: ClippingAndScrolling, state: &mut StackingContextCollectionState, )2126 fn create_real_stacking_context_for_block(
2127 &mut self,
2128 parent_stacking_context_id: StackingContextId,
2129 parent_clipping_and_scrolling: ClippingAndScrolling,
2130 state: &mut StackingContextCollectionState,
2131 );
build_display_list_for_block( &mut self, state: &mut DisplayListBuildState, border_painting_mode: BorderPaintingMode, )2132 fn build_display_list_for_block(
2133 &mut self,
2134 state: &mut DisplayListBuildState,
2135 border_painting_mode: BorderPaintingMode,
2136 );
build_display_list_for_block_no_damage( &self, state: &mut DisplayListBuildState, border_painting_mode: BorderPaintingMode, )2137 fn build_display_list_for_block_no_damage(
2138 &self,
2139 state: &mut DisplayListBuildState,
2140 border_painting_mode: BorderPaintingMode,
2141 );
build_display_list_for_background_if_applicable_with_background( &self, state: &mut DisplayListBuildState, background: &style_structs::Background, background_color: RGBA)2142 fn build_display_list_for_background_if_applicable_with_background(
2143 &self,
2144 state: &mut DisplayListBuildState,
2145 background: &style_structs::Background,
2146 background_color: RGBA);
2147
stacking_context_type( &self, flags: StackingContextCollectionFlags, ) -> Option<StackingContextType>2148 fn stacking_context_type(
2149 &self,
2150 flags: StackingContextCollectionFlags,
2151 ) -> Option<StackingContextType>;
2152 }
2153
2154 /// This structure manages ensuring that modification to StackingContextCollectionState is
2155 /// only temporary. It's useful for moving recursively down the flow tree and ensuring
2156 /// that the state is restored for siblings. To use this structure, we must call
2157 /// SavedStackingContextCollectionState::restore in order to restore the state.
2158 /// TODO(mrobinson): It would be nice to use RAII here to avoid having to call restore.
2159 pub struct SavedStackingContextCollectionState {
2160 stacking_context_id: StackingContextId,
2161 real_stacking_context_id: StackingContextId,
2162 clipping_and_scrolling: ClippingAndScrolling,
2163 containing_block_clipping_and_scrolling: ClippingAndScrolling,
2164 clips_pushed: usize,
2165 containing_block_clips_pushed: usize,
2166 stacking_relative_content_box: Rect<Au>,
2167 }
2168
2169 impl SavedStackingContextCollectionState {
new(state: &mut StackingContextCollectionState) -> SavedStackingContextCollectionState2170 fn new(state: &mut StackingContextCollectionState) -> SavedStackingContextCollectionState {
2171 SavedStackingContextCollectionState {
2172 stacking_context_id: state.current_stacking_context_id,
2173 real_stacking_context_id: state.current_real_stacking_context_id,
2174 clipping_and_scrolling: state.current_clipping_and_scrolling,
2175 containing_block_clipping_and_scrolling: state.containing_block_clipping_and_scrolling,
2176 clips_pushed: 0,
2177 containing_block_clips_pushed: 0,
2178 stacking_relative_content_box: state.parent_stacking_relative_content_box,
2179 }
2180 }
2181
switch_to_containing_block_clip(&mut self, state: &mut StackingContextCollectionState)2182 fn switch_to_containing_block_clip(&mut self, state: &mut StackingContextCollectionState) {
2183 let clip = state
2184 .containing_block_clip_stack
2185 .last()
2186 .cloned()
2187 .unwrap_or_else(MaxRect::max_rect);
2188 state.clip_stack.push(clip);
2189 self.clips_pushed += 1;
2190 }
2191
restore(self, state: &mut StackingContextCollectionState)2192 fn restore(self, state: &mut StackingContextCollectionState) {
2193 state.current_stacking_context_id = self.stacking_context_id;
2194 state.current_real_stacking_context_id = self.real_stacking_context_id;
2195 state.current_clipping_and_scrolling = self.clipping_and_scrolling;
2196 state.containing_block_clipping_and_scrolling =
2197 self.containing_block_clipping_and_scrolling;
2198 state.parent_stacking_relative_content_box = self.stacking_relative_content_box;
2199
2200 let truncate_length = state.clip_stack.len() - self.clips_pushed;
2201 state.clip_stack.truncate(truncate_length);
2202
2203 let truncate_length =
2204 state.containing_block_clip_stack.len() - self.containing_block_clips_pushed;
2205 state.containing_block_clip_stack.truncate(truncate_length);
2206 }
2207
push_clip( &mut self, state: &mut StackingContextCollectionState, clip: &Rect<Au>, positioning: StylePosition, )2208 fn push_clip(
2209 &mut self,
2210 state: &mut StackingContextCollectionState,
2211 clip: &Rect<Au>,
2212 positioning: StylePosition,
2213 ) {
2214 let mut clip = *clip;
2215 if positioning != StylePosition::Fixed {
2216 if let Some(old_clip) = state.clip_stack.last() {
2217 clip = old_clip.intersection(&clip).unwrap_or_else(Rect::zero);
2218 }
2219 }
2220
2221 state.clip_stack.push(clip);
2222 self.clips_pushed += 1;
2223
2224 if StylePosition::Absolute == positioning {
2225 state.containing_block_clip_stack.push(clip);
2226 self.containing_block_clips_pushed += 1;
2227 }
2228 }
2229 }
2230
2231 impl BlockFlowDisplayListBuilding for BlockFlow {
transform_clip_to_coordinate_space( &mut self, state: &mut StackingContextCollectionState, preserved_state: &mut SavedStackingContextCollectionState, )2232 fn transform_clip_to_coordinate_space(
2233 &mut self,
2234 state: &mut StackingContextCollectionState,
2235 preserved_state: &mut SavedStackingContextCollectionState,
2236 ) {
2237 if state.clip_stack.is_empty() {
2238 return;
2239 }
2240 let border_box = self.stacking_relative_border_box(CoordinateSystem::Parent);
2241 let transform = match self.fragment.transform_matrix(&border_box) {
2242 Some(transform) => transform,
2243 None => return,
2244 };
2245
2246 let perspective = self.fragment
2247 .perspective_matrix(&border_box)
2248 .unwrap_or(LayoutTransform::identity());
2249 let transform = transform.pre_mul(&perspective).inverse();
2250
2251 let origin = &border_box.origin;
2252 let transform_clip = |clip: &Rect<Au>| {
2253 if *clip == Rect::max_rect() {
2254 return *clip;
2255 }
2256
2257 match transform {
2258 Some(transform) if transform.m13 != 0.0 || transform.m23 != 0.0 => {
2259 // We cannot properly handle perspective transforms, because there may be a
2260 // situation where an element is transformed from outside the clip into the
2261 // clip region. Here we don't have enough information to detect when that is
2262 // happening. For the moment we just punt on trying to optimize the display
2263 // list for those cases.
2264 Rect::max_rect()
2265 },
2266 Some(transform) => {
2267 let clip = rect(
2268 (clip.origin.x - origin.x).to_f32_px(),
2269 (clip.origin.y - origin.y).to_f32_px(),
2270 clip.size.width.to_f32_px(),
2271 clip.size.height.to_f32_px(),
2272 );
2273
2274 let clip = transform.transform_rect(&clip);
2275
2276 rect(
2277 Au::from_f32_px(clip.origin.x),
2278 Au::from_f32_px(clip.origin.y),
2279 Au::from_f32_px(clip.size.width),
2280 Au::from_f32_px(clip.size.height),
2281 )
2282 },
2283 None => Rect::zero(),
2284 }
2285 };
2286
2287 if let Some(clip) = state.clip_stack.last().cloned() {
2288 state.clip_stack.push(transform_clip(&clip));
2289 preserved_state.clips_pushed += 1;
2290 }
2291
2292 if let Some(clip) = state.containing_block_clip_stack.last().cloned() {
2293 state
2294 .containing_block_clip_stack
2295 .push(transform_clip(&clip));
2296 preserved_state.containing_block_clips_pushed += 1;
2297 }
2298 }
2299
collect_stacking_contexts_for_block( &mut self, state: &mut StackingContextCollectionState, flags: StackingContextCollectionFlags, )2300 fn collect_stacking_contexts_for_block(
2301 &mut self,
2302 state: &mut StackingContextCollectionState,
2303 flags: StackingContextCollectionFlags,
2304 ) {
2305 let mut preserved_state = SavedStackingContextCollectionState::new(state);
2306
2307 let stacking_context_type = self.stacking_context_type(flags);
2308 self.base.stacking_context_id = match stacking_context_type {
2309 None => state.current_stacking_context_id,
2310 Some(sc_type) => state.allocate_stacking_context_info(sc_type),
2311 };
2312 state.current_stacking_context_id = self.base.stacking_context_id;
2313
2314 if stacking_context_type == Some(StackingContextType::Real) {
2315 state.current_real_stacking_context_id = self.base.stacking_context_id;
2316 }
2317
2318 // We are getting the id of the scroll root that contains us here, not the id of
2319 // any scroll root that we create. If we create a scroll root, its index will be
2320 // stored in state.current_clipping_and_scrolling. If we create a stacking context,
2321 // we don't want it to be contained by its own scroll root.
2322 let containing_clipping_and_scrolling = self.setup_clipping_for_block(
2323 state,
2324 &mut preserved_state,
2325 stacking_context_type,
2326 flags,
2327 );
2328
2329 if establishes_containing_block_for_absolute(flags, self.positioning()) {
2330 state.containing_block_clipping_and_scrolling = state.current_clipping_and_scrolling;
2331 }
2332
2333 match stacking_context_type {
2334 None => self.base.collect_stacking_contexts_for_children(state),
2335 Some(StackingContextType::Real) => {
2336 self.create_real_stacking_context_for_block(
2337 preserved_state.stacking_context_id,
2338 containing_clipping_and_scrolling,
2339 state,
2340 );
2341 },
2342 Some(stacking_context_type) => {
2343 self.create_pseudo_stacking_context_for_block(
2344 stacking_context_type,
2345 preserved_state.stacking_context_id,
2346 containing_clipping_and_scrolling,
2347 state,
2348 );
2349 },
2350 }
2351
2352 preserved_state.restore(state);
2353 }
2354
setup_clipping_for_block( &mut self, state: &mut StackingContextCollectionState, preserved_state: &mut SavedStackingContextCollectionState, stacking_context_type: Option<StackingContextType>, flags: StackingContextCollectionFlags, ) -> ClippingAndScrolling2355 fn setup_clipping_for_block(
2356 &mut self,
2357 state: &mut StackingContextCollectionState,
2358 preserved_state: &mut SavedStackingContextCollectionState,
2359 stacking_context_type: Option<StackingContextType>,
2360 flags: StackingContextCollectionFlags,
2361 ) -> ClippingAndScrolling {
2362 // If this block is absolutely positioned, we should be clipped and positioned by
2363 // the scroll root of our nearest ancestor that establishes a containing block.
2364 let containing_clipping_and_scrolling = match self.positioning() {
2365 StylePosition::Absolute => {
2366 preserved_state.switch_to_containing_block_clip(state);
2367 state.current_clipping_and_scrolling =
2368 state.containing_block_clipping_and_scrolling;
2369 state.containing_block_clipping_and_scrolling
2370 },
2371 StylePosition::Fixed => {
2372 preserved_state.push_clip(state, &Rect::max_rect(), StylePosition::Fixed);
2373 state.current_clipping_and_scrolling
2374 },
2375 _ => state.current_clipping_and_scrolling,
2376 };
2377 self.base.clipping_and_scrolling = Some(containing_clipping_and_scrolling);
2378
2379 let stacking_relative_border_box = if self.fragment.establishes_stacking_context() {
2380 self.stacking_relative_border_box(CoordinateSystem::Own)
2381 } else {
2382 self.stacking_relative_border_box(CoordinateSystem::Parent)
2383 };
2384
2385 if stacking_context_type == Some(StackingContextType::Real) {
2386 self.transform_clip_to_coordinate_space(state, preserved_state);
2387 }
2388
2389 if !flags.contains(StackingContextCollectionFlags::NEVER_CREATES_CLIP_SCROLL_NODE) {
2390 self.setup_clip_scroll_node_for_position(state, &stacking_relative_border_box);
2391 self.setup_clip_scroll_node_for_overflow(state, &stacking_relative_border_box);
2392 self.setup_clip_scroll_node_for_css_clip(
2393 state,
2394 preserved_state,
2395 &stacking_relative_border_box,
2396 );
2397 }
2398 self.base.clip = state
2399 .clip_stack
2400 .last()
2401 .cloned()
2402 .unwrap_or_else(Rect::max_rect);
2403
2404 // We keep track of our position so that any stickily positioned elements can
2405 // properly determine the extent of their movement relative to scrolling containers.
2406 if !flags.contains(StackingContextCollectionFlags::NEVER_CREATES_CONTAINING_BLOCK) {
2407 let border_box = if self.fragment.establishes_stacking_context() {
2408 stacking_relative_border_box
2409 } else {
2410 self.stacking_relative_border_box(CoordinateSystem::Own)
2411 };
2412 state.parent_stacking_relative_content_box =
2413 self.fragment.stacking_relative_content_box(&border_box)
2414 }
2415
2416 match self.positioning() {
2417 StylePosition::Absolute | StylePosition::Relative | StylePosition::Fixed => {
2418 state.containing_block_clipping_and_scrolling = state.current_clipping_and_scrolling
2419 },
2420 _ => {},
2421 }
2422
2423 containing_clipping_and_scrolling
2424 }
2425
setup_clip_scroll_node_for_position( &mut self, state: &mut StackingContextCollectionState, border_box: &Rect<Au>, )2426 fn setup_clip_scroll_node_for_position(
2427 &mut self,
2428 state: &mut StackingContextCollectionState,
2429 border_box: &Rect<Au>,
2430 ) {
2431 if self.positioning() != StylePosition::Sticky {
2432 return;
2433 }
2434
2435 let sticky_position = self.sticky_position();
2436 if sticky_position.left == MaybeAuto::Auto && sticky_position.right == MaybeAuto::Auto &&
2437 sticky_position.top == MaybeAuto::Auto &&
2438 sticky_position.bottom == MaybeAuto::Auto
2439 {
2440 return;
2441 }
2442
2443 // Since position: sticky elements always establish a stacking context, we will
2444 // have previously calculated our border box in our own coordinate system. In
2445 // order to properly calculate max offsets we need to compare our size and
2446 // position in our parent's coordinate system.
2447 let border_box_in_parent = self.stacking_relative_border_box(CoordinateSystem::Parent);
2448 let margins = self.fragment.margin.to_physical(
2449 self.base
2450 .early_absolute_position_info
2451 .relative_containing_block_mode,
2452 );
2453
2454 // Position:sticky elements are always restricted based on the size and position of
2455 // their containing block, which for sticky items is like relative and statically
2456 // positioned items: just the parent block.
2457 let constraint_rect = state.parent_stacking_relative_content_box;
2458
2459 let to_offset_bound = |constraint_edge: Au, moving_edge: Au| -> f32 {
2460 (constraint_edge - moving_edge).to_f32_px()
2461 };
2462
2463 // This is the minimum negative offset and then the maximum positive offset. We just
2464 // specify every edge, but if the corresponding margin is None, that offset has no effect.
2465 let vertical_offset_bounds = StickyOffsetBounds::new(
2466 to_offset_bound(
2467 constraint_rect.min_y(),
2468 border_box_in_parent.min_y() - margins.top,
2469 ),
2470 to_offset_bound(constraint_rect.max_y(), border_box_in_parent.max_y()),
2471 );
2472 let horizontal_offset_bounds = StickyOffsetBounds::new(
2473 to_offset_bound(
2474 constraint_rect.min_x(),
2475 border_box_in_parent.min_x() - margins.left,
2476 ),
2477 to_offset_bound(constraint_rect.max_x(), border_box_in_parent.max_x()),
2478 );
2479
2480 // The margins control which edges have sticky behavior.
2481 let sticky_frame_data = StickyFrameData {
2482 margins: SideOffsets2D::new(
2483 sticky_position.top.to_option().map(|v| v.to_f32_px()),
2484 sticky_position.right.to_option().map(|v| v.to_f32_px()),
2485 sticky_position.bottom.to_option().map(|v| v.to_f32_px()),
2486 sticky_position.left.to_option().map(|v| v.to_f32_px()),
2487 ),
2488 vertical_offset_bounds,
2489 horizontal_offset_bounds,
2490 };
2491
2492 let new_clip_scroll_index = state.add_clip_scroll_node(ClipScrollNode {
2493 parent_index: self.clipping_and_scrolling().scrolling,
2494 clip: ClippingRegion::from_rect(border_box.to_layout()),
2495 content_rect: LayoutRect::zero(),
2496 node_type: ClipScrollNodeType::StickyFrame(sticky_frame_data),
2497 });
2498
2499 let new_clipping_and_scrolling = ClippingAndScrolling::simple(new_clip_scroll_index);
2500 self.base.clipping_and_scrolling = Some(new_clipping_and_scrolling);
2501 state.current_clipping_and_scrolling = new_clipping_and_scrolling;
2502 }
2503
setup_clip_scroll_node_for_overflow( &mut self, state: &mut StackingContextCollectionState, border_box: &Rect<Au>, )2504 fn setup_clip_scroll_node_for_overflow(
2505 &mut self,
2506 state: &mut StackingContextCollectionState,
2507 border_box: &Rect<Au>,
2508 ) {
2509 if !self.overflow_style_may_require_clip_scroll_node() {
2510 return;
2511 }
2512
2513 let content_box = self.fragment.stacking_relative_content_box(&border_box);
2514 let has_scrolling_overflow = self.base.overflow.scroll.origin != Point2D::zero() ||
2515 self.base.overflow.scroll.size.width > content_box.size.width ||
2516 self.base.overflow.scroll.size.height > content_box.size.height ||
2517 StyleOverflow::Hidden == self.fragment.style.get_box().overflow_x ||
2518 StyleOverflow::Hidden == self.fragment.style.get_box().overflow_y;
2519
2520 self.mark_scrolling_overflow(has_scrolling_overflow);
2521 if !has_scrolling_overflow {
2522 return;
2523 }
2524
2525 let sensitivity = if StyleOverflow::Hidden == self.fragment.style.get_box().overflow_x &&
2526 StyleOverflow::Hidden == self.fragment.style.get_box().overflow_y
2527 {
2528 ScrollSensitivity::Script
2529 } else {
2530 ScrollSensitivity::ScriptAndInputEvents
2531 };
2532
2533 let border_widths = self.fragment
2534 .style
2535 .logical_border_width()
2536 .to_physical(self.fragment.style.writing_mode);
2537 let clip_rect = border_box.inner_rect(border_widths);
2538
2539 let mut clip = ClippingRegion::from_rect(clip_rect.to_layout());
2540 let radii = build_border_radius_for_inner_rect(&border_box, &self.fragment.style);
2541 if !radii.is_zero() {
2542 clip.intersect_with_rounded_rect(clip_rect.to_layout(), radii)
2543 }
2544
2545 let content_size = self.base.overflow.scroll.origin + self.base.overflow.scroll.size;
2546 let content_size = Size2D::new(content_size.x, content_size.y);
2547
2548 let external_id =
2549 ExternalScrollId(self.fragment.unique_id(), state.pipeline_id.to_webrender());
2550 let new_clip_scroll_index = state.add_clip_scroll_node(ClipScrollNode {
2551 parent_index: self.clipping_and_scrolling().scrolling,
2552 clip: clip,
2553 content_rect: Rect::new(content_box.origin, content_size).to_layout(),
2554 node_type: ClipScrollNodeType::ScrollFrame(sensitivity, external_id),
2555 });
2556
2557 let new_clipping_and_scrolling = ClippingAndScrolling::simple(new_clip_scroll_index);
2558 self.base.clipping_and_scrolling = Some(new_clipping_and_scrolling);
2559 state.current_clipping_and_scrolling = new_clipping_and_scrolling;
2560 }
2561
2562 /// Adds a scroll root for a block to take the `clip` property into account
2563 /// per CSS 2.1 § 11.1.2.
setup_clip_scroll_node_for_css_clip( &mut self, state: &mut StackingContextCollectionState, preserved_state: &mut SavedStackingContextCollectionState, stacking_relative_border_box: &Rect<Au>, )2564 fn setup_clip_scroll_node_for_css_clip(
2565 &mut self,
2566 state: &mut StackingContextCollectionState,
2567 preserved_state: &mut SavedStackingContextCollectionState,
2568 stacking_relative_border_box: &Rect<Au>,
2569 ) {
2570 // Account for `clip` per CSS 2.1 § 11.1.2.
2571 let style_clip_rect = match self.fragment.style().get_effects().clip {
2572 Either::First(style_clip_rect) => style_clip_rect,
2573 _ => return,
2574 };
2575
2576 // CSS `clip` should only apply to position:absolute or positione:fixed elements.
2577 // CSS Masking Appendix A: "Applies to: Absolutely positioned elements."
2578 match self.positioning() {
2579 StylePosition::Absolute | StylePosition::Fixed => {},
2580 _ => return,
2581 }
2582
2583 let clip_origin = Point2D::new(
2584 stacking_relative_border_box.origin.x +
2585 style_clip_rect.left.map(Au::from).unwrap_or(Au(0)),
2586 stacking_relative_border_box.origin.y +
2587 style_clip_rect.top.map(Au::from).unwrap_or(Au(0)),
2588 );
2589 let right = style_clip_rect
2590 .right
2591 .map(Au::from)
2592 .unwrap_or(stacking_relative_border_box.size.width);
2593 let bottom = style_clip_rect
2594 .bottom
2595 .map(Au::from)
2596 .unwrap_or(stacking_relative_border_box.size.height);
2597 let clip_size = Size2D::new(right - clip_origin.x, bottom - clip_origin.y);
2598
2599 let clip_rect = Rect::new(clip_origin, clip_size);
2600 preserved_state.push_clip(state, &clip_rect, self.positioning());
2601
2602 let new_index = state.add_clip_scroll_node(ClipScrollNode {
2603 parent_index: self.clipping_and_scrolling().scrolling,
2604 clip: ClippingRegion::from_rect(clip_rect.to_layout()),
2605 content_rect: LayoutRect::zero(), // content_rect isn't important for clips.
2606 node_type: ClipScrollNodeType::Clip,
2607 });
2608
2609 let new_indices = ClippingAndScrolling::new(new_index, new_index);
2610 self.base.clipping_and_scrolling = Some(new_indices);
2611 state.current_clipping_and_scrolling = new_indices;
2612 }
2613
create_pseudo_stacking_context_for_block( &mut self, stacking_context_type: StackingContextType, parent_stacking_context_id: StackingContextId, parent_clipping_and_scrolling: ClippingAndScrolling, state: &mut StackingContextCollectionState, )2614 fn create_pseudo_stacking_context_for_block(
2615 &mut self,
2616 stacking_context_type: StackingContextType,
2617 parent_stacking_context_id: StackingContextId,
2618 parent_clipping_and_scrolling: ClippingAndScrolling,
2619 state: &mut StackingContextCollectionState,
2620 ) {
2621 let new_context = self.fragment.create_stacking_context(
2622 self.base.stacking_context_id,
2623 &self.base,
2624 ScrollPolicy::Scrollable,
2625 stacking_context_type,
2626 parent_clipping_and_scrolling,
2627 );
2628 state.add_stacking_context(parent_stacking_context_id, new_context);
2629
2630 self.base.collect_stacking_contexts_for_children(state);
2631
2632 let children = state
2633 .stacking_context_info
2634 .get_mut(&self.base.stacking_context_id)
2635 .map(|info| info.take_children());
2636 if let Some(children) = children {
2637 for child in children {
2638 if child.context_type == StackingContextType::PseudoFloat {
2639 state.add_stacking_context(self.base.stacking_context_id, child);
2640 } else {
2641 state.add_stacking_context(parent_stacking_context_id, child);
2642 }
2643 }
2644 }
2645 }
2646
create_real_stacking_context_for_block( &mut self, parent_stacking_context_id: StackingContextId, parent_clipping_and_scrolling: ClippingAndScrolling, state: &mut StackingContextCollectionState, )2647 fn create_real_stacking_context_for_block(
2648 &mut self,
2649 parent_stacking_context_id: StackingContextId,
2650 parent_clipping_and_scrolling: ClippingAndScrolling,
2651 state: &mut StackingContextCollectionState,
2652 ) {
2653 let scroll_policy = if self.is_fixed() {
2654 ScrollPolicy::Fixed
2655 } else {
2656 ScrollPolicy::Scrollable
2657 };
2658
2659 let stacking_context = self.fragment.create_stacking_context(
2660 self.base.stacking_context_id,
2661 &self.base,
2662 scroll_policy,
2663 StackingContextType::Real,
2664 parent_clipping_and_scrolling,
2665 );
2666
2667 state.add_stacking_context(parent_stacking_context_id, stacking_context);
2668 self.base.collect_stacking_contexts_for_children(state);
2669 }
2670
build_display_list_for_block_no_damage( &self, state: &mut DisplayListBuildState, border_painting_mode: BorderPaintingMode, )2671 fn build_display_list_for_block_no_damage(
2672 &self,
2673 state: &mut DisplayListBuildState,
2674 border_painting_mode: BorderPaintingMode,
2675 ) {
2676 let background_border_section = self.background_border_section();
2677
2678 state.processing_scrolling_overflow_element = self.has_scrolling_overflow();
2679 let stacking_relative_border_box =
2680 self.base.stacking_relative_border_box_for_display_list(&self.fragment);
2681 // Add the box that starts the block context.
2682 self.fragment.build_display_list_no_damage(
2683 state,
2684 stacking_relative_border_box,
2685 border_painting_mode,
2686 background_border_section,
2687 &self.base.clip,
2688 );
2689
2690 self.base
2691 .build_display_items_for_debugging_tint(state, self.fragment.node);
2692
2693 state.processing_scrolling_overflow_element = false;
2694 }
2695
build_display_list_for_block( &mut self, state: &mut DisplayListBuildState, border_painting_mode: BorderPaintingMode, )2696 fn build_display_list_for_block(
2697 &mut self,
2698 state: &mut DisplayListBuildState,
2699 border_painting_mode: BorderPaintingMode,
2700 ) {
2701 self.fragment.restyle_damage.remove(ServoRestyleDamage::REPAINT);
2702 self.build_display_list_for_block_no_damage(state, border_painting_mode);
2703 }
2704
build_display_list_for_background_if_applicable_with_background( &self, state: &mut DisplayListBuildState, background: &style_structs::Background, background_color: RGBA)2705 fn build_display_list_for_background_if_applicable_with_background(
2706 &self,
2707 state: &mut DisplayListBuildState,
2708 background: &style_structs::Background,
2709 background_color: RGBA) {
2710 let stacking_relative_border_box =
2711 self.base.stacking_relative_border_box_for_display_list(&self.fragment);
2712 let background_border_section = self.background_border_section();
2713
2714 self.fragment.build_display_list_for_background_if_applicable_with_background(
2715 state, self.fragment.style(), background, background_color,
2716 background_border_section, &stacking_relative_border_box
2717 )
2718
2719 }
2720
2721 #[inline]
stacking_context_type( &self, flags: StackingContextCollectionFlags, ) -> Option<StackingContextType>2722 fn stacking_context_type(
2723 &self,
2724 flags: StackingContextCollectionFlags,
2725 ) -> Option<StackingContextType>{
2726 if flags.contains(StackingContextCollectionFlags::NEVER_CREATES_STACKING_CONTEXT) {
2727 return None;
2728 }
2729
2730 if self.fragment.establishes_stacking_context() {
2731 return Some(StackingContextType::Real);
2732 }
2733
2734 if self.base.flags.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED) {
2735 return Some(StackingContextType::PseudoPositioned);
2736 }
2737
2738 if self.fragment.style.get_box().position != StylePosition::Static {
2739 return Some(StackingContextType::PseudoPositioned);
2740 }
2741
2742 if self.base.flags.is_float() {
2743 return Some(StackingContextType::PseudoFloat);
2744 }
2745
2746 None
2747 }
2748 }
2749
2750 pub trait InlineFlowDisplayListBuilding {
collect_stacking_contexts_for_inline(&mut self, state: &mut StackingContextCollectionState)2751 fn collect_stacking_contexts_for_inline(&mut self, state: &mut StackingContextCollectionState);
build_display_list_for_inline_fragment_at_index( &mut self, state: &mut DisplayListBuildState, index: usize, )2752 fn build_display_list_for_inline_fragment_at_index(
2753 &mut self,
2754 state: &mut DisplayListBuildState,
2755 index: usize,
2756 );
build_display_list_for_inline(&mut self, state: &mut DisplayListBuildState)2757 fn build_display_list_for_inline(&mut self, state: &mut DisplayListBuildState);
2758 }
2759
2760 impl InlineFlowDisplayListBuilding for InlineFlow {
collect_stacking_contexts_for_inline(&mut self, state: &mut StackingContextCollectionState)2761 fn collect_stacking_contexts_for_inline(&mut self, state: &mut StackingContextCollectionState) {
2762 self.base.stacking_context_id = state.current_stacking_context_id;
2763 self.base.clipping_and_scrolling = Some(state.current_clipping_and_scrolling);
2764 self.base.clip = state
2765 .clip_stack
2766 .last()
2767 .cloned()
2768 .unwrap_or_else(Rect::max_rect);
2769
2770 for fragment in self.fragments.fragments.iter_mut() {
2771 let previous_cb_clipping_and_scrolling = state.containing_block_clipping_and_scrolling;
2772 if establishes_containing_block_for_absolute(
2773 StackingContextCollectionFlags::empty(),
2774 fragment.style.get_box().position,
2775 ) {
2776 state.containing_block_clipping_and_scrolling =
2777 state.current_clipping_and_scrolling;
2778 }
2779
2780 if !fragment.collect_stacking_contexts_for_blocklike_fragment(state) {
2781 if fragment.establishes_stacking_context() {
2782 fragment.stacking_context_id =
2783 state.allocate_stacking_context_info(StackingContextType::Real);
2784
2785 let current_stacking_context_id = state.current_stacking_context_id;
2786 let stacking_context = fragment.create_stacking_context(
2787 fragment.stacking_context_id,
2788 &self.base,
2789 ScrollPolicy::Scrollable,
2790 StackingContextType::Real,
2791 state.current_clipping_and_scrolling,
2792 );
2793
2794 state.add_stacking_context(current_stacking_context_id, stacking_context);
2795 } else {
2796 fragment.stacking_context_id = state.current_stacking_context_id;
2797 }
2798 }
2799
2800 state.containing_block_clipping_and_scrolling = previous_cb_clipping_and_scrolling;
2801 }
2802 }
2803
build_display_list_for_inline_fragment_at_index( &mut self, state: &mut DisplayListBuildState, index: usize, )2804 fn build_display_list_for_inline_fragment_at_index(
2805 &mut self,
2806 state: &mut DisplayListBuildState,
2807 index: usize,
2808 ) {
2809 let fragment = self.fragments.fragments.get_mut(index).unwrap();
2810 let stacking_relative_border_box =
2811 self.base.stacking_relative_border_box_for_display_list(fragment);
2812 fragment.build_display_list(
2813 state,
2814 stacking_relative_border_box,
2815 BorderPaintingMode::Separate,
2816 DisplayListSection::Content,
2817 &self.base.clip,
2818 );
2819 }
2820
build_display_list_for_inline(&mut self, state: &mut DisplayListBuildState)2821 fn build_display_list_for_inline(&mut self, state: &mut DisplayListBuildState) {
2822 debug!(
2823 "Flow: building display list for {} inline fragments",
2824 self.fragments.len()
2825 );
2826
2827 // We iterate using an index here, because we want to avoid doing a doing
2828 // a double-borrow of self (one mutable for the method call and one immutable
2829 // for the self.fragments.fragment iterator itself).
2830 for index in 0..self.fragments.fragments.len() {
2831 let (establishes_stacking_context, stacking_context_id) = {
2832 let fragment = self.fragments.fragments.get(index).unwrap();
2833 (
2834 self.base.stacking_context_id != fragment.stacking_context_id,
2835 fragment.stacking_context_id,
2836 )
2837 };
2838
2839 let parent_stacking_context_id = state.current_stacking_context_id;
2840 if establishes_stacking_context {
2841 state.current_stacking_context_id = stacking_context_id;
2842 }
2843
2844 self.build_display_list_for_inline_fragment_at_index(state, index);
2845
2846 if establishes_stacking_context {
2847 state.current_stacking_context_id = parent_stacking_context_id
2848 }
2849 }
2850
2851 if !self.fragments.fragments.is_empty() {
2852 self.base
2853 .build_display_items_for_debugging_tint(state, self.fragments.fragments[0].node);
2854 }
2855 }
2856 }
2857
2858 pub trait ListItemFlowDisplayListBuilding {
build_display_list_for_list_item(&mut self, state: &mut DisplayListBuildState)2859 fn build_display_list_for_list_item(&mut self, state: &mut DisplayListBuildState);
2860 }
2861
2862 impl ListItemFlowDisplayListBuilding for ListItemFlow {
build_display_list_for_list_item(&mut self, state: &mut DisplayListBuildState)2863 fn build_display_list_for_list_item(&mut self, state: &mut DisplayListBuildState) {
2864 // Draw the marker, if applicable.
2865 for marker in &mut self.marker_fragments {
2866 let stacking_relative_border_box =
2867 self.block_flow.base.stacking_relative_border_box_for_display_list(marker);
2868 marker.build_display_list(
2869 state,
2870 stacking_relative_border_box,
2871 BorderPaintingMode::Separate,
2872 DisplayListSection::Content,
2873 &self.block_flow.base.clip,
2874 );
2875 }
2876
2877 // Draw the rest of the block.
2878 self.block_flow
2879 .build_display_list_for_block(state, BorderPaintingMode::Separate)
2880 }
2881 }
2882
2883 pub trait FlexFlowDisplayListBuilding {
build_display_list_for_flex(&mut self, state: &mut DisplayListBuildState)2884 fn build_display_list_for_flex(&mut self, state: &mut DisplayListBuildState);
2885 }
2886
2887 impl FlexFlowDisplayListBuilding for FlexFlow {
build_display_list_for_flex(&mut self, state: &mut DisplayListBuildState)2888 fn build_display_list_for_flex(&mut self, state: &mut DisplayListBuildState) {
2889 // Draw the rest of the block.
2890 self.as_mut_block()
2891 .build_display_list_for_block(state, BorderPaintingMode::Separate)
2892 }
2893 }
2894
2895 trait BaseFlowDisplayListBuilding {
build_display_items_for_debugging_tint( &self, state: &mut DisplayListBuildState, node: OpaqueNode, )2896 fn build_display_items_for_debugging_tint(
2897 &self,
2898 state: &mut DisplayListBuildState,
2899 node: OpaqueNode,
2900 );
2901 }
2902
2903 impl BaseFlowDisplayListBuilding for BaseFlow {
build_display_items_for_debugging_tint( &self, state: &mut DisplayListBuildState, node: OpaqueNode, )2904 fn build_display_items_for_debugging_tint(
2905 &self,
2906 state: &mut DisplayListBuildState,
2907 node: OpaqueNode,
2908 ) {
2909 if !opts::get().show_debug_parallel_layout {
2910 return;
2911 }
2912
2913 let thread_id = self.thread_id;
2914 let stacking_context_relative_bounds = Rect::new(
2915 self.stacking_relative_position.to_point(),
2916 self.position.size.to_physical(self.writing_mode),
2917 );
2918
2919 let mut color = THREAD_TINT_COLORS[thread_id as usize % THREAD_TINT_COLORS.len()];
2920 color.a = 1.0;
2921 let base = state.create_base_display_item(
2922 &stacking_context_relative_bounds.inflate(Au::from_px(2), Au::from_px(2)),
2923 LocalClip::from(self.clip.to_layout()),
2924 node,
2925 None,
2926 DisplayListSection::Content,
2927 );
2928 state.add_display_item(DisplayItem::Border(Box::new(BorderDisplayItem {
2929 base: base,
2930 border_widths: SideOffsets2D::new_all_same(Au::from_px(2)).to_layout(),
2931 details: BorderDetails::Normal(simple_normal_border(
2932 color,
2933 webrender_api::BorderStyle::Solid,
2934 )),
2935 })));
2936 }
2937 }
2938
2939 trait ComputedValuesCursorUtility {
get_cursor(&self, default_cursor: CursorKind) -> Option<CursorKind>2940 fn get_cursor(&self, default_cursor: CursorKind) -> Option<CursorKind>;
2941 }
2942
2943 impl ComputedValuesCursorUtility for ComputedValues {
2944 /// Gets the cursor to use given the specific ComputedValues. `default_cursor` specifies
2945 /// the cursor to use if `cursor` is `auto`. Typically, this will be `PointerCursor`, but for
2946 /// text display items it may be `TextCursor` or `VerticalTextCursor`.
2947 #[inline]
get_cursor(&self, default_cursor: CursorKind) -> Option<CursorKind>2948 fn get_cursor(&self, default_cursor: CursorKind) -> Option<CursorKind> {
2949 match (
2950 self.get_pointing().pointer_events,
2951 self.get_pointing().cursor,
2952 ) {
2953 (PointerEvents::None, _) => None,
2954 (PointerEvents::Auto, Cursor(CursorKind::Auto)) => Some(default_cursor),
2955 (PointerEvents::Auto, Cursor(cursor)) => Some(cursor),
2956 }
2957 }
2958 }
2959
2960 /// Adjusts `content_rect` as necessary for the given spread, and blur so that the resulting
2961 /// bounding rect contains all of a shadow's ink.
shadow_bounds(content_rect: &Rect<Au>, blur: Au, spread: Au) -> Rect<Au>2962 fn shadow_bounds(content_rect: &Rect<Au>, blur: Au, spread: Au) -> Rect<Au> {
2963 let inflation = spread + blur * BLUR_INFLATION_FACTOR;
2964 content_rect.inflate(inflation, inflation)
2965 }
2966
2967 /// Adjusts borders as appropriate to account for a fragment's status as the
2968 /// first or last fragment within the range of an element.
2969 ///
2970 /// Specifically, this function sets border widths to zero on the sides for
2971 /// which the fragment is not outermost.
modify_border_width_for_inline_sides( border_width: &mut LogicalMargin<Au>, inline_border_info: InlineNodeBorderInfo, )2972 fn modify_border_width_for_inline_sides(
2973 border_width: &mut LogicalMargin<Au>,
2974 inline_border_info: InlineNodeBorderInfo,
2975 ) {
2976 if !inline_border_info.is_first_fragment_of_element {
2977 border_width.inline_start = Au(0);
2978 }
2979
2980 if !inline_border_info.is_last_fragment_of_element {
2981 border_width.inline_end = Au(0);
2982 }
2983 }
2984
2985 /// Describes how to paint the borders.
2986 #[derive(Clone, Copy)]
2987 pub enum BorderPaintingMode<'a> {
2988 /// Paint borders separately (`border-collapse: separate`).
2989 Separate,
2990 /// Paint collapsed borders.
2991 Collapse(&'a CollapsedBordersForCell),
2992 /// Paint no borders.
2993 Hidden,
2994 }
2995
convert_text_run_to_glyphs( text_run: Arc<TextRun>, range: Range<ByteIndex>, mut origin: Point2D<Au>, ) -> Vec<GlyphInstance>2996 fn convert_text_run_to_glyphs(
2997 text_run: Arc<TextRun>,
2998 range: Range<ByteIndex>,
2999 mut origin: Point2D<Au>,
3000 ) -> Vec<GlyphInstance> {
3001 let mut glyphs = vec![];
3002
3003 for slice in text_run.natural_word_slices_in_visual_order(&range) {
3004 for glyph in slice.glyphs.iter_glyphs_for_byte_range(&slice.range) {
3005 let glyph_advance = if glyph.char_is_space() {
3006 glyph.advance() + text_run.extra_word_spacing
3007 } else {
3008 glyph.advance()
3009 };
3010 if !slice.glyphs.is_whitespace() {
3011 let glyph_offset = glyph.offset().unwrap_or(Point2D::zero());
3012 let point = origin + glyph_offset.to_vector();
3013 let glyph = GlyphInstance {
3014 index: glyph.id(),
3015 point: point.to_layout(),
3016 };
3017 glyphs.push(glyph);
3018 }
3019 origin.x += glyph_advance;
3020 }
3021 }
3022 return glyphs;
3023 }
3024
3025 pub struct IndexableTextItem {
3026 /// The placement of the text item on the plane.
3027 pub origin: Point2D<Au>,
3028 /// The text run.
3029 pub text_run: Arc<TextRun>,
3030 /// The range of text within the text run.
3031 pub range: Range<ByteIndex>,
3032 /// The position of the start of the baseline of this text.
3033 pub baseline_origin: Point2D<Au>,
3034 }
3035
3036 #[derive(Default)]
3037 pub struct IndexableText {
3038 inner: FnvHashMap<OpaqueNode, Vec<IndexableTextItem>>,
3039 }
3040
3041 impl IndexableText {
insert(&mut self, node: OpaqueNode, item: IndexableTextItem)3042 fn insert(&mut self, node: OpaqueNode, item: IndexableTextItem) {
3043 let entries = self.inner.entry(node).or_insert(Vec::new());
3044 entries.push(item);
3045 }
3046
get(&self, node: OpaqueNode) -> Option<&[IndexableTextItem]>3047 pub fn get(&self, node: OpaqueNode) -> Option<&[IndexableTextItem]> {
3048 self.inner.get(&node).map(|x| x.as_slice())
3049 }
3050
3051 // Returns the text index within a node for the point of interest.
text_index(&self, node: OpaqueNode, point_in_item: Point2D<Au>) -> Option<usize>3052 pub fn text_index(&self, node: OpaqueNode, point_in_item: Point2D<Au>) -> Option<usize> {
3053 let item = self.inner.get(&node)?;
3054 // TODO(#20020): access all elements
3055 let point = point_in_item + item[0].origin.to_vector();
3056 let offset = point - item[0].baseline_origin;
3057 Some(item[0].text_run.range_index_of_advance(&item[0].range, offset.x))
3058 }
3059 }
3060