1use layout_api::LayoutNode;
6use rayon::iter::{IntoParallelIterator, ParallelIterator};
7use servo_arc::Arc;
8use style::properties::ComputedValues;
9use style::properties::longhands::list_style_position::computed_value::T as ListStylePosition;
10use style::selector_parser::PseudoElement;
11use style::str::char_is_whitespace;
12use style::values::specified::box_::DisplayOutside as StyloDisplayOutside;
13
14use super::OutsideMarker;
15use super::inline::construct::InlineFormattingContextBuilder;
16use super::inline::inline_box::InlineBox;
17use super::inline::{InlineFormattingContext, SharedInlineStyles};
18use crate::PropagatedBoxTreeData;
19use crate::cell::ArcRefCell;
20use crate::context::LayoutContext;
21use crate::dom::{BoxSlot, LayoutBox, NodeExt};
22use crate::dom_traversal::{
23 BoxTreeString, Contents, NodeAndStyleInfo, NonReplacedContents, PseudoElementContentItem,
24 TraversalHandler,
25};
26use crate::flow::float::FloatBox;
27use crate::flow::same_formatting_context_block::SameFormattingContextBlock;
28use crate::flow::{BlockContainer, BlockFormattingContext, BlockLevelBox};
29use crate::formatting_contexts::{
30 IndependentFormattingContext, IndependentFormattingContextContents,
31};
32use crate::fragment_tree::FragmentFlags;
33use crate::layout_box_base::LayoutBoxBase;
34use crate::positioned::AbsolutelyPositionedBox;
35use crate::style_ext::{ComputedValuesExt, DisplayGeneratingBox, DisplayInside, DisplayOutside};
36use crate::table::{AnonymousTableContent, Table};
37
38impl BlockFormattingContext {
39 pub(crate) fn construct(
40 context: &LayoutContext,
41 info: &NodeAndStyleInfo<'_>,
42 contents: NonReplacedContents,
43 propagated_data: PropagatedBoxTreeData,
44 is_list_item: bool,
45 ) -> Self {
46 Self::from_block_container(BlockContainer::construct(
47 context,
48 info,
49 contents,
50 propagated_data,
51 is_list_item,
52 ))
53 }
54
55 pub(crate) fn from_block_container(contents: BlockContainer) -> Self {
56 let contains_floats = contents.contains_floats();
57 Self {
58 contents,
59 contains_floats,
60 }
61 }
62}
63
64struct BlockLevelJob<'dom> {
65 info: NodeAndStyleInfo<'dom>,
66 box_slot: BoxSlot<'dom>,
67 propagated_data: PropagatedBoxTreeData,
68 kind: BlockLevelCreator,
69}
70
71pub(crate) enum BlockLevelCreator {
72 SameFormattingContextBlock(IntermediateBlockContainer),
73 Independent {
74 display_inside: DisplayInside,
75 contents: Contents,
76 },
77 OutOfFlowAbsolutelyPositionedBox {
78 display_inside: DisplayInside,
79 contents: Contents,
80 },
81 OutOfFlowFloatBox {
82 display_inside: DisplayInside,
83 contents: Contents,
84 },
85 OutsideMarker {
86 list_item_style: Arc<ComputedValues>,
87 contents: Vec<PseudoElementContentItem>,
88 },
89 AnonymousTable {
90 table_block: ArcRefCell<BlockLevelBox>,
91 },
92}
93
94impl BlockLevelCreator {
95 pub(crate) fn new_for_inflow_block_level_element<'dom>(
96 info: &NodeAndStyleInfo<'dom>,
97 display_inside: DisplayInside,
98 contents: Contents,
99 propagated_data: PropagatedBoxTreeData,
100 ) -> Self {
101 match contents {
102 Contents::NonReplaced(contents) => match display_inside {
103 DisplayInside::Flow { is_list_item }
104 if !info.style.establishes_block_formatting_context(
109 FragmentFlags::empty()
110 ) =>
111 {
112 Self::SameFormattingContextBlock(
113 IntermediateBlockContainer::Deferred {
114 contents,
115 propagated_data,
116 is_list_item,
117 },
118 )
119 },
120 _ => Self::Independent {
121 display_inside,
122 contents: Contents::NonReplaced(contents),
123 },
124 },
125 Contents::Replaced(_) | Contents::Widget(_) => Self::Independent {
126 display_inside,
127 contents,
128 },
129 }
130 }
131}
132
133pub(crate) enum IntermediateBlockContainer {
141 InlineFormattingContext(BlockContainer),
142 Deferred {
143 contents: NonReplacedContents,
144 propagated_data: PropagatedBoxTreeData,
145 is_list_item: bool,
146 },
147}
148
149pub(crate) struct BlockContainerBuilder<'dom, 'style> {
154 context: &'style LayoutContext<'style>,
155
156 info: &'style NodeAndStyleInfo<'dom>,
159
160 block_level_boxes: Vec<BlockLevelJob<'dom>>,
174
175 have_already_seen_first_formatted_line: bool,
180
181 propagated_data: PropagatedBoxTreeData,
183
184 inline_formatting_context_builder: Option<InlineFormattingContextBuilder>,
189
190 anonymous_box_info: Option<NodeAndStyleInfo<'dom>>,
193
194 anonymous_table_content: Vec<AnonymousTableContent<'dom>>,
198
199 display_contents_shared_styles: Vec<SharedInlineStyles>,
203}
204
205impl BlockContainer {
206 pub fn construct(
207 context: &LayoutContext,
208 info: &NodeAndStyleInfo<'_>,
209 contents: NonReplacedContents,
210 propagated_data: PropagatedBoxTreeData,
211 is_list_item: bool,
212 ) -> BlockContainer {
213 let mut builder = BlockContainerBuilder::new(context, info, propagated_data);
214
215 if is_list_item &&
216 let Some((marker_info, marker_contents)) = crate::lists::make_marker(context, info)
217 {
218 match marker_info.style.get_list_style_position() {
219 ListStylePosition::Inside => {
220 builder.handle_list_item_marker_inside(&marker_info, marker_contents)
221 },
222 ListStylePosition::Outside => builder.handle_list_item_marker_outside(
223 &marker_info,
224 marker_contents,
225 info.style.clone(),
226 ),
227 }
228 }
229
230 contents.traverse(context, info, &mut builder);
231 builder.finish()
232 }
233}
234
235impl<'dom, 'style> BlockContainerBuilder<'dom, 'style> {
236 pub(crate) fn new(
237 context: &'style LayoutContext,
238 info: &'style NodeAndStyleInfo<'dom>,
239 propagated_data: PropagatedBoxTreeData,
240 ) -> Self {
241 BlockContainerBuilder {
242 context,
243 info,
244 block_level_boxes: Vec::new(),
245 propagated_data,
246 have_already_seen_first_formatted_line: false,
247 anonymous_box_info: None,
248 anonymous_table_content: Vec::new(),
249 inline_formatting_context_builder: None,
250 display_contents_shared_styles: Vec::new(),
251 }
252 }
253
254 fn currently_processing_inline_box(&self) -> bool {
255 self.inline_formatting_context_builder
256 .as_ref()
257 .is_some_and(InlineFormattingContextBuilder::currently_processing_inline_box)
258 }
259
260 fn ensure_inline_formatting_context_builder(&mut self) -> &mut InlineFormattingContextBuilder {
261 self.inline_formatting_context_builder
262 .get_or_insert_with(|| {
263 let should_process_first_letter = !self.have_already_seen_first_formatted_line;
265 let mut builder = InlineFormattingContextBuilder::new(
266 self.info,
267 self.context,
268 should_process_first_letter,
269 );
270 for shared_inline_styles in self.display_contents_shared_styles.iter() {
271 builder.enter_display_contents(shared_inline_styles.clone());
272 }
273 builder
274 })
275 }
276
277 fn finish_ongoing_inline_formatting_context(&mut self) -> Option<InlineFormattingContext> {
278 self.inline_formatting_context_builder.take()?.finish(
279 self.context,
280 !self.have_already_seen_first_formatted_line,
281 self.info.node.is_single_line_text_input(),
282 self.info.style.to_bidi_level(),
283 )
284 }
285
286 pub(crate) fn finish(mut self) -> BlockContainer {
287 debug_assert!(!self.currently_processing_inline_box());
288
289 self.finish_anonymous_table_if_needed();
290
291 if let Some(inline_formatting_context) = self.finish_ongoing_inline_formatting_context() {
292 if self.block_level_boxes.is_empty() {
296 return BlockContainer::InlineFormattingContext(inline_formatting_context);
297 }
298 self.push_block_level_job_for_inline_formatting_context(inline_formatting_context);
299 }
300
301 let context = self.context;
302 let block_level_boxes = if self
303 .context
304 .should_parallelize(self.block_level_boxes.len())
305 {
306 self.block_level_boxes
307 .into_par_iter()
308 .map(|block_level_job| block_level_job.finish(context))
309 .collect()
310 } else {
311 self.block_level_boxes
312 .into_iter()
313 .map(|block_level_job| block_level_job.finish(context))
314 .collect()
315 };
316
317 BlockContainer::BlockLevelBoxes(block_level_boxes)
318 }
319
320 fn finish_anonymous_table_if_needed(&mut self) {
321 if self.anonymous_table_content.is_empty() {
322 return;
323 }
324
325 let inline_table = self.currently_processing_inline_box();
333
334 let mut contents: Vec<AnonymousTableContent<'dom>> =
335 self.anonymous_table_content.drain(..).collect();
336 let last_element_index = contents
337 .iter()
338 .rposition(|content| matches!(content, AnonymousTableContent::Element { .. }))
339 .expect("Anonymous table contents should include some table-level element");
340 let trailing_contents = contents.split_off(last_element_index + 1);
341
342 let (table_info, ifc) = Table::construct_anonymous(
343 self.context,
344 self,
345 self.info,
346 contents,
347 self.propagated_data,
348 );
349
350 if inline_table {
351 self.ensure_inline_formatting_context_builder()
352 .push_atomic(|| ArcRefCell::new(ifc), None);
353 } else {
354 let table_block = ArcRefCell::new(BlockLevelBox::Independent(ifc));
355
356 if let Some(inline_formatting_context) = self.finish_ongoing_inline_formatting_context()
357 {
358 self.push_block_level_job_for_inline_formatting_context(inline_formatting_context);
359 }
360
361 let box_slot = table_info.node.box_slot();
362 self.block_level_boxes.push(BlockLevelJob {
363 info: table_info,
364 box_slot,
365 kind: BlockLevelCreator::AnonymousTable { table_block },
366 propagated_data: self.propagated_data,
367 });
368 }
369
370 for content in trailing_contents {
380 match content {
381 AnonymousTableContent::Text(info, text) => self.handle_text(&info, text),
382 AnonymousTableContent::EnterDisplayContents(styles) => {
383 self.enter_display_contents(styles)
384 },
385 AnonymousTableContent::LeaveDisplayContents => self.leave_display_contents(),
386 AnonymousTableContent::Element { .. } => {
387 unreachable!("All elements were placed inside the table")
388 },
389 }
390 }
391 }
392}
393
394impl<'dom> TraversalHandler<'dom> for BlockContainerBuilder<'dom, '_> {
395 fn handle_element(
396 &mut self,
397 info: &NodeAndStyleInfo<'dom>,
398 display: DisplayGeneratingBox,
399 contents: Contents,
400 box_slot: BoxSlot<'dom>,
401 ) {
402 match display {
403 DisplayGeneratingBox::OutsideInside { outside, inside } => {
404 self.finish_anonymous_table_if_needed();
405
406 match outside {
407 DisplayOutside::Inline => {
408 self.handle_inline_level_element(info, inside, contents, box_slot)
409 },
410 DisplayOutside::Block => {
411 let box_style = info.style.get_box();
412 if box_style.position.is_absolutely_positioned() {
415 self.handle_absolutely_positioned_element(
416 info, inside, contents, box_slot,
417 )
418 } else if box_style.float.is_floating() {
419 self.handle_float_element(info, inside, contents, box_slot)
420 } else {
421 self.handle_block_level_element(info, inside, contents, box_slot)
422 }
423 },
424 };
425 },
426 DisplayGeneratingBox::LayoutInternal(_) => {
427 self.anonymous_table_content
428 .push(AnonymousTableContent::Element {
429 info: info.clone(),
430 display,
431 contents,
432 box_slot,
433 });
434 },
435 }
436 }
437
438 fn handle_text(&mut self, info: &NodeAndStyleInfo<'dom>, text: BoxTreeString<'dom>) {
439 if text.is_empty() && !info.node.text_node_needs_caret_placeholder() {
440 return;
441 }
442
443 if !self.anonymous_table_content.is_empty() && text.chars().all(char_is_whitespace) {
447 self.anonymous_table_content
448 .push(AnonymousTableContent::Text(info.clone(), text));
449 return;
450 } else {
451 self.finish_anonymous_table_if_needed();
452 }
453
454 self.ensure_inline_formatting_context_builder();
455 self.inline_formatting_context_builder
456 .as_mut()
457 .expect("Should be guaranteed by line above")
458 .push_text_with_possible_first_letter(text, info, self.info, self.context);
459 }
460
461 fn enter_display_contents(&mut self, styles: SharedInlineStyles) {
462 if !self.anonymous_table_content.is_empty() {
463 self.anonymous_table_content
464 .push(AnonymousTableContent::EnterDisplayContents(styles));
465 return;
466 }
467 self.display_contents_shared_styles.push(styles.clone());
468 if let Some(builder) = self.inline_formatting_context_builder.as_mut() {
469 builder.enter_display_contents(styles);
470 }
471 }
472
473 fn leave_display_contents(&mut self) {
474 if !self.anonymous_table_content.is_empty() {
475 self.anonymous_table_content
476 .push(AnonymousTableContent::LeaveDisplayContents);
477 return;
478 }
479 self.display_contents_shared_styles.pop();
480 if let Some(builder) = self.inline_formatting_context_builder.as_mut() {
481 builder.leave_display_contents();
482 }
483 }
484}
485
486impl<'dom> BlockContainerBuilder<'dom, '_> {
487 fn handle_list_item_marker_inside(
488 &mut self,
489 marker_info: &NodeAndStyleInfo<'dom>,
490 contents: Vec<crate::dom_traversal::PseudoElementContentItem>,
491 ) {
492 let box_slot = marker_info.node.box_slot();
493 self.handle_inline_level_element(
494 marker_info,
495 DisplayInside::Flow {
496 is_list_item: false,
497 },
498 Contents::for_pseudo_element(contents),
499 box_slot,
500 );
501 }
502
503 fn handle_list_item_marker_outside(
504 &mut self,
505 marker_info: &NodeAndStyleInfo<'dom>,
506 contents: Vec<crate::dom_traversal::PseudoElementContentItem>,
507 list_item_style: Arc<ComputedValues>,
508 ) {
509 let box_slot = marker_info.node.box_slot();
510 self.block_level_boxes.push(BlockLevelJob {
511 info: marker_info.clone(),
512 box_slot,
513 kind: BlockLevelCreator::OutsideMarker {
514 contents,
515 list_item_style,
516 },
517 propagated_data: self.propagated_data,
518 });
519 }
520
521 fn handle_inline_level_element(
522 &mut self,
523 info: &NodeAndStyleInfo<'dom>,
524 display_inside: DisplayInside,
525 contents: Contents,
526 box_slot: BoxSlot<'dom>,
527 ) {
528 let context = self.context;
529 let old_layout_box = box_slot.take_layout_box();
530 let (is_list_item, non_replaced_contents) = match (display_inside, contents) {
531 (
532 DisplayInside::Flow { is_list_item },
533 Contents::NonReplaced(non_replaced_contents),
534 ) => (is_list_item, non_replaced_contents),
535 (_, contents) => {
536 let propagated_data = self.propagated_data;
538
539 let construction_callback = || {
540 ArcRefCell::new(IndependentFormattingContext::construct(
541 context,
542 info,
543 display_inside,
544 contents,
545 propagated_data,
546 ))
547 };
548
549 let atomic = self
550 .ensure_inline_formatting_context_builder()
551 .push_atomic(construction_callback, old_layout_box);
552 box_slot.set(LayoutBox::InlineLevel(atomic));
553 return;
554 },
555 };
556
557 let inline_builder = self.ensure_inline_formatting_context_builder();
560 let inline_item = inline_builder.start_inline_box(
561 || ArcRefCell::new(InlineBox::new(info, context)),
562 old_layout_box,
563 );
564 box_slot.set(LayoutBox::InlineLevel(inline_item));
565
566 if info.node.is_html_wbr_element() {
567 inline_builder.push_soft_wrap_opportunity();
568 }
569
570 if is_list_item &&
571 let Some((marker_info, marker_contents)) =
572 crate::lists::make_marker(self.context, info)
573 {
574 self.handle_list_item_marker_inside(&marker_info, marker_contents)
578 }
579
580 non_replaced_contents.traverse(self.context, info, self);
581
582 self.finish_anonymous_table_if_needed();
583
584 self.inline_formatting_context_builder
585 .as_mut()
586 .expect("Should be building an InlineFormattingContext")
587 .end_inline_box();
588 }
589
590 fn handle_block_level_element(
591 &mut self,
592 info: &NodeAndStyleInfo<'dom>,
593 display_inside: DisplayInside,
594 contents: Contents,
595 box_slot: BoxSlot<'dom>,
596 ) {
597 let propagated_data = self.propagated_data;
598 let kind = BlockLevelCreator::new_for_inflow_block_level_element(
599 info,
600 display_inside,
601 contents,
602 propagated_data,
603 );
604 let job = BlockLevelJob {
605 info: info.clone(),
606 box_slot,
607 kind,
608 propagated_data,
609 };
610 if let Some(builder) = self.inline_formatting_context_builder.as_mut() {
611 if builder.currently_processing_inline_box() {
612 builder.push_block_level_box(job.finish(self.context));
613 return;
614 }
615 if let Some(context) = self.finish_ongoing_inline_formatting_context() {
616 self.push_block_level_job_for_inline_formatting_context(context);
617 }
618 }
619 self.block_level_boxes.push(job);
620
621 self.have_already_seen_first_formatted_line = true;
624 }
625
626 fn handle_absolutely_positioned_element(
627 &mut self,
628 info: &NodeAndStyleInfo<'dom>,
629 display_inside: DisplayInside,
630 contents: Contents,
631 box_slot: BoxSlot<'dom>,
632 ) {
633 let needs_inline_builder =
639 info.style.get_box().original_display.outside() == StyloDisplayOutside::Inline;
640 if needs_inline_builder {
641 self.ensure_inline_formatting_context_builder();
642 }
643 let inline_builder = self
644 .inline_formatting_context_builder
645 .as_mut()
646 .filter(|builder| needs_inline_builder || !builder.is_empty);
647 if let Some(inline_builder) = inline_builder {
648 let constructor = || {
649 ArcRefCell::new(AbsolutelyPositionedBox::construct(
650 self.context,
651 info,
652 display_inside,
653 contents,
654 ))
655 };
656 let old_layout_box = box_slot.take_layout_box();
657 let inline_level_box =
658 inline_builder.push_absolutely_positioned_box(constructor, old_layout_box);
659 box_slot.set(LayoutBox::InlineLevel(inline_level_box));
660 return;
661 }
662
663 let kind = BlockLevelCreator::OutOfFlowAbsolutelyPositionedBox {
664 contents,
665 display_inside,
666 };
667 self.block_level_boxes.push(BlockLevelJob {
668 info: info.clone(),
669 box_slot,
670 kind,
671 propagated_data: self.propagated_data,
672 });
673 }
674
675 fn handle_float_element(
676 &mut self,
677 info: &NodeAndStyleInfo<'dom>,
678 display_inside: DisplayInside,
679 contents: Contents,
680 box_slot: BoxSlot<'dom>,
681 ) {
682 if let Some(builder) = self.inline_formatting_context_builder.as_mut() &&
683 !builder.is_empty
684 {
685 let constructor = || {
686 ArcRefCell::new(FloatBox::construct(
687 self.context,
688 info,
689 display_inside,
690 contents,
691 self.propagated_data,
692 ))
693 };
694 let old_layout_box = box_slot.take_layout_box();
695 let inline_level_box = builder.push_float_box(constructor, old_layout_box);
696 box_slot.set(LayoutBox::InlineLevel(inline_level_box));
697 return;
698 }
699
700 let kind = BlockLevelCreator::OutOfFlowFloatBox {
701 contents,
702 display_inside,
703 };
704 self.block_level_boxes.push(BlockLevelJob {
705 info: info.clone(),
706 box_slot,
707 kind,
708 propagated_data: self.propagated_data,
709 });
710 }
711
712 fn push_block_level_job_for_inline_formatting_context(
713 &mut self,
714 inline_formatting_context: InlineFormattingContext,
715 ) {
716 let layout_context = self.context;
717 let anonymous_info = self
718 .anonymous_box_info
719 .get_or_insert_with(|| {
720 self.info
721 .with_pseudo_element(layout_context, PseudoElement::ServoAnonymousBox)
722 .expect("Should never fail to create anonymous box")
723 })
724 .clone();
725
726 let box_slot = anonymous_info.node.box_slot();
727 self.block_level_boxes.push(BlockLevelJob {
728 info: anonymous_info,
729 box_slot,
730 kind: BlockLevelCreator::SameFormattingContextBlock(
731 IntermediateBlockContainer::InlineFormattingContext(
732 BlockContainer::InlineFormattingContext(inline_formatting_context),
733 ),
734 ),
735 propagated_data: self.propagated_data,
736 });
737
738 self.have_already_seen_first_formatted_line = true;
739 }
740}
741
742impl BlockLevelJob<'_> {
743 fn finish(self, context: &LayoutContext) -> ArcRefCell<BlockLevelBox> {
744 let info = &self.info;
745
746 if let Some(block_level_box) = match &*self.box_slot.slot.borrow() {
749 Some(LayoutBox::BlockLevel(block_level_box)) => Some(block_level_box.clone()),
750 _ => None,
751 } {
752 return block_level_box;
753 }
754
755 let block_level_box = match self.kind {
756 BlockLevelCreator::SameFormattingContextBlock(intermediate_block_container) => {
757 let contents = intermediate_block_container.finish(context, info);
758 let contains_floats = contents.contains_floats();
759
760 let base = LayoutBoxBase::new(info.into(), info.style.clone());
761 base.set_subtree_size(contents.subtree_size() + 1);
762
763 ArcRefCell::new(BlockLevelBox::SameFormattingContextBlock(
764 SameFormattingContextBlock::new(base, contents, contains_floats),
765 ))
766 },
767 BlockLevelCreator::Independent {
768 display_inside,
769 contents,
770 } => {
771 let context = IndependentFormattingContext::construct(
772 context,
773 info,
774 display_inside,
775 contents,
776 self.propagated_data,
777 );
778 ArcRefCell::new(BlockLevelBox::Independent(context))
779 },
780 BlockLevelCreator::OutOfFlowAbsolutelyPositionedBox {
781 display_inside,
782 contents,
783 } => ArcRefCell::new(BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(
784 ArcRefCell::new(AbsolutelyPositionedBox::construct(
785 context,
786 info,
787 display_inside,
788 contents,
789 )),
790 )),
791 BlockLevelCreator::OutOfFlowFloatBox {
792 display_inside,
793 contents,
794 } => ArcRefCell::new(BlockLevelBox::OutOfFlowFloatBox(FloatBox::construct(
795 context,
796 info,
797 display_inside,
798 contents,
799 self.propagated_data,
800 ))),
801 BlockLevelCreator::OutsideMarker {
802 contents,
803 list_item_style,
804 } => {
805 let contents = NonReplacedContents::OfPseudoElement(contents);
806 let block_container = BlockContainer::construct(
807 context,
808 info,
809 contents,
810 self.propagated_data,
811 false, );
813 let block_formatting_context = BlockFormattingContext {
815 contents: block_container,
816 contains_floats: false,
817 };
818 ArcRefCell::new(BlockLevelBox::OutsideMarker(OutsideMarker {
819 context: IndependentFormattingContext::new(
820 LayoutBoxBase::new(info.into(), info.style.clone()),
821 IndependentFormattingContextContents::Flow(block_formatting_context),
822 self.propagated_data,
823 ),
824 list_item_style,
825 }))
826 },
827 BlockLevelCreator::AnonymousTable { table_block } => table_block,
828 };
829 self.box_slot
830 .set(LayoutBox::BlockLevel(block_level_box.clone()));
831 block_level_box
832 }
833}
834
835impl IntermediateBlockContainer {
836 fn finish(self, context: &LayoutContext, info: &NodeAndStyleInfo<'_>) -> BlockContainer {
837 match self {
838 IntermediateBlockContainer::Deferred {
839 contents,
840 propagated_data,
841 is_list_item,
842 } => BlockContainer::construct(context, info, contents, propagated_data, is_list_item),
843 IntermediateBlockContainer::InlineFormattingContext(block_container) => block_container,
844 }
845 }
846}