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 //! Creates flows and fragments from a DOM tree via a bottom-up, incremental traversal of the DOM.
6 //!
7 //! Each step of the traversal considers the node and existing flow, if there is one. If a node is
8 //! not dirty and an existing flow exists, then the traversal reuses that flow. Otherwise, it
9 //! proceeds to construct either a flow or a `ConstructionItem`. A construction item is a piece of
10 //! intermediate data that goes with a DOM node and hasn't found its "home" yet-maybe it's a box,
11 //! maybe it's an absolute or fixed position thing that hasn't found its containing block yet.
12 //! Construction items bubble up the tree from children to parents until they find their homes.
13
14 #![deny(unsafe_code)]
15
16 use ServoArc;
17 use block::BlockFlow;
18 use context::{LayoutContext, with_thread_local_font_context};
19 use data::{LayoutDataFlags, LayoutData};
20 use flex::FlexFlow;
21 use floats::FloatKind;
22 use flow::{AbsoluteDescendants, Flow, FlowClass, GetBaseFlow, ImmutableFlowUtils};
23 use flow::{FlowFlags, MutableFlowUtils, MutableOwnedFlowUtils};
24 use flow_ref::FlowRef;
25 use fragment::{CanvasFragmentInfo, ImageFragmentInfo, InlineAbsoluteFragmentInfo, SvgFragmentInfo};
26 use fragment::{Fragment, GeneratedContentInfo, IframeFragmentInfo, FragmentFlags};
27 use fragment::{InlineAbsoluteHypotheticalFragmentInfo, TableColumnFragmentInfo};
28 use fragment::{InlineBlockFragmentInfo, SpecificFragmentInfo, UnscannedTextFragmentInfo};
29 use fragment::WhitespaceStrippingResult;
30 use gfx::display_list::OpaqueNode;
31 use inline::{InlineFlow, InlineFragmentNodeInfo, InlineFragmentNodeFlags};
32 use linked_list::prepend_from;
33 use list_item::{ListItemFlow, ListStyleTypeContent};
34 use multicol::{MulticolColumnFlow, MulticolFlow};
35 use parallel;
36 use script_layout_interface::{LayoutElementType, LayoutNodeType, is_image_data};
37 use script_layout_interface::wrapper_traits::{PseudoElementType, ThreadSafeLayoutElement, ThreadSafeLayoutNode};
38 use servo_config::opts;
39 use servo_url::ServoUrl;
40 use std::collections::LinkedList;
41 use std::marker::PhantomData;
42 use std::mem;
43 use std::sync::Arc;
44 use std::sync::atomic::Ordering;
45 use style::computed_values::caption_side::T as CaptionSide;
46 use style::computed_values::display::T as Display;
47 use style::computed_values::empty_cells::T as EmptyCells;
48 use style::computed_values::float::T as Float;
49 use style::computed_values::list_style_position::T as ListStylePosition;
50 use style::computed_values::position::T as Position;
51 use style::context::SharedStyleContext;
52 use style::dom::TElement;
53 use style::logical_geometry::Direction;
54 use style::properties::ComputedValues;
55 use style::properties::longhands::list_style_image;
56 use style::selector_parser::{PseudoElement, RestyleDamage};
57 use style::servo::restyle_damage::ServoRestyleDamage;
58 use style::values::Either;
59 use style::values::computed::counters::ContentItem;
60 use table::TableFlow;
61 use table_caption::TableCaptionFlow;
62 use table_cell::TableCellFlow;
63 use table_colgroup::TableColGroupFlow;
64 use table_row::TableRowFlow;
65 use table_rowgroup::TableRowGroupFlow;
66 use table_wrapper::TableWrapperFlow;
67 use text::TextRunScanner;
68 use traversal::PostorderNodeMutTraversal;
69 use wrapper::{LayoutNodeLayoutData, TextContent, ThreadSafeLayoutNodeHelpers};
70
71 /// The results of flow construction for a DOM node.
72 #[derive(Clone)]
73 pub enum ConstructionResult {
74 /// This node contributes nothing at all (`display: none`). Alternately, this is what newly
75 /// created nodes have their `ConstructionResult` set to.
76 None,
77
78 /// This node contributed a flow at the proper position in the tree.
79 /// Nothing more needs to be done for this node. It has bubbled up fixed
80 /// and absolute descendant flows that have a containing block above it.
81 Flow(FlowRef, AbsoluteDescendants),
82
83 /// This node contributed some object or objects that will be needed to construct a proper flow
84 /// later up the tree, but these objects have not yet found their home.
85 ConstructionItem(ConstructionItem),
86 }
87
88 impl ConstructionResult {
get(&mut self) -> ConstructionResult89 pub fn get(&mut self) -> ConstructionResult {
90 // FIXME(pcwalton): Stop doing this with inline fragments. Cloning fragments is very
91 // inefficient!
92 (*self).clone()
93 }
94
debug_id(&self) -> usize95 pub fn debug_id(&self) -> usize {
96 match *self {
97 ConstructionResult::None => 0,
98 ConstructionResult::ConstructionItem(_) => 0,
99 ConstructionResult::Flow(ref flow_ref, _) => flow_ref.base().debug_id(),
100 }
101 }
102 }
103
104 /// Represents the output of flow construction for a DOM node that has not yet resulted in a
105 /// complete flow. Construction items bubble up the tree until they find a `Flow` to be attached
106 /// to.
107 #[derive(Clone)]
108 pub enum ConstructionItem {
109 /// Inline fragments and associated {ib} splits that have not yet found flows.
110 InlineFragments(InlineFragmentsConstructionResult),
111 /// Potentially ignorable whitespace.
112 ///
113 /// FIXME(emilio): How could whitespace have any PseudoElementType other
114 /// than Normal?
115 Whitespace(OpaqueNode, PseudoElementType, ServoArc<ComputedValues>, RestyleDamage),
116 /// TableColumn Fragment
117 TableColumnFragment(Fragment),
118 }
119
120 /// Represents inline fragments and {ib} splits that are bubbling up from an inline.
121 #[derive(Clone)]
122 pub struct InlineFragmentsConstructionResult {
123 /// Any {ib} splits that we're bubbling up.
124 pub splits: LinkedList<InlineBlockSplit>,
125
126 /// Any fragments that succeed the {ib} splits.
127 pub fragments: IntermediateInlineFragments,
128 }
129
130 /// Represents an {ib} split that has not yet found the containing block that it belongs to. This
131 /// is somewhat tricky. An example may be helpful. For this DOM fragment:
132 ///
133 /// ```html
134 /// <span>
135 /// A
136 /// <div>B</div>
137 /// C
138 /// </span>
139 /// ```
140 ///
141 /// The resulting `ConstructionItem` for the outer `span` will be:
142 ///
143 /// ```rust,ignore
144 /// ConstructionItem::InlineFragments(
145 /// InlineFragmentsConstructionResult {
146 /// splits: linked_list![
147 /// InlineBlockSplit {
148 /// predecessors: IntermediateInlineFragments {
149 /// fragments: linked_list![A],
150 /// absolute_descendents: AbsoluteDescendents {
151 /// descendant_links: vec![]
152 /// },
153 /// },
154 /// flow: B,
155 /// }
156 /// ],
157 /// fragments: linked_list![C],
158 /// },
159 /// )
160 /// ```
161 #[derive(Clone)]
162 pub struct InlineBlockSplit {
163 /// The inline fragments that precede the flow.
164 pub predecessors: IntermediateInlineFragments,
165
166 /// The flow that caused this {ib} split.
167 pub flow: FlowRef,
168 }
169
170 impl InlineBlockSplit {
171 /// Flushes the given accumulator to the new split and makes a new accumulator to hold any
172 /// subsequent fragments.
new<ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>( fragment_accumulator: &mut InlineFragmentsAccumulator, node: &ConcreteThreadSafeLayoutNode, style_context: &SharedStyleContext, flow: FlowRef, ) -> InlineBlockSplit173 fn new<ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>(
174 fragment_accumulator: &mut InlineFragmentsAccumulator,
175 node: &ConcreteThreadSafeLayoutNode,
176 style_context: &SharedStyleContext,
177 flow: FlowRef,
178 ) -> InlineBlockSplit {
179 fragment_accumulator.enclosing_node.as_mut().expect(
180 "enclosing_node is None; Are {ib} splits being generated outside of an inline node?"
181 ).flags.remove(InlineFragmentNodeFlags::LAST_FRAGMENT_OF_ELEMENT);
182
183 let split = InlineBlockSplit {
184 predecessors: mem::replace(
185 fragment_accumulator,
186 InlineFragmentsAccumulator::from_inline_node(
187 node,
188 style_context,
189 )).to_intermediate_inline_fragments::<ConcreteThreadSafeLayoutNode>(style_context),
190 flow: flow,
191 };
192
193 fragment_accumulator.enclosing_node.as_mut().unwrap().flags.remove(
194 InlineFragmentNodeFlags::FIRST_FRAGMENT_OF_ELEMENT);
195
196 split
197 }
198 }
199
200 /// Holds inline fragments and absolute descendants.
201 #[derive(Clone)]
202 pub struct IntermediateInlineFragments {
203 /// The list of fragments.
204 pub fragments: LinkedList<Fragment>,
205
206 /// The list of absolute descendants of those inline fragments.
207 pub absolute_descendants: AbsoluteDescendants,
208 }
209
210 impl IntermediateInlineFragments {
new() -> IntermediateInlineFragments211 fn new() -> IntermediateInlineFragments {
212 IntermediateInlineFragments {
213 fragments: LinkedList::new(),
214 absolute_descendants: AbsoluteDescendants::new(),
215 }
216 }
217
is_empty(&self) -> bool218 fn is_empty(&self) -> bool {
219 self.fragments.is_empty() && self.absolute_descendants.is_empty()
220 }
221
push_all(&mut self, mut other: IntermediateInlineFragments)222 fn push_all(&mut self, mut other: IntermediateInlineFragments) {
223 self.fragments.append(&mut other.fragments);
224 self.absolute_descendants.push_descendants(other.absolute_descendants);
225 }
226 }
227
228 /// Holds inline fragments that we're gathering for children of an inline node.
229 struct InlineFragmentsAccumulator {
230 /// The list of fragments.
231 fragments: IntermediateInlineFragments,
232
233 /// Information about the inline box directly enclosing the fragments being gathered, if any.
234 ///
235 /// `inline::InlineFragmentNodeInfo` also stores flags indicating whether a fragment is the
236 /// first and/or last of the corresponding inline box. This `InlineFragmentsAccumulator` may
237 /// represent only one side of an {ib} split, so we store these flags as if it represented only
238 /// one fragment. `to_intermediate_inline_fragments` later splits this hypothetical fragment
239 /// into pieces, leaving the `FIRST_FRAGMENT_OF_ELEMENT` and `LAST_FRAGMENT_OF_ELEMENT` flags,
240 /// if present, on the first and last fragments of the output.
241 enclosing_node: Option<InlineFragmentNodeInfo>,
242
243 /// Restyle damage to use for fragments created in this node.
244 restyle_damage: RestyleDamage,
245
246 /// Bidi control characters to insert before and after these fragments.
247 bidi_control_chars: Option<(&'static str, &'static str)>,
248 }
249
250 impl InlineFragmentsAccumulator {
new() -> InlineFragmentsAccumulator251 fn new() -> InlineFragmentsAccumulator {
252 InlineFragmentsAccumulator {
253 fragments: IntermediateInlineFragments::new(),
254 enclosing_node: None,
255 bidi_control_chars: None,
256 restyle_damage: RestyleDamage::empty(),
257 }
258 }
259
from_inline_node<N>(node: &N, style_context: &SharedStyleContext) -> InlineFragmentsAccumulator where N: ThreadSafeLayoutNode260 fn from_inline_node<N>(node: &N, style_context: &SharedStyleContext) -> InlineFragmentsAccumulator
261 where N: ThreadSafeLayoutNode {
262 InlineFragmentsAccumulator {
263 fragments: IntermediateInlineFragments::new(),
264 enclosing_node: Some(InlineFragmentNodeInfo {
265 address: node.opaque(),
266 pseudo: node.get_pseudo_element_type(),
267 style: node.style(style_context),
268 selected_style: node.selected_style(),
269 flags: InlineFragmentNodeFlags::FIRST_FRAGMENT_OF_ELEMENT |
270 InlineFragmentNodeFlags::LAST_FRAGMENT_OF_ELEMENT,
271 }),
272 bidi_control_chars: None,
273 restyle_damage: node.restyle_damage(),
274 }
275 }
276
push(&mut self, fragment: Fragment)277 fn push(&mut self, fragment: Fragment) {
278 self.fragments.fragments.push_back(fragment)
279 }
280
push_all(&mut self, mut fragments: IntermediateInlineFragments)281 fn push_all(&mut self, mut fragments: IntermediateInlineFragments) {
282 self.fragments.fragments.append(&mut fragments.fragments);
283 self.fragments.absolute_descendants.push_descendants(fragments.absolute_descendants);
284 }
285
to_intermediate_inline_fragments<N>( self, context: &SharedStyleContext, ) -> IntermediateInlineFragments where N: ThreadSafeLayoutNode,286 fn to_intermediate_inline_fragments<N>(
287 self,
288 context: &SharedStyleContext,
289 ) -> IntermediateInlineFragments
290 where
291 N: ThreadSafeLayoutNode,
292 {
293 let InlineFragmentsAccumulator {
294 mut fragments,
295 enclosing_node,
296 bidi_control_chars,
297 restyle_damage,
298 } = self;
299 if let Some(mut enclosing_node) = enclosing_node {
300 let fragment_count = fragments.fragments.len();
301 for (index, fragment) in fragments.fragments.iter_mut().enumerate() {
302 let mut enclosing_node = enclosing_node.clone();
303 if index != 0 {
304 enclosing_node.flags.remove(InlineFragmentNodeFlags::FIRST_FRAGMENT_OF_ELEMENT)
305 }
306 if index != fragment_count - 1 {
307 enclosing_node.flags.remove(InlineFragmentNodeFlags::LAST_FRAGMENT_OF_ELEMENT)
308 }
309 fragment.add_inline_context_style(enclosing_node);
310 }
311
312 // Control characters are later discarded in transform_text, so they don't affect the
313 // is_first/is_last styles above.
314 enclosing_node.flags.remove(InlineFragmentNodeFlags::FIRST_FRAGMENT_OF_ELEMENT |
315 InlineFragmentNodeFlags::LAST_FRAGMENT_OF_ELEMENT);
316
317 if let Some((start, end)) = bidi_control_chars {
318 fragments.fragments.push_front(
319 control_chars_to_fragment::<N::ConcreteElement>(
320 &enclosing_node,
321 context,
322 start,
323 restyle_damage,
324 )
325 );
326 fragments.fragments.push_back(
327 control_chars_to_fragment::<N::ConcreteElement>(
328 &enclosing_node,
329 context,
330 end,
331 restyle_damage,
332 )
333 );
334 }
335 }
336 fragments
337 }
338 }
339
340 /// An object that knows how to create flows.
341 pub struct FlowConstructor<'a, N: ThreadSafeLayoutNode> {
342 /// The layout context.
343 pub layout_context: &'a LayoutContext<'a>,
344 /// Satisfy the compiler about the unused parameters, which we use to improve the ergonomics of
345 /// the ensuing impl {} by removing the need to parameterize all the methods individually.
346 phantom2: PhantomData<N>,
347 }
348
349 impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
350 FlowConstructor<'a, ConcreteThreadSafeLayoutNode> {
351 /// Creates a new flow constructor.
new(layout_context: &'a LayoutContext<'a>) -> Self352 pub fn new(layout_context: &'a LayoutContext<'a>) -> Self {
353 FlowConstructor {
354 layout_context: layout_context,
355 phantom2: PhantomData,
356 }
357 }
358
359 #[inline]
style_context(&self) -> &SharedStyleContext360 fn style_context(&self) -> &SharedStyleContext {
361 self.layout_context.shared_context()
362 }
363
364 #[inline]
set_flow_construction_result(&self, node: &ConcreteThreadSafeLayoutNode, result: ConstructionResult)365 fn set_flow_construction_result(&self,
366 node: &ConcreteThreadSafeLayoutNode,
367 result: ConstructionResult) {
368 node.set_flow_construction_result(result);
369 }
370
371 /// Builds the fragment for the given block or subclass thereof.
build_fragment_for_block(&self, node: &ConcreteThreadSafeLayoutNode) -> Fragment372 fn build_fragment_for_block(&self, node: &ConcreteThreadSafeLayoutNode) -> Fragment {
373 let specific_fragment_info = match node.type_id() {
374 Some(LayoutNodeType::Element(LayoutElementType::HTMLIFrameElement)) => {
375 SpecificFragmentInfo::Iframe(IframeFragmentInfo::new(node))
376 }
377 Some(LayoutNodeType::Element(LayoutElementType::HTMLImageElement)) => {
378 let image_info = Box::new(ImageFragmentInfo::new(
379 node.image_url(), node, &self.layout_context
380 ));
381 SpecificFragmentInfo::Image(image_info)
382 }
383 Some(LayoutNodeType::Element(LayoutElementType::HTMLObjectElement)) => {
384 let image_info = Box::new(ImageFragmentInfo::new(
385 node.object_data(), node, &self.layout_context
386 ));
387 SpecificFragmentInfo::Image(image_info)
388 }
389 Some(LayoutNodeType::Element(LayoutElementType::HTMLTableElement)) => {
390 SpecificFragmentInfo::TableWrapper
391 }
392 Some(LayoutNodeType::Element(LayoutElementType::HTMLTableColElement)) => {
393 SpecificFragmentInfo::TableColumn(TableColumnFragmentInfo::new(node))
394 }
395 Some(LayoutNodeType::Element(LayoutElementType::HTMLTableCellElement)) => {
396 SpecificFragmentInfo::TableCell
397 }
398 Some(LayoutNodeType::Element(LayoutElementType::HTMLTableRowElement)) |
399 Some(LayoutNodeType::Element(LayoutElementType::HTMLTableSectionElement)) => {
400 SpecificFragmentInfo::TableRow
401 }
402 Some(LayoutNodeType::Element(LayoutElementType::HTMLCanvasElement)) => {
403 let data = node.canvas_data().unwrap();
404 SpecificFragmentInfo::Canvas(Box::new(CanvasFragmentInfo::new(data)))
405 }
406 Some(LayoutNodeType::Element(LayoutElementType::SVGSVGElement)) => {
407 let data = node.svg_data().unwrap();
408 SpecificFragmentInfo::Svg(Box::new(SvgFragmentInfo::new(data)))
409 }
410 _ => {
411 // This includes pseudo-elements.
412 SpecificFragmentInfo::Generic
413 }
414 };
415
416 Fragment::new(node, specific_fragment_info, self.layout_context)
417 }
418
419 /// Creates an inline flow from a set of inline fragments, then adds it as a child of the given
420 /// flow or pushes it onto the given flow list.
421 ///
422 /// `#[inline(always)]` because this is performance critical and LLVM will not inline it
423 /// otherwise.
424 #[inline(always)]
flush_inline_fragments_to_flow( &mut self, fragment_accumulator: InlineFragmentsAccumulator, flow: &mut FlowRef, absolute_descendants: &mut AbsoluteDescendants, legalizer: &mut Legalizer, node: &ConcreteThreadSafeLayoutNode, )425 fn flush_inline_fragments_to_flow(
426 &mut self,
427 fragment_accumulator: InlineFragmentsAccumulator,
428 flow: &mut FlowRef,
429 absolute_descendants: &mut AbsoluteDescendants,
430 legalizer: &mut Legalizer,
431 node: &ConcreteThreadSafeLayoutNode,
432 ) {
433 let mut fragments =
434 fragment_accumulator.to_intermediate_inline_fragments::<ConcreteThreadSafeLayoutNode>(
435 self.style_context(),
436 );
437 if fragments.is_empty() {
438 return
439 };
440
441 strip_ignorable_whitespace_from_start(&mut fragments.fragments);
442 strip_ignorable_whitespace_from_end(&mut fragments.fragments);
443 if fragments.fragments.is_empty() {
444 absolute_descendants.push_descendants(fragments.absolute_descendants);
445 return
446 }
447
448 // Build a list of all the inline-block fragments before fragments is moved.
449 let mut inline_block_flows = vec!();
450 for fragment in &fragments.fragments {
451 match fragment.specific {
452 SpecificFragmentInfo::InlineBlock(ref info) => {
453 inline_block_flows.push(info.flow_ref.clone())
454 }
455 SpecificFragmentInfo::InlineAbsoluteHypothetical(ref info) => {
456 inline_block_flows.push(info.flow_ref.clone())
457 }
458 SpecificFragmentInfo::InlineAbsolute(ref info) => {
459 inline_block_flows.push(info.flow_ref.clone())
460 }
461 _ => {}
462 }
463 }
464
465 // We must scan for runs before computing minimum ascent and descent because scanning
466 // for runs might collapse so much whitespace away that only hypothetical fragments
467 // remain. In that case the inline flow will compute its ascent and descent to be zero.
468 let scanned_fragments =
469 with_thread_local_font_context(self.layout_context, |font_context| {
470 TextRunScanner::new().scan_for_runs(font_context,
471 mem::replace(&mut fragments.fragments, LinkedList::new()))
472 });
473 let mut inline_flow_ref =
474 FlowRef::new(Arc::new(InlineFlow::from_fragments(scanned_fragments,
475 node.style(self.style_context()).writing_mode)));
476
477 // Add all the inline-block fragments as children of the inline flow.
478 for inline_block_flow in &inline_block_flows {
479 inline_flow_ref.add_new_child(inline_block_flow.clone());
480 }
481
482 // Set up absolute descendants as necessary.
483 //
484 // The inline flow itself may need to become the containing block for absolute descendants
485 // in order to handle cases like:
486 //
487 // <div>
488 // <span style="position: relative">
489 // <span style="position: absolute; ..."></span>
490 // </span>
491 // </div>
492 //
493 // See the comment above `flow::AbsoluteDescendantInfo` for more information.
494 inline_flow_ref.take_applicable_absolute_descendants(&mut fragments.absolute_descendants);
495 absolute_descendants.push_descendants(fragments.absolute_descendants);
496
497 {
498 // FIXME(#6503): Use Arc::get_mut().unwrap() here.
499 let inline_flow = FlowRef::deref_mut(&mut inline_flow_ref).as_mut_inline();
500 inline_flow.minimum_line_metrics =
501 with_thread_local_font_context(self.layout_context, |font_context| {
502 inline_flow.minimum_line_metrics(font_context, &node.style(self.style_context()))
503 });
504 }
505
506 inline_flow_ref.finish();
507 legalizer.add_child::<ConcreteThreadSafeLayoutNode::ConcreteElement>(
508 self.style_context(),
509 flow,
510 inline_flow_ref,
511 )
512 }
513
build_block_flow_using_construction_result_of_child( &mut self, flow: &mut FlowRef, node: &ConcreteThreadSafeLayoutNode, kid: ConcreteThreadSafeLayoutNode, inline_fragment_accumulator: &mut InlineFragmentsAccumulator, abs_descendants: &mut AbsoluteDescendants, legalizer: &mut Legalizer)514 fn build_block_flow_using_construction_result_of_child(
515 &mut self,
516 flow: &mut FlowRef,
517 node: &ConcreteThreadSafeLayoutNode,
518 kid: ConcreteThreadSafeLayoutNode,
519 inline_fragment_accumulator: &mut InlineFragmentsAccumulator,
520 abs_descendants: &mut AbsoluteDescendants,
521 legalizer: &mut Legalizer) {
522 match kid.get_construction_result() {
523 ConstructionResult::None => {}
524 ConstructionResult::Flow(kid_flow, kid_abs_descendants) => {
525 // If kid_flow is TableCaptionFlow, kid_flow should be added under
526 // TableWrapperFlow.
527 if flow.is_table() && kid_flow.is_table_caption() {
528 let construction_result =
529 ConstructionResult::Flow(kid_flow, AbsoluteDescendants::new());
530 self.set_flow_construction_result(&kid, construction_result)
531 } else {
532 if !kid_flow.base().flags.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED) {
533 // Flush any inline fragments that we were gathering up. This allows us to
534 // handle {ib} splits.
535 let old_inline_fragment_accumulator =
536 mem::replace(inline_fragment_accumulator,
537 InlineFragmentsAccumulator::new());
538 self.flush_inline_fragments_to_flow(old_inline_fragment_accumulator,
539 flow,
540 abs_descendants,
541 legalizer,
542 node);
543 }
544 legalizer.add_child::<ConcreteThreadSafeLayoutNode::ConcreteElement>(
545 self.style_context(),
546 flow,
547 kid_flow,
548 )
549 }
550 abs_descendants.push_descendants(kid_abs_descendants);
551 }
552 ConstructionResult::ConstructionItem(ConstructionItem::InlineFragments(
553 InlineFragmentsConstructionResult {
554 splits,
555 fragments: successor_fragments,
556 })) => {
557 // Add any {ib} splits.
558 for split in splits {
559 // Pull apart the {ib} split object and push its predecessor fragments
560 // onto the list.
561 let InlineBlockSplit {
562 predecessors,
563 flow: kid_flow
564 } = split;
565 inline_fragment_accumulator.push_all(predecessors);
566
567 // Flush any inline fragments that we were gathering up.
568 debug!("flushing {} inline box(es) to flow A",
569 inline_fragment_accumulator.fragments.fragments.len());
570 let old_inline_fragment_accumulator =
571 mem::replace(inline_fragment_accumulator,
572 InlineFragmentsAccumulator::new());
573 let absolute_descendants =
574 &mut inline_fragment_accumulator.fragments.absolute_descendants;
575 self.flush_inline_fragments_to_flow(old_inline_fragment_accumulator,
576 flow,
577 absolute_descendants,
578 legalizer,
579 node);
580
581 // Push the flow generated by the {ib} split onto our list of flows.
582 legalizer.add_child::<ConcreteThreadSafeLayoutNode::ConcreteElement>(
583 self.style_context(),
584 flow,
585 kid_flow,
586 )
587 }
588
589 // Add the fragments to the list we're maintaining.
590 inline_fragment_accumulator.push_all(successor_fragments);
591 }
592 ConstructionResult::ConstructionItem(ConstructionItem::Whitespace(
593 whitespace_node,
594 whitespace_pseudo,
595 whitespace_style,
596 whitespace_damage)) => {
597 // Add whitespace results. They will be stripped out later on when
598 // between block elements, and retained when between inline elements.
599 let fragment_info = SpecificFragmentInfo::UnscannedText(
600 Box::new(UnscannedTextFragmentInfo::new(Box::<str>::from(" "), None))
601 );
602 let fragment = Fragment::from_opaque_node_and_style(whitespace_node,
603 whitespace_pseudo,
604 whitespace_style,
605 node.selected_style(),
606 whitespace_damage,
607 fragment_info);
608 inline_fragment_accumulator.fragments.fragments.push_back(fragment);
609 }
610 ConstructionResult::ConstructionItem(ConstructionItem::TableColumnFragment(_)) => {
611 // TODO: Implement anonymous table objects for missing parents
612 // CSS 2.1 § 17.2.1, step 3-2
613 }
614 }
615 }
616
617 /// Constructs a block flow, beginning with the given `initial_fragments` if present and then
618 /// appending the construction results of children to the child list of the block flow. {ib}
619 /// splits and absolutely-positioned descendants are handled correctly.
build_flow_for_block_starting_with_fragments( &mut self, mut flow: FlowRef, node: &ConcreteThreadSafeLayoutNode, initial_fragments: IntermediateInlineFragments) -> ConstructionResult620 fn build_flow_for_block_starting_with_fragments(
621 &mut self,
622 mut flow: FlowRef,
623 node: &ConcreteThreadSafeLayoutNode,
624 initial_fragments: IntermediateInlineFragments)
625 -> ConstructionResult {
626 // Gather up fragments for the inline flows we might need to create.
627 let mut inline_fragment_accumulator = InlineFragmentsAccumulator::new();
628
629 inline_fragment_accumulator.fragments.push_all(initial_fragments);
630
631 // List of absolute descendants, in tree order.
632 let mut abs_descendants = AbsoluteDescendants::new();
633 let mut legalizer = Legalizer::new();
634 if !node.is_replaced_content() {
635 for kid in node.children() {
636 if kid.get_pseudo_element_type() != PseudoElementType::Normal {
637 self.process(&kid);
638 }
639
640 self.build_block_flow_using_construction_result_of_child(
641 &mut flow,
642 node,
643 kid,
644 &mut inline_fragment_accumulator,
645 &mut abs_descendants,
646 &mut legalizer);
647 }
648 }
649
650 // Perform a final flush of any inline fragments that we were gathering up to handle {ib}
651 // splits, after stripping ignorable whitespace.
652 self.flush_inline_fragments_to_flow(inline_fragment_accumulator,
653 &mut flow,
654 &mut abs_descendants,
655 &mut legalizer,
656 node);
657
658 // The flow is done.
659 legalizer.finish(&mut flow);
660 flow.finish();
661
662 // Set up the absolute descendants.
663 if flow.is_absolute_containing_block() {
664 // This is the containing block for all the absolute descendants.
665 flow.set_absolute_descendants(abs_descendants);
666
667 abs_descendants = AbsoluteDescendants::new();
668 if flow.base().flags.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED) {
669 // This is now the only absolute flow in the subtree which hasn't yet
670 // reached its CB.
671 abs_descendants.push(flow.clone());
672 }
673 }
674 ConstructionResult::Flow(flow, abs_descendants)
675 }
676
677 /// Constructs a flow for the given block node and its children. This method creates an
678 /// initial fragment as appropriate and then dispatches to
679 /// `build_flow_for_block_starting_with_fragments`. Currently the following kinds of flows get
680 /// initial content:
681 ///
682 /// * Generated content gets the initial content specified by the `content` attribute of the
683 /// CSS.
684 /// * `<input>` and `<textarea>` elements get their content.
685 ///
686 /// FIXME(pcwalton): It is not clear to me that there isn't a cleaner way to handle
687 /// `<textarea>`.
build_flow_for_block_like(&mut self, flow: FlowRef, node: &ConcreteThreadSafeLayoutNode) -> ConstructionResult688 fn build_flow_for_block_like(&mut self, flow: FlowRef, node: &ConcreteThreadSafeLayoutNode)
689 -> ConstructionResult {
690 let mut fragments = IntermediateInlineFragments::new();
691 let node_is_input_or_text_area =
692 node.type_id() == Some(LayoutNodeType::Element(LayoutElementType::HTMLInputElement)) ||
693 node.type_id() == Some(LayoutNodeType::Element(LayoutElementType::HTMLTextAreaElement));
694 if node.get_pseudo_element_type().is_replaced_content() ||
695 node_is_input_or_text_area {
696 // A TextArea's text contents are displayed through the input text
697 // box, so don't construct them.
698 if node.type_id() == Some(LayoutNodeType::Element(LayoutElementType::HTMLTextAreaElement)) {
699 for kid in node.children() {
700 self.set_flow_construction_result(&kid, ConstructionResult::None)
701 }
702 }
703
704 let context = self.style_context();
705 let mut style = node.style(context);
706 style = context.stylist.style_for_anonymous::<ConcreteThreadSafeLayoutNode::ConcreteElement>(
707 &context.guards,
708 &PseudoElement::ServoText,
709 &style,
710 );
711 if node_is_input_or_text_area {
712 style = context.stylist.style_for_anonymous::<ConcreteThreadSafeLayoutNode::ConcreteElement>(
713 &context.guards,
714 &PseudoElement::ServoInputText,
715 &style,
716 )
717 }
718
719 self.create_fragments_for_node_text_content(&mut fragments, node, &style)
720 }
721 self.build_flow_for_block_starting_with_fragments(flow, node, fragments)
722 }
723
724 /// Pushes fragments appropriate for the content of the given node onto the given list.
create_fragments_for_node_text_content(&self, fragments: &mut IntermediateInlineFragments, node: &ConcreteThreadSafeLayoutNode, style: &ServoArc<ComputedValues>)725 fn create_fragments_for_node_text_content(&self,
726 fragments: &mut IntermediateInlineFragments,
727 node: &ConcreteThreadSafeLayoutNode,
728 style: &ServoArc<ComputedValues>) {
729 // Fast path: If there is no text content, return immediately.
730 let text_content = node.text_content();
731 if text_content.is_empty() {
732 return
733 }
734
735 let style = (*style).clone();
736 let selected_style = node.selected_style();
737
738 match text_content {
739 TextContent::Text(string) => {
740 let info = Box::new(UnscannedTextFragmentInfo::new(string, node.selection()));
741 let specific_fragment_info = SpecificFragmentInfo::UnscannedText(info);
742 fragments.fragments.push_back(Fragment::from_opaque_node_and_style(
743 node.opaque(),
744 node.get_pseudo_element_type(),
745 style,
746 selected_style,
747 node.restyle_damage(),
748 specific_fragment_info))
749 }
750 TextContent::GeneratedContent(content_items) => {
751 for content_item in content_items.into_iter() {
752 let specific_fragment_info = match content_item {
753 ContentItem::String(string) => {
754 let info = Box::new(UnscannedTextFragmentInfo::new(string, None));
755 SpecificFragmentInfo::UnscannedText(info)
756 },
757 content_item => {
758 let content_item = Box::new(GeneratedContentInfo::ContentItem(content_item));
759 SpecificFragmentInfo::GeneratedContent(content_item)
760 }
761 };
762 fragments.fragments.push_back(Fragment::from_opaque_node_and_style(
763 node.opaque(),
764 node.get_pseudo_element_type(),
765 style.clone(),
766 selected_style.clone(),
767 node.restyle_damage(),
768 specific_fragment_info))
769 }
770 }
771 }
772 }
773
774 /// Builds a flow for a node with `display: block`. This yields a `BlockFlow` with possibly
775 /// other `BlockFlow`s or `InlineFlow`s underneath it, depending on whether {ib} splits needed
776 /// to happen.
build_flow_for_block(&mut self, node: &ConcreteThreadSafeLayoutNode, float_kind: Option<FloatKind>) -> ConstructionResult777 fn build_flow_for_block(&mut self, node: &ConcreteThreadSafeLayoutNode, float_kind: Option<FloatKind>)
778 -> ConstructionResult {
779 if node.style(self.style_context()).is_multicol() {
780 return self.build_flow_for_multicol(node, float_kind)
781 }
782
783 let fragment = self.build_fragment_for_block(node);
784 let flow =
785 FlowRef::new(Arc::new(BlockFlow::from_fragment_and_float_kind(fragment, float_kind)));
786 self.build_flow_for_block_like(flow, node)
787 }
788
789 /// Bubbles up {ib} splits.
accumulate_inline_block_splits(&mut self, splits: LinkedList<InlineBlockSplit>, node: &ConcreteThreadSafeLayoutNode, fragment_accumulator: &mut InlineFragmentsAccumulator, opt_inline_block_splits: &mut LinkedList<InlineBlockSplit>)790 fn accumulate_inline_block_splits(&mut self,
791 splits: LinkedList<InlineBlockSplit>,
792 node: &ConcreteThreadSafeLayoutNode,
793 fragment_accumulator: &mut InlineFragmentsAccumulator,
794 opt_inline_block_splits: &mut LinkedList<InlineBlockSplit>) {
795 for split in splits {
796 let InlineBlockSplit {
797 predecessors,
798 flow: kid_flow
799 } = split;
800 fragment_accumulator.push_all(predecessors);
801
802 opt_inline_block_splits.push_back(
803 InlineBlockSplit::new(fragment_accumulator, node, self.style_context(), kid_flow));
804 }
805 }
806
807 /// Concatenates the fragments of kids, adding in our own borders/padding/margins if necessary.
808 /// Returns the `InlineFragmentsConstructionResult`, if any. There will be no
809 /// `InlineFragmentsConstructionResult` if this node consisted entirely of ignorable
810 /// whitespace.
build_fragments_for_nonreplaced_inline_content(&mut self, node: &ConcreteThreadSafeLayoutNode) -> ConstructionResult811 fn build_fragments_for_nonreplaced_inline_content(&mut self, node: &ConcreteThreadSafeLayoutNode)
812 -> ConstructionResult {
813 let mut opt_inline_block_splits: LinkedList<InlineBlockSplit> = LinkedList::new();
814 let mut fragment_accumulator = InlineFragmentsAccumulator::from_inline_node(node, self.style_context());
815 fragment_accumulator.bidi_control_chars = bidi_control_chars(&node.style(self.style_context()));
816
817 let mut abs_descendants = AbsoluteDescendants::new();
818
819 // Concatenate all the fragments of our kids, creating {ib} splits as necessary.
820 let mut is_empty = true;
821 for kid in node.children() {
822 is_empty = false;
823 if kid.get_pseudo_element_type() != PseudoElementType::Normal {
824 self.process(&kid);
825 }
826 match kid.get_construction_result() {
827 ConstructionResult::None => {}
828 ConstructionResult::Flow(flow, kid_abs_descendants) => {
829 if !flow.base().flags.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED) {
830 opt_inline_block_splits.push_back(InlineBlockSplit::new(
831 &mut fragment_accumulator, node, self.style_context(), flow));
832 abs_descendants.push_descendants(kid_abs_descendants);
833 } else {
834 // Push the absolutely-positioned kid as an inline containing block.
835 let kid_node = flow.as_block().fragment.node;
836 let kid_pseudo = flow.as_block().fragment.pseudo.clone();
837 let kid_style = flow.as_block().fragment.style.clone();
838 let kid_selected_style = flow.as_block().fragment.selected_style.clone();
839 let kid_restyle_damage = flow.as_block().fragment.restyle_damage;
840 let fragment_info = SpecificFragmentInfo::InlineAbsolute(
841 InlineAbsoluteFragmentInfo::new(flow));
842 fragment_accumulator.push(Fragment::from_opaque_node_and_style(
843 kid_node,
844 kid_pseudo,
845 kid_style,
846 kid_selected_style,
847 kid_restyle_damage,
848 fragment_info));
849 fragment_accumulator.fragments
850 .absolute_descendants
851 .push_descendants(kid_abs_descendants);
852 }
853 }
854 ConstructionResult::ConstructionItem(ConstructionItem::InlineFragments(
855 InlineFragmentsConstructionResult {
856 splits,
857 fragments: successors,
858 })) => {
859 // Bubble up {ib} splits.
860 self.accumulate_inline_block_splits(splits,
861 node,
862 &mut fragment_accumulator,
863 &mut opt_inline_block_splits);
864
865 // Push residual fragments.
866 fragment_accumulator.push_all(successors);
867 }
868 ConstructionResult::ConstructionItem(ConstructionItem::Whitespace(
869 whitespace_node,
870 whitespace_pseudo,
871 whitespace_style,
872 whitespace_damage)) => {
873 // Instantiate the whitespace fragment.
874 let fragment_info = SpecificFragmentInfo::UnscannedText(
875 Box::new(UnscannedTextFragmentInfo::new(Box::<str>::from(" "), None))
876 );
877 let fragment =
878 Fragment::from_opaque_node_and_style(whitespace_node,
879 whitespace_pseudo,
880 whitespace_style,
881 node.selected_style(),
882 whitespace_damage,
883 fragment_info);
884 fragment_accumulator.fragments.fragments.push_back(fragment)
885 }
886 ConstructionResult::ConstructionItem(ConstructionItem::TableColumnFragment(_)) => {
887 // TODO: Implement anonymous table objects for missing parents
888 // CSS 2.1 § 17.2.1, step 3-2
889 }
890 }
891 }
892
893 let node_style = node.style(self.style_context());
894 if is_empty && node_style.has_padding_or_border() {
895 // An empty inline box needs at least one fragment to draw its background and borders.
896 let info = SpecificFragmentInfo::UnscannedText(
897 Box::new(UnscannedTextFragmentInfo::new(Box::<str>::from(""), None))
898 );
899 let fragment = Fragment::from_opaque_node_and_style(node.opaque(),
900 node.get_pseudo_element_type(),
901 node_style.clone(),
902 node.selected_style(),
903 node.restyle_damage(),
904 info);
905 fragment_accumulator.fragments.fragments.push_back(fragment)
906 }
907
908 // Finally, make a new construction result.
909 if opt_inline_block_splits.len() > 0 || !fragment_accumulator.fragments.is_empty()
910 || abs_descendants.len() > 0 {
911 fragment_accumulator.fragments.absolute_descendants.push_descendants(abs_descendants);
912
913 // If the node is positioned, then it's the containing block for all absolutely-
914 // positioned descendants.
915 if node_style.get_box().position != Position::Static {
916 fragment_accumulator.fragments
917 .absolute_descendants
918 .mark_as_having_reached_containing_block();
919 }
920
921 let construction_item = ConstructionItem::InlineFragments(
922 InlineFragmentsConstructionResult {
923 splits: opt_inline_block_splits,
924 fragments: fragment_accumulator.to_intermediate_inline_fragments::<ConcreteThreadSafeLayoutNode>(
925 self.style_context(),
926 ),
927 });
928 ConstructionResult::ConstructionItem(construction_item)
929 } else {
930 ConstructionResult::None
931 }
932 }
933
934 /// Creates an `InlineFragmentsConstructionResult` for replaced content. Replaced content
935 /// doesn't render its children, so this just nukes a child's fragments and creates a
936 /// `Fragment`.
build_fragments_for_replaced_inline_content(&mut self, node: &ConcreteThreadSafeLayoutNode) -> ConstructionResult937 fn build_fragments_for_replaced_inline_content(&mut self, node: &ConcreteThreadSafeLayoutNode)
938 -> ConstructionResult {
939 for kid in node.children() {
940 self.set_flow_construction_result(&kid, ConstructionResult::None)
941 }
942
943 let context = self.style_context();
944 let style = node.style(context);
945 // If this node is ignorable whitespace, bail out now.
946 if node.is_ignorable_whitespace(context) {
947 return ConstructionResult::ConstructionItem(ConstructionItem::Whitespace(
948 node.opaque(),
949 node.get_pseudo_element_type(),
950 context.stylist.style_for_anonymous::<ConcreteThreadSafeLayoutNode::ConcreteElement>(
951 &context.guards,
952 &PseudoElement::ServoText,
953 &style,
954 ),
955 node.restyle_damage(),
956 ))
957 }
958
959 // If this is generated content, then we need to initialize the accumulator with the
960 // fragment corresponding to that content. Otherwise, just initialize with the ordinary
961 // fragment that needs to be generated for this inline node.
962 let mut fragments = IntermediateInlineFragments::new();
963 match (node.get_pseudo_element_type(), node.type_id()) {
964 (_, Some(LayoutNodeType::Text)) => {
965 let text_style = context.stylist.style_for_anonymous::<ConcreteThreadSafeLayoutNode::ConcreteElement>(
966 &context.guards,
967 &PseudoElement::ServoText,
968 &style,
969 );
970 self.create_fragments_for_node_text_content(&mut fragments, node, &text_style)
971 }
972 (PseudoElementType::Normal, _) => {
973 fragments.fragments.push_back(self.build_fragment_for_block(node));
974 }
975 (_, _) => self.create_fragments_for_node_text_content(&mut fragments, node, &style),
976 }
977
978 let construction_item =
979 ConstructionItem::InlineFragments(InlineFragmentsConstructionResult {
980 splits: LinkedList::new(),
981 fragments: fragments,
982 });
983 ConstructionResult::ConstructionItem(construction_item)
984 }
985
986 /// Build the fragment for an inline-block or inline-flex, based on the `display` flag
build_fragment_for_inline_block_or_inline_flex( &mut self, node: &ConcreteThreadSafeLayoutNode, display: Display, ) -> ConstructionResult987 fn build_fragment_for_inline_block_or_inline_flex(
988 &mut self,
989 node: &ConcreteThreadSafeLayoutNode,
990 display: Display,
991 ) -> ConstructionResult {
992 let block_flow_result = match display {
993 Display::InlineBlock => self.build_flow_for_block(node, None),
994 Display::InlineFlex => self.build_flow_for_flex(node, None),
995 _ => panic!("The flag should be inline-block or inline-flex")
996 };
997 let (block_flow, abs_descendants) = match block_flow_result {
998 ConstructionResult::Flow(block_flow, abs_descendants) => (block_flow, abs_descendants),
999 _ => unreachable!()
1000 };
1001
1002 let context = self.style_context();
1003 let style = node.style(context);
1004 let style = context.stylist.style_for_anonymous::<ConcreteThreadSafeLayoutNode::ConcreteElement>(
1005 &context.guards,
1006 &PseudoElement::ServoInlineBlockWrapper,
1007 &style,
1008 );
1009 let fragment_info = SpecificFragmentInfo::InlineBlock(InlineBlockFragmentInfo::new(
1010 block_flow));
1011 let fragment = Fragment::from_opaque_node_and_style(node.opaque(),
1012 node.get_pseudo_element_type(),
1013 style,
1014 node.selected_style(),
1015 node.restyle_damage(),
1016 fragment_info);
1017
1018 let mut fragment_accumulator = InlineFragmentsAccumulator::new();
1019 fragment_accumulator.fragments.fragments.push_back(fragment);
1020 fragment_accumulator.fragments.absolute_descendants.push_descendants(abs_descendants);
1021
1022 let construction_item =
1023 ConstructionItem::InlineFragments(InlineFragmentsConstructionResult {
1024 splits: LinkedList::new(),
1025 fragments: fragment_accumulator
1026 .to_intermediate_inline_fragments::<ConcreteThreadSafeLayoutNode>(context),
1027 });
1028 ConstructionResult::ConstructionItem(construction_item)
1029 }
1030
1031 /// This is an annoying case, because the computed `display` value is `block`, but the
1032 /// hypothetical box is inline.
build_fragment_for_absolutely_positioned_inline(&mut self, node: &ConcreteThreadSafeLayoutNode) -> ConstructionResult1033 fn build_fragment_for_absolutely_positioned_inline(&mut self,
1034 node: &ConcreteThreadSafeLayoutNode)
1035 -> ConstructionResult {
1036 let block_flow_result = self.build_flow_for_block(node, None);
1037 let (block_flow, abs_descendants) = match block_flow_result {
1038 ConstructionResult::Flow(block_flow, abs_descendants) => (block_flow, abs_descendants),
1039 _ => unreachable!()
1040 };
1041
1042 let fragment_info = SpecificFragmentInfo::InlineAbsoluteHypothetical(
1043 InlineAbsoluteHypotheticalFragmentInfo::new(block_flow));
1044 let style_context = self.style_context();
1045 let style = node.style(style_context);
1046 let style = style_context.stylist.style_for_anonymous::<ConcreteThreadSafeLayoutNode::ConcreteElement>(
1047 &style_context.guards,
1048 &PseudoElement::ServoInlineAbsolute,
1049 &style,
1050 );
1051 let fragment = Fragment::from_opaque_node_and_style(node.opaque(),
1052 PseudoElementType::Normal,
1053 style,
1054 node.selected_style(),
1055 node.restyle_damage(),
1056 fragment_info);
1057
1058 let mut fragment_accumulator = InlineFragmentsAccumulator::from_inline_node(node, self.style_context());
1059 fragment_accumulator.fragments.fragments.push_back(fragment);
1060 fragment_accumulator.fragments.absolute_descendants.push_descendants(abs_descendants);
1061
1062 let construction_item =
1063 ConstructionItem::InlineFragments(InlineFragmentsConstructionResult {
1064 splits: LinkedList::new(),
1065 fragments: fragment_accumulator
1066 .to_intermediate_inline_fragments::<ConcreteThreadSafeLayoutNode>(style_context),
1067 });
1068 ConstructionResult::ConstructionItem(construction_item)
1069 }
1070
1071 /// Builds one or more fragments for a node with `display: inline`. This yields an
1072 /// `InlineFragmentsConstructionResult`.
build_fragments_for_inline(&mut self, node: &ConcreteThreadSafeLayoutNode) -> ConstructionResult1073 fn build_fragments_for_inline(&mut self, node: &ConcreteThreadSafeLayoutNode) -> ConstructionResult {
1074 // Is this node replaced content?
1075 if !node.is_replaced_content() {
1076 // Go to a path that concatenates our kids' fragments.
1077 self.build_fragments_for_nonreplaced_inline_content(node)
1078 } else {
1079 // Otherwise, just nuke our kids' fragments, create our fragment if any, and be done
1080 // with it.
1081 self.build_fragments_for_replaced_inline_content(node)
1082 }
1083 }
1084
1085 /// Places any table captions found under the given table wrapper, if the value of their
1086 /// `caption-side` property is equal to the given `side`.
place_table_caption_under_table_wrapper_on_side(&mut self, table_wrapper_flow: &mut FlowRef, node: &ConcreteThreadSafeLayoutNode, side: CaptionSide)1087 fn place_table_caption_under_table_wrapper_on_side(&mut self,
1088 table_wrapper_flow: &mut FlowRef,
1089 node: &ConcreteThreadSafeLayoutNode,
1090 side: CaptionSide) {
1091 // Only flows that are table captions are matched here.
1092 for kid in node.children() {
1093 match kid.get_construction_result() {
1094 ConstructionResult::Flow(kid_flow, _) => {
1095 if kid_flow.is_table_caption() &&
1096 kid_flow.as_block()
1097 .fragment
1098 .style()
1099 .get_inheritedtable()
1100 .caption_side == side {
1101 table_wrapper_flow.add_new_child(kid_flow);
1102 }
1103 }
1104 ConstructionResult::None | ConstructionResult::ConstructionItem(_) => {}
1105 }
1106 }
1107 }
1108
1109 /// Builds a flow for a node with `column-count` or `column-width` non-`auto`.
1110 /// This yields a `MulticolFlow` with a single `MulticolColumnFlow` underneath it.
build_flow_for_multicol(&mut self, node: &ConcreteThreadSafeLayoutNode, float_kind: Option<FloatKind>) -> ConstructionResult1111 fn build_flow_for_multicol(&mut self,
1112 node: &ConcreteThreadSafeLayoutNode,
1113 float_kind: Option<FloatKind>)
1114 -> ConstructionResult {
1115 let fragment = Fragment::new(node, SpecificFragmentInfo::Multicol, self.layout_context);
1116 let mut flow = FlowRef::new(Arc::new(MulticolFlow::from_fragment(fragment, float_kind)));
1117
1118 let column_fragment = Fragment::new(node, SpecificFragmentInfo::MulticolColumn, self.layout_context);
1119 let column_flow = FlowRef::new(Arc::new(MulticolColumnFlow::from_fragment(column_fragment)));
1120
1121 // First populate the column flow with its children.
1122 let construction_result = self.build_flow_for_block_like(column_flow, node);
1123
1124 let mut abs_descendants = AbsoluteDescendants::new();
1125
1126 if let ConstructionResult::Flow(column_flow, column_abs_descendants) = construction_result {
1127 flow.add_new_child(column_flow);
1128 abs_descendants.push_descendants(column_abs_descendants);
1129 }
1130
1131 // The flow is done.
1132 flow.finish();
1133 if flow.is_absolute_containing_block() {
1134 // This is the containing block for all the absolute descendants.
1135 flow.set_absolute_descendants(abs_descendants);
1136
1137 abs_descendants = AbsoluteDescendants::new();
1138
1139 if flow.base().flags.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED) {
1140 // This is now the only absolute flow in the subtree which hasn't yet
1141 // reached its containing block.
1142 abs_descendants.push(flow.clone());
1143 }
1144 }
1145
1146 ConstructionResult::Flow(flow, abs_descendants)
1147 }
1148
1149 /// Builds a flow for a node with `display: table`. This yields a `TableWrapperFlow` with
1150 /// possibly other `TableCaptionFlow`s or `TableFlow`s underneath it.
build_flow_for_table(&mut self, node: &ConcreteThreadSafeLayoutNode, float_value: Float) -> ConstructionResult1151 fn build_flow_for_table(&mut self, node: &ConcreteThreadSafeLayoutNode, float_value: Float)
1152 -> ConstructionResult {
1153 let mut legalizer = Legalizer::new();
1154
1155 let table_style;
1156 let wrapper_style;
1157 {
1158 let context = self.style_context();
1159 table_style = node.style(context);
1160 wrapper_style = context.stylist.style_for_anonymous::<ConcreteThreadSafeLayoutNode::ConcreteElement>(
1161 &context.guards,
1162 &PseudoElement::ServoTableWrapper,
1163 &table_style,
1164 );
1165 }
1166 let wrapper_fragment =
1167 Fragment::from_opaque_node_and_style(node.opaque(),
1168 PseudoElementType::Normal,
1169 wrapper_style,
1170 node.selected_style(),
1171 node.restyle_damage(),
1172 SpecificFragmentInfo::TableWrapper);
1173 let wrapper_float_kind = FloatKind::from_property(float_value);
1174 let mut wrapper_flow =
1175 FlowRef::new(Arc::new(TableWrapperFlow::from_fragment_and_float_kind(wrapper_fragment,
1176 wrapper_float_kind)));
1177
1178 let table_fragment = Fragment::new(node, SpecificFragmentInfo::Table, self.layout_context);
1179 let table_flow = FlowRef::new(Arc::new(TableFlow::from_fragment(table_fragment)));
1180
1181 // First populate the table flow with its children.
1182 let construction_result = self.build_flow_for_block_like(table_flow, node);
1183
1184 let mut abs_descendants = AbsoluteDescendants::new();
1185
1186 // The order of the caption and the table are not necessarily the same order as in the DOM
1187 // tree. All caption blocks are placed before or after the table flow, depending on the
1188 // value of `caption-side`.
1189 self.place_table_caption_under_table_wrapper_on_side(&mut wrapper_flow,
1190 node,
1191 CaptionSide::Top);
1192
1193 if let ConstructionResult::Flow(table_flow, table_abs_descendants) = construction_result {
1194 legalizer.add_child::<ConcreteThreadSafeLayoutNode::ConcreteElement>(
1195 self.style_context(),
1196 &mut wrapper_flow,
1197 table_flow,
1198 );
1199 abs_descendants.push_descendants(table_abs_descendants);
1200 }
1201
1202 // If the value of `caption-side` is `bottom`, place it now.
1203 self.place_table_caption_under_table_wrapper_on_side(&mut wrapper_flow,
1204 node,
1205 CaptionSide::Bottom);
1206
1207 // The flow is done.
1208 legalizer.finish(&mut wrapper_flow);
1209 wrapper_flow.finish();
1210
1211 if wrapper_flow.is_absolute_containing_block() {
1212 // This is the containing block for all the absolute descendants.
1213 wrapper_flow.set_absolute_descendants(abs_descendants);
1214
1215 abs_descendants = AbsoluteDescendants::new();
1216
1217 if wrapper_flow.base().flags.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED) {
1218 // This is now the only absolute flow in the subtree which hasn't yet
1219 // reached its containing block.
1220 abs_descendants.push(wrapper_flow.clone());
1221 }
1222 }
1223
1224 ConstructionResult::Flow(wrapper_flow, abs_descendants)
1225 }
1226
1227 /// Builds a flow for a node with `display: table-caption`. This yields a `TableCaptionFlow`
1228 /// with possibly other `BlockFlow`s or `InlineFlow`s underneath it.
build_flow_for_table_caption(&mut self, node: &ConcreteThreadSafeLayoutNode) -> ConstructionResult1229 fn build_flow_for_table_caption(&mut self, node: &ConcreteThreadSafeLayoutNode) -> ConstructionResult {
1230 let fragment = self.build_fragment_for_block(node);
1231 let flow = FlowRef::new(Arc::new(TableCaptionFlow::from_fragment(fragment)));
1232 self.build_flow_for_block_like(flow, node)
1233 }
1234
1235 /// Builds a flow for a node with `display: table-row-group`. This yields a `TableRowGroupFlow`
1236 /// with possibly other `TableRowFlow`s underneath it.
build_flow_for_table_rowgroup(&mut self, node: &ConcreteThreadSafeLayoutNode) -> ConstructionResult1237 fn build_flow_for_table_rowgroup(&mut self, node: &ConcreteThreadSafeLayoutNode)
1238 -> ConstructionResult {
1239 let fragment = Fragment::new(node, SpecificFragmentInfo::TableRow, self.layout_context);
1240 let flow = FlowRef::new(Arc::new(TableRowGroupFlow::from_fragment(fragment)));
1241 self.build_flow_for_block_like(flow, node)
1242 }
1243
1244 /// Builds a flow for a node with `display: table-row`. This yields a `TableRowFlow` with
1245 /// possibly other `TableCellFlow`s underneath it.
build_flow_for_table_row(&mut self, node: &ConcreteThreadSafeLayoutNode) -> ConstructionResult1246 fn build_flow_for_table_row(&mut self, node: &ConcreteThreadSafeLayoutNode) -> ConstructionResult {
1247 let fragment = Fragment::new(node, SpecificFragmentInfo::TableRow, self.layout_context);
1248 let flow = FlowRef::new(Arc::new(TableRowFlow::from_fragment(fragment)));
1249 self.build_flow_for_block_like(flow, node)
1250 }
1251
1252 /// Builds a flow for a node with `display: table-cell`. This yields a `TableCellFlow` with
1253 /// possibly other `BlockFlow`s or `InlineFlow`s underneath it.
build_flow_for_table_cell(&mut self, node: &ConcreteThreadSafeLayoutNode) -> ConstructionResult1254 fn build_flow_for_table_cell(&mut self, node: &ConcreteThreadSafeLayoutNode) -> ConstructionResult {
1255 let fragment = Fragment::new(node, SpecificFragmentInfo::TableCell, self.layout_context);
1256
1257 // Determine if the table cell should be hidden. Per CSS 2.1 § 17.6.1.1, this will be true
1258 // if the cell has any in-flow elements (even empty ones!) and has `empty-cells` set to
1259 // `hide`.
1260 let hide = node.style(self.style_context()).get_inheritedtable().empty_cells == EmptyCells::Hide &&
1261 node.children().all(|kid| {
1262 let position = kid.style(self.style_context()).get_box().position;
1263 !kid.is_content() ||
1264 position == Position::Absolute ||
1265 position == Position::Fixed
1266 });
1267
1268 let flow = FlowRef::new(Arc::new(
1269 TableCellFlow::from_node_fragment_and_visibility_flag(node, fragment, !hide)));
1270 self.build_flow_for_block_like(flow, node)
1271 }
1272
1273 /// Builds a flow for a node with `display: list-item`. This yields a `ListItemFlow` with
1274 /// possibly other `BlockFlow`s or `InlineFlow`s underneath it.
build_flow_for_list_item(&mut self, node: &ConcreteThreadSafeLayoutNode, flotation: Float) -> ConstructionResult1275 fn build_flow_for_list_item(&mut self,
1276 node: &ConcreteThreadSafeLayoutNode,
1277 flotation: Float)
1278 -> ConstructionResult {
1279 let flotation = FloatKind::from_property(flotation);
1280 let marker_fragments = match node.style(self.style_context()).get_list().list_style_image {
1281 list_style_image::computed_value::T(Either::First(ref url_value)) => {
1282 let image_info = Box::new(ImageFragmentInfo::new(
1283 url_value.url().map(|u| u.clone()), node, &self.layout_context
1284 ));
1285 vec![Fragment::new(node, SpecificFragmentInfo::Image(image_info), self.layout_context)]
1286 }
1287 list_style_image::computed_value::T(Either::Second(_none)) => {
1288 match ListStyleTypeContent::from_list_style_type(node.style(self.style_context())
1289 .get_list()
1290 .list_style_type) {
1291 ListStyleTypeContent::None => Vec::new(),
1292 ListStyleTypeContent::StaticText(ch) => {
1293 let text = format!("{}\u{a0}", ch);
1294 let mut unscanned_marker_fragments = LinkedList::new();
1295 unscanned_marker_fragments.push_back(Fragment::new(
1296 node,
1297 SpecificFragmentInfo::UnscannedText(
1298 Box::new(UnscannedTextFragmentInfo::new(Box::<str>::from(text), None))
1299 ),
1300 self.layout_context));
1301 let marker_fragments =
1302 with_thread_local_font_context(self.layout_context, |mut font_context| {
1303 TextRunScanner::new().scan_for_runs(&mut font_context,
1304 unscanned_marker_fragments)
1305 });
1306 marker_fragments.fragments
1307 }
1308 ListStyleTypeContent::GeneratedContent(info) => {
1309 vec![Fragment::new(node, SpecificFragmentInfo::GeneratedContent(info), self.layout_context)]
1310 }
1311 }
1312 }
1313 };
1314
1315 // If the list marker is outside, it becomes the special "outside fragment" that list item
1316 // flows have. If it's inside, it's just a plain old fragment. Note that this means that
1317 // we adopt Gecko's behavior rather than WebKit's when the marker causes an {ib} split,
1318 // which has caused some malaise (Bugzilla #36854) but CSS 2.1 § 12.5.1 lets me do it, so
1319 // there.
1320 let mut initial_fragments = IntermediateInlineFragments::new();
1321 let main_fragment = self.build_fragment_for_block(node);
1322 let flow = match node.style(self.style_context()).get_list().list_style_position {
1323 ListStylePosition::Outside => {
1324 Arc::new(ListItemFlow::from_fragments_and_flotation(
1325 main_fragment, marker_fragments, flotation))
1326 }
1327 ListStylePosition::Inside => {
1328 for marker_fragment in marker_fragments {
1329 initial_fragments.fragments.push_back(marker_fragment)
1330 }
1331 Arc::new(ListItemFlow::from_fragments_and_flotation(
1332 main_fragment, vec![], flotation))
1333 }
1334 };
1335
1336 self.build_flow_for_block_starting_with_fragments(FlowRef::new(flow), node, initial_fragments)
1337 }
1338
1339 /// Creates a fragment for a node with `display: table-column`.
build_fragments_for_table_column(&mut self, node: &ConcreteThreadSafeLayoutNode) -> ConstructionResult1340 fn build_fragments_for_table_column(&mut self, node: &ConcreteThreadSafeLayoutNode)
1341 -> ConstructionResult {
1342 // CSS 2.1 § 17.2.1. Treat all child fragments of a `table-column` as `display: none`.
1343 for kid in node.children() {
1344 self.set_flow_construction_result(&kid, ConstructionResult::None)
1345 }
1346
1347 let specific = SpecificFragmentInfo::TableColumn(TableColumnFragmentInfo::new(node));
1348 let construction_item = ConstructionItem::TableColumnFragment(Fragment::new(node,
1349 specific,
1350 self.layout_context));
1351 ConstructionResult::ConstructionItem(construction_item)
1352 }
1353
1354 /// Builds a flow for a node with `display: table-column-group`.
1355 /// This yields a `TableColGroupFlow`.
build_flow_for_table_colgroup(&mut self, node: &ConcreteThreadSafeLayoutNode) -> ConstructionResult1356 fn build_flow_for_table_colgroup(&mut self, node: &ConcreteThreadSafeLayoutNode)
1357 -> ConstructionResult {
1358 let fragment =
1359 Fragment::new(node,
1360 SpecificFragmentInfo::TableColumn(TableColumnFragmentInfo::new(node)),
1361 self.layout_context);
1362 let mut col_fragments = vec!();
1363 for kid in node.children() {
1364 // CSS 2.1 § 17.2.1. Treat all non-column child fragments of `table-column-group`
1365 // as `display: none`.
1366 if let ConstructionResult::ConstructionItem(ConstructionItem::TableColumnFragment(fragment)) =
1367 kid.get_construction_result() {
1368 col_fragments.push(fragment)
1369 }
1370 }
1371 if col_fragments.is_empty() {
1372 debug!("add SpecificFragmentInfo::TableColumn for empty colgroup");
1373 let specific = SpecificFragmentInfo::TableColumn(TableColumnFragmentInfo::new(node));
1374 col_fragments.push(Fragment::new(node, specific, self.layout_context));
1375 }
1376 let mut flow = FlowRef::new(Arc::new(TableColGroupFlow::from_fragments(fragment, col_fragments)));
1377 flow.finish();
1378
1379 ConstructionResult::Flow(flow, AbsoluteDescendants::new())
1380 }
1381
1382 /// Builds a flow for a node with 'display: flex'.
build_flow_for_flex(&mut self, node: &ConcreteThreadSafeLayoutNode, float_kind: Option<FloatKind>) -> ConstructionResult1383 fn build_flow_for_flex(&mut self,
1384 node: &ConcreteThreadSafeLayoutNode,
1385 float_kind: Option<FloatKind>)
1386 -> ConstructionResult {
1387 let fragment = self.build_fragment_for_block(node);
1388 let flow = FlowRef::new(Arc::new(FlexFlow::from_fragment(fragment, float_kind)));
1389 self.build_flow_for_block_like(flow, node)
1390 }
1391
1392 /// Attempts to perform incremental repair to account for recent changes to this node. This
1393 /// can fail and return false, indicating that flows will need to be reconstructed.
1394 ///
1395 /// TODO(pcwalton): Add some more fast paths, like toggling `display: none`, adding block kids
1396 /// to block parents with no {ib} splits, adding out-of-flow kids, etc.
repair_if_possible(&mut self, node: &ConcreteThreadSafeLayoutNode) -> bool1397 pub fn repair_if_possible(&mut self, node: &ConcreteThreadSafeLayoutNode) -> bool {
1398 // We can skip reconstructing the flow if we don't have to reconstruct and none of our kids
1399 // did either.
1400 //
1401 // We visit the kids first and reset their HAS_NEWLY_CONSTRUCTED_FLOW flags after checking
1402 // them. NOTE: Make sure not to bail out early before resetting all the flags!
1403 let mut need_to_reconstruct = false;
1404
1405 // If the node has display: none, it's possible that we haven't even
1406 // styled the children once, so we need to bailout early here.
1407 if node.style(self.style_context()).get_box().clone_display() == Display::None {
1408 return false;
1409 }
1410
1411 for kid in node.children() {
1412 if kid.flags().contains(LayoutDataFlags::HAS_NEWLY_CONSTRUCTED_FLOW) {
1413 kid.remove_flags(LayoutDataFlags::HAS_NEWLY_CONSTRUCTED_FLOW);
1414 need_to_reconstruct = true
1415 }
1416 }
1417
1418 if need_to_reconstruct {
1419 return false
1420 }
1421
1422 if node.restyle_damage().contains(ServoRestyleDamage::RECONSTRUCT_FLOW) {
1423 return false
1424 }
1425
1426 let mut set_has_newly_constructed_flow_flag = false;
1427 let result = {
1428 let style = node.style(self.style_context());
1429
1430 if style.can_be_fragmented() || style.is_multicol() {
1431 return false
1432 }
1433
1434 let damage = node.restyle_damage();
1435 let mut data = node.mutate_layout_data().unwrap();
1436
1437 match *node.construction_result_mut(&mut *data) {
1438 ConstructionResult::None => true,
1439 ConstructionResult::Flow(ref mut flow, _) => {
1440 // The node's flow is of the same type and has the same set of children and can
1441 // therefore be repaired by simply propagating damage and style to the flow.
1442 if !flow.is_block_flow() {
1443 return false
1444 }
1445
1446 let flow = FlowRef::deref_mut(flow);
1447 flow.mut_base().restyle_damage.insert(damage);
1448 flow.repair_style_and_bubble_inline_sizes(&style);
1449 true
1450 }
1451 ConstructionResult::ConstructionItem(ConstructionItem::InlineFragments(
1452 ref mut inline_fragments_construction_result)) => {
1453 if !inline_fragments_construction_result.splits.is_empty() {
1454 return false
1455 }
1456
1457 for fragment in inline_fragments_construction_result.fragments
1458 .fragments
1459 .iter_mut() {
1460 // Only mutate the styles of fragments that represent the dirty node (including
1461 // pseudo-element).
1462 if fragment.node != node.opaque() {
1463 continue
1464 }
1465 if fragment.pseudo != node.get_pseudo_element_type() {
1466 continue
1467 }
1468
1469 match fragment.specific {
1470 SpecificFragmentInfo::InlineBlock(ref mut inline_block_fragment) => {
1471 let flow_ref = FlowRef::deref_mut(&mut inline_block_fragment.flow_ref);
1472 flow_ref.mut_base().restyle_damage.insert(damage);
1473 // FIXME(pcwalton): Fragment restyle damage too?
1474 flow_ref.repair_style_and_bubble_inline_sizes(&style);
1475 }
1476 SpecificFragmentInfo::InlineAbsoluteHypothetical(
1477 ref mut inline_absolute_hypothetical_fragment) => {
1478 let flow_ref = FlowRef::deref_mut(
1479 &mut inline_absolute_hypothetical_fragment.flow_ref);
1480 flow_ref.mut_base().restyle_damage.insert(damage);
1481 // FIXME(pcwalton): Fragment restyle damage too?
1482 flow_ref.repair_style_and_bubble_inline_sizes(&style);
1483 }
1484 SpecificFragmentInfo::InlineAbsolute(ref mut inline_absolute_fragment) => {
1485 let flow_ref = FlowRef::deref_mut(
1486 &mut inline_absolute_fragment.flow_ref);
1487 flow_ref.mut_base().restyle_damage.insert(damage);
1488 // FIXME(pcwalton): Fragment restyle damage too?
1489 flow_ref.repair_style_and_bubble_inline_sizes(&style);
1490 }
1491 SpecificFragmentInfo::ScannedText(_) => {
1492 // Text fragments in ConstructionResult haven't been scanned yet
1493 unreachable!()
1494 }
1495 SpecificFragmentInfo::GeneratedContent(_) |
1496 SpecificFragmentInfo::UnscannedText(_) => {
1497 // We can't repair this unscanned text; we need to update the
1498 // scanned text fragments.
1499 //
1500 // TODO: Add code to find and repair the ScannedText fragments?
1501 return false
1502 }
1503 _ => {
1504 fragment.repair_style(&style);
1505 set_has_newly_constructed_flow_flag = true;
1506 }
1507 }
1508 }
1509 true
1510 }
1511 ConstructionResult::ConstructionItem(_) => {
1512 false
1513 }
1514 }
1515 };
1516 if set_has_newly_constructed_flow_flag {
1517 node.insert_flags(LayoutDataFlags::HAS_NEWLY_CONSTRUCTED_FLOW);
1518 }
1519 return result;
1520 }
1521 }
1522
1523 impl<'a, ConcreteThreadSafeLayoutNode> PostorderNodeMutTraversal<ConcreteThreadSafeLayoutNode>
1524 for FlowConstructor<'a, ConcreteThreadSafeLayoutNode>
1525 where ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode {
1526 // Construct Flow based on 'display', 'position', and 'float' values.
1527 //
1528 // CSS 2.1 Section 9.7
1529 //
1530 // TODO: This should actually consult the table in that section to get the
1531 // final computed value for 'display'.
process(&mut self, node: &ConcreteThreadSafeLayoutNode)1532 fn process(&mut self, node: &ConcreteThreadSafeLayoutNode) {
1533 node.insert_flags(LayoutDataFlags::HAS_NEWLY_CONSTRUCTED_FLOW);
1534
1535 let style = node.style(self.style_context());
1536
1537 // Bail out if this node has an ancestor with display: none.
1538 if style.is_in_display_none_subtree() {
1539 self.set_flow_construction_result(node, ConstructionResult::None);
1540 return;
1541 }
1542
1543 // Get the `display` property for this node, and determine whether this node is floated.
1544 let (display, float, positioning) = match node.type_id() {
1545 None => {
1546 // Pseudo-element.
1547 (style.get_box().display, style.get_box().float, style.get_box().position)
1548 }
1549 Some(LayoutNodeType::Element(_)) => {
1550 let original_display = style.get_box().original_display;
1551 // FIXME(emilio, #19771): This munged_display business is pretty
1552 // wrong. After we fix this we should be able to unify the
1553 // pseudo-element path too.
1554 let munged_display = match original_display {
1555 Display::Inline | Display::InlineBlock => original_display,
1556 _ => style.get_box().display,
1557 };
1558 (munged_display, style.get_box().float, style.get_box().position)
1559 }
1560 Some(LayoutNodeType::Text) =>
1561 (Display::Inline, Float::None, Position::Static),
1562 };
1563
1564 debug!("building flow for node: {:?} {:?} {:?} {:?}", display, float, positioning, node.type_id());
1565
1566 // Switch on display and floatedness.
1567 match (display, float, positioning) {
1568 // `display: none` contributes no flow construction result.
1569 (Display::None, _, _) => {
1570 self.set_flow_construction_result(node, ConstructionResult::None);
1571 }
1572
1573 // Table items contribute table flow construction results.
1574 (Display::Table, float_value, _) => {
1575 let construction_result = self.build_flow_for_table(node, float_value);
1576 self.set_flow_construction_result(node, construction_result)
1577 }
1578
1579 // Absolutely positioned elements will have computed value of
1580 // `float` as 'none' and `display` as per the table.
1581 // Only match here for block items. If an item is absolutely
1582 // positioned, but inline we shouldn't try to construct a block
1583 // flow here - instead, let it match the inline case
1584 // below.
1585 (Display::Block, _, Position::Absolute) |
1586 (Display::Block, _, Position::Fixed) => {
1587 let construction_result = self.build_flow_for_block(node, None);
1588 self.set_flow_construction_result(node, construction_result)
1589 }
1590
1591 // List items contribute their own special flows.
1592 (Display::ListItem, float_value, _) => {
1593 let construction_result = self.build_flow_for_list_item(node, float_value);
1594 self.set_flow_construction_result(node, construction_result)
1595 }
1596
1597 // Inline items that are absolutely-positioned contribute inline fragment construction
1598 // results with a hypothetical fragment.
1599 (Display::Inline, _, Position::Absolute) |
1600 (Display::InlineBlock, _, Position::Absolute) => {
1601 let construction_result =
1602 self.build_fragment_for_absolutely_positioned_inline(node);
1603 self.set_flow_construction_result(node, construction_result)
1604 }
1605
1606 // Inline items contribute inline fragment construction results.
1607 //
1608 // FIXME(pcwalton, #3307): This is not sufficient to handle floated generated content.
1609 (Display::Inline, Float::None, _) => {
1610 let construction_result = self.build_fragments_for_inline(node);
1611 self.set_flow_construction_result(node, construction_result)
1612 }
1613
1614 // Inline-block items contribute inline fragment construction results.
1615 (Display::InlineBlock, Float::None, _) => {
1616 let construction_result = self.build_fragment_for_inline_block_or_inline_flex(node,
1617 Display::InlineBlock);
1618 self.set_flow_construction_result(node, construction_result)
1619 }
1620
1621 // Table items contribute table flow construction results.
1622 (Display::TableCaption, _, _) => {
1623 let construction_result = self.build_flow_for_table_caption(node);
1624 self.set_flow_construction_result(node, construction_result)
1625 }
1626
1627 // Table items contribute table flow construction results.
1628 (Display::TableColumnGroup, _, _) => {
1629 let construction_result = self.build_flow_for_table_colgroup(node);
1630 self.set_flow_construction_result(node, construction_result)
1631 }
1632
1633 // Table items contribute table flow construction results.
1634 (Display::TableColumn, _, _) => {
1635 let construction_result = self.build_fragments_for_table_column(node);
1636 self.set_flow_construction_result(node, construction_result)
1637 }
1638
1639 // Table items contribute table flow construction results.
1640 (Display::TableRowGroup, _, _) |
1641 (Display::TableHeaderGroup, _, _) |
1642 (Display::TableFooterGroup, _, _) => {
1643 let construction_result = self.build_flow_for_table_rowgroup(node);
1644 self.set_flow_construction_result(node, construction_result)
1645 }
1646
1647 // Table items contribute table flow construction results.
1648 (Display::TableRow, _, _) => {
1649 let construction_result = self.build_flow_for_table_row(node);
1650 self.set_flow_construction_result(node, construction_result)
1651 }
1652
1653 // Table items contribute table flow construction results.
1654 (Display::TableCell, _, _) => {
1655 let construction_result = self.build_flow_for_table_cell(node);
1656 self.set_flow_construction_result(node, construction_result)
1657 }
1658
1659 // Flex items contribute flex flow construction results.
1660 (Display::Flex, float_value, _) => {
1661 let float_kind = FloatKind::from_property(float_value);
1662 let construction_result = self.build_flow_for_flex(node, float_kind);
1663 self.set_flow_construction_result(node, construction_result)
1664 }
1665
1666 (Display::InlineFlex, _, _) => {
1667 let construction_result = self.build_fragment_for_inline_block_or_inline_flex(node,
1668 Display::InlineFlex);
1669 self.set_flow_construction_result(node, construction_result)
1670 }
1671
1672 // Block flows that are not floated contribute block flow construction results.
1673 //
1674 // TODO(pcwalton): Make this only trigger for blocks and handle the other `display`
1675 // properties separately.
1676
1677 (_, float_value, _) => {
1678 let float_kind = FloatKind::from_property(float_value);
1679 let construction_result = self.build_flow_for_block(node, float_kind);
1680 self.set_flow_construction_result(node, construction_result)
1681 }
1682 }
1683 }
1684 }
1685
1686 /// A utility trait with some useful methods for node queries.
1687 trait NodeUtils {
1688 /// Returns true if this node doesn't render its kids and false otherwise.
is_replaced_content(&self) -> bool1689 fn is_replaced_content(&self) -> bool;
1690
construction_result_mut(self, layout_data: &mut LayoutData) -> &mut ConstructionResult1691 fn construction_result_mut(self, layout_data: &mut LayoutData) -> &mut ConstructionResult;
1692
1693 /// Sets the construction result of a flow.
set_flow_construction_result(self, result: ConstructionResult)1694 fn set_flow_construction_result(self, result: ConstructionResult);
1695
1696 /// Returns the construction result for this node.
get_construction_result(self) -> ConstructionResult1697 fn get_construction_result(self) -> ConstructionResult;
1698 }
1699
1700 impl<ConcreteThreadSafeLayoutNode> NodeUtils for ConcreteThreadSafeLayoutNode
1701 where ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode {
is_replaced_content(&self) -> bool1702 fn is_replaced_content(&self) -> bool {
1703 match self.type_id() {
1704 Some(LayoutNodeType::Text) |
1705 Some(LayoutNodeType::Element(LayoutElementType::HTMLImageElement)) |
1706 Some(LayoutNodeType::Element(LayoutElementType::HTMLIFrameElement)) |
1707 Some(LayoutNodeType::Element(LayoutElementType::HTMLCanvasElement)) |
1708 Some(LayoutNodeType::Element(LayoutElementType::SVGSVGElement)) => true,
1709 Some(LayoutNodeType::Element(LayoutElementType::HTMLObjectElement)) => self.has_object_data(),
1710 Some(LayoutNodeType::Element(_)) => false,
1711 None => self.get_pseudo_element_type().is_replaced_content(),
1712 }
1713 }
1714
construction_result_mut(self, data: &mut LayoutData) -> &mut ConstructionResult1715 fn construction_result_mut(self, data: &mut LayoutData) -> &mut ConstructionResult {
1716 match self.get_pseudo_element_type() {
1717 PseudoElementType::Before => &mut data.before_flow_construction_result,
1718 PseudoElementType::After => &mut data.after_flow_construction_result,
1719 PseudoElementType::DetailsSummary => &mut data.details_summary_flow_construction_result,
1720 PseudoElementType::DetailsContent => &mut data.details_content_flow_construction_result,
1721 PseudoElementType::Normal => &mut data.flow_construction_result,
1722 }
1723 }
1724
1725 #[inline(always)]
set_flow_construction_result(self, result: ConstructionResult)1726 fn set_flow_construction_result(self, result: ConstructionResult) {
1727 let mut layout_data = self.mutate_layout_data().unwrap();
1728 let dst = self.construction_result_mut(&mut *layout_data);
1729 *dst = result;
1730 }
1731
1732 #[inline(always)]
get_construction_result(self) -> ConstructionResult1733 fn get_construction_result(self) -> ConstructionResult {
1734 let mut layout_data = self.mutate_layout_data().unwrap();
1735 self.construction_result_mut(&mut *layout_data).get()
1736 }
1737 }
1738
1739 /// Methods for interacting with HTMLObjectElement nodes
1740 trait ObjectElement {
1741 /// Returns true if this node has object data that is correct uri.
has_object_data(&self) -> bool1742 fn has_object_data(&self) -> bool;
1743
1744 /// Returns the "data" attribute value parsed as a URL
object_data(&self) -> Option<ServoUrl>1745 fn object_data(&self) -> Option<ServoUrl>;
1746 }
1747
1748 impl<N> ObjectElement for N where N: ThreadSafeLayoutNode {
has_object_data(&self) -> bool1749 fn has_object_data(&self) -> bool {
1750 let elem = self.as_element().unwrap();
1751 let type_and_data = (
1752 elem.get_attr(&ns!(), &local_name!("type")),
1753 elem.get_attr(&ns!(), &local_name!("data")),
1754 );
1755 match type_and_data {
1756 (None, Some(uri)) => is_image_data(uri),
1757 _ => false
1758 }
1759 }
1760
object_data(&self) -> Option<ServoUrl>1761 fn object_data(&self) -> Option<ServoUrl> {
1762 let elem = self.as_element().unwrap();
1763 let type_and_data = (
1764 elem.get_attr(&ns!(), &local_name!("type")),
1765 elem.get_attr(&ns!(), &local_name!("data")),
1766 );
1767 match type_and_data {
1768 (None, Some(uri)) if is_image_data(uri) => ServoUrl::parse(uri).ok(),
1769 _ => None
1770 }
1771 }
1772 }
1773
1774 // This must not be public because only the layout constructor can call these
1775 // methods.
1776 trait FlowConstructionUtils {
1777 /// Adds a new flow as a child of this flow. Removes the flow from the given leaf set if
1778 /// it's present.
add_new_child(&mut self, new_child: FlowRef)1779 fn add_new_child(&mut self, new_child: FlowRef);
1780
1781 /// Finishes a flow. Once a flow is finished, no more child flows or boxes may be added to it.
1782 /// This will normally run the bubble-inline-sizes (minimum and preferred -- i.e. intrinsic --
1783 /// inline-size) calculation, unless the global `bubble_inline-sizes_separately` flag is on.
1784 ///
1785 /// All flows must be finished at some point, or they will not have their intrinsic inline-
1786 /// sizes properly computed. (This is not, however, a memory safety problem.)
finish(&mut self)1787 fn finish(&mut self);
1788 }
1789
1790 impl FlowConstructionUtils for FlowRef {
1791 /// Adds a new flow as a child of this flow. Fails if this flow is marked as a leaf.
add_new_child(&mut self, mut new_child: FlowRef)1792 fn add_new_child(&mut self, mut new_child: FlowRef) {
1793 {
1794 let kid_base = FlowRef::deref_mut(&mut new_child).mut_base();
1795 kid_base.parallel.parent = parallel::mut_owned_flow_to_unsafe_flow(self);
1796 }
1797
1798 let base = FlowRef::deref_mut(self).mut_base();
1799 base.children.push_back(new_child);
1800 let _ = base.parallel.children_count.fetch_add(1, Ordering::Relaxed);
1801 }
1802
1803 /// Finishes a flow. Once a flow is finished, no more child flows or fragments may be added to
1804 /// it. This will normally run the bubble-inline-sizes (minimum and preferred -- i.e. intrinsic
1805 /// -- inline-size) calculation, unless the global `bubble_inline-sizes_separately` flag is on.
1806 ///
1807 /// All flows must be finished at some point, or they will not have their intrinsic inline-sizes
1808 /// properly computed. (This is not, however, a memory safety problem.)
finish(&mut self)1809 fn finish(&mut self) {
1810 if !opts::get().bubble_inline_sizes_separately {
1811 FlowRef::deref_mut(self).bubble_inline_sizes();
1812 FlowRef::deref_mut(self).mut_base().restyle_damage.remove(ServoRestyleDamage::BUBBLE_ISIZES);
1813 }
1814 }
1815 }
1816
1817 /// Strips ignorable whitespace from the start of a list of fragments.
strip_ignorable_whitespace_from_start(this: &mut LinkedList<Fragment>)1818 pub fn strip_ignorable_whitespace_from_start(this: &mut LinkedList<Fragment>) {
1819 if this.is_empty() {
1820 return // Fast path.
1821 }
1822
1823 let mut leading_fragments_consisting_of_solely_bidi_control_characters = LinkedList::new();
1824 while !this.is_empty() {
1825 match this.front_mut().as_mut().unwrap().strip_leading_whitespace_if_necessary() {
1826 WhitespaceStrippingResult::RetainFragment => break,
1827 WhitespaceStrippingResult::FragmentContainedOnlyBidiControlCharacters => {
1828 leading_fragments_consisting_of_solely_bidi_control_characters.push_back(
1829 this.pop_front().unwrap())
1830 }
1831 WhitespaceStrippingResult::FragmentContainedOnlyWhitespace => {
1832 let removed_fragment = this.pop_front().unwrap();
1833 if let Some(ref mut remaining_fragment) = this.front_mut() {
1834 remaining_fragment.meld_with_prev_inline_fragment(&removed_fragment);
1835 }
1836 }
1837 }
1838 }
1839 prepend_from(this, &mut leading_fragments_consisting_of_solely_bidi_control_characters);
1840 }
1841
1842 /// Strips ignorable whitespace from the end of a list of fragments.
strip_ignorable_whitespace_from_end(this: &mut LinkedList<Fragment>)1843 pub fn strip_ignorable_whitespace_from_end(this: &mut LinkedList<Fragment>) {
1844 if this.is_empty() {
1845 return
1846 }
1847
1848 let mut trailing_fragments_consisting_of_solely_bidi_control_characters = LinkedList::new();
1849 while !this.is_empty() {
1850 match this.back_mut().as_mut().unwrap().strip_trailing_whitespace_if_necessary() {
1851 WhitespaceStrippingResult::RetainFragment => break,
1852 WhitespaceStrippingResult::FragmentContainedOnlyBidiControlCharacters => {
1853 trailing_fragments_consisting_of_solely_bidi_control_characters.push_front(
1854 this.pop_back().unwrap())
1855 }
1856 WhitespaceStrippingResult::FragmentContainedOnlyWhitespace => {
1857 let removed_fragment = this.pop_back().unwrap();
1858 if let Some(ref mut remaining_fragment) = this.back_mut() {
1859 remaining_fragment.meld_with_next_inline_fragment(&removed_fragment);
1860 }
1861 }
1862 }
1863 }
1864 this.append(&mut trailing_fragments_consisting_of_solely_bidi_control_characters);
1865 }
1866
1867 /// If the 'unicode-bidi' property has a value other than 'normal', return the bidi control codes
1868 /// to inject before and after the text content of the element.
bidi_control_chars(style: &ServoArc<ComputedValues>) -> Option<(&'static str, &'static str)>1869 fn bidi_control_chars(style: &ServoArc<ComputedValues>) -> Option<(&'static str, &'static str)> {
1870 use style::computed_values::direction::T::*;
1871 use style::computed_values::unicode_bidi::T::*;
1872
1873 let unicode_bidi = style.get_text().unicode_bidi;
1874 let direction = style.get_inheritedbox().direction;
1875
1876 // See the table in http://dev.w3.org/csswg/css-writing-modes/#unicode-bidi
1877 match (unicode_bidi, direction) {
1878 (Normal, _) => None,
1879 (Embed, Ltr) => Some(("\u{202A}", "\u{202C}")),
1880 (Embed, Rtl) => Some(("\u{202B}", "\u{202C}")),
1881 (Isolate, Ltr) => Some(("\u{2066}", "\u{2069}")),
1882 (Isolate, Rtl) => Some(("\u{2067}", "\u{2069}")),
1883 (BidiOverride, Ltr) => Some(("\u{202D}", "\u{202C}")),
1884 (BidiOverride, Rtl) => Some(("\u{202E}", "\u{202C}")),
1885 (IsolateOverride, Ltr) => Some(("\u{2068}\u{202D}", "\u{202C}\u{2069}")),
1886 (IsolateOverride, Rtl) => Some(("\u{2068}\u{202E}", "\u{202C}\u{2069}")),
1887 (Plaintext, _) => Some(("\u{2068}", "\u{2069}")),
1888 }
1889 }
1890
control_chars_to_fragment<E>( node: &InlineFragmentNodeInfo, context: &SharedStyleContext, text: &str, restyle_damage: RestyleDamage, ) -> Fragment where E: TElement,1891 fn control_chars_to_fragment<E>(
1892 node: &InlineFragmentNodeInfo,
1893 context: &SharedStyleContext,
1894 text: &str,
1895 restyle_damage: RestyleDamage,
1896 ) -> Fragment
1897 where
1898 E: TElement,
1899 {
1900 let info = SpecificFragmentInfo::UnscannedText(
1901 Box::new(UnscannedTextFragmentInfo::new(Box::<str>::from(text), None))
1902 );
1903 let text_style = context.stylist.style_for_anonymous::<E>(
1904 &context.guards,
1905 &PseudoElement::ServoText,
1906 &node.style,
1907 );
1908
1909 Fragment::from_opaque_node_and_style(node.address,
1910 node.pseudo,
1911 text_style,
1912 node.selected_style.clone(),
1913 restyle_damage,
1914 info)
1915 }
1916
1917 /// Convenience methods for computed CSS values
1918 trait ComputedValueUtils {
1919 /// Returns true if this node has non-zero padding or border.
has_padding_or_border(&self) -> bool1920 fn has_padding_or_border(&self) -> bool;
1921 }
1922
1923 impl ComputedValueUtils for ComputedValues {
has_padding_or_border(&self) -> bool1924 fn has_padding_or_border(&self) -> bool {
1925 let padding = self.get_padding();
1926 let border = self.get_border();
1927
1928 !padding.padding_top.is_definitely_zero() ||
1929 !padding.padding_right.is_definitely_zero() ||
1930 !padding.padding_bottom.is_definitely_zero() ||
1931 !padding.padding_left.is_definitely_zero() ||
1932 border.border_top_width.px() != 0. ||
1933 border.border_right_width.px() != 0. ||
1934 border.border_bottom_width.px() != 0. ||
1935 border.border_left_width.px() != 0.
1936 }
1937 }
1938
1939 /// Maintains a stack of anonymous boxes needed to ensure that the flow tree is *legal*. The tree
1940 /// is legal if it follows the rules in CSS 2.1 § 17.2.1.
1941 ///
1942 /// As an example, the legalizer makes sure that table row flows contain only table cells. If the
1943 /// flow constructor attempts to place, say, a block flow directly underneath the table row, the
1944 /// legalizer generates an anonymous table cell in between to hold the block.
1945 ///
1946 /// Generally, the flow constructor should use `Legalizer::add_child()` instead of calling
1947 /// `Flow::add_new_child()` directly. This ensures that the flow tree remains legal at all times
1948 /// and centralizes the anonymous flow generation logic in one place.
1949 struct Legalizer {
1950 /// A stack of anonymous flows that have yet to be finalized (i.e. that still could acquire new
1951 /// children).
1952 stack: Vec<FlowRef>,
1953 }
1954
1955 impl Legalizer {
1956 /// Creates a new legalizer.
new() -> Legalizer1957 fn new() -> Legalizer {
1958 Legalizer {
1959 stack: vec![],
1960 }
1961 }
1962
1963 /// Makes the `child` flow a new child of `parent`. Anonymous flows are automatically inserted
1964 /// to keep the tree legal.
add_child<E>( &mut self, context: &SharedStyleContext, parent: &mut FlowRef, mut child: FlowRef, ) where E: TElement,1965 fn add_child<E>(
1966 &mut self,
1967 context: &SharedStyleContext,
1968 parent: &mut FlowRef,
1969 mut child: FlowRef,
1970 )
1971 where
1972 E: TElement,
1973 {
1974 while !self.stack.is_empty() {
1975 if self.try_to_add_child::<E>(context, parent, &mut child) {
1976 return
1977 }
1978 self.flush_top_of_stack(parent)
1979 }
1980
1981 while !self.try_to_add_child::<E>(context, parent, &mut child) {
1982 self.push_next_anonymous_flow::<E>(context, parent)
1983 }
1984 }
1985
1986 /// Flushes all flows we've been gathering up.
finish(mut self, parent: &mut FlowRef)1987 fn finish(mut self, parent: &mut FlowRef) {
1988 while !self.stack.is_empty() {
1989 self.flush_top_of_stack(parent)
1990 }
1991 }
1992
1993 /// Attempts to make `child` a child of `parent`. On success, this returns true. If this would
1994 /// make the tree illegal, this method does nothing and returns false.
1995 ///
1996 /// This method attempts to create anonymous blocks in between `parent` and `child` if and only
1997 /// if those blocks will only ever have `child` as their sole child. At present, this is only
1998 /// true for anonymous block children of flex flows.
try_to_add_child<E>( &mut self, context: &SharedStyleContext, parent: &mut FlowRef, child: &mut FlowRef, ) -> bool where E: TElement,1999 fn try_to_add_child<E>(
2000 &mut self,
2001 context: &SharedStyleContext,
2002 parent: &mut FlowRef,
2003 child: &mut FlowRef,
2004 ) -> bool
2005 where
2006 E: TElement,
2007 {
2008 let parent = self.stack.last_mut().unwrap_or(parent);
2009 let (parent_class, child_class) = (parent.class(), child.class());
2010 match (parent_class, child_class) {
2011 (FlowClass::TableWrapper, FlowClass::Table) |
2012 (FlowClass::Table, FlowClass::TableColGroup) |
2013 (FlowClass::Table, FlowClass::TableRowGroup) |
2014 (FlowClass::Table, FlowClass::TableRow) |
2015 (FlowClass::Table, FlowClass::TableCaption) |
2016 (FlowClass::TableRowGroup, FlowClass::TableRow) |
2017 (FlowClass::TableRow, FlowClass::TableCell) => {
2018 parent.add_new_child((*child).clone());
2019 true
2020 }
2021
2022 (FlowClass::TableWrapper, _) |
2023 (FlowClass::Table, _) |
2024 (FlowClass::TableRowGroup, _) |
2025 (FlowClass::TableRow, _) |
2026 (_, FlowClass::Table) |
2027 (_, FlowClass::TableColGroup) |
2028 (_, FlowClass::TableRowGroup) |
2029 (_, FlowClass::TableRow) |
2030 (_, FlowClass::TableCaption) |
2031 (_, FlowClass::TableCell) => {
2032 false
2033 }
2034
2035 (FlowClass::Flex, FlowClass::Inline) => {
2036 FlowRef::deref_mut(child).mut_base().flags.insert(FlowFlags::MARGINS_CANNOT_COLLAPSE);
2037 let mut block_wrapper = Legalizer::create_anonymous_flow::<E, _>(
2038 context,
2039 parent,
2040 &[PseudoElement::ServoAnonymousBlock],
2041 SpecificFragmentInfo::Generic,
2042 BlockFlow::from_fragment,
2043 );
2044
2045 {
2046 let flag = if parent.as_flex().main_mode() == Direction::Inline {
2047 FragmentFlags::IS_INLINE_FLEX_ITEM
2048 } else {
2049 FragmentFlags::IS_BLOCK_FLEX_ITEM
2050 };
2051 let block = FlowRef::deref_mut(&mut block_wrapper).as_mut_block();
2052 block.base.flags.insert(FlowFlags::MARGINS_CANNOT_COLLAPSE);
2053 block.fragment.flags.insert(flag);
2054 }
2055 block_wrapper.add_new_child((*child).clone());
2056 block_wrapper.finish();
2057 parent.add_new_child(block_wrapper);
2058 true
2059 }
2060
2061 (FlowClass::Flex, _) => {
2062 {
2063 let flag = if parent.as_flex().main_mode() == Direction::Inline {
2064 FragmentFlags::IS_INLINE_FLEX_ITEM
2065 } else {
2066 FragmentFlags::IS_BLOCK_FLEX_ITEM
2067 };
2068 let block = FlowRef::deref_mut(child).as_mut_block();
2069 block.base.flags.insert(FlowFlags::MARGINS_CANNOT_COLLAPSE);
2070 block.fragment.flags.insert(flag);
2071 }
2072 parent.add_new_child((*child).clone());
2073 true
2074 }
2075
2076 _ => {
2077 parent.add_new_child((*child).clone());
2078 true
2079 }
2080 }
2081 }
2082
2083 /// Finalizes the flow on the top of the stack.
flush_top_of_stack(&mut self, parent: &mut FlowRef)2084 fn flush_top_of_stack(&mut self, parent: &mut FlowRef) {
2085 let mut child = self.stack.pop().expect("flush_top_of_stack(): stack empty");
2086 child.finish();
2087 self.stack.last_mut().unwrap_or(parent).add_new_child(child)
2088 }
2089
2090 /// Adds the anonymous flow that would be necessary to make an illegal child of `parent` legal
2091 /// to the stack.
push_next_anonymous_flow<E>( &mut self, context: &SharedStyleContext, parent: &FlowRef, ) where E: TElement,2092 fn push_next_anonymous_flow<E>(
2093 &mut self,
2094 context: &SharedStyleContext,
2095 parent: &FlowRef,
2096 )
2097 where
2098 E: TElement,
2099 {
2100 let parent_class = self.stack.last().unwrap_or(parent).class();
2101 match parent_class {
2102 FlowClass::TableRow => {
2103 self.push_new_anonymous_flow::<E, _>(
2104 context,
2105 parent,
2106 &[PseudoElement::ServoAnonymousTableCell],
2107 SpecificFragmentInfo::TableCell,
2108 TableCellFlow::from_fragment,
2109 )
2110 }
2111 FlowClass::Table | FlowClass::TableRowGroup => {
2112 self.push_new_anonymous_flow::<E, _>(
2113 context,
2114 parent,
2115 &[PseudoElement::ServoAnonymousTableRow],
2116 SpecificFragmentInfo::TableRow,
2117 TableRowFlow::from_fragment,
2118 )
2119 }
2120 FlowClass::TableWrapper => {
2121 self.push_new_anonymous_flow::<E, _>(
2122 context,
2123 parent,
2124 &[PseudoElement::ServoAnonymousTable],
2125 SpecificFragmentInfo::Table,
2126 TableFlow::from_fragment,
2127 )
2128 }
2129 _ => {
2130 self.push_new_anonymous_flow::<E, _>(
2131 context,
2132 parent,
2133 &[PseudoElement::ServoTableWrapper,
2134 PseudoElement::ServoAnonymousTableWrapper],
2135 SpecificFragmentInfo::TableWrapper,
2136 TableWrapperFlow::from_fragment,
2137 )
2138 }
2139 }
2140 }
2141
2142 /// Creates an anonymous flow and pushes it onto the stack.
push_new_anonymous_flow<E, F>( &mut self, context: &SharedStyleContext, reference: &FlowRef, pseudos: &[PseudoElement], specific_fragment_info: SpecificFragmentInfo, constructor: extern "Rust" fn(Fragment) -> F, ) where E: TElement, F: Flow,2143 fn push_new_anonymous_flow<E, F>(
2144 &mut self,
2145 context: &SharedStyleContext,
2146 reference: &FlowRef,
2147 pseudos: &[PseudoElement],
2148 specific_fragment_info: SpecificFragmentInfo,
2149 constructor: extern "Rust" fn(Fragment) -> F,
2150 )
2151 where
2152 E: TElement,
2153 F: Flow,
2154 {
2155 let new_flow = Self::create_anonymous_flow::<E, _>(
2156 context,
2157 reference,
2158 pseudos,
2159 specific_fragment_info,
2160 constructor,
2161 );
2162 self.stack.push(new_flow)
2163 }
2164
2165 /// Creates a new anonymous flow. The new flow is identical to `reference` except with all
2166 /// styles applying to every pseudo-element in `pseudos` applied.
2167 ///
2168 /// This method invokes the supplied constructor function on the given specific fragment info
2169 /// in order to actually generate the flow.
create_anonymous_flow<E, F>( context: &SharedStyleContext, reference: &FlowRef, pseudos: &[PseudoElement], specific_fragment_info: SpecificFragmentInfo, constructor: extern "Rust" fn(Fragment) -> F, ) -> FlowRef where E: TElement, F: Flow,2170 fn create_anonymous_flow<E, F>(
2171 context: &SharedStyleContext,
2172 reference: &FlowRef,
2173 pseudos: &[PseudoElement],
2174 specific_fragment_info: SpecificFragmentInfo,
2175 constructor: extern "Rust" fn(Fragment) -> F,
2176 ) -> FlowRef
2177 where
2178 E: TElement,
2179 F: Flow,
2180 {
2181 let reference_block = reference.as_block();
2182 let mut new_style = reference_block.fragment.style.clone();
2183 for pseudo in pseudos {
2184 new_style = context.stylist.style_for_anonymous::<E>(
2185 &context.guards,
2186 pseudo,
2187 &new_style,
2188 );
2189 }
2190 let fragment =
2191 reference_block.fragment.create_similar_anonymous_fragment(
2192 new_style,
2193 specific_fragment_info,
2194 );
2195 FlowRef::new(Arc::new(constructor(fragment)))
2196 }
2197 }
2198
2199