using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Interop; using System.Windows.Threading; using Microsoft.Web.WebView2.Core; using FoxBrowser.Core; using FoxBrowser.Native; using FoxBrowser.UI; namespace FoxBrowser; public partial class MainWindow : Window { private TabManager? _tabManager; private TrayIconManager? _tray; private bool _allowClose; private readonly DispatcherTimer _watchdogTimer = new() { Interval = TimeSpan.FromSeconds(30) }; private AudioSessionLabeler? _audioLabeler; public MainWindow() { InitializeComponent(); try { var icoPath = Path.Combine(AppContext.BaseDirectory, "FoxBrowser.ico"); if (File.Exists(icoPath)) Icon = new System.Windows.Media.Imaging.BitmapImage(new Uri(icoPath, UriKind.Absolute)); } catch { } SourceInitialized += (_, _) => ApplyDarkTitleBar(true); Loaded += MainWindow_Loaded; } private async void MainWindow_Loaded(object sender, RoutedEventArgs e) { try { _tray = new TrayIconManager(this, ExitApplication); var environmentFactory = new WebView2EnvironmentFactory(); var recoveryMode = false; CoreWebView2Environment environment; try { environment = await environmentFactory.CreateAsync(); } catch (Exception ex) when ((uint)ex.HResult == 0x8007139Fu) { // Another FoxBrowser/WebView2 process may hold the persistent profile lock. // Use a disposable recovery profile so the browser can still start. recoveryMode = true; var recoveryData = Path.Combine( Path.GetTempPath(), $"FoxBrowser_Recovery_{Environment.ProcessId}_{Guid.NewGuid():N}"); Directory.CreateDirectory(recoveryData); environment = await environmentFactory.CreateAsync(recoveryData); } var startupNotice = environmentFactory.TakeStartupNotice(); if (startupNotice is not null) System.Windows.MessageBox.Show(this, startupNotice, "FoxBrowser", MessageBoxButton.OK, MessageBoxImage.Information); _tabManager = new TabManager(WebViewHost, environment, !recoveryMode); _tabManager.ActiveTabStateChanged += (_, _) => Dispatcher.Invoke(RefreshAddressBar); DataContext = _tabManager; await _tabManager.InitializeAsync(); var iconPath = Path.Combine(AppContext.BaseDirectory, "FoxBrowser.ico"); try { _audioLabeler = new AudioSessionLabeler((uint)Process.GetCurrentProcess().Id, iconPath); } catch { // Audio labeling is optional; a missing endpoint must not break browsing. _audioLabeler = null; } PinnedExtensionStrip.ItemsSource = _tabManager.ExtensionManager?.PinnedExtensions; _watchdogTimer.Tick += async (_, _) => await _tabManager.SoftReloadIfNeededAsync(); _watchdogTimer.Start(); BrowserStatus.Visibility = Visibility.Collapsed; RefreshAddressBar(); } catch (Exception ex) { BrowserStatus.Text = $"WebView2 не запустився: {ex.Message}"; BrowserStatus.Visibility = Visibility.Visible; } } private async void NewTab_Click(object sender, RoutedEventArgs e) { if (_tabManager is not null) await _tabManager.AddTabAsync(); RefreshAddressBar(); } private async void AddressBox_KeyDown(object sender, System.Windows.Input.KeyEventArgs e) { if (e.Key != Key.Enter) return; await NavigateFromAddressBarAsync(); Keyboard.ClearFocus(); } private async void Go_Click(object sender, RoutedEventArgs e) => await NavigateFromAddressBarAsync(); private async void AudioOnly_Click(object sender, RoutedEventArgs e) { if (_tabManager is null) return; await _tabManager.ToggleAudioOnlyAsync(); TabStrip.Items.Refresh(); } private async Task NavigateFromAddressBarAsync() { if (_tabManager?.ActiveTab is null) return; await _tabManager.NavigateAsync(_tabManager.ActiveTab, AddressBox.Text); AddressBox.Text = _tabManager.ActiveTab.Address; } private void Back_Click(object sender, RoutedEventArgs e) => _tabManager?.GoBack(); private void Forward_Click(object sender, RoutedEventArgs e) => _tabManager?.GoForward(); private void Reload_Click(object sender, RoutedEventArgs e) => _tabManager?.Reload(); private void Extensions_Click(object sender, RoutedEventArgs e) { if (_tabManager?.ExtensionManager is null) return; var window = new ExtensionsSettingsWindow( _tabManager.ExtensionManager, _tabManager.Environment, () => _tabManager.OpenUrlAsync("https://chromewebstore.google.com/category/extensions"), _tabManager.ExtensionsAvailable) { Owner = this }; window.ShowDialog(); } private void PinnedExtension_Click(object sender, RoutedEventArgs e) { if (_tabManager?.ExtensionManager is null || sender is not System.Windows.Controls.Button button || button.DataContext is not PinnedExtensionItem item) return; if (string.IsNullOrWhiteSpace(item.PopupPage)) { Extensions_Click(sender, e); return; } var screenPositionWpf = button.PointToScreen(new System.Windows.Point(0, button.ActualHeight)); var screenPosition = new System.Drawing.Point((int)screenPositionWpf.X, (int)screenPositionWpf.Y); var popup = new ExtensionPopupWindow(_tabManager.Environment, item.Extension.Id, item.PopupPage, screenPosition) { Owner = this }; popup.Show(); } private async void TabStrip_SelectionChanged(object sender, SelectionChangedEventArgs e) { if (e.Source != TabStrip || TabStrip.SelectedItem is not BrowserTab tab || _tabManager is null) return; AddressBox.Text = tab.Address; await _tabManager.SwitchToTabAsync(tab); RefreshAddressBar(); } private void CloseTab_Click(object sender, RoutedEventArgs e) { if ((sender as System.Windows.Controls.Button)?.Tag is BrowserTab tab) _tabManager?.CloseTab(tab); RefreshAddressBar(); } private async void Window_StateChanged(object? sender, EventArgs e) { if (_tabManager is null) return; var minimized = WindowState == WindowState.Minimized; if (minimized) _tray?.MinimizeToTray(); await _tabManager.SetMinimizedModeAsync(minimized); } private async void Window_Closing(object? sender, CancelEventArgs e) { if (!_allowClose) { e.Cancel = true; _tray?.MinimizeToTray(); if (_tabManager is not null) await _tabManager.SetMinimizedModeAsync(true); } else { _watchdogTimer.Stop(); _tray?.Dispose(); _audioLabeler?.Dispose(); if (_tabManager is not null) await _tabManager.DisposeAsync(); } } private void ExitApplication() { _allowClose = true; Close(); } private void ApplyDarkTitleBar(bool enabled) { var hwnd = new WindowInteropHelper(this).Handle; Win32Interop.ApplyDarkTitleBar(hwnd, enabled); } private void RefreshAddressBar() { if (_tabManager?.ActiveTab is not null) { if (!ReferenceEquals(TabStrip.SelectedItem, _tabManager.ActiveTab)) TabStrip.SelectedItem = _tabManager.ActiveTab; AddressBox.Text = _tabManager.ActiveTab.Address; } } }