Fold WebSurfaceStore into a single PerTabSurface owner
Eleven parallel BTreeMaps (click_points, hover_points, pending_scroll_deltas, scroll_offsets, typed_texts, viewport_bounds, viewport_sizes, pending_viewport_sizes, states, etc.) made every input bug a coordination problem across 11 disjoint maps with no compile-time guarantee they stayed in sync. Collapse them into BTreeMap<TabId, PerTabSurface>: one owner per tab, one lookup per input event. keyboard_focus stays at the store level because only one tab in the window can hold focus at a time. Public API and behavior are unchanged; the previously-fixed "scroll keeps keyboard_focus" semantics are preserved. cargo test ely_browser_core + cargo test ely_app --bin ely_app green.
This commit is contained in:
@@ -7,50 +7,35 @@ use crate::services::ProfileDataMode;
|
|||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
web_surface_frame::WebSurfaceFrame,
|
web_surface_frame::WebSurfaceFrame,
|
||||||
web_surface_geometry::{
|
web_surface_geometry::{WebSurfaceClickPoint, WebSurfaceScrollDelta, WebSurfaceSize},
|
||||||
WebSurfaceClickPoint, WebSurfaceScrollDelta, WebSurfaceScrollOffset, WebSurfaceSize,
|
|
||||||
},
|
|
||||||
web_surface_permissions::WebSurfaceSitePermission,
|
web_surface_permissions::WebSurfaceSitePermission,
|
||||||
web_surface_runtime::{WebSurfaceRuntime, WebSurfaceRuntimeFrame},
|
web_surface_runtime::{WebSurfaceRuntime, WebSurfaceRuntimeFrame},
|
||||||
web_surface_state::{
|
web_surface_state::{
|
||||||
WebSurfaceClickState, WebSurfaceKeyboardFocusState, WebSurfacePendingInput,
|
PerTabSurface, WebSurfaceClickState, WebSurfaceKeyboardFocusState, WebSurfacePendingInput,
|
||||||
WebSurfaceScrollState, WebSurfaceState, WebSurfaceTextInputState,
|
WebSurfaceScrollState, WebSurfaceState, WebSurfaceTextInputState,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(super) struct WebSurfaceStore {
|
pub(super) struct WebSurfaceStore {
|
||||||
runtime: WebSurfaceRuntime,
|
runtime: WebSurfaceRuntime,
|
||||||
pending_viewport_sizes: BTreeMap<TabId, WebSurfaceSize>,
|
/// Single owner of every per-tab invariant. See [`PerTabSurface`].
|
||||||
click_points: BTreeMap<TabId, WebSurfaceClickState>,
|
surfaces: BTreeMap<TabId, PerTabSurface>,
|
||||||
hover_points: BTreeMap<TabId, WebSurfaceClickPoint>,
|
/// Singleton because only one tab at a time holds keyboard focus
|
||||||
|
/// across the whole window. Lives on the store, not per-tab.
|
||||||
keyboard_focus: Option<WebSurfaceKeyboardFocusState>,
|
keyboard_focus: Option<WebSurfaceKeyboardFocusState>,
|
||||||
pending_scroll_deltas: BTreeMap<TabId, WebSurfaceScrollDelta>,
|
|
||||||
scroll_offsets: BTreeMap<TabId, WebSurfaceScrollState>,
|
|
||||||
typed_texts: BTreeMap<TabId, WebSurfaceTextInputState>,
|
|
||||||
viewport_bounds: BTreeMap<TabId, Bounds<Pixels>>,
|
|
||||||
viewport_sizes: BTreeMap<TabId, WebSurfaceSize>,
|
|
||||||
states: BTreeMap<TabId, WebSurfaceState>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebSurfaceStore {
|
impl WebSurfaceStore {
|
||||||
pub(super) fn new() -> Self {
|
pub(super) fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
runtime: WebSurfaceRuntime::new(),
|
runtime: WebSurfaceRuntime::new(),
|
||||||
pending_viewport_sizes: BTreeMap::new(),
|
surfaces: BTreeMap::new(),
|
||||||
click_points: BTreeMap::new(),
|
|
||||||
hover_points: BTreeMap::new(),
|
|
||||||
keyboard_focus: None,
|
keyboard_focus: None,
|
||||||
pending_scroll_deltas: BTreeMap::new(),
|
|
||||||
scroll_offsets: BTreeMap::new(),
|
|
||||||
typed_texts: BTreeMap::new(),
|
|
||||||
viewport_bounds: BTreeMap::new(),
|
|
||||||
viewport_sizes: BTreeMap::new(),
|
|
||||||
states: BTreeMap::new(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn state(&self, tab_id: &TabId) -> Option<&WebSurfaceState> {
|
pub(super) fn state(&self, tab_id: &TabId) -> Option<&WebSurfaceState> {
|
||||||
self.states.get(tab_id)
|
self.surfaces.get(tab_id).and_then(|surface| surface.state.as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn ensure_surface(
|
pub(super) fn ensure_surface(
|
||||||
@@ -64,7 +49,8 @@ impl WebSurfaceStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let requested_url = tab.url().as_str().to_string();
|
let requested_url = tab.url().as_str().to_string();
|
||||||
let Some(size) = self.viewport_sizes.get(tab.id()).copied() else {
|
let Some(size) = self.surfaces.get(tab.id()).and_then(|surface| surface.viewport_size)
|
||||||
|
else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let input = self.take_pending_input(tab.id(), requested_url.as_str());
|
let input = self.take_pending_input(tab.id(), requested_url.as_str());
|
||||||
@@ -76,20 +62,17 @@ impl WebSurfaceStore {
|
|||||||
let Some(frame) = result.frame else {
|
let Some(frame) = result.frame else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
self.states.insert(tab.id().clone(), WebSurfaceState::Ready(frame));
|
self.surface_mut(tab.id()).state = Some(WebSurfaceState::Ready(frame));
|
||||||
}
|
}
|
||||||
Ok(result) if result.started_loading => {
|
Ok(result) if result.started_loading => {
|
||||||
self.states.insert(
|
self.surface_mut(tab.id()).state = Some(WebSurfaceState::Loading {
|
||||||
tab.id().clone(),
|
requested_url: result.requested_url,
|
||||||
WebSurfaceState::Loading {
|
previous_frame,
|
||||||
requested_url: result.requested_url,
|
});
|
||||||
previous_frame,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(message) => {
|
Err(message) => {
|
||||||
self.states.insert(tab.id().clone(), WebSurfaceState::Failed { message });
|
self.surface_mut(tab.id()).state = Some(WebSurfaceState::Failed { message });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -101,11 +84,11 @@ impl WebSurfaceStore {
|
|||||||
for frame in frames {
|
for frame in frames {
|
||||||
match frame {
|
match frame {
|
||||||
WebSurfaceRuntimeFrame::Ready { tab_id, frame } => {
|
WebSurfaceRuntimeFrame::Ready { tab_id, frame } => {
|
||||||
self.states.insert(tab_id, WebSurfaceState::Ready(frame));
|
self.surface_mut(&tab_id).state = Some(WebSurfaceState::Ready(frame));
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
WebSurfaceRuntimeFrame::Failed { tab_id, message } => {
|
WebSurfaceRuntimeFrame::Failed { tab_id, message } => {
|
||||||
self.states.insert(tab_id, WebSurfaceState::Failed { message });
|
self.surface_mut(&tab_id).state = Some(WebSurfaceState::Failed { message });
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -124,26 +107,26 @@ impl WebSurfaceStore {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
let state = self
|
let surface = self.surface_mut(tab_id);
|
||||||
.scroll_offsets
|
let scroll = surface
|
||||||
.entry(tab_id.clone())
|
.scroll_offset
|
||||||
.or_insert_with(|| WebSurfaceScrollState::new(requested_url.to_string()));
|
.get_or_insert_with(|| WebSurfaceScrollState::new(requested_url.to_string()));
|
||||||
if state.requested_url != requested_url {
|
if scroll.requested_url != requested_url {
|
||||||
*state = WebSurfaceScrollState::new(requested_url.to_string());
|
*scroll = WebSurfaceScrollState::new(requested_url.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
state.offset = state.offset.scrolled_by(delta);
|
scroll.offset = scroll.offset.scrolled_by(delta);
|
||||||
self.pending_scroll_deltas
|
surface.pending_scroll_delta = Some(match surface.pending_scroll_delta {
|
||||||
.entry(tab_id.clone())
|
Some(current) => current.combined_with(delta),
|
||||||
.and_modify(|current| *current = current.combined_with(delta))
|
None => delta,
|
||||||
.or_insert(delta);
|
});
|
||||||
// Drop any buffered click — its viewport coordinates were
|
// Drop any buffered click — its viewport coordinates were
|
||||||
// captured against the pre-scroll page, so applying it after
|
// captured against the pre-scroll page, so applying it after
|
||||||
// the scroll would land on the wrong DOM element. Keep
|
// the scroll would land on the wrong DOM element. Keep
|
||||||
// `keyboard_focus` and `typed_texts` though: Servo maintains
|
// `keyboard_focus` and `typed_text` though: Servo maintains
|
||||||
// its own DOM focus across scrolls, so a focused input keeps
|
// its own DOM focus across scrolls, so a focused input keeps
|
||||||
// accepting the user's keystrokes after they wheel-scroll.
|
// accepting the user's keystrokes after they wheel-scroll.
|
||||||
self.click_points.remove(tab_id);
|
surface.click_point = None;
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,26 +134,27 @@ impl WebSurfaceStore {
|
|||||||
let Some(size) = WebSurfaceSize::from_bounds(bounds) else {
|
let Some(size) = WebSurfaceSize::from_bounds(bounds) else {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
self.viewport_bounds.insert(tab_id.clone(), bounds);
|
let surface = self.surface_mut(tab_id);
|
||||||
|
surface.viewport_bounds = Some(bounds);
|
||||||
|
|
||||||
let Some(current_size) = self.viewport_sizes.get(tab_id).copied() else {
|
let Some(current_size) = surface.viewport_size else {
|
||||||
self.viewport_sizes.insert(tab_id.clone(), size);
|
surface.viewport_size = Some(size);
|
||||||
self.pending_viewport_sizes.remove(tab_id);
|
surface.pending_viewport_size = None;
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
if current_size == size {
|
if current_size == size {
|
||||||
self.pending_viewport_sizes.remove(tab_id);
|
surface.pending_viewport_size = None;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.pending_viewport_sizes.get(tab_id) != Some(&size) {
|
if surface.pending_viewport_size != Some(size) {
|
||||||
self.pending_viewport_sizes.insert(tab_id.clone(), size);
|
surface.pending_viewport_size = Some(size);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.pending_viewport_sizes.remove(tab_id);
|
surface.pending_viewport_size = None;
|
||||||
self.viewport_sizes.insert(tab_id.clone(), size);
|
surface.viewport_size = Some(size);
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,13 +163,15 @@ impl WebSurfaceStore {
|
|||||||
tab_id: &TabId,
|
tab_id: &TabId,
|
||||||
position: Point<Pixels>,
|
position: Point<Pixels>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let Some(bounds) = self.viewport_bounds.get(tab_id).copied() else {
|
let surface = self.surfaces.get_mut(tab_id).filter(|surface| surface.viewport_bounds.is_some());
|
||||||
|
let Some(surface) = surface else {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
let bounds = surface.viewport_bounds.expect("viewport_bounds checked above");
|
||||||
let Some(point) = WebSurfaceClickPoint::from_window_position(bounds, position) else {
|
let Some(point) = WebSurfaceClickPoint::from_window_position(bounds, position) else {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
self.hover_points.insert(tab_id.clone(), point);
|
surface.hover_point = Some(point);
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,16 +181,24 @@ impl WebSurfaceStore {
|
|||||||
requested_url: &str,
|
requested_url: &str,
|
||||||
position: Point<Pixels>,
|
position: Point<Pixels>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let Some(bounds) = self.viewport_bounds.get(tab_id).copied() else {
|
let Some(bounds) =
|
||||||
|
self.surfaces.get(tab_id).and_then(|surface| surface.viewport_bounds)
|
||||||
|
else {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
let Some(point) = WebSurfaceClickPoint::from_window_position(bounds, position) else {
|
let Some(point) = WebSurfaceClickPoint::from_window_position(bounds, position) else {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let scroll_offset = self
|
||||||
|
.surfaces
|
||||||
|
.get(tab_id)
|
||||||
|
.map(|surface| surface.scroll_offset_for(requested_url))
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
let state = WebSurfaceClickState {
|
let state = WebSurfaceClickState {
|
||||||
requested_url: requested_url.to_string(),
|
requested_url: requested_url.to_string(),
|
||||||
scroll_offset: self.scroll_offset_for(tab_id, requested_url),
|
scroll_offset,
|
||||||
point,
|
point,
|
||||||
};
|
};
|
||||||
self.keyboard_focus = Some(WebSurfaceKeyboardFocusState {
|
self.keyboard_focus = Some(WebSurfaceKeyboardFocusState {
|
||||||
@@ -213,8 +207,9 @@ impl WebSurfaceStore {
|
|||||||
scroll_offset: state.scroll_offset,
|
scroll_offset: state.scroll_offset,
|
||||||
click_point: state.point,
|
click_point: state.point,
|
||||||
});
|
});
|
||||||
self.typed_texts.remove(tab_id);
|
let surface = self.surface_mut(tab_id);
|
||||||
self.click_points.insert(tab_id.clone(), state);
|
surface.typed_text = None;
|
||||||
|
surface.click_point = Some(state);
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,21 +229,24 @@ impl WebSurfaceStore {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let entry =
|
let scroll_offset = focus.scroll_offset;
|
||||||
self.typed_texts.entry(tab_id.clone()).or_insert_with(|| WebSurfaceTextInputState {
|
let click_point = focus.click_point;
|
||||||
requested_url: requested_url.to_string(),
|
let surface = self.surface_mut(tab_id);
|
||||||
scroll_offset: focus.scroll_offset,
|
|
||||||
click_point: focus.click_point,
|
let entry = surface.typed_text.get_or_insert_with(|| WebSurfaceTextInputState {
|
||||||
text: String::new(),
|
requested_url: requested_url.to_string(),
|
||||||
});
|
scroll_offset,
|
||||||
|
click_point,
|
||||||
|
text: String::new(),
|
||||||
|
});
|
||||||
if entry.requested_url != requested_url
|
if entry.requested_url != requested_url
|
||||||
|| entry.scroll_offset != focus.scroll_offset
|
|| entry.scroll_offset != scroll_offset
|
||||||
|| entry.click_point != focus.click_point
|
|| entry.click_point != click_point
|
||||||
{
|
{
|
||||||
*entry = WebSurfaceTextInputState {
|
*entry = WebSurfaceTextInputState {
|
||||||
requested_url: requested_url.to_string(),
|
requested_url: requested_url.to_string(),
|
||||||
scroll_offset: focus.scroll_offset,
|
scroll_offset,
|
||||||
click_point: focus.click_point,
|
click_point,
|
||||||
text: String::new(),
|
text: String::new(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -262,22 +260,22 @@ impl WebSurfaceStore {
|
|||||||
tab_id: &TabId,
|
tab_id: &TabId,
|
||||||
requested_url: &str,
|
requested_url: &str,
|
||||||
) -> WebSurfacePendingInput {
|
) -> WebSurfacePendingInput {
|
||||||
let scroll_offset = self.scroll_offset_for(tab_id, requested_url);
|
let surface = self.surface_mut(tab_id);
|
||||||
let scroll_delta = self.pending_scroll_deltas.remove(tab_id);
|
let scroll_offset = surface.scroll_offset_for(requested_url);
|
||||||
let click_point = self
|
let scroll_delta = surface.pending_scroll_delta.take();
|
||||||
.click_points
|
let click_point = surface
|
||||||
.remove(tab_id)
|
.click_point
|
||||||
|
.take()
|
||||||
.filter(|state| {
|
.filter(|state| {
|
||||||
state.requested_url == requested_url && state.scroll_offset == scroll_offset
|
state.requested_url == requested_url && state.scroll_offset == scroll_offset
|
||||||
})
|
})
|
||||||
.map(|state| state.point);
|
.map(|state| state.point);
|
||||||
let typed_text = self
|
let typed_text = surface
|
||||||
.typed_texts
|
.typed_text
|
||||||
.remove(tab_id)
|
.take()
|
||||||
.filter(|state| state.requested_url == requested_url)
|
.filter(|state| state.requested_url == requested_url)
|
||||||
.map(|state| state.text);
|
.map(|state| state.text);
|
||||||
|
let hover_point = surface.hover_point.take();
|
||||||
let hover_point = self.hover_points.remove(tab_id);
|
|
||||||
|
|
||||||
WebSurfacePendingInput { scroll_offset, scroll_delta, click_point, hover_point, typed_text }
|
WebSurfacePendingInput { scroll_offset, scroll_delta, click_point, hover_point, typed_text }
|
||||||
}
|
}
|
||||||
@@ -288,7 +286,7 @@ impl WebSurfaceStore {
|
|||||||
requested_url: &str,
|
requested_url: &str,
|
||||||
zoom_percent: u16,
|
zoom_percent: u16,
|
||||||
) -> Option<WebSurfaceFrame> {
|
) -> Option<WebSurfaceFrame> {
|
||||||
match self.states.get(tab_id) {
|
match self.surfaces.get(tab_id).and_then(|surface| surface.state.as_ref()) {
|
||||||
Some(WebSurfaceState::Ready(frame))
|
Some(WebSurfaceState::Ready(frame))
|
||||||
if frame.requested_url == requested_url && frame.zoom_percent() == zoom_percent =>
|
if frame.requested_url == requested_url && frame.zoom_percent() == zoom_percent =>
|
||||||
{
|
{
|
||||||
@@ -304,12 +302,8 @@ impl WebSurfaceStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scroll_offset_for(&self, tab_id: &TabId, requested_url: &str) -> WebSurfaceScrollOffset {
|
fn surface_mut(&mut self, tab_id: &TabId) -> &mut PerTabSurface {
|
||||||
self.scroll_offsets
|
self.surfaces.entry(tab_id.clone()).or_insert_with(PerTabSurface::new)
|
||||||
.get(tab_id)
|
|
||||||
.filter(|state| state.requested_url == requested_url)
|
|
||||||
.map(|state| state.offset)
|
|
||||||
.unwrap_or_default()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
use ely_domain::TabId;
|
use ely_domain::TabId;
|
||||||
|
use gpui::{Bounds, Pixels};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
web_surface_frame::WebSurfaceFrame,
|
web_surface_frame::WebSurfaceFrame,
|
||||||
web_surface_geometry::{WebSurfaceClickPoint, WebSurfaceScrollOffset},
|
web_surface_geometry::{
|
||||||
|
WebSurfaceClickPoint, WebSurfaceScrollDelta, WebSurfaceScrollOffset, WebSurfaceSize,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(super) struct WebSurfaceScrollState {
|
pub(super) struct WebSurfaceScrollState {
|
||||||
@@ -41,7 +44,7 @@ pub(super) struct WebSurfaceTextInputState {
|
|||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub(super) struct WebSurfacePendingInput {
|
pub(super) struct WebSurfacePendingInput {
|
||||||
pub(super) scroll_offset: WebSurfaceScrollOffset,
|
pub(super) scroll_offset: WebSurfaceScrollOffset,
|
||||||
pub(super) scroll_delta: Option<super::web_surface_geometry::WebSurfaceScrollDelta>,
|
pub(super) scroll_delta: Option<WebSurfaceScrollDelta>,
|
||||||
pub(super) click_point: Option<WebSurfaceClickPoint>,
|
pub(super) click_point: Option<WebSurfaceClickPoint>,
|
||||||
pub(super) hover_point: Option<WebSurfaceClickPoint>,
|
pub(super) hover_point: Option<WebSurfaceClickPoint>,
|
||||||
pub(super) typed_text: Option<String>,
|
pub(super) typed_text: Option<String>,
|
||||||
@@ -52,3 +55,52 @@ pub(super) enum WebSurfaceState {
|
|||||||
Ready(WebSurfaceFrame),
|
Ready(WebSurfaceFrame),
|
||||||
Failed { message: String },
|
Failed { message: String },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// All per-tab surface invariants in one owner.
|
||||||
|
///
|
||||||
|
/// Replaces the previous 11 parallel `BTreeMap<TabId, _>` fields on
|
||||||
|
/// `WebSurfaceStore`. Every per-tab field lives here so a single
|
||||||
|
/// lookup yields the full input/render context for that tab; the
|
||||||
|
/// remaining cross-tab state (the runtime backend and the singular
|
||||||
|
/// `keyboard_focus` pointer to the active tab) stays on the store.
|
||||||
|
///
|
||||||
|
/// All inputs that depend on a measured viewport take their
|
||||||
|
/// pre-conditions from this struct (`viewport_bounds`,
|
||||||
|
/// `viewport_size`), so "viewport not ready" turns into a guard at
|
||||||
|
/// the call site instead of a `get` on a parallel map that may or
|
||||||
|
/// may not be populated.
|
||||||
|
pub(super) struct PerTabSurface {
|
||||||
|
pub(super) viewport_bounds: Option<Bounds<Pixels>>,
|
||||||
|
pub(super) viewport_size: Option<WebSurfaceSize>,
|
||||||
|
pub(super) pending_viewport_size: Option<WebSurfaceSize>,
|
||||||
|
pub(super) hover_point: Option<WebSurfaceClickPoint>,
|
||||||
|
pub(super) click_point: Option<WebSurfaceClickState>,
|
||||||
|
pub(super) pending_scroll_delta: Option<WebSurfaceScrollDelta>,
|
||||||
|
pub(super) scroll_offset: Option<WebSurfaceScrollState>,
|
||||||
|
pub(super) typed_text: Option<WebSurfaceTextInputState>,
|
||||||
|
pub(super) state: Option<WebSurfaceState>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PerTabSurface {
|
||||||
|
pub(super) fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
viewport_bounds: None,
|
||||||
|
viewport_size: None,
|
||||||
|
pending_viewport_size: None,
|
||||||
|
hover_point: None,
|
||||||
|
click_point: None,
|
||||||
|
pending_scroll_delta: None,
|
||||||
|
scroll_offset: None,
|
||||||
|
typed_text: None,
|
||||||
|
state: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn scroll_offset_for(&self, requested_url: &str) -> WebSurfaceScrollOffset {
|
||||||
|
self.scroll_offset
|
||||||
|
.as_ref()
|
||||||
|
.filter(|state| state.requested_url == requested_url)
|
||||||
|
.map(|state| state.offset)
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user