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