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