Rewild Your Web
web
browser
dweb
1--- original
2+++ modified
3@@ -37,13 +37,14 @@
4 use style::values::specified::text::TextDecorationLine;
5 use style_traits::{CSSPixel as StyloCSSPixel, DevicePixel as StyloDevicePixel};
6 use webrender_api::units::{
7- DeviceIntSize, DevicePixel, LayoutPixel, LayoutPoint, LayoutRect, LayoutSideOffsets, LayoutSize,
8+ DeviceIntSize, DevicePixel, LayoutPixel, LayoutPoint, LayoutRect, LayoutSideOffsets,
9+ LayoutSize, LayoutTransform,
10 };
11 use webrender_api::{
12 self as wr, BorderDetails, BorderRadius, BorderSide, BoxShadowClipMode, BuiltDisplayList,
13- ClipChainId, ClipMode, ColorF, CommonItemProperties, ComplexClipRegion, GlyphInstance,
14- NinePatchBorder, NinePatchBorderSource, NormalBorder, PrimitiveFlags, PropertyBinding,
15- SpatialId, SpatialTreeItemKey, units,
16+ ClipChainId, ClipMode, ColorF, CommonItemProperties, ComplexClipRegion, ExternalScrollId,
17+ GlyphInstance, NinePatchBorder, NinePatchBorderSource, NormalBorder, PrimitiveFlags,
18+ PropertyBinding, ReferenceFrameKind, SpatialId, SpatialTreeItemKey, TransformStyle, units,
19 };
20 use wr::units::LayoutVector2D;
21
22@@ -123,6 +124,10 @@
23
24 /// The collector for calculating Largest Contentful Paint
25 lcp_candidate_collector: Option<&'a mut LargestContentfulPaintCandidateCollector>,
26+
27+ /// For embedded webviews with page zoom, this is the SpatialId of the zoom reference frame.
28+ /// When present, this is used as the root spatial ID instead of root_reference_frame.
29+ zoom_reference_frame_spatial_id: Option<SpatialId>,
30 }
31
32 struct InspectorHighlight {
33@@ -188,6 +193,36 @@
34 webrender_display_list_builder.dump_serialized_display_list();
35 }
36
37+ // For embedded webviews, apply page zoom as a root reference frame transform.
38+ // This is done here (inside the display list) rather than externally (in the painter)
39+ // because embedded webviews are rendered via push_iframe from their parent's display list.
40+ let page_zoom_for_rendering = paint_info.viewport_details.page_zoom_for_rendering;
41+ let zoom_reference_frame_spatial_id = if let Some(zoom) = page_zoom_for_rendering {
42+ log::warn!(
43+ "DisplayListBuilder: applying page_zoom_for_rendering={} for pipeline {:?}",
44+ zoom,
45+ pipeline_id
46+ );
47+ let transform = LayoutTransform::scale(zoom, zoom, 1.0);
48+ let root_ref_frame = SpatialId::root_reference_frame(pipeline_id);
49+ let zoom_spatial_id = webrender_display_list_builder.push_reference_frame(
50+ LayoutPoint::zero(),
51+ root_ref_frame,
52+ TransformStyle::Flat,
53+ PropertyBinding::Value(transform),
54+ ReferenceFrameKind::Transform {
55+ is_2d_scale_translation: true,
56+ should_snap: true,
57+ paired_with_perspective: false,
58+ },
59+ // Use a unique key for this zoom reference frame
60+ SpatialTreeItemKey::new(0, u64::MAX),
61+ );
62+ Some(zoom_spatial_id)
63+ } else {
64+ None
65+ };
66+
67 let _span = profile_traits::trace_span!("DisplayListBuilder::build").entered();
68 let mut builder = DisplayListBuilder {
69 current_scroll_node_id: paint_info.root_reference_frame_id,
70@@ -201,6 +236,7 @@
71 image_resolver,
72 device_pixel_ratio,
73 lcp_candidate_collector,
74+ zoom_reference_frame_spatial_id,
75 };
76
77 builder.add_all_spatial_nodes();
78@@ -214,15 +250,20 @@
79 let pipeline_id = builder.paint_info.pipeline_id;
80 let viewport_size = builder.paint_info.viewport_details.size;
81 let viewport_rect = LayoutRect::from_size(viewport_size.cast_unit());
82+ // Use the zoom reference frame if present, otherwise use the built-in root reference frame.
83+ // This ensures the viewport hit test is in the same coordinate space as content.
84+ let viewport_spatial_id = builder
85+ .zoom_reference_frame_spatial_id
86+ .unwrap_or_else(|| SpatialId::root_reference_frame(pipeline_id));
87 builder.wr().push_hit_test(
88 viewport_rect,
89 ClipChainId::INVALID,
90- SpatialId::root_reference_frame(pipeline_id),
91+ viewport_spatial_id,
92 PrimitiveFlags::default(),
93 (0, 0), /* tag */
94 );
95
96- // Paint the canvas’ background (if any) before/under everything else
97+ // Paint the canvas' background (if any) before/under everything else
98 stacking_context_tree
99 .root_stacking_context
100 .build_canvas_background_display_list(&mut builder, fragment_tree);
101@@ -231,6 +272,11 @@
102 .build_display_list(&mut builder);
103 builder.paint_dom_inspector_highlight();
104
105+ // Pop the zoom reference frame if we pushed one
106+ if page_zoom_for_rendering.is_some() {
107+ webrender_display_list_builder.pop_reference_frame();
108+ }
109+
110 webrender_display_list_builder.end().1
111 }
112
113@@ -268,12 +314,42 @@
114 let mut scroll_tree = std::mem::take(&mut self.paint_info.scroll_tree);
115 let mut mapping = Vec::with_capacity(scroll_tree.nodes.len());
116
117- mapping.push(SpatialId::root_reference_frame(self.pipeline_id()));
118- mapping.push(SpatialId::root_scroll_node(self.pipeline_id()));
119-
120 let pipeline_id = self.pipeline_id();
121 let pipeline_tag = ((pipeline_id.0 as u64) << 32) | pipeline_id.1 as u64;
122
123+ // If we have a zoom reference frame (for embedded webviews with page zoom),
124+ // use it as the root spatial ID and define our own scroll frame as its child.
125+ // Otherwise use the built-in root_reference_frame and root_scroll_node.
126+ // This ensures all content is rendered as a descendant of the zoom transform.
127+ if let Some(zoom_spatial_id) = self.zoom_reference_frame_spatial_id {
128+ mapping.push(zoom_spatial_id);
129+
130+ // Define a scroll frame as a child of the zoom reference frame.
131+ // This replaces the built-in root_scroll_node which is not a child of our zoom frame.
132+ let viewport_size = self.paint_info.viewport_details.size;
133+ let clip_rect = LayoutRect::from_size(viewport_size.cast_unit());
134+ // Use the same rect for content - actual scroll limits are managed by the scroll tree
135+ let content_rect = clip_rect;
136+
137+ spatial_tree_count += 1;
138+ let spatial_tree_item_key = SpatialTreeItemKey::new(pipeline_tag, spatial_tree_count);
139+
140+ let root_scroll_spatial_id = self.wr().define_scroll_frame(
141+ zoom_spatial_id,
142+ ExternalScrollId(0, pipeline_id.into()),
143+ content_rect,
144+ clip_rect,
145+ LayoutVector2D::zero(), /* external_scroll_offset */
146+ 0, /* scroll_offset_generation */
147+ wr::HasScrollLinkedEffect::No,
148+ spatial_tree_item_key,
149+ );
150+ mapping.push(root_scroll_spatial_id);
151+ } else {
152+ mapping.push(SpatialId::root_reference_frame(pipeline_id));
153+ mapping.push(SpatialId::root_scroll_node(pipeline_id));
154+ }
155+
156 for node in scroll_tree.nodes.iter().skip(2) {
157 let parent_scroll_node_id = node
158 .parent