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 //! The `Fragment` type, which represents the leaves of the layout tree.
6 
7 #![deny(unsafe_code)]
8 
9 use ServoArc;
10 use app_units::Au;
11 use canvas_traits::canvas::CanvasMsg;
12 use context::{LayoutContext, with_thread_local_font_context};
13 use display_list::ToLayout;
14 use euclid::{Point2D, Vector2D, Rect, Size2D};
15 use floats::ClearType;
16 use flow::{GetBaseFlow, ImmutableFlowUtils};
17 use flow_ref::FlowRef;
18 use gfx;
19 use gfx::display_list::{BLUR_INFLATION_FACTOR, OpaqueNode};
20 use gfx::text::glyph::ByteIndex;
21 use gfx::text::text_run::{TextRun, TextRunSlice};
22 use gfx_traits::StackingContextId;
23 use inline::{InlineFragmentNodeFlags, InlineFragmentContext, InlineFragmentNodeInfo};
24 use inline::{InlineMetrics, LineMetrics};
25 use ipc_channel::ipc::IpcSender;
26 #[cfg(debug_assertions)]
27 use layout_debug;
28 use model::{self, IntrinsicISizes, IntrinsicISizesContribution, MaybeAuto, SizeConstraint};
29 use model::style_length;
30 use msg::constellation_msg::{BrowsingContextId, PipelineId};
31 use net_traits::image::base::{Image, ImageMetadata};
32 use net_traits::image_cache::{ImageOrMetadataAvailable, UsePlaceholder};
33 use range::*;
34 use script_layout_interface::{HTMLCanvasData, HTMLCanvasDataSource};
35 use script_layout_interface::SVGSVGData;
36 use script_layout_interface::wrapper_traits::{PseudoElementType, ThreadSafeLayoutElement, ThreadSafeLayoutNode};
37 use serde::ser::{Serialize, SerializeStruct, Serializer};
38 use servo_url::ServoUrl;
39 use std::{f32, fmt};
40 use std::borrow::ToOwned;
41 use std::cmp::{Ordering, max, min};
42 use std::collections::LinkedList;
43 use std::sync::{Arc, Mutex};
44 use style::computed_values::border_collapse::T as BorderCollapse;
45 use style::computed_values::box_sizing::T as BoxSizing;
46 use style::computed_values::clear::T as Clear;
47 use style::computed_values::color::T as Color;
48 use style::computed_values::display::T as Display;
49 use style::computed_values::mix_blend_mode::T as MixBlendMode;
50 use style::computed_values::overflow_wrap::T as OverflowWrap;
51 use style::computed_values::overflow_x::T as StyleOverflow;
52 use style::computed_values::position::T as Position;
53 use style::computed_values::text_decoration_line::T as TextDecorationLine;
54 use style::computed_values::transform_style::T as TransformStyle;
55 use style::computed_values::white_space::T as WhiteSpace;
56 use style::computed_values::word_break::T as WordBreak;
57 use style::logical_geometry::{Direction, LogicalMargin, LogicalRect, LogicalSize, WritingMode};
58 use style::properties::ComputedValues;
59 use style::selector_parser::RestyleDamage;
60 use style::servo::restyle_damage::ServoRestyleDamage;
61 use style::str::char_is_whitespace;
62 use style::values::computed::{Length, LengthOrPercentage, LengthOrPercentageOrAuto};
63 use style::values::computed::counters::ContentItem;
64 use style::values::generics::box_::{Perspective, VerticalAlign};
65 use style::values::generics::transform;
66 use text;
67 use text::TextRunScanner;
68 use webrender_api::{self, LayoutTransform};
69 use wrapper::ThreadSafeLayoutNodeHelpers;
70 
71 // From gfxFontConstants.h in Firefox.
72 static FONT_SUBSCRIPT_OFFSET_RATIO: f32 = 0.20;
73 static FONT_SUPERSCRIPT_OFFSET_RATIO: f32 = 0.34;
74 
75 // https://drafts.csswg.org/css-images/#default-object-size
76 static DEFAULT_REPLACED_WIDTH: i32 = 300;
77 static DEFAULT_REPLACED_HEIGHT: i32 = 150;
78 
79 /// Fragments (`struct Fragment`) are the leaves of the layout tree. They cannot position
80 /// themselves. In general, fragments do not have a simple correspondence with CSS fragments in the
81 /// specification:
82 ///
83 /// * Several fragments may correspond to the same CSS box or DOM node. For example, a CSS text box
84 /// broken across two lines is represented by two fragments.
85 ///
86 /// * Some CSS fragments are not created at all, such as some anonymous block fragments induced by
87 ///   inline fragments with block-level sibling fragments. In that case, Servo uses an `InlineFlow`
88 ///   with `BlockFlow` siblings; the `InlineFlow` is block-level, but not a block container. It is
89 ///   positioned as if it were a block fragment, but its children are positioned according to
90 ///   inline flow.
91 ///
92 /// A `SpecificFragmentInfo::Generic` is an empty fragment that contributes only borders, margins,
93 /// padding, and backgrounds. It is analogous to a CSS nonreplaced content box.
94 ///
95 /// A fragment's type influences how its styles are interpreted during layout. For example,
96 /// replaced content such as images are resized differently from tables, text, or other content.
97 /// Different types of fragments may also contain custom data; for example, text fragments contain
98 /// text.
99 ///
100 /// Do not add fields to this structure unless they're really really mega necessary! Fragments get
101 /// moved around a lot and thus their size impacts performance of layout quite a bit.
102 ///
103 /// FIXME(#2260, pcwalton): This can be slimmed down some by (at least) moving `inline_context`
104 /// to be on `InlineFlow` only.
105 #[derive(Clone)]
106 pub struct Fragment {
107     /// An opaque reference to the DOM node that this `Fragment` originates from.
108     pub node: OpaqueNode,
109 
110     /// The CSS style of this fragment.
111     pub style: ServoArc<ComputedValues>,
112 
113     /// The CSS style of this fragment when it's selected
114     pub selected_style: ServoArc<ComputedValues>,
115 
116     /// The position of this fragment relative to its owning flow. The size includes padding and
117     /// border, but not margin.
118     ///
119     /// NB: This does not account for relative positioning.
120     /// NB: Collapsed borders are not included in this.
121     pub border_box: LogicalRect<Au>,
122 
123     /// The sum of border and padding; i.e. the distance from the edge of the border box to the
124     /// content edge of the fragment.
125     pub border_padding: LogicalMargin<Au>,
126 
127     /// The margin of the content box.
128     pub margin: LogicalMargin<Au>,
129 
130     /// Info specific to the kind of fragment. Keep this enum small.
131     pub specific: SpecificFragmentInfo,
132 
133     /// Holds the style context information for fragments that are part of an inline formatting
134     /// context.
135     pub inline_context: Option<InlineFragmentContext>,
136 
137     /// How damaged this fragment is since last reflow.
138     pub restyle_damage: RestyleDamage,
139 
140     /// The pseudo-element that this fragment represents.
141     pub pseudo: PseudoElementType,
142 
143     /// Various flags for this fragment.
144     pub flags: FragmentFlags,
145 
146     /// A debug ID that is consistent for the life of this fragment (via transform etc).
147     /// This ID should not be considered stable across multiple layouts or fragment
148     /// manipulations.
149     debug_id: DebugId,
150 
151     /// The ID of the StackingContext that contains this fragment. This is initialized
152     /// to 0, but it assigned during the collect_stacking_contexts phase of display
153     /// list construction.
154     pub stacking_context_id: StackingContextId,
155 }
156 
157 impl Serialize for Fragment {
serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error>158     fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
159         let mut serializer = serializer.serialize_struct("fragment", 3)?;
160         serializer.serialize_field("id", &self.debug_id)?;
161         serializer.serialize_field("border_box", &self.border_box)?;
162         serializer.serialize_field("margin", &self.margin)?;
163         serializer.end()
164     }
165 }
166 
167 /// Info specific to the kind of fragment.
168 ///
169 /// Keep this enum small. As in, no more than one word. Or pcwalton will yell at you.
170 #[derive(Clone)]
171 pub enum SpecificFragmentInfo {
172     Generic,
173 
174     /// A piece of generated content that cannot be resolved into `ScannedText` until the generated
175     /// content resolution phase (e.g. an ordered list item marker).
176     GeneratedContent(Box<GeneratedContentInfo>),
177 
178     Iframe(IframeFragmentInfo),
179     Image(Box<ImageFragmentInfo>),
180     Canvas(Box<CanvasFragmentInfo>),
181     Svg(Box<SvgFragmentInfo>),
182 
183     /// A hypothetical box (see CSS 2.1 § 10.3.7) for an absolutely-positioned block that was
184     /// declared with `display: inline;`.
185     InlineAbsoluteHypothetical(InlineAbsoluteHypotheticalFragmentInfo),
186 
187     InlineBlock(InlineBlockFragmentInfo),
188 
189     /// An inline fragment that establishes an absolute containing block for its descendants (i.e.
190     /// a positioned inline fragment).
191     InlineAbsolute(InlineAbsoluteFragmentInfo),
192 
193     ScannedText(Box<ScannedTextFragmentInfo>),
194     Table,
195     TableCell,
196     TableColumn(TableColumnFragmentInfo),
197     TableRow,
198     TableWrapper,
199     Multicol,
200     MulticolColumn,
201     UnscannedText(Box<UnscannedTextFragmentInfo>),
202 
203     /// A container for a fragment that got truncated by text-overflow.
204     /// "Totally truncated fragments" are not rendered at all.
205     /// Text fragments may be partially truncated (in which case this renders like a text fragment).
206     /// Other fragments can only be totally truncated or not truncated at all.
207     TruncatedFragment(Box<TruncatedFragmentInfo>),
208 }
209 
210 impl SpecificFragmentInfo {
restyle_damage(&self) -> RestyleDamage211     fn restyle_damage(&self) -> RestyleDamage {
212         let flow =
213             match *self {
214                 SpecificFragmentInfo::Canvas(_) |
215                 SpecificFragmentInfo::GeneratedContent(_) |
216                 SpecificFragmentInfo::Iframe(_) |
217                 SpecificFragmentInfo::Image(_) |
218                 SpecificFragmentInfo::ScannedText(_) |
219                 SpecificFragmentInfo::Svg(_) |
220                 SpecificFragmentInfo::Table |
221                 SpecificFragmentInfo::TableCell |
222                 SpecificFragmentInfo::TableColumn(_) |
223                 SpecificFragmentInfo::TableRow |
224                 SpecificFragmentInfo::TableWrapper |
225                 SpecificFragmentInfo::Multicol |
226                 SpecificFragmentInfo::MulticolColumn |
227                 SpecificFragmentInfo::UnscannedText(_) |
228                 SpecificFragmentInfo::TruncatedFragment(_) |
229                 SpecificFragmentInfo::Generic => return RestyleDamage::empty(),
230                 SpecificFragmentInfo::InlineAbsoluteHypothetical(ref info) => &info.flow_ref,
231                 SpecificFragmentInfo::InlineAbsolute(ref info) => &info.flow_ref,
232                 SpecificFragmentInfo::InlineBlock(ref info) => &info.flow_ref,
233             };
234 
235         flow.base().restyle_damage
236     }
237 
get_type(&self) -> &'static str238     pub fn get_type(&self) -> &'static str {
239         match *self {
240             SpecificFragmentInfo::Canvas(_) => "SpecificFragmentInfo::Canvas",
241             SpecificFragmentInfo::Generic => "SpecificFragmentInfo::Generic",
242             SpecificFragmentInfo::GeneratedContent(_) => "SpecificFragmentInfo::GeneratedContent",
243             SpecificFragmentInfo::Iframe(_) => "SpecificFragmentInfo::Iframe",
244             SpecificFragmentInfo::Image(_) => "SpecificFragmentInfo::Image",
245             SpecificFragmentInfo::InlineAbsolute(_) => "SpecificFragmentInfo::InlineAbsolute",
246             SpecificFragmentInfo::InlineAbsoluteHypothetical(_) => {
247                 "SpecificFragmentInfo::InlineAbsoluteHypothetical"
248             }
249             SpecificFragmentInfo::InlineBlock(_) => "SpecificFragmentInfo::InlineBlock",
250             SpecificFragmentInfo::ScannedText(_) => "SpecificFragmentInfo::ScannedText",
251             SpecificFragmentInfo::Svg(_) => "SpecificFragmentInfo::Svg",
252             SpecificFragmentInfo::Table => "SpecificFragmentInfo::Table",
253             SpecificFragmentInfo::TableCell => "SpecificFragmentInfo::TableCell",
254             SpecificFragmentInfo::TableColumn(_) => "SpecificFragmentInfo::TableColumn",
255             SpecificFragmentInfo::TableRow => "SpecificFragmentInfo::TableRow",
256             SpecificFragmentInfo::TableWrapper => "SpecificFragmentInfo::TableWrapper",
257             SpecificFragmentInfo::Multicol => "SpecificFragmentInfo::Multicol",
258             SpecificFragmentInfo::MulticolColumn => "SpecificFragmentInfo::MulticolColumn",
259             SpecificFragmentInfo::UnscannedText(_) => "SpecificFragmentInfo::UnscannedText",
260             SpecificFragmentInfo::TruncatedFragment(_) => "SpecificFragmentInfo::TruncatedFragment"
261         }
262     }
263 }
264 
265 impl fmt::Debug for SpecificFragmentInfo {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result266     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
267         match *self {
268             SpecificFragmentInfo::ScannedText(ref info) => write!(f, "{:?}", info.text()),
269             SpecificFragmentInfo::UnscannedText(ref info) => write!(f, "{:?}", info.text),
270             _ => Ok(())
271         }
272     }
273 }
274 
275 /// Information for generated content.
276 #[derive(Clone)]
277 pub enum GeneratedContentInfo {
278     ListItem,
279     ContentItem(ContentItem),
280     /// Placeholder for elements with generated content that did not generate any fragments.
281     Empty,
282 }
283 
284 /// A hypothetical box (see CSS 2.1 § 10.3.7) for an absolutely-positioned block that was declared
285 /// with `display: inline;`.
286 ///
287 /// FIXME(pcwalton): Stop leaking this `FlowRef` to layout; that is not memory safe because layout
288 /// can clone it.
289 #[derive(Clone)]
290 pub struct InlineAbsoluteHypotheticalFragmentInfo {
291     pub flow_ref: FlowRef,
292 }
293 
294 impl InlineAbsoluteHypotheticalFragmentInfo {
new(flow_ref: FlowRef) -> InlineAbsoluteHypotheticalFragmentInfo295     pub fn new(flow_ref: FlowRef) -> InlineAbsoluteHypotheticalFragmentInfo {
296         InlineAbsoluteHypotheticalFragmentInfo {
297             flow_ref: flow_ref,
298         }
299     }
300 }
301 
302 /// A fragment that represents an inline-block element.
303 ///
304 /// FIXME(pcwalton): Stop leaking this `FlowRef` to layout; that is not memory safe because layout
305 /// can clone it.
306 #[derive(Clone)]
307 pub struct InlineBlockFragmentInfo {
308     pub flow_ref: FlowRef,
309 }
310 
311 impl InlineBlockFragmentInfo {
new(flow_ref: FlowRef) -> InlineBlockFragmentInfo312     pub fn new(flow_ref: FlowRef) -> InlineBlockFragmentInfo {
313         InlineBlockFragmentInfo {
314             flow_ref: flow_ref,
315         }
316     }
317 }
318 
319 /// An inline fragment that establishes an absolute containing block for its descendants (i.e.
320 /// a positioned inline fragment).
321 ///
322 /// FIXME(pcwalton): Stop leaking this `FlowRef` to layout; that is not memory safe because layout
323 /// can clone it.
324 #[derive(Clone)]
325 pub struct InlineAbsoluteFragmentInfo {
326     pub flow_ref: FlowRef,
327 }
328 
329 impl InlineAbsoluteFragmentInfo {
new(flow_ref: FlowRef) -> InlineAbsoluteFragmentInfo330     pub fn new(flow_ref: FlowRef) -> InlineAbsoluteFragmentInfo {
331         InlineAbsoluteFragmentInfo {
332             flow_ref: flow_ref,
333         }
334     }
335 }
336 
337 #[derive(Clone)]
338 pub enum CanvasFragmentSource {
339     WebGL(webrender_api::ImageKey),
340     Image(Option<Arc<Mutex<IpcSender<CanvasMsg>>>>)
341 }
342 
343 #[derive(Clone)]
344 pub struct CanvasFragmentInfo {
345     pub source: CanvasFragmentSource,
346     pub dom_width: Au,
347     pub dom_height: Au,
348 }
349 
350 impl CanvasFragmentInfo {
new(data: HTMLCanvasData) -> CanvasFragmentInfo351     pub fn new(data: HTMLCanvasData) -> CanvasFragmentInfo {
352         let source = match data.source {
353             HTMLCanvasDataSource::WebGL(texture_id) => {
354                 CanvasFragmentSource::WebGL(texture_id)
355             },
356             HTMLCanvasDataSource::Image(ipc_sender) => {
357                 CanvasFragmentSource::Image(ipc_sender.map(|renderer| Arc::new(Mutex::new(renderer))))
358             }
359         };
360 
361         CanvasFragmentInfo {
362             source: source,
363             dom_width: Au::from_px(data.width as i32),
364             dom_height: Au::from_px(data.height as i32),
365         }
366     }
367 }
368 
369 #[derive(Clone)]
370 pub struct SvgFragmentInfo {
371     pub dom_width: Au,
372     pub dom_height: Au,
373 }
374 
375 impl SvgFragmentInfo {
new(data: SVGSVGData) -> SvgFragmentInfo376     pub fn new(data: SVGSVGData) -> SvgFragmentInfo {
377         SvgFragmentInfo {
378             dom_width: Au::from_px(data.width as i32),
379             dom_height: Au::from_px(data.height as i32),
380         }
381     }
382 }
383 
384 
385 /// A fragment that represents a replaced content image and its accompanying borders, shadows, etc.
386 #[derive(Clone)]
387 pub struct ImageFragmentInfo {
388     pub image: Option<Arc<Image>>,
389     pub metadata: Option<ImageMetadata>,
390 }
391 
392 impl ImageFragmentInfo {
393     /// Creates a new image fragment from the given URL and local image cache.
394     ///
395     /// FIXME(pcwalton): The fact that image fragments store the cache in the fragment makes little
396     /// sense to me.
new<N: ThreadSafeLayoutNode>(url: Option<ServoUrl>, node: &N, layout_context: &LayoutContext) -> ImageFragmentInfo397     pub fn new<N: ThreadSafeLayoutNode>(url: Option<ServoUrl>,
398                                         node: &N,
399                                         layout_context: &LayoutContext)
400                -> ImageFragmentInfo {
401         let image_or_metadata = url.and_then(|url| {
402             layout_context.get_or_request_image_or_meta(node.opaque(),
403                                                         url,
404                                                         UsePlaceholder::Yes)
405         });
406 
407         let (image, metadata) = match image_or_metadata {
408             Some(ImageOrMetadataAvailable::ImageAvailable(i, _)) => {
409                 (Some(i.clone()), Some(ImageMetadata { height: i.height, width: i.width } ))
410             }
411             Some(ImageOrMetadataAvailable::MetadataAvailable(m)) => {
412                 (None, Some(m))
413             }
414             None => {
415                 (None, None)
416             }
417         };
418 
419         ImageFragmentInfo {
420             image: image,
421             metadata: metadata,
422         }
423     }
424 }
425 
426 /// A fragment that represents an inline frame (iframe). This stores the frame ID so that the
427 /// size of this iframe can be communicated via the constellation to the iframe's own layout thread.
428 #[derive(Clone)]
429 pub struct IframeFragmentInfo {
430     /// The frame ID of this iframe. None if there is no nested browsing context.
431     pub browsing_context_id: Option<BrowsingContextId>,
432     /// The pipelineID of this iframe. None if there is no nested browsing context.
433     pub pipeline_id: Option<PipelineId>,
434 }
435 
436 impl IframeFragmentInfo {
437     /// Creates the information specific to an iframe fragment.
new<N: ThreadSafeLayoutNode>(node: &N) -> IframeFragmentInfo438     pub fn new<N: ThreadSafeLayoutNode>(node: &N) -> IframeFragmentInfo {
439         let browsing_context_id = node.iframe_browsing_context_id();
440         let pipeline_id = node.iframe_pipeline_id();
441         IframeFragmentInfo {
442             browsing_context_id: browsing_context_id,
443             pipeline_id: pipeline_id,
444         }
445     }
446 }
447 
448 /// A scanned text fragment represents a single run of text with a distinct style. A `TextFragment`
449 /// may be split into two or more fragments across line breaks. Several `TextFragment`s may
450 /// correspond to a single DOM text node. Split text fragments are implemented by referring to
451 /// subsets of a single `TextRun` object.
452 #[derive(Clone)]
453 pub struct ScannedTextFragmentInfo {
454     /// The text run that this represents.
455     pub run: Arc<TextRun>,
456 
457     /// The intrinsic size of the text fragment.
458     pub content_size: LogicalSize<Au>,
459 
460     /// The byte offset of the insertion point, if any.
461     pub insertion_point: Option<ByteIndex>,
462 
463     /// The range within the above text run that this represents.
464     pub range: Range<ByteIndex>,
465 
466     /// The endpoint of the above range, including whitespace that was stripped out. This exists
467     /// so that we can restore the range to its original value (before line breaking occurred) when
468     /// performing incremental reflow.
469     pub range_end_including_stripped_whitespace: ByteIndex,
470 
471     pub flags: ScannedTextFlags,
472 }
473 
474 bitflags! {
475     pub struct ScannedTextFlags: u8 {
476         /// Whether a line break is required after this fragment if wrapping on newlines (e.g. if
477         /// `white-space: pre` is in effect).
478         const REQUIRES_LINE_BREAK_AFTERWARD_IF_WRAPPING_ON_NEWLINES = 0x01;
479 
480         /// Is this fragment selected?
481         const SELECTED = 0x02;
482 
483         /// Suppress line breaking between this and the previous fragment
484         ///
485         /// This handles cases like Foo<span>bar</span>
486         const SUPPRESS_LINE_BREAK_BEFORE = 0x04;
487     }
488 }
489 
490 impl ScannedTextFragmentInfo {
491     /// Creates the information specific to a scanned text fragment from a range and a text run.
new(run: Arc<TextRun>, range: Range<ByteIndex>, content_size: LogicalSize<Au>, insertion_point: Option<ByteIndex>, flags: ScannedTextFlags) -> ScannedTextFragmentInfo492     pub fn new(run: Arc<TextRun>,
493                range: Range<ByteIndex>,
494                content_size: LogicalSize<Au>,
495                insertion_point: Option<ByteIndex>,
496                flags: ScannedTextFlags)
497                -> ScannedTextFragmentInfo {
498         ScannedTextFragmentInfo {
499             run: run,
500             range: range,
501             insertion_point: insertion_point,
502             content_size: content_size,
503             range_end_including_stripped_whitespace: range.end(),
504             flags: flags,
505         }
506     }
507 
text(&self) -> &str508     pub fn text(&self) -> &str {
509         &self.run.text[self.range.begin().to_usize() .. self.range.end().to_usize()]
510     }
511 
requires_line_break_afterward_if_wrapping_on_newlines(&self) -> bool512     pub fn requires_line_break_afterward_if_wrapping_on_newlines(&self) -> bool {
513         self.flags.contains(ScannedTextFlags::REQUIRES_LINE_BREAK_AFTERWARD_IF_WRAPPING_ON_NEWLINES)
514     }
515 
selected(&self) -> bool516     pub fn selected(&self) -> bool {
517         self.flags.contains(ScannedTextFlags::SELECTED)
518     }
519 }
520 
521 /// Describes how to split a fragment. This is used during line breaking as part of the return
522 /// value of `find_split_info_for_inline_size()`.
523 #[derive(Clone, Debug)]
524 pub struct SplitInfo {
525     // TODO(bjz): this should only need to be a single character index, but both values are
526     // currently needed for splitting in the `inline::try_append_*` functions.
527     pub range: Range<ByteIndex>,
528     pub inline_size: Au,
529 }
530 
531 impl SplitInfo {
new(range: Range<ByteIndex>, info: &ScannedTextFragmentInfo) -> SplitInfo532     fn new(range: Range<ByteIndex>, info: &ScannedTextFragmentInfo) -> SplitInfo {
533         let inline_size = info.run.advance_for_range(&range);
534         SplitInfo {
535             range: range,
536             inline_size: inline_size,
537         }
538     }
539 }
540 
541 /// Describes how to split a fragment into two. This contains up to two `SplitInfo`s.
542 pub struct SplitResult {
543     /// The part of the fragment that goes on the first line.
544     pub inline_start: Option<SplitInfo>,
545     /// The part of the fragment that goes on the second line.
546     pub inline_end: Option<SplitInfo>,
547     /// The text run which is being split.
548     pub text_run: Arc<TextRun>,
549 }
550 
551 /// Describes how a fragment should be truncated.
552 struct TruncationResult {
553     /// The part of the fragment remaining after truncation.
554     split: SplitInfo,
555     /// The text run which is being truncated.
556     text_run: Arc<TextRun>,
557 }
558 
559 /// Data for an unscanned text fragment. Unscanned text fragments are the results of flow
560 /// construction that have not yet had their inline-size determined.
561 #[derive(Clone)]
562 pub struct UnscannedTextFragmentInfo {
563     /// The text inside the fragment.
564     pub text: Box<str>,
565 
566     /// The selected text range.  An empty range represents the insertion point.
567     pub selection: Option<Range<ByteIndex>>,
568 }
569 
570 impl UnscannedTextFragmentInfo {
571     /// Creates a new instance of `UnscannedTextFragmentInfo` from the given text.
572     #[inline]
new(text: Box<str>, selection: Option<Range<ByteIndex>>) -> UnscannedTextFragmentInfo573     pub fn new(text: Box<str>, selection: Option<Range<ByteIndex>>) -> UnscannedTextFragmentInfo {
574         UnscannedTextFragmentInfo {
575             text: text,
576             selection: selection,
577         }
578     }
579 }
580 
581 /// A fragment that represents a table column.
582 #[derive(Clone, Copy)]
583 pub struct TableColumnFragmentInfo {
584     /// the number of columns a <col> element should span
585     pub span: u32,
586 }
587 
588 impl TableColumnFragmentInfo {
589     /// Create the information specific to an table column fragment.
new<N: ThreadSafeLayoutNode>(node: &N) -> TableColumnFragmentInfo590     pub fn new<N: ThreadSafeLayoutNode>(node: &N) -> TableColumnFragmentInfo {
591         let element = node.as_element().unwrap();
592         let span = element.get_attr(&ns!(), &local_name!("span"))
593                           .and_then(|string| string.parse().ok())
594                           .unwrap_or(0);
595         TableColumnFragmentInfo {
596             span: span,
597         }
598     }
599 }
600 
601 /// A wrapper for fragments that have been truncated by the `text-overflow` property.
602 /// This may have an associated text node, or, if the fragment was completely truncated,
603 /// it may act as an invisible marker for incremental reflow.
604 #[derive(Clone)]
605 pub struct TruncatedFragmentInfo {
606     pub text_info: Option<ScannedTextFragmentInfo>,
607     pub full: Fragment,
608 }
609 
610 impl Fragment {
611     /// Constructs a new `Fragment` instance.
new<N: ThreadSafeLayoutNode>(node: &N, specific: SpecificFragmentInfo, ctx: &LayoutContext) -> Fragment612     pub fn new<N: ThreadSafeLayoutNode>(node: &N, specific: SpecificFragmentInfo, ctx: &LayoutContext) -> Fragment {
613         let shared_context = ctx.shared_context();
614         let style = node.style(shared_context);
615         let writing_mode = style.writing_mode;
616 
617         let mut restyle_damage = node.restyle_damage();
618         restyle_damage.remove(ServoRestyleDamage::RECONSTRUCT_FLOW);
619 
620         Fragment {
621             node: node.opaque(),
622             style: style,
623             selected_style: node.selected_style(),
624             restyle_damage: restyle_damage,
625             border_box: LogicalRect::zero(writing_mode),
626             border_padding: LogicalMargin::zero(writing_mode),
627             margin: LogicalMargin::zero(writing_mode),
628             specific: specific,
629             inline_context: None,
630             pseudo: node.get_pseudo_element_type(),
631             flags: FragmentFlags::empty(),
632             debug_id: DebugId::new(),
633             stacking_context_id: StackingContextId::root(),
634         }
635     }
636 
637     /// Constructs a new `Fragment` instance from an opaque node.
from_opaque_node_and_style(node: OpaqueNode, pseudo: PseudoElementType, style: ServoArc<ComputedValues>, selected_style: ServoArc<ComputedValues>, mut restyle_damage: RestyleDamage, specific: SpecificFragmentInfo) -> Fragment638     pub fn from_opaque_node_and_style(node: OpaqueNode,
639                                       pseudo: PseudoElementType,
640                                       style: ServoArc<ComputedValues>,
641                                       selected_style: ServoArc<ComputedValues>,
642                                       mut restyle_damage: RestyleDamage,
643                                       specific: SpecificFragmentInfo)
644                                       -> Fragment {
645         let writing_mode = style.writing_mode;
646 
647         restyle_damage.remove(ServoRestyleDamage::RECONSTRUCT_FLOW);
648 
649         Fragment {
650             node: node,
651             style: style,
652             selected_style: selected_style,
653             restyle_damage: restyle_damage,
654             border_box: LogicalRect::zero(writing_mode),
655             border_padding: LogicalMargin::zero(writing_mode),
656             margin: LogicalMargin::zero(writing_mode),
657             specific: specific,
658             inline_context: None,
659             pseudo: pseudo,
660             flags: FragmentFlags::empty(),
661             debug_id: DebugId::new(),
662             stacking_context_id: StackingContextId::root(),
663         }
664     }
665 
666     /// Creates an anonymous fragment just like this one but with the given style and fragment
667     /// type. For the new anonymous fragment, layout-related values (border box, etc.) are reset to
668     /// initial values.
create_similar_anonymous_fragment(&self, style: ServoArc<ComputedValues>, specific: SpecificFragmentInfo) -> Fragment669     pub fn create_similar_anonymous_fragment(&self,
670                                              style: ServoArc<ComputedValues>,
671                                              specific: SpecificFragmentInfo)
672                                              -> Fragment {
673         let writing_mode = style.writing_mode;
674         Fragment {
675             node: self.node,
676             style: style,
677             selected_style: self.selected_style.clone(),
678             restyle_damage: self.restyle_damage,
679             border_box: LogicalRect::zero(writing_mode),
680             border_padding: LogicalMargin::zero(writing_mode),
681             margin: LogicalMargin::zero(writing_mode),
682             specific: specific,
683             inline_context: None,
684             pseudo: self.pseudo,
685             flags: FragmentFlags::empty(),
686             debug_id: DebugId::new(),
687             stacking_context_id: StackingContextId::root(),
688         }
689     }
690 
691     /// Transforms this fragment into another fragment of the given type, with the given size,
692     /// preserving all the other data.
transform(&self, size: LogicalSize<Au>, info: SpecificFragmentInfo) -> Fragment693     pub fn transform(&self, size: LogicalSize<Au>, info: SpecificFragmentInfo)
694                      -> Fragment {
695         let new_border_box = LogicalRect::from_point_size(self.style.writing_mode,
696                                                           self.border_box.start,
697                                                           size);
698 
699         let mut restyle_damage = RestyleDamage::rebuild_and_reflow();
700         restyle_damage.remove(ServoRestyleDamage::RECONSTRUCT_FLOW);
701 
702         Fragment {
703             node: self.node,
704             style: self.style.clone(),
705             selected_style: self.selected_style.clone(),
706             restyle_damage: restyle_damage,
707             border_box: new_border_box,
708             border_padding: self.border_padding,
709             margin: self.margin,
710             specific: info,
711             inline_context: self.inline_context.clone(),
712             pseudo: self.pseudo.clone(),
713             flags: FragmentFlags::empty(),
714             debug_id: self.debug_id.clone(),
715             stacking_context_id: StackingContextId::root(),
716         }
717     }
718 
719     /// Transforms this fragment using the given `SplitInfo`, preserving all the other data.
720     ///
721     /// If this is the first half of a split, `first` is true
transform_with_split_info(&self, split: &SplitInfo, text_run: Arc<TextRun>, first: bool) -> Fragment722     pub fn transform_with_split_info(&self, split: &SplitInfo, text_run: Arc<TextRun>,
723                                      first: bool) -> Fragment {
724         let size = LogicalSize::new(self.style.writing_mode,
725                                     split.inline_size,
726                                     self.border_box.size.block);
727         // Preserve the insertion point if it is in this fragment's range or it is at line end.
728         let (mut flags, insertion_point) = match self.specific {
729             SpecificFragmentInfo::ScannedText(ref info) => {
730                 match info.insertion_point {
731                     Some(index) if split.range.contains(index) => (info.flags, info.insertion_point),
732                     Some(index) if index == ByteIndex(text_run.text.chars().count() as isize - 1) &&
733                         index == split.range.end() => (info.flags, info.insertion_point),
734                     _ => (info.flags, None)
735                 }
736             },
737             _ => (ScannedTextFlags::empty(), None)
738         };
739 
740         if !first {
741             flags.set(ScannedTextFlags::SUPPRESS_LINE_BREAK_BEFORE, false);
742         }
743 
744         let info = Box::new(ScannedTextFragmentInfo::new(
745             text_run,
746             split.range,
747             size,
748             insertion_point,
749             flags,
750         ));
751         self.transform(size, SpecificFragmentInfo::ScannedText(info))
752     }
753 
754     /// Transforms this fragment into an ellipsis fragment, preserving all the other data.
transform_into_ellipsis(&self, layout_context: &LayoutContext, text_overflow_string: String) -> Fragment755     pub fn transform_into_ellipsis(&self,
756                                    layout_context: &LayoutContext,
757                                    text_overflow_string: String)
758                                    -> Fragment {
759         let mut unscanned_ellipsis_fragments = LinkedList::new();
760         let mut ellipsis_fragment = self.transform(
761             self.border_box.size,
762             SpecificFragmentInfo::UnscannedText(
763                 Box::new(UnscannedTextFragmentInfo::new(text_overflow_string.into_boxed_str(), None))
764             )
765         );
766         unscanned_ellipsis_fragments.push_back(ellipsis_fragment);
767         let ellipsis_fragments = with_thread_local_font_context(layout_context, |font_context| {
768             TextRunScanner::new().scan_for_runs(font_context, unscanned_ellipsis_fragments)
769         });
770         debug_assert_eq!(ellipsis_fragments.len(), 1);
771         ellipsis_fragment = ellipsis_fragments.fragments.into_iter().next().unwrap();
772         ellipsis_fragment.flags |= FragmentFlags::IS_ELLIPSIS;
773         ellipsis_fragment
774     }
775 
restyle_damage(&self) -> RestyleDamage776     pub fn restyle_damage(&self) -> RestyleDamage {
777         self.restyle_damage | self.specific.restyle_damage()
778     }
779 
contains_node(&self, node_address: OpaqueNode) -> bool780     pub fn contains_node(&self, node_address: OpaqueNode) -> bool {
781         node_address == self.node ||
782         self.inline_context.as_ref().map_or(false, |ctx| {
783             ctx.contains_node(node_address)
784         })
785     }
786 
787     /// Adds a style to the inline context for this fragment. If the inline context doesn't exist
788     /// yet, it will be created.
add_inline_context_style(&mut self, node_info: InlineFragmentNodeInfo)789     pub fn add_inline_context_style(&mut self, node_info: InlineFragmentNodeInfo) {
790         if self.inline_context.is_none() {
791             self.inline_context = Some(InlineFragmentContext::new());
792         }
793         self.inline_context.as_mut().unwrap().nodes.push(node_info);
794     }
795 
796     /// Determines which quantities (border/padding/margin/specified) should be included in the
797     /// intrinsic inline size of this fragment.
quantities_included_in_intrinsic_inline_size(&self) -> QuantitiesIncludedInIntrinsicInlineSizes798     fn quantities_included_in_intrinsic_inline_size(&self)
799                                                     -> QuantitiesIncludedInIntrinsicInlineSizes {
800         match self.specific {
801             SpecificFragmentInfo::Canvas(_) |
802             SpecificFragmentInfo::Generic |
803             SpecificFragmentInfo::GeneratedContent(_) |
804             SpecificFragmentInfo::Iframe(_) |
805             SpecificFragmentInfo::Image(_) |
806             SpecificFragmentInfo::InlineAbsolute(_) |
807             SpecificFragmentInfo::Multicol |
808             SpecificFragmentInfo::Svg(_) => {
809                 QuantitiesIncludedInIntrinsicInlineSizes::all()
810             }
811             SpecificFragmentInfo::Table => {
812                 QuantitiesIncludedInIntrinsicInlineSizes::INTRINSIC_INLINE_SIZE_INCLUDES_SPECIFIED |
813                     QuantitiesIncludedInIntrinsicInlineSizes::INTRINSIC_INLINE_SIZE_INCLUDES_PADDING |
814                     QuantitiesIncludedInIntrinsicInlineSizes::INTRINSIC_INLINE_SIZE_INCLUDES_BORDER
815             }
816             SpecificFragmentInfo::TableCell => {
817                 let base_quantities = QuantitiesIncludedInIntrinsicInlineSizes::INTRINSIC_INLINE_SIZE_INCLUDES_PADDING |
818                     QuantitiesIncludedInIntrinsicInlineSizes::INTRINSIC_INLINE_SIZE_INCLUDES_SPECIFIED;
819                 if self.style.get_inheritedtable().border_collapse ==
820                         BorderCollapse::Separate {
821                     base_quantities | QuantitiesIncludedInIntrinsicInlineSizes::INTRINSIC_INLINE_SIZE_INCLUDES_BORDER
822                 } else {
823                     base_quantities
824                 }
825             }
826             SpecificFragmentInfo::TableWrapper => {
827                 let base_quantities = QuantitiesIncludedInIntrinsicInlineSizes::INTRINSIC_INLINE_SIZE_INCLUDES_MARGINS |
828                     QuantitiesIncludedInIntrinsicInlineSizes::INTRINSIC_INLINE_SIZE_INCLUDES_SPECIFIED;
829                 if self.style.get_inheritedtable().border_collapse ==
830                         BorderCollapse::Separate {
831                     base_quantities | QuantitiesIncludedInIntrinsicInlineSizes::INTRINSIC_INLINE_SIZE_INCLUDES_BORDER
832                 } else {
833                     base_quantities
834                 }
835             }
836             SpecificFragmentInfo::TableRow => {
837                 let base_quantities =
838                     QuantitiesIncludedInIntrinsicInlineSizes::INTRINSIC_INLINE_SIZE_INCLUDES_SPECIFIED;
839                 if self.style.get_inheritedtable().border_collapse ==
840                         BorderCollapse::Separate {
841                     base_quantities | QuantitiesIncludedInIntrinsicInlineSizes::INTRINSIC_INLINE_SIZE_INCLUDES_BORDER
842                 } else {
843                     base_quantities
844                 }
845             }
846             SpecificFragmentInfo::TruncatedFragment(_) |
847             SpecificFragmentInfo::ScannedText(_) |
848             SpecificFragmentInfo::TableColumn(_) |
849             SpecificFragmentInfo::UnscannedText(_) |
850             SpecificFragmentInfo::InlineAbsoluteHypothetical(_) |
851             SpecificFragmentInfo::InlineBlock(_) |
852             SpecificFragmentInfo::MulticolColumn => {
853                 QuantitiesIncludedInIntrinsicInlineSizes::empty()
854             }
855         }
856     }
857 
858     /// Returns the portion of the intrinsic inline-size that consists of borders/padding and
859     /// margins, respectively.
860     ///
861     /// FIXME(#2261, pcwalton): This won't work well for inlines: is this OK?
surrounding_intrinsic_inline_size(&self) -> (Au, Au)862     pub fn surrounding_intrinsic_inline_size(&self) -> (Au, Au) {
863         let flags = self.quantities_included_in_intrinsic_inline_size();
864         let style = self.style();
865 
866         // FIXME(pcwalton): Percentages should be relative to any definite size per CSS-SIZING.
867         // This will likely need to be done by pushing down definite sizes during selector
868         // cascading.
869         let margin = if flags.contains(
870                 QuantitiesIncludedInIntrinsicInlineSizes::INTRINSIC_INLINE_SIZE_INCLUDES_MARGINS) {
871             let margin = style.logical_margin();
872             (MaybeAuto::from_style(margin.inline_start, Au(0)).specified_or_zero() +
873              MaybeAuto::from_style(margin.inline_end, Au(0)).specified_or_zero())
874         } else {
875             Au(0)
876         };
877 
878         // FIXME(pcwalton): Percentages should be relative to any definite size per CSS-SIZING.
879         // This will likely need to be done by pushing down definite sizes during selector
880         // cascading.
881         let padding = if flags.contains(
882                 QuantitiesIncludedInIntrinsicInlineSizes::INTRINSIC_INLINE_SIZE_INCLUDES_PADDING) {
883             let padding = style.logical_padding();
884             (padding.inline_start.to_used_value(Au(0)) +
885              padding.inline_end.to_used_value(Au(0)))
886         } else {
887             Au(0)
888         };
889 
890         let border = if flags.contains(
891                 QuantitiesIncludedInIntrinsicInlineSizes::INTRINSIC_INLINE_SIZE_INCLUDES_BORDER) {
892             self.border_width().inline_start_end()
893         } else {
894             Au(0)
895         };
896 
897         (border + padding, margin)
898     }
899 
900     /// Uses the style only to estimate the intrinsic inline-sizes. These may be modified for text
901     /// or replaced elements.
style_specified_intrinsic_inline_size(&self) -> IntrinsicISizesContribution902     pub fn style_specified_intrinsic_inline_size(&self) -> IntrinsicISizesContribution {
903         let flags = self.quantities_included_in_intrinsic_inline_size();
904         let style = self.style();
905 
906         // FIXME(#2261, pcwalton): This won't work well for inlines: is this OK?
907         let (border_padding, margin) = self.surrounding_intrinsic_inline_size();
908 
909         let mut specified = Au(0);
910         if flags.contains(QuantitiesIncludedInIntrinsicInlineSizes::INTRINSIC_INLINE_SIZE_INCLUDES_SPECIFIED) {
911             specified = MaybeAuto::from_style(style.content_inline_size(),
912                                               Au(0)).specified_or_zero();
913             specified = max(style.min_inline_size().to_used_value(Au(0)), specified);
914             if let Some(max) = style.max_inline_size().to_used_value(Au(0)) {
915                 specified = min(specified, max)
916             }
917 
918             if self.style.get_position().box_sizing == BoxSizing::BorderBox {
919                 specified = max(Au(0), specified - border_padding);
920             }
921         }
922 
923         IntrinsicISizesContribution {
924             content_intrinsic_sizes: IntrinsicISizes {
925                 minimum_inline_size: specified,
926                 preferred_inline_size: specified,
927             },
928             surrounding_size: border_padding + margin,
929         }
930     }
931 
932     /// intrinsic width of this replaced element.
933     #[inline]
intrinsic_width(&self) -> Au934     pub fn intrinsic_width(&self) -> Au {
935         match self.specific {
936             SpecificFragmentInfo::Image(ref info) => {
937                 if let Some(ref data) = info.metadata {
938                     Au::from_px(data.width as i32)
939                 } else {
940                     Au(0)
941                 }
942             }
943             SpecificFragmentInfo::Canvas(ref info) => info.dom_width,
944             SpecificFragmentInfo::Svg(ref info) => info.dom_width,
945             // Note: Currently for replaced element with no intrinsic size,
946             // this function simply returns the default object size. As long as
947             // these elements do not have intrinsic aspect ratio this should be
948             // sufficient, but we may need to investigate if this is enough for
949             // use cases like SVG.
950             SpecificFragmentInfo::Iframe(_) => Au::from_px(DEFAULT_REPLACED_WIDTH),
951             _ => panic!("Trying to get intrinsic width on non-replaced element!")
952         }
953     }
954 
955     /// intrinsic width of this replaced element.
956     #[inline]
intrinsic_height(&self) -> Au957     pub fn intrinsic_height(&self) -> Au {
958         match self.specific {
959             SpecificFragmentInfo::Image(ref info) => {
960                 if let Some(ref data) = info.metadata {
961                     Au::from_px(data.height as i32)
962                 } else {
963                     Au(0)
964                 }
965             }
966             SpecificFragmentInfo::Canvas(ref info) => info.dom_height,
967             SpecificFragmentInfo::Svg(ref info) => info.dom_height,
968             SpecificFragmentInfo::Iframe(_) => Au::from_px(DEFAULT_REPLACED_HEIGHT),
969             _ => panic!("Trying to get intrinsic height on non-replaced element!")
970         }
971     }
972 
973     /// Whether this replace element has intrinsic aspect ratio.
has_intrinsic_ratio(&self) -> bool974     pub fn has_intrinsic_ratio(&self) -> bool {
975         match self.specific {
976             SpecificFragmentInfo::Image(_)  |
977             SpecificFragmentInfo::Canvas(_) |
978             // TODO(stshine): According to the SVG spec, whether a SVG element has intrinsic
979             // aspect ratio is determined by the `preserveAspectRatio` attribute. Since for
980             // now SVG is far from implemented, we simply choose the default behavior that
981             // the intrinsic aspect ratio is preserved.
982             // https://svgwg.org/svg2-draft/coords.html#PreserveAspectRatioAttribute
983             SpecificFragmentInfo::Svg(_) =>
984                 self.intrinsic_width() != Au(0) && self.intrinsic_height() != Au(0),
985             _ => false
986         }
987     }
988 
989     /// CSS 2.1 § 10.3.2 & 10.6.2 Calculate the used width and height of a replaced element.
990     /// When a parameter is `None` it means the specified size in certain direction
991     /// is unconstrained. The inline containing size can also be `None` since this
992     /// method is also used for calculating intrinsic inline size contribution.
calculate_replaced_sizes(&self, containing_inline_size: Option<Au>, containing_block_size: Option<Au>) -> (Au, Au)993     pub fn calculate_replaced_sizes(&self,
994                                     containing_inline_size: Option<Au>,
995                                     containing_block_size: Option<Au>)
996                                     -> (Au, Au) {
997         let (intrinsic_inline_size, intrinsic_block_size) = if self.style.writing_mode.is_vertical() {
998             (self.intrinsic_height(), self.intrinsic_width())
999         } else {
1000             (self.intrinsic_width(), self.intrinsic_height())
1001         };
1002 
1003         // Make sure the size we used here is for content box since they may be
1004         // transferred by the intrinsic aspect ratio.
1005         let inline_size = style_length(self.style.content_inline_size(), containing_inline_size)
1006                                      .map(|x| x - self.box_sizing_boundary(Direction::Inline));
1007         let block_size = style_length(self.style.content_block_size(), containing_block_size)
1008                                      .map(|x| x - self.box_sizing_boundary(Direction::Block));
1009         let inline_constraint = self.size_constraint(containing_inline_size, Direction::Inline);
1010         let block_constraint = self.size_constraint(containing_block_size, Direction::Block);
1011 
1012         // https://drafts.csswg.org/css-images-3/#default-sizing
1013         match (inline_size, block_size) {
1014             // If the specified size is a definite width and height, the concrete
1015             // object size is given that width and height.
1016             (MaybeAuto::Specified(inline_size), MaybeAuto::Specified(block_size)) =>
1017                 (inline_constraint.clamp(inline_size), block_constraint.clamp(block_size)),
1018 
1019             // If the specified size is only a width or height (but not both)
1020             // then the concrete object size is given that specified width or
1021             // height. The other dimension is calculated as follows:
1022             //
1023             // If the object has an intrinsic aspect ratio, the missing dimension
1024             // of the concrete object size is calculated using the intrinsic
1025             // aspect ratio and the present dimension.
1026             //
1027             // Otherwise, if the missing dimension is present in the object’s intrinsic
1028             // dimensions, the missing dimension is taken from the object’s intrinsic
1029             // dimensions. Otherwise it is taken from the default object size.
1030             (MaybeAuto::Specified(inline_size), MaybeAuto::Auto) => {
1031                 let inline_size = inline_constraint.clamp(inline_size);
1032                 let block_size = if self.has_intrinsic_ratio() {
1033                     // Note: We can not precompute the ratio and store it as a float, because
1034                     // doing so may result one pixel difference in calculation for certain
1035                     // images, thus make some tests fail.
1036                     Au::new((inline_size.0 as i64 * intrinsic_block_size.0 as i64 /
1037                         intrinsic_inline_size.0 as i64) as i32)
1038                 } else {
1039                     intrinsic_block_size
1040                 };
1041                 (inline_size, block_constraint.clamp(block_size))
1042             }
1043             (MaybeAuto::Auto, MaybeAuto::Specified(block_size)) => {
1044                 let block_size = block_constraint.clamp(block_size);
1045                 let inline_size = if self.has_intrinsic_ratio() {
1046                     Au::new((block_size.0 as i64 * intrinsic_inline_size.0 as i64 /
1047                        intrinsic_block_size.0 as i64) as i32)
1048                 } else {
1049                     intrinsic_inline_size
1050                 };
1051                 (inline_constraint.clamp(inline_size), block_size)
1052             }
1053             // https://drafts.csswg.org/css2/visudet.html#min-max-widths
1054             (MaybeAuto::Auto, MaybeAuto::Auto) => {
1055                 if self.has_intrinsic_ratio() {
1056                     // This approch follows the spirit of cover and contain constraint.
1057                     // https://drafts.csswg.org/css-images-3/#cover-contain
1058 
1059                     // First, create two rectangles that keep aspect ratio while may be clamped
1060                     // by the contraints;
1061                     let first_isize = inline_constraint.clamp(intrinsic_inline_size);
1062                     let first_bsize = Au::new((first_isize.0 as i64 * intrinsic_block_size.0 as i64 /
1063                                           intrinsic_inline_size.0 as i64) as i32);
1064                     let second_bsize = block_constraint.clamp(intrinsic_block_size);
1065                     let second_isize = Au::new((second_bsize.0 as i64 * intrinsic_inline_size.0 as i64 /
1066                                            intrinsic_block_size.0 as i64) as i32);
1067                     let (inline_size, block_size) = match (first_isize.cmp(&intrinsic_inline_size) ,
1068                                                            second_isize.cmp(&intrinsic_inline_size)) {
1069                         (Ordering::Equal, Ordering::Equal) =>
1070                             (first_isize, first_bsize),
1071                         // When only one rectangle is clamped, use it;
1072                         (Ordering::Equal, _) =>
1073                             (second_isize, second_bsize),
1074                         (_, Ordering::Equal) =>
1075                             (first_isize, first_bsize),
1076                         // When both rectangles grow (smaller than min sizes),
1077                         // Choose the larger one;
1078                         (Ordering::Greater, Ordering::Greater) =>
1079                             if first_isize > second_isize {
1080                                 (first_isize, first_bsize)
1081                             } else {
1082                                 (second_isize, second_bsize)
1083                             },
1084                         // When both rectangles shrink (larger than max sizes),
1085                         // Choose the smaller one;
1086                         (Ordering::Less, Ordering::Less) =>
1087                             if first_isize > second_isize {
1088                                 (second_isize, second_bsize)
1089                             } else {
1090                                 (first_isize, first_bsize)
1091                             },
1092                         // It does not matter which we choose here, because both sizes
1093                         // will be clamped to constraint;
1094                         (Ordering::Less, Ordering::Greater) | (Ordering::Greater, Ordering::Less) =>
1095                             (first_isize, first_bsize)
1096                     };
1097                     // Clamp the result and we are done :-)
1098                     (inline_constraint.clamp(inline_size), block_constraint.clamp(block_size))
1099                 } else {
1100                     (inline_constraint.clamp(intrinsic_inline_size),
1101                     block_constraint.clamp(intrinsic_block_size))
1102                 }
1103             }
1104         }
1105     }
1106 
1107     /// Return a size constraint that can be used the clamp size in given direction.
1108     /// To take `box-sizing: border-box` into account, the `border_padding` field
1109     /// must be initialized first.
1110     ///
1111     /// TODO(stshine): Maybe there is a more convenient way.
size_constraint(&self, containing_size: Option<Au>, direction: Direction) -> SizeConstraint1112     pub fn size_constraint(&self, containing_size: Option<Au>, direction: Direction) -> SizeConstraint {
1113         let (style_min_size, style_max_size) = match direction {
1114             Direction::Inline => (self.style.min_inline_size(), self.style.max_inline_size()),
1115             Direction::Block => (self.style.min_block_size(), self.style.max_block_size())
1116         };
1117 
1118         let border = if self.style().get_position().box_sizing == BoxSizing::BorderBox {
1119             Some(self.border_padding.start_end(direction))
1120         } else {
1121             None
1122         };
1123 
1124         SizeConstraint::new(containing_size, style_min_size, style_max_size, border)
1125     }
1126 
1127     /// Returns a guess as to the distances from the margin edge of this fragment to its content
1128     /// in the inline direction. This will generally be correct unless percentages are involved.
1129     ///
1130     /// This is used for the float placement speculation logic.
guess_inline_content_edge_offsets(&self) -> SpeculatedInlineContentEdgeOffsets1131     pub fn guess_inline_content_edge_offsets(&self) -> SpeculatedInlineContentEdgeOffsets {
1132         let logical_margin = self.style.logical_margin();
1133         let logical_padding = self.style.logical_padding();
1134         let border_width = self.border_width();
1135         SpeculatedInlineContentEdgeOffsets {
1136             start: MaybeAuto::from_style(logical_margin.inline_start, Au(0)).specified_or_zero() +
1137                 logical_padding.inline_start.to_used_value(Au(0)) +
1138                 border_width.inline_start,
1139             end: MaybeAuto::from_style(logical_margin.inline_end, Au(0)).specified_or_zero() +
1140                 logical_padding.inline_end.to_used_value(Au(0)) +
1141                 border_width.inline_end,
1142         }
1143     }
1144 
1145     /// Returns the sum of the inline-sizes of all the borders of this fragment. Note that this
1146     /// can be expensive to compute, so if possible use the `border_padding` field instead.
1147     #[inline]
border_width(&self) -> LogicalMargin<Au>1148     pub fn border_width(&self) -> LogicalMargin<Au> {
1149         let style_border_width = self.style().logical_border_width();
1150 
1151         // NOTE: We can have nodes with different writing mode inside
1152         // the inline fragment context, so we need to overwrite the
1153         // writing mode to compute the child logical sizes.
1154         let writing_mode = self.style.writing_mode;
1155         let context_border = match self.inline_context {
1156             None => LogicalMargin::zero(writing_mode),
1157             Some(ref inline_fragment_context) => {
1158                 inline_fragment_context.nodes.iter().fold(style_border_width, |accumulator, node| {
1159                     let mut this_border_width =
1160                         node.style.border_width_for_writing_mode(writing_mode);
1161                     if !node.flags.contains(InlineFragmentNodeFlags::FIRST_FRAGMENT_OF_ELEMENT) {
1162                         this_border_width.inline_start = Au(0)
1163                     }
1164                     if !node.flags.contains(InlineFragmentNodeFlags::LAST_FRAGMENT_OF_ELEMENT) {
1165                         this_border_width.inline_end = Au(0)
1166                     }
1167                     accumulator + this_border_width
1168                 })
1169             }
1170         };
1171         style_border_width + context_border
1172     }
1173 
1174     /// Returns the border width in given direction if this fragment has property
1175     /// 'box-sizing: border-box'. The `border_padding` field must have been initialized.
box_sizing_boundary(&self, direction: Direction) -> Au1176     pub fn box_sizing_boundary(&self, direction: Direction) -> Au {
1177         match (self.style().get_position().box_sizing, direction) {
1178             (BoxSizing::BorderBox, Direction::Inline) => {
1179                 self.border_padding.inline_start_end()
1180             }
1181             (BoxSizing::BorderBox, Direction::Block) => {
1182                 self.border_padding.block_start_end()
1183             }
1184             _ => Au(0)
1185         }
1186     }
1187 
1188     /// Computes the margins in the inline direction from the containing block inline-size and the
1189     /// style. After this call, the inline direction of the `margin` field will be correct.
1190     ///
1191     /// Do not use this method if the inline direction margins are to be computed some other way
1192     /// (for example, via constraint solving for blocks).
compute_inline_direction_margins(&mut self, containing_block_inline_size: Au)1193     pub fn compute_inline_direction_margins(&mut self, containing_block_inline_size: Au) {
1194         match self.specific {
1195             SpecificFragmentInfo::Table |
1196             SpecificFragmentInfo::TableCell |
1197             SpecificFragmentInfo::TableRow |
1198             SpecificFragmentInfo::TableColumn(_) |
1199             SpecificFragmentInfo::InlineAbsoluteHypothetical(_) => {
1200                 self.margin.inline_start = Au(0);
1201                 self.margin.inline_end = Au(0);
1202                 return
1203             }
1204             _ => {
1205                 let margin = self.style().logical_margin();
1206                 self.margin.inline_start =
1207                     MaybeAuto::from_style(margin.inline_start,
1208                                           containing_block_inline_size).specified_or_zero();
1209                 self.margin.inline_end =
1210                     MaybeAuto::from_style(margin.inline_end,
1211                                           containing_block_inline_size).specified_or_zero();
1212             }
1213         }
1214 
1215         if let Some(ref inline_context) = self.inline_context {
1216             for node in &inline_context.nodes {
1217                 let margin = node.style.logical_margin();
1218                 let this_inline_start_margin = if !node.flags.contains(
1219                         InlineFragmentNodeFlags::FIRST_FRAGMENT_OF_ELEMENT) {
1220                     Au(0)
1221                 } else {
1222                     MaybeAuto::from_style(margin.inline_start,
1223                                           containing_block_inline_size).specified_or_zero()
1224                 };
1225                 let this_inline_end_margin = if!node.flags.contains(
1226                         InlineFragmentNodeFlags::LAST_FRAGMENT_OF_ELEMENT) {
1227                     Au(0)
1228                 } else {
1229                     MaybeAuto::from_style(margin.inline_end,
1230                                           containing_block_inline_size).specified_or_zero()
1231                 };
1232 
1233                 self.margin.inline_start += this_inline_start_margin;
1234                 self.margin.inline_end += this_inline_end_margin;
1235             }
1236         }
1237     }
1238 
1239     /// Computes the margins in the block direction from the containing block inline-size and the
1240     /// style. After this call, the block direction of the `margin` field will be correct.
1241     ///
1242     /// Do not use this method if the block direction margins are to be computed some other way
1243     /// (for example, via constraint solving for absolutely-positioned flows).
compute_block_direction_margins(&mut self, containing_block_inline_size: Au)1244     pub fn compute_block_direction_margins(&mut self, containing_block_inline_size: Au) {
1245         match self.specific {
1246             SpecificFragmentInfo::Table |
1247             SpecificFragmentInfo::TableCell |
1248             SpecificFragmentInfo::TableRow |
1249             SpecificFragmentInfo::TableColumn(_) => {
1250                 self.margin.block_start = Au(0);
1251                 self.margin.block_end = Au(0)
1252             }
1253             _ => {
1254                 // NB: Percentages are relative to containing block inline-size (not block-size)
1255                 // per CSS 2.1.
1256                 let margin = self.style().logical_margin();
1257                 self.margin.block_start =
1258                     MaybeAuto::from_style(margin.block_start, containing_block_inline_size)
1259                     .specified_or_zero();
1260                 self.margin.block_end =
1261                     MaybeAuto::from_style(margin.block_end, containing_block_inline_size)
1262                     .specified_or_zero();
1263             }
1264         }
1265     }
1266 
1267     /// Computes the border and padding in both inline and block directions from the containing
1268     /// block inline-size and the style. After this call, the `border_padding` field will be
1269     /// correct.
compute_border_and_padding(&mut self, containing_block_inline_size: Au)1270     pub fn compute_border_and_padding(&mut self,
1271                                       containing_block_inline_size: Au) {
1272         // Compute border.
1273         let border = match self.style.get_inheritedtable().border_collapse {
1274             BorderCollapse::Separate => self.border_width(),
1275             BorderCollapse::Collapse => LogicalMargin::zero(self.style.writing_mode),
1276         };
1277 
1278         // Compute padding from the fragment's style.
1279         let padding_from_style = match self.specific {
1280             SpecificFragmentInfo::TableColumn(_) |
1281             SpecificFragmentInfo::TableRow |
1282             SpecificFragmentInfo::TableWrapper => LogicalMargin::zero(self.style.writing_mode),
1283             _ => model::padding_from_style(self.style(), containing_block_inline_size, self.style().writing_mode),
1284         };
1285 
1286         // Compute padding from the inline fragment context.
1287         let padding_from_inline_fragment_context = match (&self.specific, &self.inline_context) {
1288             (_, &None) |
1289             (&SpecificFragmentInfo::TableColumn(_), _) |
1290             (&SpecificFragmentInfo::TableRow, _) |
1291             (&SpecificFragmentInfo::TableWrapper, _) => {
1292                 LogicalMargin::zero(self.style.writing_mode)
1293             }
1294             (_, &Some(ref inline_fragment_context)) => {
1295                 let writing_mode = self.style.writing_mode;
1296                 let zero_padding = LogicalMargin::zero(writing_mode);
1297                 inline_fragment_context.nodes.iter().fold(zero_padding, |accumulator, node| {
1298                     let mut padding = model::padding_from_style(&*node.style, Au(0), writing_mode);
1299                     if !node.flags.contains(InlineFragmentNodeFlags::FIRST_FRAGMENT_OF_ELEMENT) {
1300                         padding.inline_start = Au(0)
1301                     }
1302                     if !node.flags.contains(InlineFragmentNodeFlags::LAST_FRAGMENT_OF_ELEMENT) {
1303                         padding.inline_end = Au(0)
1304                     }
1305                     accumulator + padding
1306                 })
1307             }
1308         };
1309 
1310         self.border_padding = border + padding_from_style + padding_from_inline_fragment_context
1311     }
1312 
1313     // Return offset from original position because of `position: relative`.
relative_position(&self, containing_block_size: &LogicalSize<Au>) -> LogicalSize<Au>1314     pub fn relative_position(&self, containing_block_size: &LogicalSize<Au>) -> LogicalSize<Au> {
1315         fn from_style(style: &ComputedValues, container_size: &LogicalSize<Au>)
1316                       -> LogicalSize<Au> {
1317             let offsets = style.logical_position();
1318             let offset_i = if offsets.inline_start != LengthOrPercentageOrAuto::Auto {
1319                 MaybeAuto::from_style(offsets.inline_start,
1320                                       container_size.inline).specified_or_zero()
1321             } else {
1322                 -MaybeAuto::from_style(offsets.inline_end,
1323                                        container_size.inline).specified_or_zero()
1324             };
1325             let offset_b = if offsets.block_start != LengthOrPercentageOrAuto::Auto {
1326                 MaybeAuto::from_style(offsets.block_start,
1327                                       container_size.block).specified_or_zero()
1328             } else {
1329                 -MaybeAuto::from_style(offsets.block_end,
1330                                        container_size.block).specified_or_zero()
1331             };
1332             LogicalSize::new(style.writing_mode, offset_i, offset_b)
1333         }
1334 
1335         // Go over the ancestor fragments and add all relative offsets (if any).
1336         let mut rel_pos = if self.style().get_box().position == Position::Relative {
1337             from_style(self.style(), containing_block_size)
1338         } else {
1339             LogicalSize::zero(self.style.writing_mode)
1340         };
1341 
1342         if let Some(ref inline_fragment_context) = self.inline_context {
1343             for node in &inline_fragment_context.nodes {
1344                 if node.style.get_box().position == Position::Relative {
1345                     rel_pos = rel_pos + from_style(&*node.style, containing_block_size);
1346                 }
1347             }
1348         }
1349 
1350         rel_pos
1351     }
1352 
1353     /// Always inline for SCCP.
1354     ///
1355     /// FIXME(pcwalton): Just replace with the clear type from the style module for speed?
1356     #[inline(always)]
clear(&self) -> Option<ClearType>1357     pub fn clear(&self) -> Option<ClearType> {
1358         let style = self.style();
1359         match style.get_box().clear {
1360             Clear::None => None,
1361             Clear::Left => Some(ClearType::Left),
1362             Clear::Right => Some(ClearType::Right),
1363             Clear::Both => Some(ClearType::Both),
1364         }
1365     }
1366 
1367     #[inline(always)]
style(&self) -> &ComputedValues1368     pub fn style(&self) -> &ComputedValues {
1369         &*self.style
1370     }
1371 
1372     #[inline(always)]
selected_style(&self) -> &ComputedValues1373     pub fn selected_style(&self) -> &ComputedValues {
1374         &*self.selected_style
1375     }
1376 
white_space(&self) -> WhiteSpace1377     pub fn white_space(&self) -> WhiteSpace {
1378         self.style().get_inheritedtext().white_space
1379     }
1380 
color(&self) -> Color1381     pub fn color(&self) -> Color {
1382         self.style().get_color().color
1383     }
1384 
1385     /// Returns the text decoration line of this fragment, according to the style of the nearest ancestor
1386     /// element.
1387     ///
1388     /// NB: This may not be the actual text decoration line, because of the override rules specified in
1389     /// CSS 2.1 § 16.3.1. Unfortunately, computing this properly doesn't really fit into Servo's
1390     /// model. Therefore, this is a best lower bound approximation, but the end result may actually
1391     /// have the various decoration flags turned on afterward.
text_decoration_line(&self) -> TextDecorationLine1392     pub fn text_decoration_line(&self) -> TextDecorationLine {
1393         self.style().get_text().text_decoration_line
1394     }
1395 
1396     /// Returns the inline-start offset from margin edge to content edge.
1397     ///
1398     /// FIXME(#2262, pcwalton): I think this method is pretty bogus, because it won't work for
1399     /// inlines.
inline_start_offset(&self) -> Au1400     pub fn inline_start_offset(&self) -> Au {
1401         match self.specific {
1402             SpecificFragmentInfo::TableWrapper => self.margin.inline_start,
1403             SpecificFragmentInfo::Table |
1404             SpecificFragmentInfo::TableCell |
1405             SpecificFragmentInfo::TableRow => self.border_padding.inline_start,
1406             SpecificFragmentInfo::TableColumn(_) => Au(0),
1407             _ => self.margin.inline_start + self.border_padding.inline_start,
1408         }
1409     }
1410 
1411     /// If this is a Column fragment, get the col span
1412     ///
1413     /// Panics for non-column fragments
column_span(&self) -> u321414     pub fn column_span(&self) -> u32 {
1415         match self.specific {
1416             SpecificFragmentInfo::TableColumn(col_fragment) => max(col_fragment.span, 1),
1417             _ => panic!("non-table-column fragment inside table column?!"),
1418         }
1419     }
1420 
1421     /// Returns true if this element can be split. This is true for text fragments, unless
1422     /// `white-space: pre` or `white-space: nowrap` is set.
can_split(&self) -> bool1423     pub fn can_split(&self) -> bool {
1424         self.is_scanned_text_fragment() && self.white_space().allow_wrap()
1425     }
1426 
1427     /// Returns true if and only if this fragment is a generated content fragment.
is_unscanned_generated_content(&self) -> bool1428     pub fn is_unscanned_generated_content(&self) -> bool {
1429         match self.specific {
1430             SpecificFragmentInfo::GeneratedContent(ref content) => match **content {
1431                 GeneratedContentInfo::Empty => false,
1432                 _ => true,
1433             },
1434             _ => false,
1435         }
1436     }
1437 
1438     /// Returns true if and only if this is a scanned text fragment.
is_scanned_text_fragment(&self) -> bool1439     pub fn is_scanned_text_fragment(&self) -> bool {
1440         match self.specific {
1441             SpecificFragmentInfo::ScannedText(..) => true,
1442             _ => false,
1443         }
1444     }
1445 
suppress_line_break_before(&self) -> bool1446     pub fn suppress_line_break_before(&self) -> bool {
1447         match self.specific {
1448             SpecificFragmentInfo::ScannedText(ref st) =>
1449                 st.flags.contains(ScannedTextFlags::SUPPRESS_LINE_BREAK_BEFORE),
1450             _ => false,
1451         }
1452     }
1453 
1454     /// Computes the intrinsic inline-sizes of this fragment.
compute_intrinsic_inline_sizes(&mut self) -> IntrinsicISizesContribution1455     pub fn compute_intrinsic_inline_sizes(&mut self) -> IntrinsicISizesContribution {
1456         let mut result = self.style_specified_intrinsic_inline_size();
1457         match self.specific {
1458             SpecificFragmentInfo::Generic |
1459             SpecificFragmentInfo::GeneratedContent(_) |
1460             SpecificFragmentInfo::Table |
1461             SpecificFragmentInfo::TableCell |
1462             SpecificFragmentInfo::TableColumn(_) |
1463             SpecificFragmentInfo::TableRow |
1464             SpecificFragmentInfo::TableWrapper |
1465             SpecificFragmentInfo::Multicol |
1466             SpecificFragmentInfo::MulticolColumn |
1467             SpecificFragmentInfo::InlineAbsoluteHypothetical(_) => {}
1468             SpecificFragmentInfo::InlineBlock(ref info) => {
1469                 let block_flow = info.flow_ref.as_block();
1470                 result.union_block(&block_flow.base.intrinsic_inline_sizes)
1471             }
1472             SpecificFragmentInfo::InlineAbsolute(ref info) => {
1473                 let block_flow = info.flow_ref.as_block();
1474                 result.union_block(&block_flow.base.intrinsic_inline_sizes)
1475             }
1476             SpecificFragmentInfo::Image(_) |
1477             SpecificFragmentInfo::Canvas(_) |
1478             SpecificFragmentInfo::Iframe(_) |
1479             SpecificFragmentInfo::Svg(_) => {
1480                 let mut inline_size = match self.style.content_inline_size() {
1481                     LengthOrPercentageOrAuto::Auto |
1482                     LengthOrPercentageOrAuto::Percentage(_) => {
1483                         // We have to initialize the `border_padding` field first to make
1484                         // the size constraints work properly.
1485                         // TODO(stshine): Find a cleaner way to do this.
1486                         let padding = self.style.logical_padding();
1487                         self.border_padding.inline_start = padding.inline_start.to_used_value(Au(0));
1488                         self.border_padding.inline_end = padding.inline_end.to_used_value(Au(0));
1489                         self.border_padding.block_start = padding.block_start.to_used_value(Au(0));
1490                         self.border_padding.block_end = padding.block_end.to_used_value(Au(0));
1491                         let border = self.border_width();
1492                         self.border_padding.inline_start += border.inline_start;
1493                         self.border_padding.inline_end += border.inline_end;
1494                         self.border_padding.block_start += border.block_start;
1495                         self.border_padding.block_end += border.block_end;
1496                         let (result_inline, _) = self.calculate_replaced_sizes(None, None);
1497                         result_inline
1498                     }
1499                     LengthOrPercentageOrAuto::Length(length) => Au::from(length),
1500                     LengthOrPercentageOrAuto::Calc(calc) => {
1501                         // TODO(nox): This is probably wrong, because it accounts neither for
1502                         // clamping (not sure if necessary here) nor percentage.
1503                         Au::from(calc.unclamped_length())
1504                     },
1505                 };
1506 
1507                 let size_constraint = self.size_constraint(None, Direction::Inline);
1508                 inline_size = size_constraint.clamp(inline_size);
1509 
1510                 result.union_block(&IntrinsicISizes {
1511                     minimum_inline_size: inline_size,
1512                     preferred_inline_size: inline_size,
1513                 });
1514             }
1515 
1516             SpecificFragmentInfo::TruncatedFragment(ref t) if t.text_info.is_some() => {
1517                 let text_fragment_info = t.text_info.as_ref().unwrap();
1518                 handle_text(text_fragment_info, self, &mut result)
1519             }
1520             SpecificFragmentInfo::ScannedText(ref text_fragment_info) => {
1521                 handle_text(text_fragment_info, self, &mut result)
1522             }
1523 
1524             SpecificFragmentInfo::TruncatedFragment(_) => {
1525                 return IntrinsicISizesContribution::new()
1526             }
1527 
1528             SpecificFragmentInfo::UnscannedText(..) => {
1529                 panic!("Unscanned text fragments should have been scanned by now!")
1530             }
1531         };
1532 
1533         fn handle_text(text_fragment_info: &ScannedTextFragmentInfo, self_: &Fragment,
1534                        result: &mut IntrinsicISizesContribution) {
1535             let range = &text_fragment_info.range;
1536 
1537             // See http://dev.w3.org/csswg/css-sizing/#max-content-inline-size.
1538             // TODO: Account for soft wrap opportunities.
1539             let max_line_inline_size = text_fragment_info.run
1540                                                          .metrics_for_range(range)
1541                                                          .advance_width;
1542 
1543             let min_line_inline_size = if self_.white_space().allow_wrap() {
1544                 text_fragment_info.run.min_width_for_range(range)
1545             } else {
1546                 max_line_inline_size
1547             };
1548 
1549             result.union_block(&IntrinsicISizes {
1550                 minimum_inline_size: min_line_inline_size,
1551                 preferred_inline_size: max_line_inline_size,
1552             })
1553         }
1554 
1555         // Take borders and padding for parent inline fragments into account.
1556         let writing_mode = self.style.writing_mode;
1557         if let Some(ref context) = self.inline_context {
1558             for node in &context.nodes {
1559                 let mut border_width = node.style.logical_border_width();
1560                 let mut padding = model::padding_from_style(&*node.style, Au(0), writing_mode);
1561                 let mut margin = model::specified_margin_from_style(&*node.style, writing_mode);
1562                 if !node.flags.contains(InlineFragmentNodeFlags::FIRST_FRAGMENT_OF_ELEMENT) {
1563                     border_width.inline_start = Au(0);
1564                     padding.inline_start = Au(0);
1565                     margin.inline_start = Au(0);
1566                 }
1567                 if !node.flags.contains(InlineFragmentNodeFlags::LAST_FRAGMENT_OF_ELEMENT) {
1568                     border_width.inline_end = Au(0);
1569                     padding.inline_end = Au(0);
1570                     margin.inline_end = Au(0);
1571                 }
1572 
1573                 result.surrounding_size =
1574                     result.surrounding_size +
1575                     border_width.inline_start_end() +
1576                     padding.inline_start_end() +
1577                     margin.inline_start_end();
1578             }
1579         }
1580 
1581         result
1582     }
1583 
1584     /// Returns the narrowest inline-size that the first splittable part of this fragment could
1585     /// possibly be split to. (In most cases, this returns the inline-size of the first word in
1586     /// this fragment.)
minimum_splittable_inline_size(&self) -> Au1587     pub fn minimum_splittable_inline_size(&self) -> Au {
1588         match self.specific {
1589             SpecificFragmentInfo::TruncatedFragment(ref t) if t.text_info.is_some() => {
1590                 let text = t.text_info.as_ref().unwrap();
1591                 text.run.minimum_splittable_inline_size(&text.range)
1592             }
1593             SpecificFragmentInfo::ScannedText(ref text) => {
1594                 text.run.minimum_splittable_inline_size(&text.range)
1595             }
1596             _ => Au(0),
1597         }
1598     }
1599 
1600     /// Returns the dimensions of the content box.
1601     ///
1602     /// This is marked `#[inline]` because it is frequently called when only one or two of the
1603     /// values are needed and that will save computation.
1604     #[inline]
content_box(&self) -> LogicalRect<Au>1605     pub fn content_box(&self) -> LogicalRect<Au> {
1606         self.border_box - self.border_padding
1607     }
1608 
1609     /// Attempts to find the split positions of a text fragment so that its inline-size is no more
1610     /// than `max_inline_size`.
1611     ///
1612     /// A return value of `None` indicates that the fragment could not be split. Otherwise the
1613     /// information pertaining to the split is returned. The inline-start and inline-end split
1614     /// information are both optional due to the possibility of them being whitespace.
calculate_split_position(&self, max_inline_size: Au, starts_line: bool) -> Option<SplitResult>1615     pub fn calculate_split_position(&self, max_inline_size: Au, starts_line: bool)
1616                                     -> Option<SplitResult> {
1617         let text_fragment_info = match self.specific {
1618             SpecificFragmentInfo::ScannedText(ref text_fragment_info)
1619                 => text_fragment_info,
1620             _   => return None,
1621         };
1622 
1623         let mut flags = SplitOptions::empty();
1624         if starts_line {
1625             flags.insert(SplitOptions::STARTS_LINE);
1626             if self.style().get_inheritedtext().overflow_wrap == OverflowWrap::BreakWord {
1627                 flags.insert(SplitOptions::RETRY_AT_CHARACTER_BOUNDARIES)
1628             }
1629         }
1630 
1631         match self.style().get_inheritedtext().word_break {
1632             WordBreak::Normal | WordBreak::KeepAll => {
1633                 // Break at normal word boundaries. keep-all forbids soft wrap opportunities.
1634                 let natural_word_breaking_strategy =
1635                     text_fragment_info.run.natural_word_slices_in_range(&text_fragment_info.range);
1636                 self.calculate_split_position_using_breaking_strategy(
1637                     natural_word_breaking_strategy,
1638                     max_inline_size,
1639                     flags)
1640             }
1641             WordBreak::BreakAll => {
1642                 // Break at character boundaries.
1643                 let character_breaking_strategy =
1644                     text_fragment_info.run.character_slices_in_range(&text_fragment_info.range);
1645                 flags.remove(SplitOptions::RETRY_AT_CHARACTER_BOUNDARIES);
1646                 self.calculate_split_position_using_breaking_strategy(
1647                     character_breaking_strategy,
1648                     max_inline_size,
1649                     flags)
1650             }
1651         }
1652     }
1653 
1654     /// Does this fragment start on a glyph run boundary?
is_on_glyph_run_boundary(&self) -> bool1655     pub fn is_on_glyph_run_boundary(&self) -> bool {
1656         let text_fragment_info = match self.specific {
1657             SpecificFragmentInfo::ScannedText(ref text_fragment_info)
1658                 => text_fragment_info,
1659             _   => return true,
1660         };
1661         text_fragment_info.run.on_glyph_run_boundary(text_fragment_info.range.begin())
1662     }
1663 
1664     /// Truncates this fragment to the given `max_inline_size`, using a character-based breaking
1665     /// strategy. The resulting fragment will have `SpecificFragmentInfo::TruncatedFragment`,
1666     /// preserving the original fragment for use in incremental reflow.
1667     ///
1668     /// This function will panic if self is already truncated.
truncate_to_inline_size(self, max_inline_size: Au) -> Fragment1669     pub fn truncate_to_inline_size(self, max_inline_size: Au) -> Fragment {
1670         if let SpecificFragmentInfo::TruncatedFragment(_) = self.specific {
1671             panic!("Cannot truncate an already truncated fragment");
1672         }
1673         let info = self.calculate_truncate_to_inline_size(max_inline_size);
1674         let (size, text_info) = match info {
1675             Some(TruncationResult { split: SplitInfo { inline_size, range }, text_run } ) => {
1676                 let size = LogicalSize::new(self.style.writing_mode,
1677                                             inline_size,
1678                                             self.border_box.size.block);
1679                 // Preserve the insertion point if it is in this fragment's range or it is at line end.
1680                 let (flags, insertion_point) = match self.specific {
1681                     SpecificFragmentInfo::ScannedText(ref info) => {
1682                         match info.insertion_point {
1683                             Some(index) if range.contains(index) => (info.flags, info.insertion_point),
1684                             Some(index) if index == ByteIndex(text_run.text.chars().count() as isize - 1) &&
1685                                 index == range.end() => (info.flags, info.insertion_point),
1686                             _ => (info.flags, None)
1687                         }
1688                     },
1689                     _ => (ScannedTextFlags::empty(), None)
1690                 };
1691                 let text_info = ScannedTextFragmentInfo::new(
1692                     text_run,
1693                     range,
1694                     size,
1695                     insertion_point,
1696                     flags);
1697                 (size, Some(text_info))
1698             }
1699             None =>
1700                 (LogicalSize::zero(self.style.writing_mode), None)
1701         };
1702         let mut result = self.transform(size, SpecificFragmentInfo::Generic);
1703         result.specific = SpecificFragmentInfo::TruncatedFragment(Box::new(TruncatedFragmentInfo {
1704             text_info: text_info,
1705             full: self,
1706         }));
1707         result
1708     }
1709 
1710     /// Truncates this fragment to the given `max_inline_size`, using a character-based breaking
1711     /// strategy. If no characters could fit, returns `None`.
calculate_truncate_to_inline_size(&self, max_inline_size: Au) -> Option<TruncationResult>1712     fn calculate_truncate_to_inline_size(&self, max_inline_size: Au) -> Option<TruncationResult> {
1713         let text_fragment_info =
1714             if let SpecificFragmentInfo::ScannedText(ref text_fragment_info) = self.specific {
1715                 text_fragment_info
1716             } else {
1717                 return None
1718             };
1719 
1720         let character_breaking_strategy =
1721             text_fragment_info.run.character_slices_in_range(&text_fragment_info.range);
1722 
1723         let split_info = self.calculate_split_position_using_breaking_strategy(
1724                 character_breaking_strategy,
1725                 max_inline_size,
1726                 SplitOptions::empty())?;
1727 
1728         let split = split_info.inline_start?;
1729         Some(TruncationResult {
1730             split: split,
1731             text_run: split_info.text_run.clone(),
1732         })
1733     }
1734 
1735     /// A helper method that uses the breaking strategy described by `slice_iterator` (at present,
1736     /// either natural word breaking or character breaking) to split this fragment.
calculate_split_position_using_breaking_strategy<'a, I>( &self, slice_iterator: I, max_inline_size: Au, flags: SplitOptions) -> Option<SplitResult> where I: Iterator<Item=TextRunSlice<'a>>1737     fn calculate_split_position_using_breaking_strategy<'a, I>(
1738             &self,
1739             slice_iterator: I,
1740             max_inline_size: Au,
1741             flags: SplitOptions)
1742             -> Option<SplitResult>
1743             where I: Iterator<Item=TextRunSlice<'a>> {
1744         let text_fragment_info = match self.specific {
1745             SpecificFragmentInfo::ScannedText(ref text_fragment_info)
1746                 => text_fragment_info,
1747             _   => return None,
1748         };
1749 
1750         let mut remaining_inline_size = max_inline_size - self.border_padding.inline_start_end();
1751         let mut inline_start_range = Range::new(text_fragment_info.range.begin(), ByteIndex(0));
1752         let mut inline_end_range = None;
1753         let mut overflowing = false;
1754 
1755         debug!("calculate_split_position_using_breaking_strategy: splitting text fragment \
1756                 (strlen={}, range={:?}, max_inline_size={:?})",
1757                text_fragment_info.run.text.len(),
1758                text_fragment_info.range,
1759                max_inline_size);
1760 
1761         for slice in slice_iterator {
1762             debug!("calculate_split_position_using_breaking_strategy: considering slice \
1763                     (offset={:?}, slice range={:?}, remaining_inline_size={:?})",
1764                    slice.offset,
1765                    slice.range,
1766                    remaining_inline_size);
1767 
1768             // Use the `remaining_inline_size` to find a split point if possible. If not, go around
1769             // the loop again with the next slice.
1770             let metrics = text_fragment_info.run.metrics_for_slice(slice.glyphs, &slice.range);
1771             let advance = metrics.advance_width;
1772 
1773             // Have we found the split point?
1774             if advance <= remaining_inline_size || slice.glyphs.is_whitespace() {
1775                 // Keep going; we haven't found the split point yet.
1776                 debug!("calculate_split_position_using_breaking_strategy: enlarging span");
1777                 remaining_inline_size = remaining_inline_size - advance;
1778                 inline_start_range.extend_by(slice.range.length());
1779                 continue
1780             }
1781 
1782             // The advance is more than the remaining inline-size, so split here. First, check to
1783             // see if we're going to overflow the line. If so, perform a best-effort split.
1784             let mut remaining_range = slice.text_run_range();
1785             let split_is_empty = inline_start_range.is_empty() &&
1786                     !(self.requires_line_break_afterward_if_wrapping_on_newlines() &&
1787                       !self.white_space().allow_wrap());
1788             if split_is_empty {
1789                 // We're going to overflow the line.
1790                 overflowing = true;
1791                 inline_start_range = slice.text_run_range();
1792                 remaining_range = Range::new(slice.text_run_range().end(), ByteIndex(0));
1793                 remaining_range.extend_to(text_fragment_info.range.end());
1794             }
1795 
1796             // Check to see if we need to create an inline-end chunk.
1797             let slice_begin = remaining_range.begin();
1798             if slice_begin < text_fragment_info.range.end() {
1799                 // There still some things left over at the end of the line, so create the
1800                 // inline-end chunk.
1801                 let mut inline_end = remaining_range;
1802                 inline_end.extend_to(text_fragment_info.range.end());
1803                 inline_end_range = Some(inline_end);
1804                 debug!("calculate_split_position: splitting remainder with inline-end range={:?}",
1805                        inline_end);
1806             }
1807 
1808             // If we failed to find a suitable split point, we're on the verge of overflowing the
1809             // line.
1810             if split_is_empty || overflowing {
1811                 // If we've been instructed to retry at character boundaries (probably via
1812                 // `overflow-wrap: break-word`), do so.
1813                 if flags.contains(SplitOptions::RETRY_AT_CHARACTER_BOUNDARIES) {
1814                     let character_breaking_strategy =
1815                         text_fragment_info.run
1816                                           .character_slices_in_range(&text_fragment_info.range);
1817                     let mut flags = flags;
1818                     flags.remove(SplitOptions::RETRY_AT_CHARACTER_BOUNDARIES);
1819                     return self.calculate_split_position_using_breaking_strategy(
1820                         character_breaking_strategy,
1821                         max_inline_size,
1822                         flags)
1823                 }
1824 
1825                 // We aren't at the start of the line, so don't overflow. Let inline layout wrap to
1826                 // the next line instead.
1827                 if !flags.contains(SplitOptions::STARTS_LINE) {
1828                     return None
1829                 }
1830             }
1831 
1832             break
1833         }
1834 
1835         let split_is_empty = inline_start_range.is_empty() &&
1836                 !self.requires_line_break_afterward_if_wrapping_on_newlines();
1837         let inline_start = if !split_is_empty {
1838             Some(SplitInfo::new(inline_start_range, &**text_fragment_info))
1839         } else {
1840             None
1841         };
1842         let inline_end = inline_end_range.map(|inline_end_range| {
1843             SplitInfo::new(inline_end_range, &**text_fragment_info)
1844         });
1845 
1846         Some(SplitResult {
1847             inline_start: inline_start,
1848             inline_end: inline_end,
1849             text_run: text_fragment_info.run.clone(),
1850         })
1851     }
1852 
1853     /// The opposite of `calculate_split_position_using_breaking_strategy`: merges this fragment
1854     /// with the next one.
merge_with(&mut self, next_fragment: Fragment)1855     pub fn merge_with(&mut self, next_fragment: Fragment) {
1856         match (&mut self.specific, &next_fragment.specific) {
1857             (&mut SpecificFragmentInfo::ScannedText(ref mut this_info),
1858              &SpecificFragmentInfo::ScannedText(ref other_info)) => {
1859                 debug_assert!(Arc::ptr_eq(&this_info.run, &other_info.run));
1860                 this_info.range_end_including_stripped_whitespace =
1861                     other_info.range_end_including_stripped_whitespace;
1862                 if other_info.requires_line_break_afterward_if_wrapping_on_newlines() {
1863                     this_info.flags.insert(ScannedTextFlags::REQUIRES_LINE_BREAK_AFTERWARD_IF_WRAPPING_ON_NEWLINES);
1864                 }
1865                 if other_info.insertion_point.is_some() {
1866                     this_info.insertion_point = other_info.insertion_point;
1867                 }
1868                 self.border_padding.inline_end = next_fragment.border_padding.inline_end;
1869                 self.margin.inline_end = next_fragment.margin.inline_end;
1870             }
1871             _ => panic!("Can only merge two scanned-text fragments!"),
1872         }
1873         self.reset_text_range_and_inline_size();
1874         self.meld_with_next_inline_fragment(&next_fragment);
1875     }
1876 
1877     /// Restore any whitespace that was stripped from a text fragment, and recompute inline metrics
1878     /// if necessary.
reset_text_range_and_inline_size(&mut self)1879     pub fn reset_text_range_and_inline_size(&mut self) {
1880         if let SpecificFragmentInfo::ScannedText(ref mut info) = self.specific {
1881             if info.run.extra_word_spacing != Au(0) {
1882                 Arc::make_mut(&mut info.run).extra_word_spacing = Au(0);
1883             }
1884 
1885             // FIXME (mbrubeck): Do we need to restore leading too?
1886             let range_end = info.range_end_including_stripped_whitespace;
1887             if info.range.end() == range_end {
1888                 return
1889             }
1890             info.range.extend_to(range_end);
1891             info.content_size.inline = info.run.metrics_for_range(&info.range).advance_width;
1892             self.border_box.size.inline = info.content_size.inline +
1893                 self.border_padding.inline_start_end();
1894         }
1895     }
1896 
1897     /// Assigns replaced inline-size, padding, and margins for this fragment only if it is replaced
1898     /// content per CSS 2.1 § 10.3.2.
assign_replaced_inline_size_if_necessary(&mut self, container_inline_size: Au, container_block_size: Option<Au>)1899     pub fn assign_replaced_inline_size_if_necessary(&mut self,
1900                                                     container_inline_size: Au,
1901                                                     container_block_size: Option<Au>) {
1902         match self.specific {
1903             SpecificFragmentInfo::TruncatedFragment(ref t) if t.text_info.is_none() => return,
1904             SpecificFragmentInfo::Generic |
1905             SpecificFragmentInfo::GeneratedContent(_) |
1906             SpecificFragmentInfo::Table |
1907             SpecificFragmentInfo::TableCell |
1908             SpecificFragmentInfo::TableRow |
1909             SpecificFragmentInfo::TableWrapper |
1910             SpecificFragmentInfo::Multicol |
1911             SpecificFragmentInfo::MulticolColumn => return,
1912             SpecificFragmentInfo::TableColumn(_) => {
1913                 panic!("Table column fragments do not have inline size")
1914             }
1915             SpecificFragmentInfo::UnscannedText(_) => {
1916                 panic!("Unscanned text fragments should have been scanned by now!")
1917             }
1918             SpecificFragmentInfo::Canvas(_) |
1919             SpecificFragmentInfo::Image(_) |
1920             SpecificFragmentInfo::Iframe(_) |
1921             SpecificFragmentInfo::InlineBlock(_) |
1922             SpecificFragmentInfo::InlineAbsoluteHypothetical(_) |
1923             SpecificFragmentInfo::InlineAbsolute(_) |
1924             SpecificFragmentInfo::ScannedText(_) |
1925             SpecificFragmentInfo::TruncatedFragment(_) |
1926             SpecificFragmentInfo::Svg(_) => {}
1927         };
1928 
1929         match self.specific {
1930             // Inline blocks
1931             SpecificFragmentInfo::InlineAbsoluteHypothetical(ref mut info) => {
1932                 let block_flow = FlowRef::deref_mut(&mut info.flow_ref).as_mut_block();
1933                 block_flow.base.position.size.inline =
1934                     block_flow.base.intrinsic_inline_sizes.preferred_inline_size;
1935 
1936                 // This is a hypothetical box, so it takes up no space.
1937                 self.border_box.size.inline = Au(0);
1938             }
1939             SpecificFragmentInfo::InlineBlock(ref mut info) => {
1940                 let block_flow = FlowRef::deref_mut(&mut info.flow_ref).as_mut_block();
1941                 self.border_box.size.inline =
1942                     max(block_flow.base.intrinsic_inline_sizes.minimum_inline_size,
1943                         block_flow.base.intrinsic_inline_sizes.preferred_inline_size);
1944                 block_flow.base.block_container_inline_size = self.border_box.size.inline;
1945                 block_flow.base.block_container_writing_mode = self.style.writing_mode;
1946             }
1947             SpecificFragmentInfo::InlineAbsolute(ref mut info) => {
1948                 let block_flow = FlowRef::deref_mut(&mut info.flow_ref).as_mut_block();
1949                 self.border_box.size.inline =
1950                     max(block_flow.base.intrinsic_inline_sizes.minimum_inline_size,
1951                         block_flow.base.intrinsic_inline_sizes.preferred_inline_size);
1952                 block_flow.base.block_container_inline_size = self.border_box.size.inline;
1953                 block_flow.base.block_container_writing_mode = self.style.writing_mode;
1954             }
1955 
1956             // Text
1957             SpecificFragmentInfo::TruncatedFragment(ref t) if t.text_info.is_some() => {
1958                 let info = t.text_info.as_ref().unwrap();
1959                 // Scanned text fragments will have already had their content inline-sizes assigned
1960                 // by this point.
1961                 self.border_box.size.inline = info.content_size.inline +
1962                     self.border_padding.inline_start_end();
1963             }
1964             SpecificFragmentInfo::ScannedText(ref info) => {
1965                 // Scanned text fragments will have already had their content inline-sizes assigned
1966                 // by this point.
1967                 self.border_box.size.inline = info.content_size.inline +
1968                     self.border_padding.inline_start_end();
1969             }
1970 
1971             // Replaced elements
1972             _ if self.is_replaced() => {
1973                 let (inline_size, block_size) =
1974                     self.calculate_replaced_sizes(Some(container_inline_size), container_block_size);
1975                 self.border_box.size.inline = inline_size + self.border_padding.inline_start_end();
1976                 self.border_box.size.block = block_size + self.border_padding.block_start_end();
1977             }
1978 
1979             ref unhandled @ _ => panic!("this case should have been handled above: {:?}", unhandled),
1980         }
1981     }
1982 
1983     /// Assign block-size for this fragment if it is replaced content. The inline-size must have
1984     /// been assigned first.
1985     ///
1986     /// Ideally, this should follow CSS 2.1 § 10.6.2.
assign_replaced_block_size_if_necessary(&mut self)1987     pub fn assign_replaced_block_size_if_necessary(&mut self) {
1988         match self.specific {
1989             SpecificFragmentInfo::TruncatedFragment(ref t) if t.text_info.is_none() => return,
1990             SpecificFragmentInfo::Generic |
1991             SpecificFragmentInfo::GeneratedContent(_) |
1992             SpecificFragmentInfo::Table |
1993             SpecificFragmentInfo::TableCell |
1994             SpecificFragmentInfo::TableRow |
1995             SpecificFragmentInfo::TableWrapper |
1996             SpecificFragmentInfo::Multicol |
1997             SpecificFragmentInfo::MulticolColumn => return,
1998             SpecificFragmentInfo::TableColumn(_) => {
1999                 panic!("Table column fragments do not have block size")
2000             }
2001             SpecificFragmentInfo::UnscannedText(_) => {
2002                 panic!("Unscanned text fragments should have been scanned by now!")
2003             }
2004             SpecificFragmentInfo::Canvas(_) |
2005             SpecificFragmentInfo::Iframe(_) |
2006             SpecificFragmentInfo::Image(_) |
2007             SpecificFragmentInfo::InlineBlock(_) |
2008             SpecificFragmentInfo::InlineAbsoluteHypothetical(_) |
2009             SpecificFragmentInfo::InlineAbsolute(_) |
2010             SpecificFragmentInfo::ScannedText(_) |
2011             SpecificFragmentInfo::TruncatedFragment(_) |
2012             SpecificFragmentInfo::Svg(_) => {}
2013         }
2014 
2015         match self.specific {
2016             // Text
2017             SpecificFragmentInfo::TruncatedFragment(ref t) if t.text_info.is_some() => {
2018                 let info = t.text_info.as_ref().unwrap();
2019                 // Scanned text fragments' content block-sizes are calculated by the text run
2020                 // scanner during flow construction.
2021                 self.border_box.size.block = info.content_size.block +
2022                     self.border_padding.block_start_end();
2023             }
2024             SpecificFragmentInfo::ScannedText(ref info) => {
2025                 // Scanned text fragments' content block-sizes are calculated by the text run
2026                 // scanner during flow construction.
2027                 self.border_box.size.block = info.content_size.block +
2028                     self.border_padding.block_start_end();
2029             }
2030 
2031             // Inline blocks
2032             SpecificFragmentInfo::InlineBlock(ref mut info) => {
2033                 // Not the primary fragment, so we do not take the noncontent size into account.
2034                 let block_flow = FlowRef::deref_mut(&mut info.flow_ref).as_block();
2035                 self.border_box.size.block = block_flow.base.position.size.block +
2036                     block_flow.fragment.margin.block_start_end()
2037             }
2038             SpecificFragmentInfo::InlineAbsoluteHypothetical(ref mut info) => {
2039                 // Not the primary fragment, so we do not take the noncontent size into account.
2040                 let block_flow = FlowRef::deref_mut(&mut info.flow_ref).as_block();
2041                 self.border_box.size.block = block_flow.base.position.size.block;
2042             }
2043             SpecificFragmentInfo::InlineAbsolute(ref mut info) => {
2044                 // Not the primary fragment, so we do not take the noncontent size into account.
2045                 let block_flow = FlowRef::deref_mut(&mut info.flow_ref).as_block();
2046                 self.border_box.size.block = block_flow.base.position.size.block +
2047                     block_flow.fragment.margin.block_start_end()
2048             }
2049 
2050             // Replaced elements
2051             _ if self.is_replaced() => {},
2052 
2053             ref unhandled @ _ => panic!("should have been handled above: {:?}", unhandled),
2054         }
2055     }
2056 
2057     /// Returns true if this fragment is replaced content.
is_replaced(&self) -> bool2058     pub fn is_replaced(&self) -> bool {
2059         match self.specific {
2060             SpecificFragmentInfo::Iframe(_) |
2061             SpecificFragmentInfo::Canvas(_) |
2062             SpecificFragmentInfo::Image(_) |
2063             SpecificFragmentInfo::Svg(_) => true,
2064             _ => false
2065         }
2066     }
2067 
2068     /// Returns true if this fragment is replaced content or an inline-block or false otherwise.
is_replaced_or_inline_block(&self) -> bool2069     pub fn is_replaced_or_inline_block(&self) -> bool {
2070         match self.specific {
2071             SpecificFragmentInfo::InlineAbsoluteHypothetical(_) |
2072             SpecificFragmentInfo::InlineBlock(_) => true,
2073             _ => self.is_replaced(),
2074         }
2075     }
2076 
2077     /// Calculates block-size above baseline, depth below baseline, and ascent for this fragment
2078     /// when used in an inline formatting context. See CSS 2.1 § 10.8.1.
2079     ///
2080     /// This does not take `vertical-align` into account. For that, use `aligned_inline_metrics()`.
content_inline_metrics(&self, layout_context: &LayoutContext) -> InlineMetrics2081     fn content_inline_metrics(&self, layout_context: &LayoutContext) -> InlineMetrics {
2082         // CSS 2.1 § 10.8: "The height of each inline-level box in the line box is
2083         // calculated. For replaced elements, inline-block elements, and inline-table
2084         // elements, this is the height of their margin box."
2085         //
2086         // FIXME(pcwalton): We have to handle `Generic` and `GeneratedContent` here to avoid
2087         // crashing in a couple of `css21_dev/html4/content-` WPTs, but I don't see how those two
2088         // fragment types should end up inside inlines. (In the case of `GeneratedContent`, those
2089         // fragment types should have been resolved by now…)
2090         let inline_metrics = match self.specific {
2091             SpecificFragmentInfo::Canvas(_) | SpecificFragmentInfo::Iframe(_) |
2092             SpecificFragmentInfo::Image(_) | SpecificFragmentInfo::Svg(_) |
2093             SpecificFragmentInfo::Generic | SpecificFragmentInfo::GeneratedContent(_) => {
2094                 let ascent = self.border_box.size.block + self.margin.block_end;
2095                 InlineMetrics {
2096                     space_above_baseline: ascent + self.margin.block_start,
2097                     space_below_baseline: Au(0),
2098                     ascent: ascent,
2099                 }
2100             }
2101             SpecificFragmentInfo::TruncatedFragment(ref t) if t.text_info.is_some() => {
2102                 let info = t.text_info.as_ref().unwrap();
2103                 inline_metrics_of_text(info, self, layout_context)
2104             }
2105             SpecificFragmentInfo::ScannedText(ref info) => {
2106                 inline_metrics_of_text(info, self, layout_context)
2107             }
2108             SpecificFragmentInfo::InlineBlock(ref info) => {
2109                 inline_metrics_of_block(&info.flow_ref, &*self.style)
2110             }
2111             SpecificFragmentInfo::InlineAbsoluteHypothetical(ref info) => {
2112                 inline_metrics_of_block(&info.flow_ref, &*self.style)
2113             }
2114             SpecificFragmentInfo::TruncatedFragment(..) |
2115             SpecificFragmentInfo::InlineAbsolute(_) => {
2116                 InlineMetrics::new(Au(0), Au(0), Au(0))
2117             }
2118             SpecificFragmentInfo::Table |
2119             SpecificFragmentInfo::TableCell |
2120             SpecificFragmentInfo::TableColumn(_) |
2121             SpecificFragmentInfo::TableRow |
2122             SpecificFragmentInfo::TableWrapper |
2123             SpecificFragmentInfo::Multicol |
2124             SpecificFragmentInfo::MulticolColumn |
2125             SpecificFragmentInfo::UnscannedText(_) => {
2126                 unreachable!("Shouldn't see fragments of this type here!")
2127             }
2128         };
2129         return inline_metrics;
2130 
2131         fn inline_metrics_of_text(info: &ScannedTextFragmentInfo, self_: &Fragment,
2132                                   layout_context: &LayoutContext) -> InlineMetrics {
2133             // Fragments with no glyphs don't contribute any inline metrics.
2134             // TODO: Filter out these fragments during flow construction?
2135             if info.insertion_point.is_none() && info.content_size.inline == Au(0) {
2136                 return InlineMetrics::new(Au(0), Au(0), Au(0));
2137             }
2138             // See CSS 2.1 § 10.8.1.
2139             let font_metrics = with_thread_local_font_context(layout_context, |font_context| {
2140                 text::font_metrics_for_style(font_context, self_.style.clone_font())
2141             });
2142             let line_height = text::line_height_from_style(&*self_.style, &font_metrics);
2143             InlineMetrics::from_font_metrics(&info.run.font_metrics, line_height)
2144         }
2145 
2146         fn inline_metrics_of_block(flow: &FlowRef, style: &ComputedValues) -> InlineMetrics {
2147             // CSS 2.1 § 10.8: "The height of each inline-level box in the line box is calculated.
2148             // For replaced elements, inline-block elements, and inline-table elements, this is the
2149             // height of their margin box."
2150             //
2151             // CSS 2.1 § 10.8.1: "The baseline of an 'inline-block' is the baseline of its last
2152             // line box in the normal flow, unless it has either no in-flow line boxes or if its
2153             // 'overflow' property has a computed value other than 'visible', in which case the
2154             // baseline is the bottom margin edge."
2155             //
2156             // NB: We must use `block_flow.fragment.border_box.size.block` here instead of
2157             // `block_flow.base.position.size.block` because sometimes the latter is late-computed
2158             // and isn't up to date at this point.
2159             let block_flow = flow.as_block();
2160             let start_margin = block_flow.fragment.margin.block_start;
2161             let end_margin = block_flow.fragment.margin.block_end;
2162             let border_box_block_size = block_flow.fragment.border_box.size.block;
2163 
2164             //     --------
2165             //      margin
2166             // top -------- + +
2167             //              | |
2168             //              | |
2169             //  A  ..pogo.. | + baseline_offset_of_last_line_box_in_flow()
2170             //              |
2171             //     -------- + border_box_block_size
2172             //      margin
2173             //  B  --------
2174             //
2175             // § 10.8.1 says that the baseline (and thus ascent, which is the
2176             // distance from the baseline to the top) should be A if it has an
2177             // in-flow line box and if overflow: visible, and B otherwise.
2178             let ascent =
2179                 match (flow.baseline_offset_of_last_line_box_in_flow(),
2180                        style.get_box().overflow_y) {
2181                 // Case A
2182                 (Some(baseline_offset), StyleOverflow::Visible) => baseline_offset,
2183                 // Case B
2184                 _ => border_box_block_size + end_margin,
2185             };
2186 
2187             let space_below_baseline = border_box_block_size + end_margin - ascent;
2188             let space_above_baseline = ascent + start_margin;
2189 
2190             InlineMetrics::new(space_above_baseline, space_below_baseline, ascent)
2191         }
2192     }
2193 
2194     /// Calculates the offset from the baseline that applies to this fragment due to
2195     /// `vertical-align`. Positive values represent downward displacement.
2196     ///
2197     /// If `actual_line_metrics` is supplied, then these metrics are used to determine the
2198     /// displacement of the fragment when `top` or `bottom` `vertical-align` values are
2199     /// encountered. If this is not supplied, then `top` and `bottom` values are ignored.
vertical_alignment_offset(&self, layout_context: &LayoutContext, content_inline_metrics: &InlineMetrics, minimum_line_metrics: &LineMetrics, actual_line_metrics: Option<&LineMetrics>) -> Au2200     fn vertical_alignment_offset(&self,
2201                                  layout_context: &LayoutContext,
2202                                  content_inline_metrics: &InlineMetrics,
2203                                  minimum_line_metrics: &LineMetrics,
2204                                  actual_line_metrics: Option<&LineMetrics>)
2205                                  -> Au {
2206         let mut offset = Au(0);
2207         for style in self.inline_styles() {
2208             // If any of the inline styles say `top` or `bottom`, adjust the vertical align
2209             // appropriately.
2210             //
2211             // FIXME(#5624, pcwalton): This passes our current reftests but isn't the right thing
2212             // to do.
2213             match style.get_box().vertical_align {
2214                 VerticalAlign::Baseline => {}
2215                 VerticalAlign::Middle => {
2216                     let font_metrics = with_thread_local_font_context(layout_context, |font_context| {
2217                         text::font_metrics_for_style(font_context, self.style.clone_font())
2218                     });
2219                     offset += (content_inline_metrics.ascent -
2220                                content_inline_metrics.space_below_baseline -
2221                                font_metrics.x_height).scale_by(0.5)
2222                 }
2223                 VerticalAlign::Sub => {
2224                     offset += minimum_line_metrics.space_needed()
2225                                                   .scale_by(FONT_SUBSCRIPT_OFFSET_RATIO)
2226                 }
2227                 VerticalAlign::Super => {
2228                     offset -= minimum_line_metrics.space_needed()
2229                                                   .scale_by(FONT_SUPERSCRIPT_OFFSET_RATIO)
2230                 }
2231                 VerticalAlign::TextTop => {
2232                     offset = self.content_inline_metrics(layout_context).ascent -
2233                         minimum_line_metrics.space_above_baseline
2234                 }
2235                 VerticalAlign::TextBottom => {
2236                     offset = minimum_line_metrics.space_below_baseline -
2237                         self.content_inline_metrics(layout_context).space_below_baseline
2238                 }
2239                 VerticalAlign::Top => {
2240                     if let Some(actual_line_metrics) = actual_line_metrics {
2241                         offset = content_inline_metrics.ascent -
2242                             actual_line_metrics.space_above_baseline
2243                     }
2244                 }
2245                 VerticalAlign::Bottom => {
2246                     if let Some(actual_line_metrics) = actual_line_metrics {
2247                         offset = actual_line_metrics.space_below_baseline -
2248                             content_inline_metrics.space_below_baseline
2249                     }
2250                 }
2251                 VerticalAlign::Length(LengthOrPercentage::Length(length)) => {
2252                     offset -= Au::from(length)
2253                 }
2254                 VerticalAlign::Length(LengthOrPercentage::Percentage(percentage)) => {
2255                     offset -= minimum_line_metrics.space_needed().scale_by(percentage.0)
2256                 }
2257                 VerticalAlign::Length(LengthOrPercentage::Calc(formula)) => {
2258                     offset -= formula.to_used_value(Some(minimum_line_metrics.space_needed())).unwrap()
2259                 }
2260             }
2261         }
2262         offset
2263     }
2264 
2265     /// Calculates block-size above baseline, depth below baseline, and ascent for this fragment
2266     /// when used in an inline formatting context, taking `vertical-align` (other than `top` or
2267     /// `bottom`) into account. See CSS 2.1 § 10.8.1.
2268     ///
2269     /// If `actual_line_metrics` is supplied, then these metrics are used to determine the
2270     /// displacement of the fragment when `top` or `bottom` `vertical-align` values are
2271     /// encountered. If this is not supplied, then `top` and `bottom` values are ignored.
aligned_inline_metrics(&self, layout_context: &LayoutContext, minimum_line_metrics: &LineMetrics, actual_line_metrics: Option<&LineMetrics>) -> InlineMetrics2272     pub fn aligned_inline_metrics(&self,
2273                                   layout_context: &LayoutContext,
2274                                   minimum_line_metrics: &LineMetrics,
2275                                   actual_line_metrics: Option<&LineMetrics>)
2276                                   -> InlineMetrics {
2277         let content_inline_metrics = self.content_inline_metrics(layout_context);
2278         let vertical_alignment_offset = self.vertical_alignment_offset(layout_context,
2279                                                                        &content_inline_metrics,
2280                                                                        minimum_line_metrics,
2281                                                                        actual_line_metrics);
2282         let mut space_above_baseline = match actual_line_metrics {
2283             None => content_inline_metrics.space_above_baseline,
2284             Some(actual_line_metrics) => actual_line_metrics.space_above_baseline,
2285         };
2286         space_above_baseline = space_above_baseline - vertical_alignment_offset;
2287         let space_below_baseline = content_inline_metrics.space_below_baseline +
2288             vertical_alignment_offset;
2289         let ascent = content_inline_metrics.ascent - vertical_alignment_offset;
2290         InlineMetrics::new(space_above_baseline, space_below_baseline, ascent)
2291     }
2292 
2293     /// Returns true if this fragment is a hypothetical box. See CSS 2.1 § 10.3.7.
is_hypothetical(&self) -> bool2294     pub fn is_hypothetical(&self) -> bool {
2295         match self.specific {
2296             SpecificFragmentInfo::InlineAbsoluteHypothetical(_) => true,
2297             _ => false,
2298         }
2299     }
2300 
2301     /// Returns true if this fragment can merge with another immediately-following fragment or
2302     /// false otherwise.
can_merge_with_fragment(&self, other: &Fragment) -> bool2303     pub fn can_merge_with_fragment(&self, other: &Fragment) -> bool {
2304         match (&self.specific, &other.specific) {
2305             (&SpecificFragmentInfo::UnscannedText(ref first_unscanned_text),
2306              &SpecificFragmentInfo::UnscannedText(_)) => {
2307                 // FIXME: Should probably use a whitelist of styles that can safely differ (#3165)
2308                 if self.style().get_font() != other.style().get_font() ||
2309                         self.text_decoration_line() != other.text_decoration_line() ||
2310                         self.white_space() != other.white_space() ||
2311                         self.color() != other.color() {
2312                     return false
2313                 }
2314 
2315                 if first_unscanned_text.text.ends_with('\n') {
2316                     return false
2317                 }
2318 
2319                 // If this node has any styles that have border/padding/margins on the following
2320                 // side, then we can't merge with the next fragment.
2321                 if let Some(ref inline_context) = self.inline_context {
2322                     for inline_context_node in inline_context.nodes.iter() {
2323                         if !inline_context_node.flags.contains(InlineFragmentNodeFlags::LAST_FRAGMENT_OF_ELEMENT) {
2324                             continue
2325                         }
2326                         if inline_context_node.style.logical_margin().inline_end !=
2327                                 LengthOrPercentageOrAuto::Length(Length::new(0.)) {
2328                             return false
2329                         }
2330                         if inline_context_node.style.logical_padding().inline_end !=
2331                                 LengthOrPercentage::Length(Length::new(0.)) {
2332                             return false
2333                         }
2334                         if inline_context_node.style.logical_border_width().inline_end != Au(0) {
2335                             return false
2336                         }
2337                     }
2338                 }
2339 
2340                 // If the next fragment has any styles that have border/padding/margins on the
2341                 // preceding side, then it can't merge with us.
2342                 if let Some(ref inline_context) = other.inline_context {
2343                     for inline_context_node in inline_context.nodes.iter() {
2344                         if !inline_context_node.flags.contains(InlineFragmentNodeFlags::FIRST_FRAGMENT_OF_ELEMENT) {
2345                             continue
2346                         }
2347                         if inline_context_node.style.logical_margin().inline_start !=
2348                                 LengthOrPercentageOrAuto::Length(Length::new(0.)) {
2349                             return false
2350                         }
2351                         if inline_context_node.style.logical_padding().inline_start !=
2352                                 LengthOrPercentage::Length(Length::new(0.)) {
2353                             return false
2354                         }
2355                         if inline_context_node.style.logical_border_width().inline_start != Au(0) {
2356                             return false
2357                         }
2358                     }
2359                 }
2360 
2361                 true
2362             }
2363             _ => false,
2364         }
2365     }
2366 
2367     /// Returns true if and only if this is the *primary fragment* for the fragment's style object
2368     /// (conceptually, though style sharing makes this not really true, of course). The primary
2369     /// fragment is the one that draws backgrounds, borders, etc., and takes borders, padding and
2370     /// margins into account. Every style object has at most one primary fragment.
2371     ///
2372     /// At present, all fragments are primary fragments except for inline-block and table wrapper
2373     /// fragments. Inline-block fragments are not primary fragments because the corresponding block
2374     /// flow is the primary fragment, while table wrapper fragments are not primary fragments
2375     /// because the corresponding table flow is the primary fragment.
is_primary_fragment(&self) -> bool2376     pub fn is_primary_fragment(&self) -> bool {
2377         match self.specific {
2378             SpecificFragmentInfo::InlineBlock(_) |
2379             SpecificFragmentInfo::InlineAbsoluteHypothetical(_) |
2380             SpecificFragmentInfo::InlineAbsolute(_) |
2381             SpecificFragmentInfo::MulticolColumn |
2382             SpecificFragmentInfo::TableWrapper => false,
2383             SpecificFragmentInfo::Canvas(_) |
2384             SpecificFragmentInfo::Generic |
2385             SpecificFragmentInfo::GeneratedContent(_) |
2386             SpecificFragmentInfo::Iframe(_) |
2387             SpecificFragmentInfo::Image(_) |
2388             SpecificFragmentInfo::ScannedText(_) |
2389             SpecificFragmentInfo::Svg(_) |
2390             SpecificFragmentInfo::Table |
2391             SpecificFragmentInfo::TableCell |
2392             SpecificFragmentInfo::TableColumn(_) |
2393             SpecificFragmentInfo::TableRow |
2394             SpecificFragmentInfo::TruncatedFragment(_) |
2395             SpecificFragmentInfo::Multicol |
2396             SpecificFragmentInfo::UnscannedText(_) => true,
2397         }
2398     }
2399 
2400     /// Determines the inline sizes of inline-block fragments. These cannot be fully computed until
2401     /// inline size assignment has run for the child flow: thus it is computed "late", during
2402     /// block size assignment.
update_late_computed_replaced_inline_size_if_necessary(&mut self)2403     pub fn update_late_computed_replaced_inline_size_if_necessary(&mut self) {
2404         if let SpecificFragmentInfo::InlineBlock(ref mut inline_block_info) = self.specific {
2405             let block_flow = FlowRef::deref_mut(&mut inline_block_info.flow_ref).as_block();
2406             self.border_box.size.inline = block_flow.fragment.margin_box_inline_size();
2407         }
2408     }
2409 
update_late_computed_inline_position_if_necessary(&mut self)2410     pub fn update_late_computed_inline_position_if_necessary(&mut self) {
2411         if let SpecificFragmentInfo::InlineAbsoluteHypothetical(ref mut info) = self.specific {
2412             let position = self.border_box.start.i;
2413             FlowRef::deref_mut(&mut info.flow_ref)
2414                 .update_late_computed_inline_position_if_necessary(position)
2415         }
2416     }
2417 
update_late_computed_block_position_if_necessary(&mut self)2418     pub fn update_late_computed_block_position_if_necessary(&mut self) {
2419         if let SpecificFragmentInfo::InlineAbsoluteHypothetical(ref mut info) = self.specific {
2420             let position = self.border_box.start.b;
2421             FlowRef::deref_mut(&mut info.flow_ref)
2422                 .update_late_computed_block_position_if_necessary(position)
2423         }
2424     }
2425 
repair_style(&mut self, new_style: &ServoArc<ComputedValues>)2426     pub fn repair_style(&mut self, new_style: &ServoArc<ComputedValues>) {
2427         self.style = (*new_style).clone()
2428     }
2429 
2430     /// Given the stacking-context-relative position of the containing flow, returns the border box
2431     /// of this fragment relative to the parent stacking context. This takes `position: relative`
2432     /// into account.
2433     ///
2434     /// If `coordinate_system` is `Parent`, this returns the border box in the parent stacking
2435     /// context's coordinate system. Otherwise, if `coordinate_system` is `Own` and this fragment
2436     /// establishes a stacking context itself, this returns a border box anchored at (0, 0). (If
2437     /// this fragment does not establish a stacking context, then it always belongs to its parent
2438     /// stacking context and thus `coordinate_system` is ignored.)
2439     ///
2440     /// This is the method you should use for display list construction as well as
2441     /// `getBoundingClientRect()` and so forth.
stacking_relative_border_box(&self, stacking_relative_flow_origin: &Vector2D<Au>, relative_containing_block_size: &LogicalSize<Au>, relative_containing_block_mode: WritingMode, coordinate_system: CoordinateSystem) -> Rect<Au>2442     pub fn stacking_relative_border_box(&self,
2443                                         stacking_relative_flow_origin: &Vector2D<Au>,
2444                                         relative_containing_block_size: &LogicalSize<Au>,
2445                                         relative_containing_block_mode: WritingMode,
2446                                         coordinate_system: CoordinateSystem)
2447                                         -> Rect<Au> {
2448         let container_size =
2449             relative_containing_block_size.to_physical(relative_containing_block_mode);
2450         let border_box = self.border_box.to_physical(self.style.writing_mode, container_size);
2451         if coordinate_system == CoordinateSystem::Own && self.establishes_stacking_context() {
2452             return Rect::new(Point2D::zero(), border_box.size)
2453         }
2454 
2455         // FIXME(pcwalton): This can double-count relative position sometimes for inlines (e.g.
2456         // `<div style="position:relative">x</div>`, because the `position:relative` trickles down
2457         // to the inline flow. Possibly we should extend the notion of "primary fragment" to fix
2458         // this.
2459         let relative_position = self.relative_position(relative_containing_block_size);
2460         border_box.translate_by_size(&relative_position.to_physical(self.style.writing_mode))
2461                   .translate(&stacking_relative_flow_origin)
2462     }
2463 
2464     /// Given the stacking-context-relative border box, returns the stacking-context-relative
2465     /// content box.
stacking_relative_content_box(&self, stacking_relative_border_box: &Rect<Au>) -> Rect<Au>2466     pub fn stacking_relative_content_box(&self, stacking_relative_border_box: &Rect<Au>)
2467                                          -> Rect<Au> {
2468         let border_padding = self.border_padding.to_physical(self.style.writing_mode);
2469         Rect::new(Point2D::new(stacking_relative_border_box.origin.x + border_padding.left,
2470                                stacking_relative_border_box.origin.y + border_padding.top),
2471                   Size2D::new(stacking_relative_border_box.size.width - border_padding.horizontal(),
2472                               stacking_relative_border_box.size.height - border_padding.vertical()))
2473     }
2474 
2475     /// Returns true if this fragment has a filter, transform, or perspective property set.
has_filter_transform_or_perspective(&self) -> bool2476     pub fn has_filter_transform_or_perspective(&self) -> bool {
2477            !self.style().get_box().transform.0.is_empty() ||
2478            !self.style().get_effects().filter.0.is_empty() ||
2479            self.style().get_box().perspective != Perspective::None
2480     }
2481 
2482     /// Returns true if this fragment establishes a new stacking context and false otherwise.
establishes_stacking_context(&self) -> bool2483     pub fn establishes_stacking_context(&self) -> bool {
2484         // Text fragments shouldn't create stacking contexts.
2485         match self.specific {
2486             SpecificFragmentInfo::TruncatedFragment(_) |
2487             SpecificFragmentInfo::ScannedText(_) |
2488             SpecificFragmentInfo::UnscannedText(_) => return false,
2489             _ => {}
2490         }
2491 
2492         if self.style().get_effects().opacity != 1.0 {
2493             return true
2494         }
2495 
2496         if self.style().get_effects().mix_blend_mode != MixBlendMode::Normal {
2497             return true
2498         }
2499 
2500         if self.has_filter_transform_or_perspective() {
2501             return true;
2502         }
2503 
2504         if self.style().get_box().transform_style == TransformStyle::Preserve3d ||
2505            self.style().overrides_transform_style() {
2506             return true
2507         }
2508 
2509         // Fixed position and sticky position always create stacking contexts.
2510         if self.style().get_box().position == Position::Fixed ||
2511            self.style().get_box().position == Position::Sticky  {
2512             return true
2513         }
2514 
2515         // Statically positioned fragments don't establish stacking contexts if the previous
2516         // conditions are not fulfilled. Furthermore, z-index doesn't apply to statically
2517         // positioned fragments.
2518         if self.style().get_box().position == Position::Static {
2519             return false;
2520         }
2521 
2522         // For absolutely and relatively positioned fragments we only establish a stacking
2523         // context if there is a z-index set.
2524         // See https://www.w3.org/TR/CSS2/visuren.html#z-index
2525         !self.style().get_position().z_index.is_auto()
2526     }
2527 
2528     // Get the effective z-index of this fragment. Z-indices only apply to positioned element
2529     // per CSS 2 9.9.1 (http://www.w3.org/TR/CSS2/visuren.html#z-index), so this value may differ
2530     // from the value specified in the style.
effective_z_index(&self) -> i322531     pub fn effective_z_index(&self) -> i32 {
2532         match self.style().get_box().position {
2533             Position::Static => {},
2534             _ => return self.style().get_position().z_index.integer_or(0),
2535         }
2536 
2537         if !self.style().get_box().transform.0.is_empty() {
2538             return self.style().get_position().z_index.integer_or(0);
2539         }
2540 
2541         match self.style().get_box().display {
2542             Display::Flex => self.style().get_position().z_index.integer_or(0),
2543             _ => 0,
2544         }
2545     }
2546 
2547     /// Computes the overflow rect of this fragment relative to the start of the flow.
compute_overflow(&self, flow_size: &Size2D<Au>, relative_containing_block_size: &LogicalSize<Au>) -> Overflow2548     pub fn compute_overflow(&self,
2549                             flow_size: &Size2D<Au>,
2550                             relative_containing_block_size: &LogicalSize<Au>)
2551                             -> Overflow {
2552         let mut border_box = self.border_box.to_physical(self.style.writing_mode, *flow_size);
2553 
2554         // Relative position can cause us to draw outside our border box.
2555         //
2556         // FIXME(pcwalton): I'm not a fan of the way this makes us crawl though so many styles all
2557         // the time. Can't we handle relative positioning by just adjusting `border_box`?
2558         let relative_position = self.relative_position(relative_containing_block_size);
2559         border_box =
2560             border_box.translate_by_size(&relative_position.to_physical(self.style.writing_mode));
2561         let mut overflow = Overflow::from_rect(&border_box);
2562 
2563         // Box shadows cause us to draw outside our border box.
2564         for box_shadow in &self.style().get_effects().box_shadow.0 {
2565             let offset = Vector2D::new(Au::from(box_shadow.base.horizontal),
2566                                        Au::from(box_shadow.base.vertical));
2567             let inflation = Au::from(box_shadow.spread) +
2568                             Au::from(box_shadow.base.blur) * BLUR_INFLATION_FACTOR;
2569             overflow.paint = overflow.paint.union(&border_box.translate(&offset)
2570                                                              .inflate(inflation, inflation))
2571         }
2572 
2573         // Outlines cause us to draw outside our border box.
2574         let outline_width = Au::from(self.style.get_outline().outline_width);
2575         if outline_width != Au(0) {
2576             overflow.paint = overflow.paint.union(&border_box.inflate(outline_width,
2577                                                                       outline_width))
2578         }
2579 
2580         // Include the overflow of the block flow, if any.
2581         match self.specific {
2582             SpecificFragmentInfo::InlineBlock(ref info) => {
2583                 let block_flow = info.flow_ref.as_block();
2584                 overflow.union(&block_flow.base().overflow);
2585             }
2586             SpecificFragmentInfo::InlineAbsolute(ref info) => {
2587                 let block_flow = info.flow_ref.as_block();
2588                 overflow.union(&block_flow.base().overflow);
2589             }
2590             _ => (),
2591         }
2592 
2593         // FIXME(pcwalton): Sometimes excessively fancy glyphs can make us draw outside our border
2594         // box too.
2595         overflow
2596     }
2597 
requires_line_break_afterward_if_wrapping_on_newlines(&self) -> bool2598     pub fn requires_line_break_afterward_if_wrapping_on_newlines(&self) -> bool {
2599         match self.specific {
2600             SpecificFragmentInfo::TruncatedFragment(ref t) if t.text_info.is_some() => {
2601                 let text = t.text_info.as_ref().unwrap();
2602                 text.requires_line_break_afterward_if_wrapping_on_newlines()
2603             }
2604             SpecificFragmentInfo::ScannedText(ref text) => {
2605                 text.requires_line_break_afterward_if_wrapping_on_newlines()
2606             }
2607             _ => false,
2608         }
2609     }
2610 
strip_leading_whitespace_if_necessary(&mut self) -> WhitespaceStrippingResult2611     pub fn strip_leading_whitespace_if_necessary(&mut self) -> WhitespaceStrippingResult {
2612         if self.white_space().preserve_spaces() {
2613             return WhitespaceStrippingResult::RetainFragment
2614         }
2615 
2616         return match self.specific {
2617             SpecificFragmentInfo::TruncatedFragment(ref mut t) if t.text_info.is_some() => {
2618                 let scanned_text_fragment_info = t.text_info.as_mut().unwrap();
2619                 scanned_text(scanned_text_fragment_info, &mut self.border_box)
2620             }
2621             SpecificFragmentInfo::ScannedText(ref mut scanned_text_fragment_info) => {
2622                 scanned_text(scanned_text_fragment_info, &mut self.border_box)
2623             }
2624             SpecificFragmentInfo::UnscannedText(ref mut unscanned_text_fragment_info) => {
2625                 let mut new_text_string = String::new();
2626                 let mut modified = false;
2627                 for (i, character) in unscanned_text_fragment_info.text.char_indices() {
2628                     if gfx::text::util::is_bidi_control(character) {
2629                         new_text_string.push(character);
2630                         continue
2631                     }
2632                     if char_is_whitespace(character) {
2633                         modified = true;
2634                         continue
2635                     }
2636                     // Finished processing leading control chars and whitespace.
2637                     if modified {
2638                         new_text_string.push_str(&unscanned_text_fragment_info.text[i..]);
2639                     }
2640                     break
2641                 }
2642                 if modified {
2643                     unscanned_text_fragment_info.text = new_text_string.into_boxed_str();
2644                 }
2645 
2646                 WhitespaceStrippingResult::from_unscanned_text_fragment_info(
2647                     &unscanned_text_fragment_info)
2648             }
2649             _ => WhitespaceStrippingResult::RetainFragment,
2650         };
2651 
2652         fn scanned_text(scanned_text_fragment_info: &mut ScannedTextFragmentInfo,
2653                         border_box: &mut LogicalRect<Au>)
2654                         -> WhitespaceStrippingResult {
2655             let leading_whitespace_byte_count = scanned_text_fragment_info.text()
2656                 .find(|c| !char_is_whitespace(c))
2657                 .unwrap_or(scanned_text_fragment_info.text().len());
2658 
2659             let whitespace_len = ByteIndex(leading_whitespace_byte_count as isize);
2660             let whitespace_range = Range::new(scanned_text_fragment_info.range.begin(),
2661                                               whitespace_len);
2662             let text_bounds =
2663                 scanned_text_fragment_info.run.metrics_for_range(&whitespace_range).bounding_box;
2664             border_box.size.inline = border_box.size.inline - text_bounds.size.width;
2665             scanned_text_fragment_info.content_size.inline =
2666                 scanned_text_fragment_info.content_size.inline - text_bounds.size.width;
2667 
2668             scanned_text_fragment_info.range.adjust_by(whitespace_len, -whitespace_len);
2669 
2670             WhitespaceStrippingResult::RetainFragment
2671         }
2672     }
2673 
2674     /// Returns true if the entire fragment was stripped.
strip_trailing_whitespace_if_necessary(&mut self) -> WhitespaceStrippingResult2675     pub fn strip_trailing_whitespace_if_necessary(&mut self) -> WhitespaceStrippingResult {
2676         if self.white_space().preserve_spaces() {
2677             return WhitespaceStrippingResult::RetainFragment
2678         }
2679 
2680         return match self.specific {
2681             SpecificFragmentInfo::TruncatedFragment(ref mut t) if t.text_info.is_some() => {
2682                 let scanned_text_fragment_info = t.text_info.as_mut().unwrap();
2683                 scanned_text(scanned_text_fragment_info, &mut self.border_box)
2684             }
2685             SpecificFragmentInfo::ScannedText(ref mut scanned_text_fragment_info) => {
2686                 scanned_text(scanned_text_fragment_info, &mut self.border_box)
2687             }
2688             SpecificFragmentInfo::UnscannedText(ref mut unscanned_text_fragment_info) => {
2689                 let mut trailing_bidi_control_characters_to_retain = Vec::new();
2690                 let (mut modified, mut last_character_index) = (true, 0);
2691                 for (i, character) in unscanned_text_fragment_info.text.char_indices().rev() {
2692                     if gfx::text::util::is_bidi_control(character) {
2693                         trailing_bidi_control_characters_to_retain.push(character);
2694                         continue
2695                     }
2696                     if char_is_whitespace(character) {
2697                         modified = true;
2698                         continue
2699                     }
2700                     last_character_index = i + character.len_utf8();
2701                     break
2702                 }
2703                 if modified {
2704                     let mut text = unscanned_text_fragment_info.text.to_string();
2705                     text.truncate(last_character_index);
2706                     for character in trailing_bidi_control_characters_to_retain.iter().rev() {
2707                         text.push(*character);
2708                     }
2709                     unscanned_text_fragment_info.text = text.into_boxed_str();
2710                 }
2711 
2712                 WhitespaceStrippingResult::from_unscanned_text_fragment_info(
2713                     &unscanned_text_fragment_info)
2714             }
2715             _ => WhitespaceStrippingResult::RetainFragment,
2716         };
2717 
2718         fn scanned_text(scanned_text_fragment_info: &mut ScannedTextFragmentInfo,
2719                         border_box: &mut LogicalRect<Au>)
2720                         -> WhitespaceStrippingResult {
2721                 let mut trailing_whitespace_start_byte = 0;
2722                 for (i, c) in scanned_text_fragment_info.text().char_indices().rev() {
2723                     if !char_is_whitespace(c) {
2724                         trailing_whitespace_start_byte = i + c.len_utf8();
2725                         break;
2726                     }
2727                 }
2728                 let whitespace_start = ByteIndex(trailing_whitespace_start_byte as isize);
2729                 let whitespace_len = scanned_text_fragment_info.range.length() - whitespace_start;
2730                 let mut whitespace_range = Range::new(whitespace_start, whitespace_len);
2731                 whitespace_range.shift_by(scanned_text_fragment_info.range.begin());
2732 
2733                 let text_bounds = scanned_text_fragment_info.run
2734                                                         .metrics_for_range(&whitespace_range)
2735                                                         .bounding_box;
2736                 border_box.size.inline -= text_bounds.size.width;
2737                 scanned_text_fragment_info.content_size.inline -= text_bounds.size.width;
2738 
2739                 scanned_text_fragment_info.range.extend_by(-whitespace_len);
2740                 WhitespaceStrippingResult::RetainFragment
2741         }
2742     }
2743 
inline_styles(&self) -> InlineStyleIterator2744     pub fn inline_styles(&self) -> InlineStyleIterator {
2745         InlineStyleIterator::new(self)
2746     }
2747 
2748     /// Returns the inline-size of this fragment's margin box.
margin_box_inline_size(&self) -> Au2749     pub fn margin_box_inline_size(&self) -> Au {
2750         self.border_box.size.inline + self.margin.inline_start_end()
2751     }
2752 
2753     /// Returns true if this node *or any of the nodes within its inline fragment context* have
2754     /// non-`static` `position`.
is_positioned(&self) -> bool2755     pub fn is_positioned(&self) -> bool {
2756         if self.style.get_box().position != Position::Static {
2757             return true
2758         }
2759         if let Some(ref inline_context) = self.inline_context {
2760             for node in inline_context.nodes.iter() {
2761                 if node.style.get_box().position != Position::Static {
2762                     return true
2763                 }
2764             }
2765         }
2766         false
2767     }
2768 
2769     /// Returns true if this node is absolutely positioned.
is_absolutely_positioned(&self) -> bool2770     pub fn is_absolutely_positioned(&self) -> bool {
2771         self.style.get_box().position == Position::Absolute
2772     }
2773 
is_inline_absolute(&self) -> bool2774     pub fn is_inline_absolute(&self) -> bool {
2775         match self.specific {
2776             SpecificFragmentInfo::InlineAbsolute(..) => true,
2777             _ => false,
2778         }
2779     }
2780 
meld_with_next_inline_fragment(&mut self, next_fragment: &Fragment)2781     pub fn meld_with_next_inline_fragment(&mut self, next_fragment: &Fragment) {
2782         if let Some(ref mut inline_context_of_this_fragment) = self.inline_context {
2783             if let Some(ref inline_context_of_next_fragment) = next_fragment.inline_context {
2784                 for (inline_context_node_from_this_fragment,
2785                      inline_context_node_from_next_fragment)
2786                     in inline_context_of_this_fragment.nodes.iter_mut().rev()
2787                         .zip(inline_context_of_next_fragment.nodes.iter().rev())
2788                 {
2789                     if !inline_context_node_from_next_fragment.flags.contains(
2790                             InlineFragmentNodeFlags::LAST_FRAGMENT_OF_ELEMENT) {
2791                         continue
2792                     }
2793                     if inline_context_node_from_next_fragment.address !=
2794                             inline_context_node_from_this_fragment.address {
2795                         continue
2796                     }
2797                     inline_context_node_from_this_fragment.flags.insert(
2798                         InlineFragmentNodeFlags::LAST_FRAGMENT_OF_ELEMENT);
2799                 }
2800             }
2801         }
2802     }
2803 
meld_with_prev_inline_fragment(&mut self, prev_fragment: &Fragment)2804     pub fn meld_with_prev_inline_fragment(&mut self, prev_fragment: &Fragment) {
2805         if let Some(ref mut inline_context_of_this_fragment) = self.inline_context {
2806             if let Some(ref inline_context_of_prev_fragment) = prev_fragment.inline_context {
2807                 for (inline_context_node_from_prev_fragment,
2808                      inline_context_node_from_this_fragment)
2809                     in inline_context_of_prev_fragment.nodes.iter().rev().zip(
2810                             inline_context_of_this_fragment.nodes.iter_mut().rev())
2811                 {
2812                     if !inline_context_node_from_prev_fragment.flags.contains(
2813                             InlineFragmentNodeFlags::FIRST_FRAGMENT_OF_ELEMENT) {
2814                         continue
2815                     }
2816                     if inline_context_node_from_prev_fragment.address !=
2817                             inline_context_node_from_this_fragment.address {
2818                         continue
2819                     }
2820                     inline_context_node_from_this_fragment.flags.insert(
2821                         InlineFragmentNodeFlags::FIRST_FRAGMENT_OF_ELEMENT);
2822                 }
2823             }
2824         }
2825     }
2826 
2827     /// Returns true if any of the inline styles associated with this fragment have
2828     /// `vertical-align` set to `top` or `bottom`.
is_vertically_aligned_to_top_or_bottom(&self) -> bool2829     pub fn is_vertically_aligned_to_top_or_bottom(&self) -> bool {
2830         match self.style.get_box().vertical_align {
2831             VerticalAlign::Top | VerticalAlign::Bottom => return true,
2832             _ => {}
2833         }
2834         if let Some(ref inline_context) = self.inline_context {
2835             for node in &inline_context.nodes {
2836                 match node.style.get_box().vertical_align {
2837                     VerticalAlign::Top | VerticalAlign::Bottom => return true,
2838                     _ => {}
2839                 }
2840             }
2841         }
2842         false
2843     }
2844 
is_text_or_replaced(&self) -> bool2845     pub fn is_text_or_replaced(&self) -> bool {
2846         match self.specific {
2847             SpecificFragmentInfo::Generic |
2848             SpecificFragmentInfo::InlineAbsolute(_) |
2849             SpecificFragmentInfo::InlineAbsoluteHypothetical(_) |
2850             SpecificFragmentInfo::InlineBlock(_) |
2851             SpecificFragmentInfo::Multicol |
2852             SpecificFragmentInfo::MulticolColumn |
2853             SpecificFragmentInfo::Table |
2854             SpecificFragmentInfo::TableCell |
2855             SpecificFragmentInfo::TableColumn(_) |
2856             SpecificFragmentInfo::TableRow |
2857             SpecificFragmentInfo::TableWrapper => false,
2858             SpecificFragmentInfo::Canvas(_) |
2859             SpecificFragmentInfo::GeneratedContent(_) |
2860             SpecificFragmentInfo::Iframe(_) |
2861             SpecificFragmentInfo::Image(_) |
2862             SpecificFragmentInfo::ScannedText(_) |
2863             SpecificFragmentInfo::TruncatedFragment(_) |
2864             SpecificFragmentInfo::Svg(_) |
2865             SpecificFragmentInfo::UnscannedText(_) => true
2866         }
2867     }
2868 
2869     /// Returns the 4D matrix representing this fragment's transform.
transform_matrix(&self, stacking_relative_border_box: &Rect<Au>) -> Option<LayoutTransform>2870     pub fn transform_matrix(&self, stacking_relative_border_box: &Rect<Au>) -> Option<LayoutTransform> {
2871         let list = &self.style.get_box().transform;
2872         let transform = LayoutTransform::from_untyped(
2873             &list.to_transform_3d_matrix(Some(stacking_relative_border_box)).ok()?.0);
2874 
2875         let transform_origin = &self.style.get_box().transform_origin;
2876         let transform_origin_x =
2877             transform_origin.horizontal
2878                 .to_used_value(stacking_relative_border_box.size.width)
2879                 .to_f32_px();
2880         let transform_origin_y =
2881             transform_origin.vertical
2882                 .to_used_value(stacking_relative_border_box.size.height)
2883                 .to_f32_px();
2884         let transform_origin_z = transform_origin.depth.px();
2885 
2886         let pre_transform = LayoutTransform::create_translation(
2887             transform_origin_x,
2888             transform_origin_y,
2889             transform_origin_z);
2890         let post_transform = LayoutTransform::create_translation(
2891             -transform_origin_x,
2892             -transform_origin_y,
2893             -transform_origin_z);
2894 
2895         Some(pre_transform.pre_mul(&transform).pre_mul(&post_transform))
2896     }
2897 
2898     /// Returns the 4D matrix representing this fragment's perspective.
perspective_matrix(&self, stacking_relative_border_box: &Rect<Au>) -> Option<LayoutTransform>2899     pub fn perspective_matrix(&self, stacking_relative_border_box: &Rect<Au>) -> Option<LayoutTransform> {
2900         match self.style().get_box().perspective {
2901             Perspective::Length(length) => {
2902                 let perspective_origin = self.style().get_box().perspective_origin;
2903                 let perspective_origin =
2904                     Point2D::new(
2905                         perspective_origin.horizontal
2906                             .to_used_value(stacking_relative_border_box.size.width),
2907                         perspective_origin.vertical
2908                             .to_used_value(stacking_relative_border_box.size.height)
2909                     ).to_layout();
2910 
2911                 let pre_transform = LayoutTransform::create_translation(
2912                     perspective_origin.x,
2913                     perspective_origin.y,
2914                     0.0);
2915                 let post_transform = LayoutTransform::create_translation(
2916                     -perspective_origin.x,
2917                     -perspective_origin.y,
2918                     0.0);
2919 
2920                 let perspective_matrix = LayoutTransform::from_untyped(
2921                     &transform::create_perspective_matrix(length.px()));
2922 
2923                 Some(pre_transform.pre_mul(&perspective_matrix).pre_mul(&post_transform))
2924             }
2925             Perspective::None => {
2926                 None
2927             }
2928         }
2929     }
2930 }
2931 
2932 impl fmt::Debug for Fragment {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result2933     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2934         let border_padding_string = if !self.border_padding.is_zero() {
2935             format!("\nborder_padding={:?}", self.border_padding)
2936         } else {
2937             "".to_owned()
2938         };
2939 
2940         let margin_string = if !self.margin.is_zero() {
2941             format!("\nmargin={:?}", self.margin)
2942         } else {
2943             "".to_owned()
2944         };
2945 
2946         let damage_string = if self.restyle_damage != RestyleDamage::empty() {
2947             format!("\ndamage={:?}", self.restyle_damage)
2948         } else {
2949             "".to_owned()
2950         };
2951 
2952         write!(f, "\n{}({}) [{:?}]\nborder_box={:?}{}{}{}",
2953             self.specific.get_type(),
2954             self.debug_id,
2955             self.specific,
2956             self.border_box,
2957             border_padding_string,
2958             margin_string,
2959             damage_string)
2960     }
2961 }
2962 
2963 bitflags! {
2964     struct QuantitiesIncludedInIntrinsicInlineSizes: u8 {
2965         const INTRINSIC_INLINE_SIZE_INCLUDES_MARGINS = 0x01;
2966         const INTRINSIC_INLINE_SIZE_INCLUDES_PADDING = 0x02;
2967         const INTRINSIC_INLINE_SIZE_INCLUDES_BORDER = 0x04;
2968         const INTRINSIC_INLINE_SIZE_INCLUDES_SPECIFIED = 0x08;
2969     }
2970 }
2971 
2972 bitflags! {
2973     // Various flags we can use when splitting fragments. See
2974     // `calculate_split_position_using_breaking_strategy()`.
2975     struct SplitOptions: u8 {
2976         #[doc = "True if this is the first fragment on the line."]
2977         const STARTS_LINE = 0x01;
2978         #[doc = "True if we should attempt to split at character boundaries if this split fails. \
2979                  This is used to implement `overflow-wrap: break-word`."]
2980         const RETRY_AT_CHARACTER_BOUNDARIES = 0x02;
2981     }
2982 }
2983 
2984 /// A top-down fragment border box iteration handler.
2985 pub trait FragmentBorderBoxIterator {
2986     /// The operation to perform.
process(&mut self, fragment: &Fragment, level: i32, overflow: &Rect<Au>)2987     fn process(&mut self, fragment: &Fragment, level: i32, overflow: &Rect<Au>);
2988 
2989     /// Returns true if this fragment must be processed in-order. If this returns false,
2990     /// we skip the operation for this fragment, but continue processing siblings.
should_process(&mut self, fragment: &Fragment) -> bool2991     fn should_process(&mut self, fragment: &Fragment) -> bool;
2992 }
2993 
2994 /// The coordinate system used in `stacking_relative_border_box()`. See the documentation of that
2995 /// method for details.
2996 #[derive(Clone, Debug, PartialEq)]
2997 pub enum CoordinateSystem {
2998     /// The border box returned is relative to the fragment's parent stacking context.
2999     Parent,
3000     /// The border box returned is relative to the fragment's own stacking context, if applicable.
3001     Own,
3002 }
3003 
3004 pub struct InlineStyleIterator<'a> {
3005     fragment: &'a Fragment,
3006     inline_style_index: usize,
3007     primary_style_yielded: bool,
3008 }
3009 
3010 impl<'a> Iterator for InlineStyleIterator<'a> {
3011     type Item = &'a ComputedValues;
3012 
next(&mut self) -> Option<&'a ComputedValues>3013     fn next(&mut self) -> Option<&'a ComputedValues> {
3014         if !self.primary_style_yielded {
3015             self.primary_style_yielded = true;
3016             return Some(&*self.fragment.style)
3017         }
3018         let inline_context = self.fragment.inline_context.as_ref()?;
3019         let inline_style_index = self.inline_style_index;
3020         if inline_style_index == inline_context.nodes.len() {
3021             return None
3022         }
3023         self.inline_style_index += 1;
3024         Some(&*inline_context.nodes[inline_style_index].style)
3025     }
3026 }
3027 
3028 impl<'a> InlineStyleIterator<'a> {
new(fragment: &Fragment) -> InlineStyleIterator3029     fn new(fragment: &Fragment) -> InlineStyleIterator {
3030         InlineStyleIterator {
3031             fragment: fragment,
3032             inline_style_index: 0,
3033             primary_style_yielded: false,
3034         }
3035     }
3036 }
3037 
3038 #[derive(Clone, Copy, Debug, PartialEq)]
3039 pub enum WhitespaceStrippingResult {
3040     RetainFragment,
3041     FragmentContainedOnlyBidiControlCharacters,
3042     FragmentContainedOnlyWhitespace,
3043 }
3044 
3045 impl WhitespaceStrippingResult {
from_unscanned_text_fragment_info(info: &UnscannedTextFragmentInfo) -> WhitespaceStrippingResult3046     fn from_unscanned_text_fragment_info(info: &UnscannedTextFragmentInfo)
3047                                          -> WhitespaceStrippingResult {
3048         if info.text.is_empty() {
3049             WhitespaceStrippingResult::FragmentContainedOnlyWhitespace
3050         } else if info.text.chars().all(gfx::text::util::is_bidi_control) {
3051             WhitespaceStrippingResult::FragmentContainedOnlyBidiControlCharacters
3052         } else {
3053             WhitespaceStrippingResult::RetainFragment
3054         }
3055     }
3056 }
3057 
3058 /// The overflow area. We need two different notions of overflow: paint overflow and scrollable
3059 /// overflow.
3060 #[derive(Clone, Copy, Debug)]
3061 pub struct Overflow {
3062     pub scroll: Rect<Au>,
3063     pub paint: Rect<Au>,
3064 }
3065 
3066 impl Overflow {
new() -> Overflow3067     pub fn new() -> Overflow {
3068         Overflow {
3069             scroll: Rect::zero(),
3070             paint: Rect::zero(),
3071         }
3072     }
3073 
from_rect(border_box: &Rect<Au>) -> Overflow3074     pub fn from_rect(border_box: &Rect<Au>) -> Overflow {
3075         Overflow {
3076             scroll: *border_box,
3077             paint: *border_box,
3078         }
3079     }
3080 
union(&mut self, other: &Overflow)3081     pub fn union(&mut self, other: &Overflow) {
3082         self.scroll = self.scroll.union(&other.scroll);
3083         self.paint = self.paint.union(&other.paint);
3084     }
3085 
translate(&mut self, by: &Vector2D<Au>)3086     pub fn translate(&mut self, by: &Vector2D<Au>) {
3087         self.scroll = self.scroll.translate(by);
3088         self.paint = self.paint.translate(by);
3089     }
3090 }
3091 
3092 bitflags! {
3093     pub struct FragmentFlags: u8 {
3094         // TODO(stshine): find a better name since these flags can also be used for grid item.
3095         /// Whether this fragment represents a child in a row flex container.
3096         const IS_INLINE_FLEX_ITEM = 0b0000_0001;
3097         /// Whether this fragment represents a child in a column flex container.
3098         const IS_BLOCK_FLEX_ITEM = 0b0000_0010;
3099         /// Whether this fragment represents the generated text from a text-overflow clip.
3100         const IS_ELLIPSIS = 0b0000_0100;
3101     }
3102 }
3103 
3104 /// Specified distances from the margin edge of a block to its content in the inline direction.
3105 /// These are returned by `guess_inline_content_edge_offsets()` and are used in the float placement
3106 /// speculation logic.
3107 #[derive(Clone, Copy, Debug)]
3108 pub struct SpeculatedInlineContentEdgeOffsets {
3109     pub start: Au,
3110     pub end: Au,
3111 }
3112 
3113 #[cfg(not(debug_assertions))]
3114 #[derive(Clone)]
3115 struct DebugId;
3116 
3117 #[cfg(debug_assertions)]
3118 #[derive(Clone)]
3119 struct DebugId(u16);
3120 
3121 #[cfg(not(debug_assertions))]
3122 impl DebugId {
new() -> DebugId3123     pub fn new() -> DebugId {
3124         DebugId
3125     }
3126 }
3127 
3128 #[cfg(debug_assertions)]
3129 impl DebugId {
new() -> DebugId3130     pub fn new() -> DebugId {
3131         DebugId(layout_debug::generate_unique_debug_id())
3132     }
3133 }
3134 
3135 #[cfg(not(debug_assertions))]
3136 impl fmt::Display for DebugId {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result3137     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3138         write!(f, "{:p}", &self)
3139     }
3140 }
3141 
3142 #[cfg(debug_assertions)]
3143 impl fmt::Display for DebugId {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result3144     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3145         write!(f, "{}", self.0)
3146     }
3147 }
3148 
3149 #[cfg(not(debug_assertions))]
3150 impl Serialize for DebugId {
serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error>3151     fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
3152         serializer.serialize_str(&format!("{:p}", &self))
3153     }
3154 }
3155 
3156 #[cfg(debug_assertions)]
3157 impl Serialize for DebugId {
serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error>3158     fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
3159         serializer.serialize_u16(self.0)
3160     }
3161 }
3162