You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Embedded browser: "open in new window" no longer opens a new browser tab
Summary
In the embedded WebView2 browser, requesting a new window — via target="_blank", window.open(), middle-click, or any path that fires CoreWebView2.NewWindowRequested — no longer opens a new browser tab in the workspace. The owner reports this regressed on/after 2026-08-14. Impact: users cannot open web links in additional tabs from within the embedded browser; the click either silently does nothing or spawns an external window (defeating the point of the in-app browser).
ConfiguredWebView.OnNewWindowRequested (ConfiguredWebView.cs:193-231) reads the Request URI via reflection, sets args.Handled = true, then calls this.ViewModel.RaiseOpenNewWindow(urlString).
WebViewModel.RaiseOpenNewWindow (Phantom.Workspaces\ViewModels\WebViewModel.cs:222-238) is:
publicasyncvoidRaiseOpenNewWindow(stringurl){if(this.tabService==null){return;// <-- silent no-op when tabService is null}varnewTab=newWebViewModel(url,this.tabService){ ...};awaitthis.tabService.OpenTabAsync(newTab,insertAfterTabId:this.Id);}
Confirmed broken wiring site
The WebViewModel instances constructed on the layout-restore path are built without a tabService, so RaiseOpenNewWindow early-returns and no tab is created. All three restore call sites in Phantom.Workspaces\ViewModels\MainWindowViewModel.cs omit the tabService argument:
MainWindowViewModel.cs:3513 (persisted BrowserDockTabDescriptor → new tab):
MainWindowViewModel.cs:3720 (CreateTabFromEntityAsync URL fallback): same shape.
Because WebViewModel's constructor signature is (string initialUrl, IWorkspaceTabService? tabService = null, bool titleFixed = false, IUrlOpener? urlOpener = null), omitting the second argument leaves tabService = null. As a result, every browser tab that was restored from a saved workspace layout (the vast majority of tabs the user sees on subsequent launches) silently drops NewWindowRequested.
Because ConfiguredWebView.OnNewWindowRequesteddoes set args.Handled = true before calling RaiseOpenNewWindow, WebView2 also suppresses the default popup — so the click looks like a total no-op.
Subscribes NewWindowRequested, sets Handled, forwards to VM.
Phantom.Workspaces\ViewModels\WebViewModel.cs
RaiseOpenNewWindow — silently returns when tabService is null.
Design / Fix
Pass this (the MainWindowViewModel, which implements IWorkspaceTabService) as the tabService argument on every layout-restore-path WebViewModel construction in Phantom.Workspaces\ViewModels\MainWindowViewModel.cs. This is the sole change required to restore the reported scenario: it re-connects NewWindowRequested → RaiseOpenNewWindow → OpenTabAsync for restored browser tabs.
// MainWindowViewModel.cs:3513returnnewWebViewModel(browserDesc.Url,this,titleFixed:browserDesc.IsTitleExplicit){Id=tabId,Title=!string.IsNullOrEmpty(browserDesc.Title)?browserDesc.Title:browserDesc.Url,DockRegion="full",};// MainWindowViewModel.cs:3565returnnewWebViewModel(url.GetString()!,this){Id=ReadString(tab,"tab-id")??url.GetString()!,Title=ReadString(tab,"title")??url.GetString()!,DockRegion=ReadString(tab,"dock")??"full",};// MainWindowViewModel.cs:3720 (same shape as :3565)returnnewWebViewModel(url.GetString()!,this){Id=ReadString(tab,"tab-id")??url.GetString()!,Title=ReadString(tab,"title")??url.GetString()!,DockRegion=ReadString(tab,"dock")??"full",};
This restores the NewWindowRequested → OpenTabAsync chain for every restored tab, matching the behavior of freshly-opened browser tabs (which already pass tabService). No other change is required to fix the reported scenario.
Considered / Background (NOT required for this fix)
The following options were considered while diagnosing this bug and are preserved here for context. They are not required to resolve the reported scenario; Option A above is sufficient.
B. Harden WebViewModel.RaiseOpenNewWindow against a null tabService (defensive logging / fallback resolution). This would turn a future mis-wired construction into a logged warning rather than a silent no-op, e.g.:
publicasyncvoidRaiseOpenNewWindow(stringurl){varsvc=this.tabService;if(svcisnull){Trace.TraceWarning("WebViewModel.RaiseOpenNewWindow: tabService is null for tab {0}; new-window request for {1} dropped.",this.Id,url);return;}
...}
This is optional future robustness only and is not required for the reported scenario. It could be tracked separately if desired.
C. Audit WorkspaceDockFactory.CreateDocumentDock dock-Id behavior during restore (introduced by 2d330f54 / Web tabs in newly-created horizontal/vertical docks use wrong tab header template (no web icon, unconstrained width) #1307 — the override mints a fresh GuidId on every call, which was a suspected contributor because a mismatched dock identity during restore could interfere with OpenTabAsync's dock lookup). Investigation now considers this not the cause of the reported scenario: the missing-tabService construction in Option A fully explains the observed symptom (silent no-op on restored tabs), and RaiseOpenNewWindow early-returns before any dock lookup would occur. This audit is preserved as background regression-window context only.
Verify ConfiguredWebView.OnNewWindowRequested continues to set args.Handled = truebefore returning on any error path, so WebView2 does not spawn a background OS window when our new-tab creation fails.
Regression window
Worked before 2026-08-14; broke on/after 2026-08-14. The user-visible scenario is caused by the missing-tabService construction on the restore path (see Root Cause). Option A fixes this regardless of which commit made the pre-existing null-tabService construction newly reachable in typical usage.
Candidate commits from the regression window (background context only — a bisect is not required to apply the fix):
07579a53 — Fix #1310: single-source Ctrl+W handling + focus-aware CloseActiveTabCommand (2026-08-14 15:21). Removed the CloseTabRequested legacy typed event from AcceleratorAwareWebView.cs and unsubscribed from it in ConfiguredWebView.cs:97-99. The diff does not touch NewWindowRequested, but it is the largest churn on the WebView event-fan-out surface in the window.
2d330f54 — Fix #1307: split-created document docks are WorkspaceContentDock (2026-08-14 15:25). Overrides WorkspaceDockFactory.CreateDocumentDock to mint WorkspaceContentDock instances with a fresh GuidId on every call. See Option C above — no longer considered the cause of the reported scenario.
Expected Tests
Naming follows the existing WebViewModelTests / MainWindowIntegrationTests conventions (Subject_Scenario_ExpectedOutcome).
Restore a persisted BrowserDockTabDescriptor and assert the resulting WebViewModel has a non-null tabService (via reflection or an internal accessor). Directly validates Option A.
Restore a browser tab, invoke RaiseOpenNewWindow, and assert a new WorkspaceDocument appears in the pane's DocumentDock.VisibleDockables immediately right of the source.
End-to-end: simulate a _blank navigation on a restored tab and assert a new WebViewModel tab appears in the selected pane.
Reference existing tests: Phantom.Workspaces.Tests\WebViewModelTests.cs:552 RaiseOpenNewWindow_InsertsNewTabImmediatelyRightOfSourceTab (covers the fresh-tab path only) and Phantom.Workspaces.Tests\MainWindowIntegrationTests.cs:6406 OpenTabAsync_ExistingTab_PushesNavigationEntry.
Considered / Background tests (NOT required for this fix)
WebViewModel_RaiseOpenNewWindow_WhenTabServiceNull_LogsAndDoesNotDropRequest — validates the demoted Option B (defensive logging in RaiseOpenNewWindow when tabService is null). Would only be added if Option B is pursued as separate future work.
Embedded browser: "open in new window" no longer opens a new browser tab
Summary
In the embedded WebView2 browser, requesting a new window — via
target="_blank",window.open(), middle-click, or any path that firesCoreWebView2.NewWindowRequested— no longer opens a new browser tab in the workspace. The owner reports this regressed on/after 2026-08-14. Impact: users cannot open web links in additional tabs from within the embedded browser; the click either silently does nothing or spawns an external window (defeating the point of the in-app browser).Root Cause
The
NewWindowRequested→ new-tab pipeline is:Phantom.Workspaces\Controls\ConfiguredWebView.cs:89subscribesthis.NewWindowRequested += OnNewWindowRequested;ConfiguredWebView.OnNewWindowRequested(ConfiguredWebView.cs:193-231) reads theRequestURI via reflection, setsargs.Handled = true, then callsthis.ViewModel.RaiseOpenNewWindow(urlString).WebViewModel.RaiseOpenNewWindow(Phantom.Workspaces\ViewModels\WebViewModel.cs:222-238) is:Confirmed broken wiring site
The
WebViewModelinstances constructed on the layout-restore path are built without atabService, soRaiseOpenNewWindowearly-returns and no tab is created. All three restore call sites inPhantom.Workspaces\ViewModels\MainWindowViewModel.csomit thetabServiceargument:MainWindowViewModel.cs:3513(persistedBrowserDockTabDescriptor→ new tab):MainWindowViewModel.cs:3565(TryFetchWorkspaceTabAsyncURL fallback):MainWindowViewModel.cs:3720(CreateTabFromEntityAsyncURL fallback): same shape.Because
WebViewModel's constructor signature is(string initialUrl, IWorkspaceTabService? tabService = null, bool titleFixed = false, IUrlOpener? urlOpener = null), omitting the second argument leavestabService = null. As a result, every browser tab that was restored from a saved workspace layout (the vast majority of tabs the user sees on subsequent launches) silently dropsNewWindowRequested.Because
ConfiguredWebView.OnNewWindowRequesteddoes setargs.Handled = truebefore callingRaiseOpenNewWindow, WebView2 also suppresses the default popup — so the click looks like a total no-op.Affected Files
Phantom.Workspaces\ViewModels\MainWindowViewModel.csWebViewModelwithout passingtabService. Fix site.Phantom.Workspaces\Controls\ConfiguredWebView.csNewWindowRequested, setsHandled, forwards to VM.Phantom.Workspaces\ViewModels\WebViewModel.csRaiseOpenNewWindow— silently returns whentabServiceisnull.Design / Fix
Pass
this(theMainWindowViewModel, which implementsIWorkspaceTabService) as thetabServiceargument on every layout-restore-pathWebViewModelconstruction inPhantom.Workspaces\ViewModels\MainWindowViewModel.cs. This is the sole change required to restore the reported scenario: it re-connectsNewWindowRequested → RaiseOpenNewWindow → OpenTabAsyncfor restored browser tabs.This restores the
NewWindowRequested→OpenTabAsyncchain for every restored tab, matching the behavior of freshly-opened browser tabs (which already passtabService). No other change is required to fix the reported scenario.Considered / Background (NOT required for this fix)
The following options were considered while diagnosing this bug and are preserved here for context. They are not required to resolve the reported scenario; Option A above is sufficient.
B. Harden
WebViewModel.RaiseOpenNewWindowagainst a nulltabService(defensive logging / fallback resolution). This would turn a future mis-wired construction into a logged warning rather than a silent no-op, e.g.:This is optional future robustness only and is not required for the reported scenario. It could be tracked separately if desired.
C. Audit
WorkspaceDockFactory.CreateDocumentDockdock-Idbehavior during restore (introduced by2d330f54/ Web tabs in newly-created horizontal/vertical docks use wrong tab header template (no web icon, unconstrained width) #1307 — the override mints a freshGuidIdon every call, which was a suspected contributor because a mismatched dock identity during restore could interfere withOpenTabAsync's dock lookup). Investigation now considers this not the cause of the reported scenario: the missing-tabServiceconstruction in Option A fully explains the observed symptom (silent no-op on restored tabs), andRaiseOpenNewWindowearly-returns before any dock lookup would occur. This audit is preserved as background regression-window context only.Verify
ConfiguredWebView.OnNewWindowRequestedcontinues to setargs.Handled = truebefore returning on any error path, so WebView2 does not spawn a background OS window when our new-tab creation fails.Regression window
Worked before 2026-08-14; broke on/after 2026-08-14. The user-visible scenario is caused by the missing-
tabServiceconstruction on the restore path (see Root Cause). Option A fixes this regardless of which commit made the pre-existing null-tabServiceconstruction newly reachable in typical usage.Candidate commits from the regression window (background context only — a bisect is not required to apply the fix):
07579a53—Fix #1310: single-source Ctrl+W handling + focus-aware CloseActiveTabCommand(2026-08-14 15:21). Removed theCloseTabRequestedlegacy typed event fromAcceleratorAwareWebView.csand unsubscribed from it inConfiguredWebView.cs:97-99. The diff does not touchNewWindowRequested, but it is the largest churn on the WebView event-fan-out surface in the window.2d330f54—Fix #1307: split-created document docks are WorkspaceContentDock(2026-08-14 15:25). OverridesWorkspaceDockFactory.CreateDocumentDockto mintWorkspaceContentDockinstances with a freshGuidIdon every call. See Option C above — no longer considered the cause of the reported scenario.Expected Tests
Naming follows the existing
WebViewModelTests/MainWindowIntegrationTestsconventions (Subject_Scenario_ExpectedOutcome).MainWindowViewModel_RestoredBrowserTab_HasTabServiceWiredBrowserDockTabDescriptorand assert the resultingWebViewModelhas a non-nulltabService(via reflection or an internal accessor). Directly validates Option A.MainWindowViewModel_RestoredBrowserTab_RaiseOpenNewWindow_InsertsNewTabInDockRaiseOpenNewWindow, and assert a newWorkspaceDocumentappears in the pane'sDocumentDock.VisibleDockablesimmediately right of the source.ConfiguredWebView_OnNewWindowRequested_WhenHandlerRuns_SetsArgsHandledTrueMainWindowIntegration_BlankTargetNavigation_OpensNewWebTabInSameWorkspacePane_blanknavigation on a restored tab and assert a newWebViewModeltab appears in the selected pane.Reference existing tests:
Phantom.Workspaces.Tests\WebViewModelTests.cs:552 RaiseOpenNewWindow_InsertsNewTabImmediatelyRightOfSourceTab(covers the fresh-tab path only) andPhantom.Workspaces.Tests\MainWindowIntegrationTests.cs:6406 OpenTabAsync_ExistingTab_PushesNavigationEntry.Considered / Background tests (NOT required for this fix)
WebViewModel_RaiseOpenNewWindow_WhenTabServiceNull_LogsAndDoesNotDropRequest— validates the demoted Option B (defensive logging inRaiseOpenNewWindowwhentabServiceis null). Would only be added if Option B is pursued as separate future work.