@@ -434,12 +434,45 @@ describe('browser-agent session', () => {
434434 expect ( content . addChildView ) . toHaveBeenCalledWith ( tab . view )
435435 expect ( view . setBounds ) . toHaveBeenCalledWith ( { x : 100 , y : 50 , width : 800 , height : 600 } )
436436
437- // Panel hidden: the view detaches.
437+ // Panel hidden: the view stops painting but stays attached. Detaching
438+ // would give up its compositor surface, and rebuilding that on the way
439+ // back is the blank repaint that reads as the page having reloaded —
440+ // which is every switch to another resource and back.
438441 const removeChildView = (
439442 win as unknown as { contentView : { removeChildView : ReturnType < typeof vi . fn > } }
440443 ) . contentView . removeChildView
444+ view . setVisible . mockClear ( )
441445 panel . setPanelBounds ( null )
442- expect ( removeChildView ) . toHaveBeenCalledWith ( tab . view )
446+ expect ( view . setVisible ) . toHaveBeenCalledWith ( false )
447+ expect ( removeChildView ) . not . toHaveBeenCalled ( )
448+
449+ // Showing it again reuses the attached view rather than re-adding it.
450+ content . addChildView . mockClear ( )
451+ panel . setPanelBounds ( { x : 100 , y : 50 , width : 800 , height : 600 } )
452+ expect ( view . setVisible ) . toHaveBeenLastCalledWith ( true )
453+ expect ( content . addChildView ) . not . toHaveBeenCalled ( )
454+ } )
455+
456+ it ( 'detaches the previous view when another tab becomes active' , ( ) => {
457+ const first = session . ensureTab ( )
458+ panel . setPanelBounds ( { x : 0 , y : 0 , width : 800 , height : 600 } )
459+ const content = (
460+ win as unknown as {
461+ contentView : {
462+ addChildView : ReturnType < typeof vi . fn >
463+ removeChildView : ReturnType < typeof vi . fn >
464+ }
465+ }
466+ ) . contentView
467+ content . addChildView . mockClear ( )
468+ content . removeChildView . mockClear ( )
469+
470+ const second = session . addTab ( )
471+
472+ // Hiding keeps a view attached, but a tab switch still has to detach:
473+ // two native views stacked in the window would composite over each other.
474+ expect ( content . removeChildView ) . toHaveBeenCalledWith ( first . view )
475+ expect ( content . addChildView ) . toHaveBeenCalledWith ( second . view )
443476 } )
444477
445478 // The measured report is the sole writer of bounds. A main-process
@@ -560,7 +593,7 @@ describe('browser-agent session', () => {
560593 expect ( view . setBounds ) . toHaveBeenLastCalledWith ( { x : 500 , y : 40 , width : 500 , height : 760 } )
561594 } )
562595
563- it ( 'drops the resize listener when the view detaches ' , ( ) => {
596+ it ( 'drops the resize listener while the panel is hidden ' , ( ) => {
564597 session . ensureTab ( )
565598 panel . setPanelBounds ( { x : 100 , y : 50 , width : 800 , height : 600 } )
566599 const onResize = hostResizeHandler ( win )
@@ -835,12 +868,14 @@ describe('browser-agent session', () => {
835868 win as unknown as { contentView : { removeChildView : ReturnType < typeof vi . fn > } }
836869 ) . contentView
837870 contentView . removeChildView . mockClear ( )
871+ const view = session . requireTab ( ) . view as unknown as MockView
872+ view . setVisible . mockClear ( )
838873
839874 // The renderer goes silent — crashed, unmounted, or wedged. Without the
840875 // lease the native view keeps floating over whatever replaced the panel.
841876 await vi . advanceTimersByTimeAsync ( 6_000 )
842877
843- expect ( contentView . removeChildView ) . toHaveBeenCalled ( )
878+ expect ( view . setVisible ) . toHaveBeenCalledWith ( false )
844879 } finally {
845880 vi . useRealTimers ( )
846881 }
0 commit comments