fix: present frames during continuous input
This commit is contained in:
@@ -90,8 +90,9 @@ Real and verified:
|
|||||||
macOS Metal toolchain so `cargo run`'s shader-build failure is fixed.
|
macOS Metal toolchain so `cargo run`'s shader-build failure is fixed.
|
||||||
- Scroll input: consecutive same-document wheel requests coalesce at the
|
- Scroll input: consecutive same-document wheel requests coalesce at the
|
||||||
profile worker boundary while preserving total device-pixel distance;
|
profile worker boundary while preserving total device-pixel distance;
|
||||||
clicks, text, and allow-once permission transfers retain strict ordering.
|
frames from the active render epoch remain presentable while newer input is
|
||||||
Verified by a blocked-worker burst regression and the live-site scroll e2e.
|
queued. Clicks, text, and allow-once permission transfers retain strict
|
||||||
|
ordering. Verified by generation, blocked-worker burst, and live-site tests.
|
||||||
- Website color scheme: resolved System/Light/Dark state crosses sidecar
|
- Website color scheme: resolved System/Light/Dark state crosses sidecar
|
||||||
protocol v4 and reaches `WebView::notify_theme_change`; GPUI appearance
|
protocol v4 and reaches `WebView::notify_theme_change`; GPUI appearance
|
||||||
events trigger a shell repaint, and first-navigation WebView replacement
|
events trigger a shell repaint, and first-navigation WebView replacement
|
||||||
|
|||||||
@@ -104,9 +104,11 @@ impl WebSurfaceRuntime {
|
|||||||
let submitted_at = Instant::now();
|
let submitted_at = Instant::now();
|
||||||
let started_loading = {
|
let started_loading = {
|
||||||
let session = session_for_scope(&mut self.sessions, tab.id(), scope.clone());
|
let session = session_for_scope(&mut self.sessions, tab.id(), scope.clone());
|
||||||
let started_loading = session.started_loading(&requested_url, size, zoom_percent);
|
let started_loading =
|
||||||
|
session.started_loading(&requested_url, size, zoom_percent, self.color_scheme);
|
||||||
if started_loading {
|
if started_loading {
|
||||||
session.pending_user_navigation = false;
|
session.pending_user_navigation = false;
|
||||||
|
session.frame_generation_floor = Some(generation);
|
||||||
}
|
}
|
||||||
if user_navigation_input {
|
if user_navigation_input {
|
||||||
session.pending_user_navigation = true;
|
session.pending_user_navigation = true;
|
||||||
@@ -114,6 +116,7 @@ impl WebSurfaceRuntime {
|
|||||||
session.requested_url = requested_url.clone();
|
session.requested_url = requested_url.clone();
|
||||||
session.size = size;
|
session.size = size;
|
||||||
session.zoom_percent = zoom_percent;
|
session.zoom_percent = zoom_percent;
|
||||||
|
session.color_scheme = Some(self.color_scheme);
|
||||||
session.scroll_offset = next_scroll_offset;
|
session.scroll_offset = next_scroll_offset;
|
||||||
session.generation = Some(generation);
|
session.generation = Some(generation);
|
||||||
session.cadence.note_ensure(input_kind, started_loading, submitted_at);
|
session.cadence.note_ensure(input_kind, started_loading, submitted_at);
|
||||||
@@ -296,7 +299,7 @@ impl WebSurfaceRuntime {
|
|||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
if !self.sessions.get(&tab_id_obj).is_some_and(|session| {
|
if !self.sessions.get(&tab_id_obj).is_some_and(|session| {
|
||||||
&session.scope == scope && session.generation == Some(generation)
|
&session.scope == scope && session.accepts_frame_generation(generation)
|
||||||
}) {
|
}) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -145,6 +145,35 @@ fn late_frame_is_discarded_after_tab_session_closes() -> Result<(), String> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn earlier_frame_from_same_render_epoch_remains_presentable() -> Result<(), String> {
|
||||||
|
let mut runtime = WebSurfaceRuntime::new_with_client_factory(empty_client_factory);
|
||||||
|
let tab_id = TabId::new();
|
||||||
|
let profile_id = ProfileId::new();
|
||||||
|
let tab = web_tab(tab_id.clone(), profile_id.clone(), "https://example.com/page")?;
|
||||||
|
|
||||||
|
runtime.ensure_tab(&tab, surface_size(), ProfileDataMode::Transient, &[], pending_input())?;
|
||||||
|
let earlier = current_generation(&runtime, &tab_id)?;
|
||||||
|
runtime.ensure_tab(&tab, surface_size(), ProfileDataMode::Transient, &[], pending_input())?;
|
||||||
|
let latest = current_generation(&runtime, &tab_id)?;
|
||||||
|
|
||||||
|
let mut frames = Vec::new();
|
||||||
|
runtime.collect_responses(
|
||||||
|
&scope(&profile_id),
|
||||||
|
vec![WorkerResponse::Frame {
|
||||||
|
generation: earlier,
|
||||||
|
tab_id: tab_id.as_str().to_string(),
|
||||||
|
frame: live_frame(),
|
||||||
|
}],
|
||||||
|
Instant::now(),
|
||||||
|
&mut frames,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(earlier < latest);
|
||||||
|
assert!(matches!(frames.as_slice(), [WebSurfaceRuntimeFrame::Ready { .. }]));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn scope_change_returns_submitted_permission_grants_on_the_next_tick() -> Result<(), String> {
|
fn scope_change_returns_submitted_permission_grants_on_the_next_tick() -> Result<(), String> {
|
||||||
let mut runtime = WebSurfaceRuntime::new_with_client_factory(empty_client_factory);
|
let mut runtime = WebSurfaceRuntime::new_with_client_factory(empty_client_factory);
|
||||||
|
|||||||
@@ -57,7 +57,9 @@ fn successful_frame_resets_scope_retry_state() {
|
|||||||
let scope = WebSurfaceRuntimeScope::new(ProfileId::new(), ProfileDataMode::Transient);
|
let scope = WebSurfaceRuntimeScope::new(ProfileId::new(), ProfileDataMode::Transient);
|
||||||
let tab_id = TabId::new();
|
let tab_id = TabId::new();
|
||||||
let generation = RequestGeneration::new(1);
|
let generation = RequestGeneration::new(1);
|
||||||
session_for_scope(&mut runtime.sessions, &tab_id, scope.clone()).generation = Some(generation);
|
let session = session_for_scope(&mut runtime.sessions, &tab_id, scope.clone());
|
||||||
|
session.generation = Some(generation);
|
||||||
|
session.frame_generation_floor = Some(generation);
|
||||||
runtime.note_scope_failure(&scope, Instant::now());
|
runtime.note_scope_failure(&scope, Instant::now());
|
||||||
let mut frames = Vec::new();
|
let mut frames = Vec::new();
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use crate::services::{
|
|||||||
transient_profile_data_dir,
|
transient_profile_data_dir,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use ely_domain::{ProfileId, TabId};
|
use ely_domain::{ColorScheme, ProfileId, TabId};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
web_surface_cadence::WebSurfacePollCadence,
|
web_surface_cadence::WebSurfacePollCadence,
|
||||||
@@ -42,6 +42,8 @@ pub(super) struct WebSurfaceSession {
|
|||||||
pub(super) scroll_offset: WebSurfaceScrollOffset,
|
pub(super) scroll_offset: WebSurfaceScrollOffset,
|
||||||
pub(super) pending_user_navigation: bool,
|
pub(super) pending_user_navigation: bool,
|
||||||
pub(super) generation: Option<RequestGeneration>,
|
pub(super) generation: Option<RequestGeneration>,
|
||||||
|
pub(super) frame_generation_floor: Option<RequestGeneration>,
|
||||||
|
pub(super) color_scheme: Option<ColorScheme>,
|
||||||
pub(super) cadence: WebSurfacePollCadence,
|
pub(super) cadence: WebSurfacePollCadence,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,6 +57,8 @@ impl WebSurfaceSession {
|
|||||||
scroll_offset: WebSurfaceScrollOffset::default(),
|
scroll_offset: WebSurfaceScrollOffset::default(),
|
||||||
pending_user_navigation: false,
|
pending_user_navigation: false,
|
||||||
generation: None,
|
generation: None,
|
||||||
|
frame_generation_floor: None,
|
||||||
|
color_scheme: None,
|
||||||
cadence: WebSurfacePollCadence::default(),
|
cadence: WebSurfacePollCadence::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -64,10 +68,17 @@ impl WebSurfaceSession {
|
|||||||
requested_url: &str,
|
requested_url: &str,
|
||||||
size: WebSurfaceSize,
|
size: WebSurfaceSize,
|
||||||
zoom_percent: u16,
|
zoom_percent: u16,
|
||||||
|
color_scheme: ColorScheme,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.requested_url != requested_url
|
self.requested_url != requested_url
|
||||||
|| self.size != size
|
|| self.size != size
|
||||||
|| self.zoom_percent != zoom_percent
|
|| self.zoom_percent != zoom_percent
|
||||||
|
|| self.color_scheme != Some(color_scheme)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn accepts_frame_generation(&self, generation: RequestGeneration) -> bool {
|
||||||
|
self.frame_generation_floor.is_some_and(|floor| generation >= floor)
|
||||||
|
&& self.generation.is_some_and(|latest| generation <= latest)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn url_change_for(
|
pub(super) fn url_change_for(
|
||||||
|
|||||||
Reference in New Issue
Block a user