ignore late page/title events from suspended tabs (white screen on tab restore)
when a loading tab was suspended its webview was destroyed while events were still queued, and a finished load from the dying webview could overwrite the tab's stored URL with an intermediate redirect URL. on switch-back the tab then reloaded the wrong page and stayed blank. each webview instance now gets a generation (epoch): events carry it and are dropped when they don't match, and suspending a tab invalidates its epoch. verified tab cycling still restores pages normally.
This commit is contained in:
parent
38a7b3a626
commit
0c6b62ae3b
1 changed files with 49 additions and 6 deletions
|
|
@ -39,10 +39,12 @@ enum UserEvent {
|
|||
ChromeIpc(String),
|
||||
TitleChanged {
|
||||
tab_id: u32,
|
||||
epoch: u32,
|
||||
title: String,
|
||||
},
|
||||
PageLoaded {
|
||||
tab_id: u32,
|
||||
epoch: u32,
|
||||
url: String,
|
||||
},
|
||||
DownloadStarted {
|
||||
|
|
@ -73,6 +75,10 @@ struct Tab {
|
|||
url: String,
|
||||
kir_enabled: bool,
|
||||
webview: Option<WebView>,
|
||||
// Identifies the current webview instance for this tab. Events (page load,
|
||||
// title) carry the epoch they were emitted from so that late events from a
|
||||
// destroyed webview (suspended tab) cannot corrupt the tab state.
|
||||
epoch: u32,
|
||||
back_stack: Vec<String>,
|
||||
forward_stack: Vec<String>,
|
||||
pending_nav_from_stack: bool,
|
||||
|
|
@ -94,6 +100,7 @@ struct BrowserState {
|
|||
downloads_panel_open: bool,
|
||||
active: usize,
|
||||
next_id: u32,
|
||||
next_epoch: u32,
|
||||
private: bool,
|
||||
user_agent: Option<String>,
|
||||
web_context: Option<WebContext>,
|
||||
|
|
@ -133,6 +140,13 @@ impl BrowserState {
|
|||
self.next_id += 1;
|
||||
id
|
||||
}
|
||||
fn alloc_epoch(&mut self) -> u32 {
|
||||
self.next_epoch = self.next_epoch.wrapping_add(1);
|
||||
if self.next_epoch == 0 {
|
||||
self.next_epoch = 1;
|
||||
}
|
||||
self.next_epoch
|
||||
}
|
||||
fn find_index(&self, tab_id: u32) -> Option<usize> {
|
||||
self.tabs.iter().position(|t| t.id == tab_id)
|
||||
}
|
||||
|
|
@ -1182,6 +1196,7 @@ pub fn launch_webkit(args: &Cli) -> Result<()> {
|
|||
downloads_panel_open: false,
|
||||
active: 0,
|
||||
next_id: 1,
|
||||
next_epoch: 1,
|
||||
private: args.private,
|
||||
user_agent: if args.ua_chrome {
|
||||
Some(chrome_user_agent())
|
||||
|
|
@ -1385,16 +1400,23 @@ fn handle_event(
|
|||
) -> Result<()> {
|
||||
match ev {
|
||||
UserEvent::ChromeIpc(msg) => handle_chrome_ipc(&msg, window, proxy, state)?,
|
||||
UserEvent::TitleChanged { tab_id, title } => {
|
||||
UserEvent::TitleChanged {
|
||||
tab_id,
|
||||
epoch,
|
||||
title,
|
||||
} => {
|
||||
if let Some(i) = state.find_index(tab_id) {
|
||||
if !title.trim().is_empty() {
|
||||
if state.tabs[i].epoch == epoch && !title.trim().is_empty() {
|
||||
state.tabs[i].title = title;
|
||||
push_tabs(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
UserEvent::PageLoaded { tab_id, url } => {
|
||||
UserEvent::PageLoaded { tab_id, epoch, url } => {
|
||||
if let Some(i) = state.find_index(tab_id) {
|
||||
if state.tabs[i].epoch != epoch {
|
||||
return Ok(());
|
||||
}
|
||||
if !url.is_empty() && url != "about:blank" {
|
||||
let previous = state.tabs[i].url.clone();
|
||||
if url != previous {
|
||||
|
|
@ -1701,6 +1723,7 @@ fn handle_chrome_ipc(
|
|||
state.tabs[i].kir_enabled = !state.tabs[i].kir_enabled;
|
||||
if i != state.active && !state.tabs[i].kir_enabled {
|
||||
state.tabs[i].webview = None;
|
||||
state.tabs[i].epoch = 0;
|
||||
}
|
||||
apply_visibility(state);
|
||||
push_tabs(state);
|
||||
|
|
@ -1883,6 +1906,7 @@ fn build_tab_webview(
|
|||
user_agent: Option<&str>,
|
||||
mut web_context: Option<&mut WebContext>,
|
||||
tab_id: u32,
|
||||
epoch: u32,
|
||||
url: &str,
|
||||
bounds: Rect,
|
||||
) -> Result<(WebView, WebViewHostMode)> {
|
||||
|
|
@ -1902,6 +1926,7 @@ fn build_tab_webview(
|
|||
user_agent,
|
||||
&init_script,
|
||||
tab_id,
|
||||
epoch,
|
||||
url,
|
||||
bounds,
|
||||
)
|
||||
|
|
@ -1919,6 +1944,7 @@ fn build_tab_webview(
|
|||
user_agent,
|
||||
&init_script,
|
||||
tab_id,
|
||||
epoch,
|
||||
url,
|
||||
bounds,
|
||||
)
|
||||
|
|
@ -1935,6 +1961,7 @@ fn build_tab_webview(
|
|||
user_agent,
|
||||
&init_script,
|
||||
tab_id,
|
||||
epoch,
|
||||
url,
|
||||
bounds,
|
||||
)
|
||||
|
|
@ -1957,6 +1984,7 @@ fn build_tab_webview(
|
|||
user_agent,
|
||||
&init_script,
|
||||
tab_id,
|
||||
epoch,
|
||||
url,
|
||||
bounds,
|
||||
)
|
||||
|
|
@ -1982,6 +2010,7 @@ fn build_tab_webview(
|
|||
user_agent,
|
||||
&init_script,
|
||||
tab_id,
|
||||
epoch,
|
||||
url,
|
||||
bounds,
|
||||
)
|
||||
|
|
@ -2009,6 +2038,7 @@ fn tab_webview_builder<'a>(
|
|||
user_agent: Option<&str>,
|
||||
init_script: &str,
|
||||
tab_id: u32,
|
||||
epoch: u32,
|
||||
url: &str,
|
||||
bounds: Rect,
|
||||
) -> WebViewBuilder<'a> {
|
||||
|
|
@ -2036,11 +2066,15 @@ fn tab_webview_builder<'a>(
|
|||
false
|
||||
})
|
||||
.with_document_title_changed_handler(move |title| {
|
||||
let _ = title_proxy.send_event(UserEvent::TitleChanged { tab_id, title });
|
||||
let _ = title_proxy.send_event(UserEvent::TitleChanged {
|
||||
tab_id,
|
||||
epoch,
|
||||
title,
|
||||
});
|
||||
})
|
||||
.with_on_page_load_handler(move |event, url| {
|
||||
if matches!(event, PageLoadEvent::Finished) {
|
||||
let _ = page_proxy.send_event(UserEvent::PageLoaded { tab_id, url });
|
||||
let _ = page_proxy.send_event(UserEvent::PageLoaded { tab_id, epoch, url });
|
||||
}
|
||||
})
|
||||
.with_download_started_handler(move |url, destination| {
|
||||
|
|
@ -2126,6 +2160,7 @@ fn open_tab(
|
|||
url: String,
|
||||
) -> Result<()> {
|
||||
let tab_id = state.alloc_id();
|
||||
let epoch = state.alloc_epoch();
|
||||
let chrome_h = chrome_height(state);
|
||||
let tab_bounds = content_bounds(window, chrome_h, state.host_mode);
|
||||
let (webview, effective_host_mode) = build_tab_webview(
|
||||
|
|
@ -2138,6 +2173,7 @@ fn open_tab(
|
|||
state.user_agent.as_deref(),
|
||||
state.web_context.as_mut(),
|
||||
tab_id,
|
||||
epoch,
|
||||
&url,
|
||||
tab_bounds,
|
||||
)?;
|
||||
|
|
@ -2149,6 +2185,7 @@ fn open_tab(
|
|||
url,
|
||||
kir_enabled: false,
|
||||
webview: Some(webview),
|
||||
epoch,
|
||||
back_stack: Vec::new(),
|
||||
forward_stack: Vec::new(),
|
||||
pending_nav_from_stack: false,
|
||||
|
|
@ -2227,6 +2264,7 @@ fn ensure_tab_loaded(
|
|||
let chrome_h = chrome_height(state);
|
||||
let tab_bounds = content_bounds(window, chrome_h, state.host_mode);
|
||||
let tab_id = state.tabs[index].id;
|
||||
let epoch = state.alloc_epoch();
|
||||
let tab_url = state.tabs[index].url.clone();
|
||||
let (webview, effective_host_mode) = build_tab_webview(
|
||||
window,
|
||||
|
|
@ -2238,18 +2276,23 @@ fn ensure_tab_loaded(
|
|||
state.user_agent.as_deref(),
|
||||
state.web_context.as_mut(),
|
||||
tab_id,
|
||||
epoch,
|
||||
&tab_url,
|
||||
tab_bounds,
|
||||
)?;
|
||||
state.host_mode = effective_host_mode;
|
||||
state.tabs[index].webview = Some(webview);
|
||||
state.tabs[index].epoch = epoch;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn suspend_background_tabs(state: &mut BrowserState) {
|
||||
for (i, tab) in state.tabs.iter_mut().enumerate() {
|
||||
if i != state.active && !tab.kir_enabled {
|
||||
if i != state.active && !tab.kir_enabled && tab.webview.is_some() {
|
||||
tab.webview = None;
|
||||
// Invalidate pending events from the destroyed webview so they
|
||||
// cannot overwrite this tab's url/title while it is suspended.
|
||||
tab.epoch = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue