1use std::ops::Range;
2
3use crate::net::ResourceHandler;
4use crate::node::NodeFlags;
5use crate::{
6 BaseDocument, net::ImageHandler, node::ImageResourceData, node::Status, util::ImageLayerKind,
7};
8use crate::{NON_INCREMENTAL, Node};
9use style::properties::ComputedValues;
10use style::properties::generated::longhands::position::computed_value::T as Position;
11use style::selector_parser::RestyleDamage;
12use style::servo::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 #[cfg(feature = "incremental")]
35 pub(crate) fn propagate_damage_flags(
36 &mut self,
37 node_id: usize,
38 damage_from_parent: RestyleDamage,
39 ) -> RestyleDamage {
40 let mut damage = if let Some(data) = self.nodes[node_id].stylo_element_data.get_mut() {
41 data.damage
42 } else {
43 return RestyleDamage::empty();
44 };
45 damage |= damage_from_parent;
46
47 let damage_for_children = RestyleDamage::empty();
48 let children = std::mem::take(&mut self.nodes[node_id].children);
49 let layout_children = std::mem::take(self.nodes[node_id].layout_children.get_mut());
50 let use_layout_children = self.nodes[node_id].should_traverse_layout_children();
51 if use_layout_children {
52 let layout_children = layout_children.as_ref().unwrap();
53 for child in layout_children.iter() {
54 damage |= self.propagate_damage_flags(*child, damage_for_children);
55 }
56 } else {
57 for child in children.iter() {
58 damage |= self.propagate_damage_flags(*child, damage_for_children);
59 }
60 if let Some(before_id) = self.nodes[node_id].before {
61 damage |= self.propagate_damage_flags(before_id, damage_for_children);
62 }
63 if let Some(after_id) = self.nodes[node_id].after {
64 damage |= self.propagate_damage_flags(after_id, damage_for_children);
65 }
66 }
67
68 let node = &mut self.nodes[node_id];
69
70 node.children = children;
72 *node.layout_children.get_mut() = layout_children;
73
74 if damage.contains(CONSTRUCT_BOX) {
75 damage.insert(RestyleDamage::RELAYOUT);
76 }
77
78 let damage_for_parent = damage; if damage.intersects(ONLY_RELAYOUT | CONSTRUCT_BOX) {
84 node.cache.clear();
85 if let Some(inline_layout) = node
86 .data
87 .downcast_element_mut()
88 .and_then(|el| el.inline_layout_data.as_mut())
89 {
90 inline_layout.content_widths = None;
91 }
92 damage.remove(ONLY_RELAYOUT);
93 }
94
95 node.set_damage(damage);
97
98 damage_for_parent
117 }
118}
119
120pub(crate) fn compute_layout_damage(old: &ComputedValues, new: &ComputedValues) -> RestyleDamage {
149 let box_tree_needs_rebuild = || {
150 let old_box = old.get_box();
151 let new_box = new.get_box();
152
153 if old_box.display != new_box.display
154 || old_box.float != new_box.float
155 || old_box.position != new_box.position
156 || old.clone_visibility() != new.clone_visibility()
157 {
158 return true;
159 }
160
161 if old.get_font() != new.get_font() {
162 return true;
163 }
164
165 if new_box.display.outside() == DisplayOutside::Block
166 && new_box.display.inside() == DisplayInside::Flow
167 {
168 let alignment_establishes_new_block_formatting_context = |style: &ComputedValues| {
169 style.get_position().align_content.primary() != AlignFlags::NORMAL
170 };
171
172 let old_column = old.get_column();
173 let new_column = new.get_column();
174 if old_box.overflow_x.is_scrollable() != new_box.overflow_x.is_scrollable()
175 || old_column.is_multicol() != new_column.is_multicol()
176 || old_column.column_span != new_column.column_span
177 || alignment_establishes_new_block_formatting_context(old)
178 != alignment_establishes_new_block_formatting_context(new)
179 {
180 return true;
181 }
182 }
183
184 if old_box.display.is_list_item() {
185 let old_list = old.get_list();
186 let new_list = new.get_list();
187 if old_list.list_style_position != new_list.list_style_position
188 || old_list.list_style_image != new_list.list_style_image
189 || (new_list.list_style_image == StyloImage::None
190 && old_list.list_style_type != new_list.list_style_type)
191 {
192 return true;
193 }
194 }
195
196 if new.is_pseudo_style() && old.get_counters().content != new.get_counters().content {
197 return true;
198 }
199
200 false
201 };
202
203 let text_shaping_needs_recollect = || {
204 if old.clone_direction() != new.clone_direction()
205 || old.clone_unicode_bidi() != new.clone_unicode_bidi()
206 {
207 return true;
208 }
209
210 let old_text = old.get_inherited_text();
211 let new_text = new.get_inherited_text();
212 if !std::ptr::eq(old_text, new_text)
213 && (old_text.white_space_collapse != new_text.white_space_collapse
214 || old_text.text_transform != new_text.text_transform
215 || old_text.word_break != new_text.word_break
216 || old_text.overflow_wrap != new_text.overflow_wrap
217 || old_text.letter_spacing != new_text.letter_spacing
218 || old_text.word_spacing != new_text.word_spacing
219 || old_text.text_rendering != new_text.text_rendering)
220 {
221 return true;
222 }
223
224 false
225 };
226
227 #[allow(
228 clippy::if_same_then_else,
229 reason = "these branches will soon be different"
230 )]
231 if box_tree_needs_rebuild() {
232 ALL_DAMAGE
233 } else if text_shaping_needs_recollect() {
234 ALL_DAMAGE
235 } else {
236 RestyleDamage::RELAYOUT
240 }
241}
242
243#[derive(Debug, Clone)]
245pub struct HoistedPaintChild {
246 pub node_id: usize,
247 pub z_index: i32,
248 pub position: taffy::Point<f32>,
249}
250
251#[derive(Debug)]
252pub struct HoistedPaintChildren {
253 pub children: Vec<HoistedPaintChild>,
254 pub negative_z_count: u32,
256
257 pub content_area: taffy::Rect<f32>,
258}
259
260impl HoistedPaintChildren {
261 fn new() -> Self {
262 Self {
263 children: Vec::new(),
264 negative_z_count: 0,
265 content_area: taffy::Rect::ZERO,
266 }
267 }
268
269 pub fn reset(&mut self) {
270 self.children.clear();
271 self.negative_z_count = 0;
272 }
273
274 pub fn compute_content_size(&mut self, doc: &BaseDocument) {
275 fn child_pos(child: &HoistedPaintChild, doc: &BaseDocument) -> Rect<f32> {
276 let node = &doc.nodes[child.node_id];
277 let left = child.position.x + node.final_layout.location.x;
278 let top = child.position.y + node.final_layout.location.y;
279 let right = left + node.final_layout.size.width;
280 let bottom = top + node.final_layout.size.height;
281
282 taffy::Rect {
283 top,
284 left,
285 bottom,
286 right,
287 }
288 }
289
290 if self.children.is_empty() {
291 self.content_area = taffy::Rect::ZERO;
292 } else {
293 self.content_area = child_pos(&self.children[0], doc);
294 for child in self.children[1..].iter() {
295 let pos = child_pos(child, doc);
296 self.content_area.left = self.content_area.left.min(pos.left);
297 self.content_area.top = self.content_area.top.min(pos.top);
298 self.content_area.right = self.content_area.right.max(pos.right);
299 self.content_area.bottom = self.content_area.bottom.max(pos.bottom);
300 }
301 }
302 }
303
304 pub fn sort(&mut self) {
305 self.children.sort_by_key(|c| c.z_index);
306 self.negative_z_count = self.children.iter().take_while(|c| c.z_index < 0).count() as u32;
307 }
308
309 pub fn neg_z_range(&self) -> Range<usize> {
310 0..(self.negative_z_count as usize)
311 }
312
313 pub fn pos_z_range(&self) -> Range<usize> {
314 (self.negative_z_count as usize)..self.children.len()
315 }
316
317 pub fn neg_z_hoisted_children(
318 &self,
319 ) -> impl ExactSizeIterator<Item = &HoistedPaintChild> + DoubleEndedIterator {
320 self.children[self.neg_z_range()].iter()
321 }
322
323 pub fn pos_z_hoisted_children(
324 &self,
325 ) -> impl ExactSizeIterator<Item = &HoistedPaintChild> + DoubleEndedIterator {
326 self.children[self.pos_z_range()].iter()
327 }
328}
329
330impl BaseDocument {
331 pub(crate) fn invalidate_inline_contexts(&mut self) {
332 let scale = self.viewport.scale();
333
334 let font_ctx = &self.font_ctx;
335 let layout_ctx = &mut self.layout_ctx;
336
337 let mut anon_nodes = Vec::new();
338
339 for (_, node) in self.nodes.iter_mut() {
340 if !(node.flags.contains(NodeFlags::IS_IN_DOCUMENT)) {
341 continue;
342 }
343
344 let Some(element) = node.data.downcast_element_mut() else {
345 continue;
346 };
347
348 if element.inline_layout_data.is_some() {
349 if node.is_anonymous() {
350 anon_nodes.push(node.id);
351 } else {
352 node.insert_damage(ALL_DAMAGE);
353 }
354 } else if let Some(input) = element.text_input_data_mut() {
355 input.editor.set_scale(scale);
356 let mut font_ctx = font_ctx.lock().unwrap();
357 input.editor.refresh_layout(&mut font_ctx, layout_ctx);
358 node.insert_damage(ONLY_RELAYOUT);
359 }
360 }
361
362 for node_id in anon_nodes {
363 if let Some(parent_id) = *(self.nodes[node_id].layout_parent.get_mut()) {
364 self.nodes[parent_id].insert_damage(ALL_DAMAGE);
365 }
366 }
367 }
368
369 pub fn flush_styles_to_layout(&mut self, node_id: usize) {
370 self.flush_styles_to_layout_impl(node_id, None);
371 }
372
373 fn flush_image_layers_from_style(&mut self, node_id: usize, kind: ImageLayerKind) {
376 let doc_id = self.id();
377 let node = self.nodes.get_mut(node_id).unwrap();
378 let stylo_element_data = node.stylo_element_data.get();
379 let primary_styles = stylo_element_data
380 .as_ref()
381 .and_then(|data| data.styles.get_primary());
382 let Some(style) = primary_styles else {
383 return;
384 };
385 let Some(elem) = node.data.downcast_element_mut() else {
386 return;
387 };
388
389 let (style_images, elem_images) = match kind {
390 ImageLayerKind::Background => (
391 &style.get_background().background_image.0,
392 &mut elem.background_images,
393 ),
394 ImageLayerKind::Mask => (&style.get_svg().mask_image.0, &mut elem.mask_images),
395 };
396
397 let len = style_images.len();
398 elem_images.resize_with(len, || None);
399
400 for idx in 0..len {
401 let style_image = &style_images[idx];
402 let new_image = match style_image {
403 StyloImage::Url(ComputedUrl::Valid(new_url)) => {
404 let old_image = elem_images[idx].as_ref();
405 let old_image_url = old_image.map(|data| &data.url);
406 if old_image_url.is_some_and(|old_url| **new_url == **old_url) {
407 break;
408 }
409
410 let url_str = new_url.as_str();
412 if let Some(cached_image) = self.image_cache.get(url_str) {
413 #[cfg(feature = "tracing")]
414 tracing::info!("Loading image {url_str} from cache");
415 Some(ImageResourceData {
416 url: new_url.clone(),
417 status: Status::Ok,
418 image: cached_image.clone(),
419 })
420 } else if let Some(waiting_list) = self.pending_images.get_mut(url_str) {
421 #[cfg(feature = "tracing")]
423 tracing::info!("Image {url_str} already pending, queueing node {node_id}");
424 waiting_list.push((node_id, kind.image_type(idx)));
425 Some(ImageResourceData::new(new_url.clone()))
426 } else {
427 #[cfg(feature = "tracing")]
429 tracing::info!("Fetching image {url_str}");
430 self.pending_images
431 .insert(url_str.to_string(), vec![(node_id, kind.image_type(idx))]);
432
433 self.net_provider.fetch(
434 doc_id,
435 crate::net::stamped_request(
436 (**new_url).clone(),
437 self.abort_signal.as_ref(),
438 ),
439 ResourceHandler::boxed(
440 self.tx.clone(),
441 doc_id,
442 None, self.shell_provider.clone(),
444 ImageHandler::new(kind.image_type(idx)),
445 ),
446 );
447
448 Some(ImageResourceData::new(new_url.clone()))
449 }
450 }
451 _ => None,
452 };
453
454 elem_images[idx] = new_image;
456 }
457 }
458
459 fn flush_styles_to_layout_impl(
461 &mut self,
462 node_id: usize,
463 parent_stacking_context: Option<&mut HoistedPaintChildren>,
464 ) {
465 let mut new_stacking_context: HoistedPaintChildren = HoistedPaintChildren::new();
466 let stacking_context = &mut new_stacking_context;
467
468 self.flush_image_layers_from_style(node_id, ImageLayerKind::Background);
470 self.flush_image_layers_from_style(node_id, ImageLayerKind::Mask);
471
472 let display = {
473 let node = self.nodes.get_mut(node_id).unwrap();
474 let _damage = node.damage().unwrap_or(ALL_DAMAGE);
475 let stylo_element_data = node.stylo_element_data.get();
476 let primary_styles = stylo_element_data
477 .as_ref()
478 .and_then(|data| data.styles.get_primary());
479
480 let Some(style) = primary_styles else {
481 return;
482 };
483
484 node.style = stylo_taffy::to_taffy_style(style);
486 node.display_constructed_as = style.clone_display();
487 if NON_INCREMENTAL {
492 node.cache.clear();
493 if let Some(inline_layout) = node
494 .data
495 .downcast_element_mut()
496 .and_then(|el| el.inline_layout_data.as_mut())
497 {
498 inline_layout.content_widths = None;
499 }
500 }
501
502 node.style.display
503 };
504
505 let children = self.nodes[node_id].layout_children.borrow_mut().take();
507 if let Some(mut children) = children {
508 let is_flex_or_grid = matches!(display, taffy::Display::Flex | taffy::Display::Grid);
509
510 for &child in children.iter() {
512 self.flush_styles_to_layout_impl(
513 child,
514 match self.nodes[child].is_stacking_context_root(is_flex_or_grid) {
515 true => None,
516 false => Some(stacking_context),
517 },
518 );
519 }
520
521 if is_flex_or_grid {
523 children.sort_by(|left, right| {
524 let left_node = self.nodes.get(*left).unwrap();
525 let right_node = self.nodes.get(*right).unwrap();
526 left_node.order().cmp(&right_node.order())
527 });
528 }
529
530 let mut paint_children = self.nodes[node_id].paint_children.borrow_mut();
532 if paint_children.is_none() {
533 *paint_children = Some(Vec::new());
534 }
535 let paint_children = paint_children.as_mut().unwrap();
536 paint_children.clear();
537 paint_children.reserve(children.len());
538
539 for &child_id in children.iter() {
541 let child = &self.nodes[child_id];
542
543 let Some(style) = child.primary_styles() else {
544 paint_children.push(child_id);
545 continue;
546 };
547
548 let position = style.clone_position();
549 let z_index = style.clone_z_index().integer_or(0);
550
551 if position != Position::Static && z_index != 0 {
553 stacking_context.children.push(HoistedPaintChild {
554 node_id: child_id,
555 z_index,
556 position: taffy::Point::ZERO,
557 })
558 } else {
559 paint_children.push(child_id);
560 }
561 }
562
563 paint_children.sort_by(|left, right| {
565 let left_node = self.nodes.get(*left).unwrap();
566 let right_node = self.nodes.get(*right).unwrap();
567 node_to_paint_order(left_node, is_flex_or_grid)
568 .cmp(&node_to_paint_order(right_node, is_flex_or_grid))
569 });
570
571 *self.nodes[node_id].layout_children.borrow_mut() = Some(children);
573 }
574
575 if let Some(parent_stacking_context) = parent_stacking_context {
576 let position = self.nodes[node_id].final_layout.location;
577 let scroll_offset = self.nodes[node_id].scroll_offset;
578 for hoisted in stacking_context.children.iter_mut() {
579 hoisted.position.x += position.x - scroll_offset.x as f32;
580 hoisted.position.y += position.y - scroll_offset.y as f32;
581 }
582 parent_stacking_context
583 .children
584 .extend(stacking_context.children.iter().cloned());
585 } else {
586 stacking_context.sort();
587 stacking_context.compute_content_size(self);
588 self.nodes[node_id].stacking_context = Some(Box::new(new_stacking_context));
589 }
590 }
591}
592
593#[inline(always)]
594fn position_to_order(pos: Position) -> i32 {
595 match pos {
596 Position::Static | Position::Relative | Position::Sticky => 0,
597 Position::Absolute | Position::Fixed => 2,
598 }
599}
600#[inline(always)]
601fn float_to_order(pos: Float) -> i32 {
602 match pos {
603 Float::None => 0,
604 _ => 1,
605 }
606}
607
608#[inline(always)]
609fn node_to_paint_order(node: &Node, is_flex_or_grid: bool) -> i32 {
610 let Some(style) = node.primary_styles() else {
611 return 0;
612 };
613 if is_flex_or_grid {
614 match style.clone_position() {
615 Position::Static | Position::Relative | Position::Sticky => style.clone_order(),
616 Position::Absolute | Position::Fixed => 0,
617 }
618 } else {
619 position_to_order(style.clone_position()) + float_to_order(style.clone_float())
620 }
621}