fix: preserve the active page when removing earlier tabs (#1220)

Adjust tab-removal bookkeeping so closing or losing a page before the active tab keeps the session pointed at the same logical page instead of silently shifting to the next one.

Add regression coverage for earlier-tab removal, later-tab removal, last-tab clamping, and the empty-page case.

Signed-off-by: Asish Kumar <officialasishkumar@gmail.com>
This commit is contained in:
Asish Kumar
2026-04-13 16:50:46 -05:00
committed by GitHub
parent 2114bdf847
commit 50323499c8
+50 -5
View File
@@ -111,6 +111,26 @@ fn update_page_target_info_in_pages(pages: &mut [PageInfo], target: &TargetInfo)
false
}
fn active_page_index_after_removal(
active_page_index: usize,
removed_index: usize,
remaining_pages: usize,
) -> usize {
if remaining_pages == 0 {
return 0;
}
if removed_index < active_page_index {
return active_page_index - 1;
}
if active_page_index >= remaining_pages {
return remaining_pages - 1;
}
active_page_index
}
/// Converts common error messages into AI-friendly, actionable descriptions.
pub fn to_ai_friendly_error(error: &str) -> String {
let lower = error.to_lowercase();
@@ -830,6 +850,14 @@ impl BrowserManager {
}
}
fn update_active_page_after_removal(&mut self, removed_index: usize) {
self.active_page_index = active_page_index_after_removal(
self.active_page_index,
removed_index,
self.pages.len(),
);
}
pub fn tab_list(&self) -> Vec<Value> {
self.pages
.iter()
@@ -929,6 +957,7 @@ impl BrowserManager {
}
let page = self.pages.remove(target_index);
self.update_active_page_after_removal(target_index);
let _ = self
.client
.send_command_typed::<_, Value>(
@@ -940,10 +969,6 @@ impl BrowserManager {
)
.await;
if self.active_page_index >= self.pages.len() {
self.active_page_index = self.pages.len() - 1;
}
let session_id = self.pages[self.active_page_index].session_id.clone();
self.enable_domains(&session_id).await?;
@@ -1201,7 +1226,7 @@ impl BrowserManager {
pub fn remove_page_by_target_id(&mut self, target_id: &str) {
if let Some(pos) = self.pages.iter().position(|p| p.target_id == target_id) {
self.pages.remove(pos);
self.update_active_page_if_needed();
self.update_active_page_after_removal(pos);
}
}
@@ -1526,6 +1551,26 @@ mod tests {
assert_eq!(pages[0].title, "Popup");
}
#[test]
fn test_active_page_index_after_removal_shifts_when_earlier_tab_is_removed() {
assert_eq!(active_page_index_after_removal(2, 0, 3), 1);
}
#[test]
fn test_active_page_index_after_removal_keeps_same_slot_when_later_tab_is_removed() {
assert_eq!(active_page_index_after_removal(1, 2, 3), 1);
}
#[test]
fn test_active_page_index_after_removal_clamps_when_active_last_tab_is_removed() {
assert_eq!(active_page_index_after_removal(3, 3, 3), 2);
}
#[test]
fn test_active_page_index_after_removal_resets_when_last_page_disappears() {
assert_eq!(active_page_index_after_removal(0, 0, 0), 0);
}
#[test]
fn test_validate_launch_options_extensions_and_cdp() {
let ext = vec!["/path/to/ext".to_string()];