1use std::ops::Range;
2
3use crate::Node;
4use crate::net::ResourceHandler;
5use crate::node::NodeFlags;
6use crate::{
7 BaseDocument, net::ImageHandler, node::ImageResourceData, node::Status, util::ImageLayerKind,
8};
9use style::properties::ComputedValues;
10use style::properties::generated::longhands::position::computed_value::T as Position;
11use style::selector_parser::RestyleDamage;
12use style::url::ComputedUrl;
13use style::values::computed::Float;
14use style::values::generics::image::Image as StyloImage;
15use style::values::specified::align::AlignFlags;
16use style::values::specified::box_::DisplayInside;
17use style::values::specified::box_::DisplayOutside;
18use taffy::Rect;
19
20pub(crate) const CONSTRUCT_BOX: RestyleDamage =
21 RestyleDamage::from_bits_retain(0b_0000_0000_0001_0000);
22pub(crate) const CONSTRUCT_FC: RestyleDamage =
23 RestyleDamage::from_bits_retain(0b_0000_0000_0010_0000);
24pub(crate) const CONSTRUCT_DESCENDENT: RestyleDamage =
25 RestyleDamage::from_bits_retain(0b_0000_0000_0100_0000);
26
27pub(crate) const ONLY_RELAYOUT: RestyleDamage =
28 RestyleDamage::from_bits_retain(0b_0000_0000_0000_1000);
29
30pub(crate) const ALL_DAMAGE: RestyleDamage =
31 RestyleDamage::from_bits_retain(0b_0000_0000_0111_1111);
32
33impl BaseDocument {
34 pub(crate) fn propagate_damage_flags(
35 &mut self,
36 node_id: usize,
37 damage_from_parent: RestyleDamage,
38 ) -> RestyleDamage {
39 let mut damage = if let Some(data) = self.nodes[node_id].stylo_element_data.get_mut() {
40 data.damage
41 } else {
42 return RestyleDamage::empty();
43 };
44 damage |= damage_from_parent;
45
46 self.sync_pseudo_element_styles(node_id);
51
52 let damage_for_children = RestyleDamage::empty();
53 let children = std::mem::take(&mut self.nodes[node_id].children);
54 let layout_children = std::mem::take(self.nodes[node_id].layout_children.get_mut());
55 let use_layout_children = self.nodes[node_id].should_traverse_layout_children();
56 if use_layout_children {
57 let layout_children = layout_children.as_ref().unwrap();
58 for child in layout_children.iter() {
59 damage |= self.propagate_damage_flags(*child, damage_for_children);
60 }
61 } else {
62 for child in children.iter() {
63 damage |= self.propagate_damage_flags(*child, damage_for_children);
64 }
65 if let Some(before_id) = self.nodes[node_id].before {
66 damage |= self.propagate_damage_flags(before_id, damage_for_children);
67 }
68 if let Some(after_id) = self.nodes[node_id].after {
69 damage |= self.propagate_damage_flags(after_id, damage_for_children);
70 }
71 }
72
73 let node = &mut self.nodes[node_id];
74
75 node.children = children;
77 *node.layout_children.get_mut() = layout_children;
78
79 if damage.contains(CONSTRUCT_BOX) {
80 damage.insert(RestyleDamage::RELAYOUT);
81 }
82
83 let damage_for_parent = damage; if damage.intersects(ONLY_RELAYOUT | CONSTRUCT_BOX) {
89 #[cfg(feature = "log-phase-times")]
90 crate::layout::layout_counters::note_cache_cleared();
91 node.cache.clear();
92 if let Some(inline_layout) = node
93 .data
94 .downcast_element_mut()
95 .and_then(|el| el.inline_layout_data.as_mut())
96 {
97 inline_layout.content_widths = None;
98 }
99 damage.remove(ONLY_RELAYOUT);
100 }
101
102 node.set_damage(damage);
104
105 damage_for_parent
124 }
125
126 fn sync_pseudo_element_styles(&mut self, node_id: usize) {
137 let node = &self.nodes[node_id];
138
139 let before_node_id = node.before;
140 let after_node_id = node.after;
141 if before_node_id.is_none() && after_node_id.is_none() {
142 return;
143 }
144
145 let (before_style, after_style) = {
146 let style_data = node.stylo_element_data.get();
147 let Some(style_data) = style_data.as_ref() else {
148 return;
149 };
150 let pseudos = style_data.styles.pseudos.as_array();
152 (pseudos[1].clone(), pseudos[0].clone())
153 };
154
155 for (pe_node_id, pe_style) in [(before_node_id, before_style), (after_node_id, after_style)]
159 {
160 let (Some(pe_node_id), Some(pe_style)) = (pe_node_id, pe_style) else {
161 continue;
162 };
163 let mut pe_data = self.nodes[pe_node_id].stylo_element_data.get_mut();
164 let Some(pe_data) = pe_data.as_mut() else {
165 continue;
166 };
167 let Some(old_style) = pe_data.styles.primary.clone() else {
168 continue;
169 };
170 if std::ptr::eq(&*old_style, &*pe_style) {
171 continue;
172 }
173
174 let diff = RestyleDamage::compute_style_difference::<&Node>(&old_style, &pe_style);
175 pe_data.damage.insert(diff.damage);
176 pe_data.styles.primary = Some(pe_style);
177 pe_data.set_restyled();
178 }
179 }
180}
181
182pub(crate) fn compute_layout_damage(old: &ComputedValues, new: &ComputedValues) -> RestyleDamage {
211 let box_tree_needs_rebuild = || {
212 let old_box = old.get_box();
213 let new_box = new.get_box();
214
215 if old_box.display != new_box.display
216 || old_box.float != new_box.float
217 || old_box.position != new_box.position
218 || old.clone_visibility() != new.clone_visibility()
219 {
220 return true;
221 }
222
223 if old.get_font() != new.get_font() {
224 return true;
225 }
226
227 if new_box.display.outside() == DisplayOutside::Block
228 && new_box.display.inside() == DisplayInside::Flow
229 {
230 let alignment_establishes_new_block_formatting_context = |style: &ComputedValues| {
231 style.get_position().align_content.primary() != AlignFlags::NORMAL
232 };
233
234 let old_column = old.get_column();
235 let new_column = new.get_column();
236 if old_box.overflow_x.is_scrollable() != new_box.overflow_x.is_scrollable()
237 || old_column.is_multicol() != new_column.is_multicol()
238 || old_column.column_span != new_column.column_span
239 || alignment_establishes_new_block_formatting_context(old)
240 != alignment_establishes_new_block_formatting_context(new)
241 {
242 return true;
243 }
244 }
245
246 if old_box.display.is_list_item() {
247 let old_list = old.get_list();
248 let new_list = new.get_list();
249 if old_list.list_style_position != new_list.list_style_position
250 || old_list.list_style_image != new_list.list_style_image
251 || (new_list.list_style_image == StyloImage::None
252 && old_list.list_style_type != new_list.list_style_type)
253 {
254 return true;
255 }
256 }
257
258 if new.is_pseudo_style() && old.get_counters().content != new.get_counters().content {
259 return true;
260 }
261
262 false
263 };
264
265 let text_shaping_needs_recollect = || {
266 if old.clone_direction() != new.clone_direction()
267 || old.clone_unicode_bidi() != new.clone_unicode_bidi()
268 {
269 return true;
270 }
271
272 let old_text = old.get_inherited_text();
273 let new_text = new.get_inherited_text();
274 if !std::ptr::eq(old_text, new_text)
275 && (old_text.white_space_collapse != new_text.white_space_collapse
276 || old_text.text_transform != new_text.text_transform
277 || old_text.word_break != new_text.word_break
278 || old_text.overflow_wrap != new_text.overflow_wrap
279 || old_text.letter_spacing != new_text.letter_spacing
280 || old_text.word_spacing != new_text.word_spacing
281 || old_text.text_rendering != new_text.text_rendering)
282 {
283 return true;
284 }
285
286 false
287 };
288
289 #[allow(
290 clippy::if_same_then_else,
291 reason = "these branches will soon be different"
292 )]
293 if box_tree_needs_rebuild() {
294 ALL_DAMAGE
295 } else if text_shaping_needs_recollect() {
296 ALL_DAMAGE
297 } else {
298 RestyleDamage::RELAYOUT
302 }
303}
304
305#[derive(Debug, Clone)]
307pub struct HoistedPaintChild {
308 pub node_id: usize,
309 pub z_index: i32,
310 pub position: taffy::Point<f32>,
311}
312
313#[derive(Debug)]
314pub struct HoistedPaintChildren {
315 pub children: Vec<HoistedPaintChild>,
316 pub negative_z_count: u32,
318
319 pub content_area: taffy::Rect<f32>,
320}
321
322impl HoistedPaintChildren {
323 fn new() -> Self {
324 Self {
325 children: Vec::new(),
326 negative_z_count: 0,
327 content_area: taffy::Rect::ZERO,
328 }
329 }
330
331 pub fn reset(&mut self) {
332 self.children.clear();
333 self.negative_z_count = 0;
334 }
335
336 pub fn compute_content_size(&mut self, doc: &BaseDocument) {
337 fn child_pos(child: &HoistedPaintChild, doc: &BaseDocument) -> Rect<f32> {
338 let node = &doc.nodes[child.node_id];
339 let left = child.position.x + node.final_layout.location.x;
340 let top = child.position.y + node.final_layout.location.y;
341 let right = left + node.final_layout.size.width;
342 let bottom = top + node.final_layout.size.height;
343
344 taffy::Rect {
345 top,
346 left,
347 bottom,
348 right,
349 }
350 }
351
352 if self.children.is_empty() {
353 self.content_area = taffy::Rect::ZERO;
354 } else {
355 self.content_area = child_pos(&self.children[0], doc);
356 for child in self.children[1..].iter() {
357 let pos = child_pos(child, doc);
358 self.content_area.left = self.content_area.left.min(pos.left);
359 self.content_area.top = self.content_area.top.min(pos.top);
360 self.content_area.right = self.content_area.right.max(pos.right);
361 self.content_area.bottom = self.content_area.bottom.max(pos.bottom);
362 }
363 }
364 }
365
366 pub fn sort(&mut self) {
367 self.children.sort_by_key(|c| c.z_index);
368 self.negative_z_count = self.children.iter().take_while(|c| c.z_index < 0).count() as u32;
369 }
370
371 pub fn neg_z_range(&self) -> Range<usize> {
372 0..(self.negative_z_count as usize)
373 }
374
375 pub fn pos_z_range(&self) -> Range<usize> {
376 (self.negative_z_count as usize)..self.children.len()
377 }
378
379 pub fn neg_z_hoisted_children(
380 &self,
381 ) -> impl ExactSizeIterator<Item = &HoistedPaintChild> + DoubleEndedIterator {
382 self.children[self.neg_z_range()].iter()
383 }
384
385 pub fn pos_z_hoisted_children(
386 &self,
387 ) -> impl ExactSizeIterator<Item = &HoistedPaintChild> + DoubleEndedIterator {
388 self.children[self.pos_z_range()].iter()
389 }
390}
391
392impl BaseDocument {
393 pub(crate) fn invalidate_inline_contexts(&mut self) {
394 let scale = self.viewport.scale();
395
396 let font_ctx = &self.font_ctx;
397 let layout_ctx = &mut self.layout_ctx;
398
399 let mut anon_nodes = Vec::new();
400
401 for (_, node) in self.nodes.iter_mut() {
402 if !(node.flags.contains(NodeFlags::IS_IN_DOCUMENT)) {
403 continue;
404 }
405
406 let Some(element) = node.data.downcast_element_mut() else {
407 continue;
408 };
409
410 if element.inline_layout_data.is_some() {
411 if node.is_anonymous() {
412 anon_nodes.push(node.id);
413 } else {
414 node.insert_damage(ALL_DAMAGE);
415 }
416 } else if let Some(input) = element.text_input_data_mut() {
417 input.editor.set_scale(scale);
418 let mut font_ctx = font_ctx.lock().unwrap();
419 input.editor.refresh_layout(&mut font_ctx, layout_ctx);
420 node.insert_damage(ONLY_RELAYOUT);
421 }
422 }
423
424 for node_id in anon_nodes {
425 if let Some(parent_id) = *(self.nodes[node_id].layout_parent.get_mut()) {
426 self.nodes[parent_id].insert_damage(ALL_DAMAGE);
427 }
428 }
429 }
430
431 pub fn flush_styles_to_layout(&mut self, node_id: usize) {
432 self.flush_styles_to_layout_impl(node_id, None);
433 }
434
435 fn flush_image_layers_from_style(&mut self, node_id: usize, kind: ImageLayerKind) {
438 let doc_id = self.id();
439 let node = self.nodes.get_mut(node_id).unwrap();
440 let stylo_element_data = node.stylo_element_data.get();
441 let primary_styles = stylo_element_data
442 .as_ref()
443 .and_then(|data| data.styles.get_primary());
444 let Some(style) = primary_styles else {
445 return;
446 };
447 let Some(elem) = node.data.downcast_element_mut() else {
448 return;
449 };
450
451 let (style_images, elem_images) = match kind {
452 ImageLayerKind::Background => (
453 &style.get_background().background_image.0,
454 &mut elem.background_images,
455 ),
456 ImageLayerKind::Mask => (&style.get_svg().mask_image.0, &mut elem.mask_images),
457 };
458
459 let len = style_images.len();
460 elem_images.resize_with(len, || None);
461
462 for idx in 0..len {
463 let style_image = &style_images[idx];
464 let new_image = match style_image {
465 StyloImage::Url(ComputedUrl::Valid(new_url)) => {
466 let old_image = elem_images[idx].as_ref();
467 let old_image_url = old_image.map(|data| &data.url);
468 if old_image_url.is_some_and(|old_url| **new_url == **old_url) {
469 break;
470 }
471
472 let url_str = new_url.as_str();
474 if let Some(cached_image) = self.image_cache.get(url_str) {
475 #[cfg(feature = "tracing")]
476 tracing::info!("Loading image {url_str} from cache");
477 Some(ImageResourceData {
478 url: new_url.clone(),
479 status: Status::Ok,
480 image: cached_image.clone(),
481 })
482 } else if let Some(waiting_list) = self.pending_images.get_mut(url_str) {
483 #[cfg(feature = "tracing")]
485 tracing::info!("Image {url_str} already pending, queueing node {node_id}");
486 waiting_list.push((node_id, kind.image_type(idx)));
487 Some(ImageResourceData::new(new_url.clone()))
488 } else {
489 #[cfg(feature = "tracing")]
491 tracing::info!("Fetching image {url_str}");
492 self.pending_images
493 .insert(url_str.to_string(), vec![(node_id, kind.image_type(idx))]);
494
495 self.net_provider.fetch(
496 doc_id,
497 crate::net::stamped_request(
498 (**new_url).clone(),
499 self.abort_signal.as_ref(),
500 ),
501 ResourceHandler::boxed(
502 self.tx.clone(),
503 doc_id,
504 None, self.shell_provider.clone(),
506 ImageHandler::new(kind.image_type(idx)),
507 ),
508 );
509
510 Some(ImageResourceData::new(new_url.clone()))
511 }
512 }
513 _ => None,
514 };
515
516 elem_images[idx] = new_image;
518 }
519 }
520
521 fn flush_styles_to_layout_impl(
523 &mut self,
524 node_id: usize,
525 parent_stacking_context: Option<&mut HoistedPaintChildren>,
526 ) {
527 let mut new_stacking_context: HoistedPaintChildren = HoistedPaintChildren::new();
528 let stacking_context = &mut new_stacking_context;
529
530 self.flush_image_layers_from_style(node_id, ImageLayerKind::Background);
532 self.flush_image_layers_from_style(node_id, ImageLayerKind::Mask);
533
534 let incremental = self.incremental_layout;
535 let display = {
536 let node = self.nodes.get_mut(node_id).unwrap();
537 let _damage = node.damage().unwrap_or(ALL_DAMAGE);
538 let stylo_element_data = node.stylo_element_data.get();
539 let primary_styles = stylo_element_data
540 .as_ref()
541 .and_then(|data| data.styles.get_primary());
542
543 let Some(style) = primary_styles else {
544 return;
545 };
546
547 node.style = stylo_taffy::to_taffy_style(style);
549 node.display_constructed_as = style.clone_display();
550 if !incremental {
555 node.cache.clear();
556 if let Some(inline_layout) = node
557 .data
558 .downcast_element_mut()
559 .and_then(|el| el.inline_layout_data.as_mut())
560 {
561 inline_layout.content_widths = None;
562 }
563 }
564
565 node.style.display
566 };
567
568 let children = self.nodes[node_id].layout_children.borrow_mut().take();
570 if let Some(mut children) = children {
571 let is_flex_or_grid = matches!(display, taffy::Display::Flex | taffy::Display::Grid);
572
573 for &child in children.iter() {
575 self.flush_styles_to_layout_impl(
576 child,
577 match self.nodes[child].is_stacking_context_root(is_flex_or_grid) {
578 true => None,
579 false => Some(stacking_context),
580 },
581 );
582 }
583
584 if is_flex_or_grid {
586 children.sort_by(|left, right| {
587 let left_node = self.nodes.get(*left).unwrap();
588 let right_node = self.nodes.get(*right).unwrap();
589 left_node.order().cmp(&right_node.order())
590 });
591 }
592
593 let mut paint_children = self.nodes[node_id].paint_children.borrow_mut();
595 if paint_children.is_none() {
596 *paint_children = Some(Vec::new());
597 }
598 let paint_children = paint_children.as_mut().unwrap();
599 paint_children.clear();
600 paint_children.reserve(children.len());
601
602 for &child_id in children.iter() {
604 let child = &self.nodes[child_id];
605
606 let Some(style) = child.primary_styles() else {
607 paint_children.push(child_id);
608 continue;
609 };
610
611 let position = style.clone_position();
612 let z_index = style.clone_z_index().integer_or(0);
613
614 if z_index != 0 && (position != Position::Static || is_flex_or_grid) {
618 stacking_context.children.push(HoistedPaintChild {
619 node_id: child_id,
620 z_index,
621 position: taffy::Point::ZERO,
622 })
623 } else {
624 paint_children.push(child_id);
625 }
626 }
627
628 paint_children.sort_by(|left, right| {
630 let left_node = self.nodes.get(*left).unwrap();
631 let right_node = self.nodes.get(*right).unwrap();
632 node_to_paint_order(left_node, is_flex_or_grid)
633 .cmp(&node_to_paint_order(right_node, is_flex_or_grid))
634 });
635
636 *self.nodes[node_id].layout_children.borrow_mut() = Some(children);
638 }
639
640 if let Some(parent_stacking_context) = parent_stacking_context {
641 let position = self.nodes[node_id].final_layout.location;
642 let scroll_offset = self.nodes[node_id].scroll_offset;
643 for hoisted in stacking_context.children.iter_mut() {
644 hoisted.position.x += position.x - scroll_offset.x as f32;
645 hoisted.position.y += position.y - scroll_offset.y as f32;
646 }
647 parent_stacking_context
648 .children
649 .extend(stacking_context.children.iter().cloned());
650 } else {
651 stacking_context.sort();
652 stacking_context.compute_content_size(self);
653 self.nodes[node_id].stacking_context = Some(Box::new(new_stacking_context));
654 }
655 }
656}
657
658#[inline(always)]
659fn position_to_order(pos: Position) -> i32 {
660 match pos {
661 Position::Static => 0,
662 Position::Relative | Position::Sticky | Position::Absolute | Position::Fixed => 2,
666 }
667}
668#[inline(always)]
669fn float_to_order(pos: Float) -> i32 {
670 match pos {
671 Float::None => 0,
672 _ => 1,
673 }
674}
675
676#[inline(always)]
681fn node_to_paint_order(node: &Node, is_flex_or_grid: bool) -> (i32, i32) {
682 let Some(style) = node.primary_styles() else {
683 return (0, 0);
684 };
685 let position = style.clone_position();
686 if is_flex_or_grid {
687 match position {
688 Position::Static => (0, style.clone_order()),
689 Position::Relative | Position::Sticky => (2, style.clone_order()),
690 Position::Absolute | Position::Fixed => (2, 0),
693 }
694 } else {
695 (
696 position_to_order(position) + float_to_order(style.clone_float()),
697 0,
698 )
699 }
700}