Scroll from script should trigger a reflow

Scrolling from script should flow layout and send a display list to
WebRender. This allows all of the scroll nodes to exist in WebRender
before asking it to move the node.

See https://gist.github.com/paulirish/5d52fb081b3570c81e3a.
Fixes #29659.
This commit is contained in:
Martin Robinson 2023-04-25 08:29:09 +02:00
parent 2ae158dec1
commit 3ab5e2a188
25 changed files with 161 additions and 188 deletions

View file

@ -645,9 +645,6 @@ impl LayoutThread {
Msg::CreateLayoutThread(..) => LayoutHangAnnotation::CreateLayoutThread,
Msg::SetFinalUrl(..) => LayoutHangAnnotation::SetFinalUrl,
Msg::SetScrollStates(..) => LayoutHangAnnotation::SetScrollStates,
Msg::UpdateScrollStateFromScript(..) => {
LayoutHangAnnotation::UpdateScrollStateFromScript
},
Msg::RegisterPaint(..) => LayoutHangAnnotation::RegisterPaint,
Msg::SetNavigationStart(..) => LayoutHangAnnotation::SetNavigationStart,
};
@ -753,19 +750,6 @@ impl LayoutThread {
Msg::SetScrollStates(new_scroll_states) => {
self.set_scroll_states(new_scroll_states, possibly_locked_rw_data);
},
Msg::UpdateScrollStateFromScript(state) => {
let mut rw_data = possibly_locked_rw_data.lock();
rw_data
.scroll_offsets
.insert(state.scroll_id, state.scroll_offset);
let point = Point2D::new(-state.scroll_offset.x, -state.scroll_offset.y);
self.webrender_api.send_scroll_node(
webrender_api::units::LayoutPoint::from_untyped(point),
state.scroll_id,
webrender_api::ScrollClamping::ToContentBounds,
);
},
Msg::CollectReports(reports_chan) => {
self.collect_reports(reports_chan, possibly_locked_rw_data);
},
@ -1250,7 +1234,9 @@ impl LayoutThread {
rw_data.inner_window_dimensions_response = None;
},
},
ReflowGoal::Full | ReflowGoal::TickAnimations => {},
ReflowGoal::Full |
ReflowGoal::TickAnimations |
ReflowGoal::UpdateScrollNode(_) => {},
}
return;
},
@ -1619,10 +1605,26 @@ impl LayoutThread {
.cloned();
},
},
ReflowGoal::UpdateScrollNode(scroll_state) => {
self.update_scroll_node_state(&scroll_state, rw_data);
},
ReflowGoal::Full | ReflowGoal::TickAnimations => {},
}
}
fn update_scroll_node_state(&self, state: &ScrollState, rw_data: &mut LayoutThreadData) {
rw_data
.scroll_offsets
.insert(state.scroll_id, state.scroll_offset);
let point = Point2D::new(-state.scroll_offset.x, -state.scroll_offset.y);
self.webrender_api.send_scroll_node(
webrender_api::units::LayoutPoint::from_untyped(point),
state.scroll_id,
webrender_api::ScrollClamping::ToContentBounds,
);
}
fn set_scroll_states<'a, 'b>(
&mut self,
new_scroll_states: Vec<ScrollState>,

View file

@ -616,9 +616,6 @@ impl LayoutThread {
Msg::CreateLayoutThread(..) => LayoutHangAnnotation::CreateLayoutThread,
Msg::SetFinalUrl(..) => LayoutHangAnnotation::SetFinalUrl,
Msg::SetScrollStates(..) => LayoutHangAnnotation::SetScrollStates,
Msg::UpdateScrollStateFromScript(..) => {
LayoutHangAnnotation::UpdateScrollStateFromScript
},
Msg::RegisterPaint(..) => LayoutHangAnnotation::RegisterPaint,
Msg::SetNavigationStart(..) => LayoutHangAnnotation::SetNavigationStart,
};
@ -724,19 +721,6 @@ impl LayoutThread {
Msg::SetScrollStates(new_scroll_states) => {
self.set_scroll_states(new_scroll_states, possibly_locked_rw_data);
},
Msg::UpdateScrollStateFromScript(state) => {
let mut rw_data = possibly_locked_rw_data.lock();
rw_data
.scroll_offsets
.insert(state.scroll_id, state.scroll_offset);
let point = Point2D::new(-state.scroll_offset.x, -state.scroll_offset.y);
self.webrender_api.send_scroll_node(
webrender_api::units::LayoutPoint::from_untyped(point),
state.scroll_id,
webrender_api::ScrollClamping::ToContentBounds,
);
},
Msg::CollectReports(reports_chan) => {
self.collect_reports(reports_chan, possibly_locked_rw_data);
},
@ -935,7 +919,9 @@ impl LayoutThread {
.cloned();
},
},
ReflowGoal::Full | ReflowGoal::TickAnimations => {},
ReflowGoal::Full |
ReflowGoal::TickAnimations |
ReflowGoal::UpdateScrollNode(_) => {},
}
return;
},
@ -1256,10 +1242,26 @@ impl LayoutThread {
rw_data.inner_window_dimensions_response = None;
},
},
ReflowGoal::UpdateScrollNode(scroll_state) => {
self.update_scroll_node_state(&scroll_state, rw_data);
},
ReflowGoal::Full | ReflowGoal::TickAnimations => {},
}
}
fn update_scroll_node_state(&self, state: &ScrollState, rw_data: &mut LayoutThreadData) {
rw_data
.scroll_offsets
.insert(state.scroll_id, state.scroll_offset);
let point = Point2D::new(-state.scroll_offset.x, -state.scroll_offset.y);
self.webrender_api.send_scroll_node(
webrender_api::units::LayoutPoint::from_untyped(point),
state.scroll_id,
webrender_api::ScrollClamping::ToContentBounds,
);
}
fn set_scroll_states<'a, 'b>(
&mut self,
new_scroll_states: Vec<ScrollState>,

View file

@ -168,26 +168,27 @@ enum WindowState {
#[derive(Debug, MallocSizeOf)]
pub enum ReflowReason {
CachedPageNeededReflow,
RefreshTick,
FirstLoad,
KeyEvent,
MouseEvent,
Query,
Timer,
Viewport,
WindowResize,
DOMContentLoaded,
DocumentLoaded,
StylesheetLoaded,
ImageLoaded,
RequestAnimationFrame,
WebFontLoaded,
WorkletLoaded,
ElementStateChanged,
FirstLoad,
FramedContentChanged,
IFrameLoadEvent,
ImageLoaded,
KeyEvent,
MissingExplicitReflow,
ElementStateChanged,
MouseEvent,
PendingReflow,
Query,
RefreshTick,
RequestAnimationFrame,
ScrollFromScript,
StylesheetLoaded,
Timer,
Viewport,
WebFontLoaded,
WindowResize,
WorkletLoaded,
}
#[dom_struct]
@ -1755,15 +1756,13 @@ impl Window {
// TODO Step 1
// TODO(mrobinson, #18709): Add smooth scrolling support to WebRender so that we can
// properly process ScrollBehavior here.
match self.layout_chan() {
Some(chan) => chan
.send(Msg::UpdateScrollStateFromScript(ScrollState {
scroll_id,
scroll_offset: Vector2D::new(-x, -y),
}))
.unwrap(),
None => warn!("Layout channel unavailable"),
}
self.reflow(
ReflowGoal::UpdateScrollNode(ScrollState {
scroll_id,
scroll_offset: Vector2D::new(-x, -y),
}),
ReflowReason::ScrollFromScript,
);
}
pub fn update_viewport_for_scroll(&self, x: f32, y: f32) {
@ -2719,6 +2718,7 @@ fn debug_reflow_events(id: PipelineId, reflow_goal: &ReflowGoal, reason: &Reflow
let goal_string = match *reflow_goal {
ReflowGoal::Full => "\tFull",
ReflowGoal::TickAnimations => "\tTickAnimations",
ReflowGoal::UpdateScrollNode(_) => "\tUpdateScrollNode",
ReflowGoal::LayoutQuery(ref query_msg, _) => match query_msg {
&QueryMsg::ContentBoxQuery(_n) => "\tContentBoxQuery",
&QueryMsg::ContentBoxesQuery(_n) => "\tContentBoxesQuery",

View file

@ -81,10 +81,6 @@ pub enum Msg {
/// Tells layout about the new scrolling offsets of each scrollable stacking context.
SetScrollStates(Vec<ScrollState>),
/// Tells layout about a single new scrolling offset from the script. The rest will
/// remain untouched and layout won't forward this back to script.
UpdateScrollStateFromScript(ScrollState),
/// Tells layout that script has added some paint worklet modules.
RegisterPaint(Atom, Vec<Atom>, Box<dyn Painter>),
@ -125,6 +121,10 @@ pub enum ReflowGoal {
Full,
TickAnimations,
LayoutQuery(QueryMsg, u64),
/// Tells layout about a single new scrolling offset from the script. The rest will
/// remain untouched and layout won't forward this back to script.
UpdateScrollNode(ScrollState),
}
impl ReflowGoal {
@ -132,7 +132,7 @@ impl ReflowGoal {
/// be present or false if it only needs stacking-relative positions.
pub fn needs_display_list(&self) -> bool {
match *self {
ReflowGoal::Full | ReflowGoal::TickAnimations => true,
ReflowGoal::Full | ReflowGoal::TickAnimations | ReflowGoal::UpdateScrollNode(_) => true,
ReflowGoal::LayoutQuery(ref querymsg, _) => match *querymsg {
QueryMsg::NodesFromPointQuery(..) |
QueryMsg::TextIndexQuery(..) |
@ -155,7 +155,7 @@ impl ReflowGoal {
/// false if a layout_thread display list is sufficient.
pub fn needs_display(&self) -> bool {
match *self {
ReflowGoal::Full | ReflowGoal::TickAnimations => true,
ReflowGoal::Full | ReflowGoal::TickAnimations | ReflowGoal::UpdateScrollNode(_) => true,
ReflowGoal::LayoutQuery(ref querymsg, _) => match *querymsg {
QueryMsg::NodesFromPointQuery(..) |
QueryMsg::TextIndexQuery(..) |

View file

@ -791,7 +791,7 @@ bitflags! {
}
/// The scroll state of a stacking context.
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
pub struct ScrollState {
/// The ID of the scroll root.
pub scroll_id: ExternalScrollId,