Functions/GenXdev.Webbrowser/Open-Webbrowser.ps1
############################################################################### <# .SYNOPSIS Opens URLs in one or more browser windows with optional positioning and styling. .DESCRIPTION This function provides an advanced wrapper around browser launching with extensive options for window positioning, browser selection, and behavior customization. It supports multiple browsers including Edge, Chrome, and Firefox with features like private browsing, application mode, and precise window management. Key features: - Smart browser detection and selection - Window positioning (left, right, top, bottom, centered, fullscreen) - Multi-monitor support with automatic or manual monitor selection - Private/incognito browsing mode support - Application mode for distraction-free browsing - Extension and popup blocking options - Focus management and window manipulation - Batch URL opening across multiple browsers - Keystroke automation to browser windows The function can automatically detect system capabilities and adjust behavior accordingly. For browsers not installed on the system, operations are silently skipped without errors. .PARAMETER Url The URLs to open in the browser. Accepts pipeline input and automatically handles file paths (converts to file:// URLs). When no URL is provided, opens the default GenXdev PowerShell help page. .PARAMETER Monitor The monitor to use for window placement: - 0 = Primary monitor - -1 = Discard positioning - -2 = Configured secondary monitor (uses $Global:DefaultSecondaryMonitor or defaults to monitor 2) - 1+ = Specific monitor number .PARAMETER Width The initial width of the browser window in pixels. When not specified, uses the monitor's working area width or half-width for side positioning. .PARAMETER Height The initial height of the browser window in pixels. When not specified, uses the monitor's working area height or half-height for top/bottom positioning. .PARAMETER X The initial X coordinate for window placement. When not specified, uses the monitor's left edge. Can be specified relative to the selected monitor. .PARAMETER Y The initial Y coordinate for window placement. When not specified, uses the monitor's top edge. Can be specified relative to the selected monitor. .PARAMETER AcceptLang Sets the browser's Accept-Language HTTP header for internationalization. Useful for testing websites in different languages. .PARAMETER Force Forces enabling of the debugging port by stopping existing browser instances if needed. Useful when browser debugging features are required. .PARAMETER Edge Specifically opens URLs in Microsoft Edge browser. .PARAMETER Chrome Specifically opens URLs in Google Chrome browser. .PARAMETER Chromium Opens URLs in either Microsoft Edge or Google Chrome, depending on which is set as the default browser. Prefers Chromium-based browsers. .PARAMETER Firefox Specifically opens URLs in Mozilla Firefox browser. .PARAMETER All Opens the specified URLs in all installed modern browsers simultaneously. .PARAMETER Left Positions the browser window on the left half of the screen. .PARAMETER Right Positions the browser window on the right half of the screen. .PARAMETER Top Positions the browser window on the top half of the screen. .PARAMETER Bottom Positions the browser window on the bottom half of the screen. .PARAMETER Centered Centers the browser window on the screen using 80% of the screen dimensions. .PARAMETER FullScreen Opens the browser in fullscreen mode using F11 key simulation. .PARAMETER Private Opens the browser in private/incognito browsing mode. Uses InPrivate for Edge and incognito for Chrome. Not supported for the default browser mode. .PARAMETER ApplicationMode Hides browser controls for a distraction-free experience. Creates an app-like interface for web applications. .PARAMETER NoBrowserExtensions Prevents loading of browser extensions. Uses safe mode for Firefox and --disable-extensions for Chromium browsers. .PARAMETER DisablePopupBlocker Disables the browser's popup blocking functionality. .PARAMETER NewWindow Forces creation of a new browser window instead of reusing existing windows. .PARAMETER FocusWindow Gives focus to the browser window after opening. .PARAMETER SetForeground Brings the browser window to the foreground after opening. .PARAMETER Maximize Maximizes the browser window after positioning. .PARAMETER PassThru Returns PowerShell objects representing the browser processes created. .PARAMETER NoBorders Removes the borders of the browser window. .PARAMETER RestoreFocus Returns focus to the PowerShell window after opening the browser. Useful for automated workflows where you want to continue working in PowerShell. .PARAMETER SideBySide Position browser window either fullscreen on different monitor than PowerShell, or side by side with PowerShell on the same monitor. .PARAMETER KeysToSend Keystrokes to send to the browser window after opening. Uses the same format as the GenXdev.Windows\Send-Key cmdlet. .PARAMETER SendKeyEscape Escapes control characters when sending keystrokes to the browser. .PARAMETER SendKeyHoldKeyboardFocus Prevents returning keyboard focus to PowerShell after sending keystrokes. .PARAMETER SendKeyUseShiftEnter Uses Shift+Enter instead of regular Enter for line breaks when sending keys. .PARAMETER SendKeyDelayMilliSeconds Delay between sending different key sequences in milliseconds. .PARAMETER SessionOnly Use alternative settings stored in session for AI preferences. .PARAMETER ClearSession Clear alternative settings stored in session for AI preferences. .PARAMETER SkipSession Store settings only in persistent preferences without affecting session. .EXAMPLE Open-Webbrowser -Url "https://github.com" Opens GitHub in the default browser. .EXAMPLE Open-Webbrowser -Url "https://stackoverflow.com" -Monitor 1 -Left Opens Stack Overflow in the left half of monitor 1. .EXAMPLE wb "https://google.com" -m 0 -fs Opens Google in fullscreen mode on the primary monitor using aliases. .EXAMPLE Open-Webbrowser -Chrome -Private -NewWindow Opens a new Chrome window in incognito mode. .EXAMPLE "https://github.com", "https://stackoverflow.com" | Open-Webbrowser -All Opens multiple URLs in all installed browsers via pipeline. .EXAMPLE Open-Webbrowser -Monitor 0 -Right Re-positions an already open browser window to the right side of the primary monitor. .EXAMPLE Open-Webbrowser -ApplicationMode -Url "https://app.example.com" Opens a web application in app mode without browser controls. .NOTES Requires Windows 10+ Operating System. This cmdlet is designed for interactive use and performs window manipulation tricks including Alt-Tab keystrokes. Avoid touching keyboard/mouse during positioning operations. For fast launches of multiple URLs: - Set Monitor to -1 - Avoid using positioning switches (-X, -Y, -Left, -Right, -Top, -Bottom, -RestoreFocus) For browsers not installed on the system, operations are silently skipped. #> ############################################################################### function Open-Webbrowser { [CmdletBinding()] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidGlobalVars', '')] [Alias('wb')] param( ####################################################################### [Parameter( Mandatory = $false, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $false, HelpMessage = 'The URLs to open in the browser' )] [Alias('Value', 'Uri', 'FullName', 'Website', 'WebsiteUrl')] [string[]] $Url, ######################################################################## [Parameter( Mandatory = $false, Position = 1, HelpMessage = ('The monitor to use, 0 = default, -1 is discard, ' + '-2 = Configured secondary monitor, defaults to ' + "`$Global:DefaultSecondaryMonitor or 2 if not found") )] [Alias('m', 'mon')] [int] $Monitor = -2, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = 'The initial width of the webbrowser window' )] [int] $Width = -1, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = 'The initial height of the webbrowser window' )] [int] $Height = -1, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = 'The initial X position of the webbrowser window' )] [int] $X = -999999, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = 'The initial Y position of the webbrowser window' )] [int] $Y = -999999, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = 'Set the browser accept-lang http header' )] [Alias('lang', 'locale')] [string] $AcceptLang = $null, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = 'Force enable debugging port, stopping existing browsers if needed' )] [switch] $Force, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = 'Opens in Microsoft Edge' )] [Alias('e')] [switch] $Edge, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = 'Opens in Google Chrome' )] [Alias('ch')] [switch] $Chrome, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = ('Opens in Microsoft Edge or Google Chrome, ' + 'depending on what the default browser is') )] [Alias('c')] [switch] $Chromium, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = 'Opens in Firefox' )] [Alias('ff')] [switch] $Firefox, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = 'Opens in all registered modern browsers' )] [switch] $All, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = 'Place browser window on the left side of the screen' )] [switch] $Left, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = 'Place browser window on the right side of the screen' )] [switch] $Right, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = 'Place browser window on the top side of the screen' )] [switch] $Top, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = 'Place browser window on the bottom side of the screen' )] [switch] $Bottom, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = 'Place browser window in the center of the screen' )] [switch] $Centered, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = 'Opens in fullscreen mode' )] [Alias('fs', 'f')] [switch] $FullScreen, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = 'Opens in incognito/private browsing mode' )] [Alias('incognito', 'inprivate')] [switch] $Private, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = 'Hide the browser controls' )] [Alias('a', 'app', 'appmode')] [switch] $ApplicationMode, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = 'Prevent loading of browser extensions' )] [Alias('de', 'ne', 'NoExtensions')] [switch] $NoBrowserExtensions, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = 'Disable the popup blocker' )] [Alias('allowpopups')] [switch] $DisablePopupBlocker, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = ("Don't re-use existing browser window, instead, " + 'create a new one') )] [Alias('nw', 'new')] [switch] $NewWindow, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = 'Focus the browser window after opening' )] [Alias('fw','focus')] [switch] $FocusWindow, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = 'Set the browser window to foreground after opening' )] [Alias('fg')] [switch] $SetForeground, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = 'Maximize the window after positioning' )] [switch] $Maximize, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = ('Returns a PowerShell object of ' + 'the browserprocess') )] [Alias('pt')] [switch]$PassThru, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = 'Removes the borders of the window' )] [Alias('nb')] [switch] $NoBorders, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = 'Restore PowerShell window focus' )] [Alias('rf', 'bg')] [switch] $RestoreFocus, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = 'Position browser window either fullscreen on different monitor than PowerShell, or side by side with PowerShell on the same monitor.' )] [Alias('sbs')] [switch] $SideBySide, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = ('Keystrokes to send to the Window, ' + 'see documentation for cmdlet GenXdev.Windows\Send-Key') )] [string[]] $KeysToSend, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = 'Escape control characters and modifiers when sending keys' )] [Alias('Escape')] [switch] $SendKeyEscape, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = 'Hold keyboard focus on target window when sending keys' )] [Alias('HoldKeyboardFocus')] [switch] $SendKeyHoldKeyboardFocus, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = 'Use Shift+Enter instead of Enter when sending keys' )] [Alias('UseShiftEnter')] [switch] $SendKeyUseShiftEnter, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = ('Delay between different input strings in ' + 'milliseconds when sending keys') )] [Alias('DelayMilliSeconds')] [int] $SendKeyDelayMilliSeconds, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = ('Use alternative settings stored in session for AI ' + 'preferences') )] [switch] $SessionOnly, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = ('Clear alternative settings stored in session for AI ' + 'preferences') )] [switch] $ClearSession, ####################################################################### [Parameter( Mandatory = $false, HelpMessage = ('Store settings only in persistent preferences without ' + 'affecting session') )] [Alias('FromPreferences')] [switch] $SkipSession #######################################################################ɧ ) begin { # force new window creation if keystrokes need to be sent to browser if ($KeysToSend -and ($KeysToSend.Count -gt 0)) { $NewWindow = $true } # store original parameters for later use in key sending operations $wbParams = $PSBoundParameters # get all available screens/monitors on the system $allScreens = @([WpfScreenHelper.Screen]::AllScreens | Microsoft.PowerShell.Core\ForEach-Object { $PSItem }) # output diagnostic information about the function call Microsoft.PowerShell.Utility\Write-Verbose ('Open-Webbrowser ' + "monitor = $Monitor, urls=$($Url | Microsoft.PowerShell.Utility\ConvertTo-Json)") # track if url parameter was explicitly provided by user [bool] $urlSpecified = $true # check if no url was specified by the user if (($null -eq $Url) -or ($Url.Length -lt 1)) { $urlSpecified = $false # show the default help page from github when no url provided $Url = @('https://powershell.genxdev.net/') } else { # process and normalize each url provided $Url = $($Url | Microsoft.PowerShell.Core\ForEach-Object { # clean up url by trimming quotes and spaces $newUrl = $PSItem.Trim(' "'''.ToCharArray()) $filePath = $newUrl try { # try to expand the path in case it's a relative file path $filePath = (GenXdev.FileSystem\Expand-Path $newUrl) } catch { # ignore expansion errors for urls that aren't file paths } # check if the url refers to an existing local file if ([System.IO.File]::Exists($filePath)) { # convert local file path to file:// url format $newUrl = ('file://' + [Uri]::EscapeUriString($filePath.Replace('\', '/'))) } $newUrl } ) } # get reference to the powershell main window for focus restoration $powerShellWindow = GenXdev.Windows\Get-PowershellMainWindow # retrieve list of all available/installed modern webbrowsers $browsers = GenXdev.Webbrowser\Get-Webbrowser # get the system's configured default webbrowser $defaultBrowser = GenXdev.Webbrowser\Get-DefaultWebbrowser # set primary monitor as the initial screen reference $screen = [WpfScreenHelper.Screen]::PrimaryScreen $allScreens = @([WpfScreenHelper.Screen]::AllScreens | Microsoft.PowerShell.Core\ForEach-Object { $PSItem }) # determine which monitor to use based on monitor parameter if ($Monitor -eq 0) { Microsoft.PowerShell.Utility\Write-Verbose ('Choosing primary ' + 'monitor, because default monitor requested using -Monitor 0') } else { # check if secondary monitor was requested and global variable is set if ($Monitor -eq -2 -and $Global:DefaultSecondaryMonitor -is [int] -and $Global:DefaultSecondaryMonitor -ge 0) { Microsoft.PowerShell.Utility\Write-Verbose ('Picking monitor ' + "$((($Global:DefaultSecondaryMonitor-1) % $allScreens.Length)) " + 'as secondary (requested with -monitor -2) set by ' + "`$Global:DefaultSecondaryMonitor") $screen = $allScreens[($Global:DefaultSecondaryMonitor - 1) % $allScreens.Length] } elseif ($Monitor -eq -2 -and (-not ($Global:DefaultSecondaryMonitor -is [int] -and $Global:DefaultSecondaryMonitor -ge 0)) -and ((GenXdev.Windows\Get-MonitorCount) -gt 1)) { Microsoft.PowerShell.Utility\Write-Verbose (('Picking monitor ' + '#1 as default secondary (requested with -monitor -2), ' + "because `$Global:DefaultSecondaryMonitor not set")) $screen = $allScreens[1] } # check if specific monitor number was requested elseif ($Monitor -ge 1) { Microsoft.PowerShell.Utility\Write-Verbose ('Picking monitor ' + "#$(($Monitor - 1) % $allScreens.Length) as requested by " + 'the -Monitor parameter') $screen = $allScreens[($Monitor - 1) % $allScreens.Length] } else { try { Microsoft.PowerShell.Utility\Write-Verbose ('Picking monitor ' + '#1 (same as PowerShell), because no monitor specified') $screen = [WpfScreenHelper.Screen]::FromPoint(@{ X = $powerShellWindow[0].Position().X Y = $powerShellWindow[0].Position().Y }) } catch { $screen = [WpfScreenHelper.Screen]::PrimaryScreen } } } # determine if any window positioning parameters were provided [bool] $havePositioning = (($Monitor -ge 0 -or $Monitor -eq -2) -or ($Left -or $Right -or $Top -or $Bottom -or $Centered -or (($X -is [int]) -and ($X -gt -999999)) -or (($Y -is [int]) -and ($Y -gt -999999)))) -and -not $FullScreen if ($havePositioning -and $Monitor -eq -1) { $Monitor = 0 } # initialize window x position based on parameters or screen defaults if (($X -le -999999) -or ($X -isnot [int])) { $X = $screen.WorkingArea.X } else { # adjust x position relative to selected monitor if monitor specified if ($Monitor -ge 0) { $X = $screen.WorkingArea.X + $X } } # initialize window y position based on parameters or screen defaults if (($Y -le -999999) -or ($Y -isnot [int])) { $Y = $screen.WorkingArea.Y } else { # adjust y position relative to selected monitor if monitor specified if ($Monitor -ge 0) { $Y = $screen.WorkingArea.Y + $Y } } # create state object to track browser window positioning and processes $state = @{ existingWindow = $false hadVisibleBrowser = $false Browser = $null IsDefaultBrowser = ((-not $All) -and ((-not $Chromium) -or ($defaultBrowser.Name -like '*chrome*') -or ($defaultBrowser.Name -like '*edge*')) -and ((-not $Chrome) -or ($defaultBrowser.Name -like '*chrome*')) -and ((-not $Edge) -or ($defaultBrowser.Name -like '*edge*')) -and ((-not $Firefox) -or ($defaultBrowser.Name -like '*firefox*'))) FirstProcess = $null PositioningDone = $false BrowserWindow = $null } # determine if we can use simple start-process instead of complex positioning $useStartProcess = (-not ($havePositioning -or $FullScreen)) -and $state.IsDefaultBrowser -and ($Monitor -eq -1) -and (-not $NewWindow) # configure window dimensions and positioning if positioning is required if ($havePositioning -or $FullScreen) { # check if width parameter was explicitly provided $widthProvided = ($Width -gt 0) -and ($Width -is [int]) # check if height parameter was explicitly provided $heightProvided = ($Height -gt 0) -and ($Height -is [int]) # set default width if not provided by user if ($widthProvided -eq $false) { $Width = $screen.WorkingArea.Width } # set default height if not provided by user if ($heightProvided -eq $false) { $Height = $screen.WorkingArea.Height } # configure window position and size for left side placement if ($Left -eq $true) { $X = $screen.WorkingArea.X # use half screen width if width not explicitly provided if ($widthProvided -eq $false) { $Width = [Math]::Min($screen.WorkingArea.Width / 2, $Width) } # use full screen height if height not explicitly provided if ($heightProvided -eq $false) { $Height = [Math]::Min($screen.WorkingArea.Height, $Height) } $Y = $screen.WorkingArea.Y } # configure window position and size for right side placement if ($Right -eq $true) { # use half screen width if width not explicitly provided if ($widthProvided -eq $false) { $Width = [Math]::Min($screen.WorkingArea.Width / 2, $Width) } # use full screen height if height not explicitly provided if ($heightProvided -eq $false) { $Height = [Math]::Min($screen.WorkingArea.Height, $Height) } # position window on right side of screen $X = $screen.WorkingArea.X + $screen.WorkingArea.Width - $Width $Y = $screen.WorkingArea.Y } # configure window position and size for top placement if ($Top -eq $true) { $Y = $screen.WorkingArea.Y # use half screen height if height not explicitly provided if ($heightProvided -eq $false) { $Height = [Math]::Min($screen.WorkingArea.Height / 2, $Height) } $Width = $screen.WorkingArea.Width $X = $screen.WorkingArea.X } # configure window position and size for bottom placement if ($Bottom -eq $true) { # use half screen height if height not explicitly provided if ($heightProvided -eq $false) { $Height = [Math]::Min($screen.WorkingArea.Height / 2, $Height) } $Width = $screen.WorkingArea.Width # position window at bottom of screen $Y = $screen.WorkingArea.Y + $screen.WorkingArea.Height - $Height $X = $screen.WorkingArea.X } # configure window position and size for centered placement if ($Centered -eq $true) { # use 80% of screen height if height not explicitly provided if ($heightProvided -eq $false) { $Height = [Math]::Round([Math]::Min( $screen.WorkingArea.Height * 0.8, $Height), 0) } # use 80% of screen width if width not explicitly provided if ($widthProvided -eq $false) { $Width = [Math]::Round([Math]::Min( $screen.WorkingArea.Width * 0.8, $Width), 0) } # center window on screen $X = ($screen.WorkingArea.X + [Math]::Round(($screen.WorkingArea.Width - $Width) / 2, 0)) $Y = ($screen.WorkingArea.Y + [Math]::Round(($screen.WorkingArea.Height - $Height) / 2, 0)) } } } ######################################################################## process { #################################################################### <# .SYNOPSIS Ensures minimum delay between browser window close and open operations. .DESCRIPTION This helper function prevents timing issues when repositioning browser windows by enforcing a minimum delay since the last browser close. .PARAMETER browser The browser object to check timing delays for. #> function enforceMinimumDelays($browser) { # skip delay enforcement if no positioning is required if ($havePositioning -eq $false) { return } # get the last close time for this specific browser $last = (Microsoft.PowerShell.Utility\Get-Variable -Scope Global ` -Name "_LastClose$($browser.Name)" -ErrorAction SilentlyContinue) # check if we have a valid last close timestamp if (($null -ne $last) -and ($last.Value -is [DateTime])) { $now = [DateTime]::UtcNow # enforce minimum 1 second delay since last close if ($now - $last.Value -lt [System.TimeSpan]::FromSeconds(1)) { Microsoft.PowerShell.Utility\Start-Sleep -Milliseconds 200 } } } #################################################################### <# .SYNOPSIS Constructs browser-specific command line arguments. .DESCRIPTION Builds the appropriate command line argument list based on the browser type and user-specified parameters for launching the browser process. .PARAMETER browser The browser object containing executable path and type information. .PARAMETER currentUrl The URL to open in the browser. .PARAMETER state The state object tracking browser window positioning and process info. #> function constructArgumentList($browser, $currentUrl, $state) { # initialize empty argument list for browser command line $argumentList = @() ############################################################# # handle firefox-specific command line arguments if ($browser.Name -like '*Firefox*') { # set default firefox command line parameters $argumentList = @() # add window size parameters if both width and height specified if (($Width -is [int]) -and ($Width -gt 0) -and ($Height -is [int]) -and ($Height -gt 0)) { $argumentList = $argumentList + @('-width', $Width, '-height', $Height) } # set foreground mode unless restore focus is requested if ($RestoreFocus -ne $true) { # set firefox to foreground on startup $argumentList = $argumentList + @('-foreground') } # disable browser extensions if requested if ($NoBrowserExtensions -eq $true) { $argumentList = $argumentList + @('-safe-mode') } # disable popup blocker if requested if ($DisablePopupBlocker -eq $true) { $argumentList = $argumentList + @('-disable-popup-blocking') } # set accept language header if provided if ($null -ne $AcceptLang) { $argumentList = $argumentList + @('--lang', $AcceptLang) } # handle private browsing mode for firefox if ($Private -eq $true) { # open url in firefox private window $argumentList = $argumentList + @('-private-window', $currentUrl) } else { # handle application mode for firefox if ($ApplicationMode -eq $true) { Microsoft.PowerShell.Utility\Write-Warning ('Firefox ' + 'does not support -ApplicationMode at this time') GenXdev.Webbrowser\Approve-FirefoxDebugging # use single site browser mode for firefox app mode $argumentList = $argumentList + @('--ssb', $currentUrl) } else { # handle new window creation for firefox if ((-not $state.PositioningDone) -and ($NewWindow -eq $true)) { # create new firefox window with url $argumentList = $argumentList + @('--new-window', $currentUrl) } else { # open url in existing or new firefox tab $argumentList = $argumentList + @('-url', $currentUrl) } } } } else { ########################################################## # handle chromium-based browsers (edge and chrome) if ($browser.Name -like '*Edge*' -or $browser.Name -like '*Chrome*') { # get the appropriate debugging port for this browser type $port = GenXdev.Webbrowser\Get-ChromiumRemoteDebuggingPort ` -Chrome:$Chrome -Edge:$Edge # set default chromium command line parameters # reference: https://peter.sh/experiments/chromium-command-line-switches/ $argumentList = $argumentList + @( '--disable-infobars', '--hide-crash-restore-bubble', '--no-first-run', '--disable-session-crashed-bubble', '--disable-crash-reporter', '--no-default-browser-check', '--disable-restore-tabs', '--remote-allow-origins=*', "--remote-debugging-port=$port" ) # add window size if both dimensions are specified if (($Width -is [int]) -and ($Width -gt 0) -and ($Height -is [int]) -and ($Height -gt 0)) { $argumentList = $argumentList + @("--window-size=$Width,$Height") } # set initial window position $argumentList = $argumentList + @("--window-position=$X,$Y") # disable browser extensions if requested if ($NoBrowserExtensions -eq $true) { $argumentList = $argumentList + @('--disable-extensions') } # disable popup blocker if requested if ($DisablePopupBlocker -eq $true) { $argumentList = $argumentList + @('--disable-popup-blocking') } # set accept language header if provided if ($null -ne $AcceptLang) { $argumentList = $argumentList + @("--accept-lang=$AcceptLang") } # handle private browsing mode for chromium browsers if ($Private -eq $true) { # force new window for private mode $NewWindow = $true # set appropriate private browsing flag if ($browser.Name -like '*Edge*') { # use edge inprivate mode $argumentList = $argumentList + @('-InPrivate') } else { # use chrome incognito mode $argumentList = $argumentList + @('--incognito') } } # force new window creation if requested and not positioned yet if ((-not $state.PositioningDone) -and ($NewWindow -eq $true)) { # force creation of new browser window $argumentList = $argumentList + @('--new-window') + @('--force-launch-browser') } # set window to start maximized by default $argumentList = $argumentList + @('--start-maximized') # handle application mode for chromium browsers if ($ApplicationMode -eq $true) { # run browser in application mode with specific url $argumentList = $argumentList + @("--app=$currentUrl") } else { # add url to standard command line arguments $argumentList = $argumentList + @($currentUrl) } } else { ###################################################### # handle default/other browsers if ($Private -eq $true) { # private mode not supported for default browser return } # add url as only argument for default browser $argumentList = @($currentUrl) } } $argumentList } #################################################################### <# .SYNOPSIS Finds and returns the browser process and main window. .DESCRIPTION Locates the browser process after launch and gets a reference to its main window handle for positioning and management operations. .PARAMETER browser The browser object containing executable information. .PARAMETER process The initial process object from browser launch. .PARAMETER state The state object tracking browser window and process information. #> function findProcess($browser, $process, $state) { # initialize window tracking variables $state.existingWindow = $false $window = @() # retry loop to find the browser process and window do { try { # wait briefly for process to initialize $null = [System.Threading.Thread]::Sleep(100) # find the most recent browser process with main window $processesNew = @(Microsoft.PowerShell.Management\Get-Process ` ([IO.Path]::GetFileNameWithoutExtension($browser.Path)) ` -ErrorAction SilentlyContinue | Microsoft.PowerShell.Core\Where-Object -Property Path ` -EQ $browser.Path | Microsoft.PowerShell.Core\Where-Object -Property MainWindowHandle ` -NE 0 | Microsoft.PowerShell.Utility\Sort-Object ` { $PSItem.StartTime } -Descending | Microsoft.PowerShell.Utility\Select-Object -First 1) # check if no process was found if (($processesNew.Length -eq 0) -or ($null -eq $processesNew[0])) { Microsoft.PowerShell.Utility\Write-Verbose ('No process ' + 'found, retrying..') $window = @() $null = [System.Threading.Thread]::Sleep(80) } else { Microsoft.PowerShell.Utility\Write-Verbose 'Found new process' # get window helper utility for main window of process $state.existingWindow = $state.hadVisibleBrowser $process = $processesNew[0] $window = [GenXdev.Helpers.WindowObj]::GetMainWindow($process, 1, 80) break } } catch { Microsoft.PowerShell.Utility\Write-Verbose ('Error: ' + "$($_.Exception.Message)") $window = @() $null = [System.Threading.Thread]::Sleep(100) } } while (($i++ -lt 50) -and ($window.length -le 0)) # return process and window information @{ Process = $process Window = $window } } #################################################################### <# .SYNOPSIS Sends keystrokes to the browser window if specified. .DESCRIPTION Helper function to send keystrokes to the browser window after a delay to ensure the window is ready. Handles window handle detection and parameter copying for the Send-Key function. .PARAMETER window The browser window array to send keystrokes to. #> function sendKeysIfSpecified($window) { # send keys if specified, after a delay to ensure window is ready if ($null -ne $KeysToSend -and ($KeysToSend.Count -gt 0)) { Microsoft.PowerShell.Utility\Write-Verbose 'Sending keystrokes to browser window after 4 second delay' Microsoft.PowerShell.Utility\Start-Sleep 6 # copy key sending parameters $invocationParams = GenXdev.Helpers\Copy-IdenticalParamValues ` -BoundParameters $wbParams ` -FunctionName 'GenXdev.Windows\Send-Key' if ($window.Length -eq 1) { $invocationParams.WindowHandle = $window[0].Handle } $null = GenXdev.Windows\Send-Key @invocationParams Microsoft.PowerShell.Utility\Start-Sleep 1 } } #################################################################### <# .SYNOPSIS Opens a browser with the specified URL and configuration. .DESCRIPTION Launches a browser process with the provided URL and handles window positioning, process management, and browser-specific configurations. .PARAMETER browser The browser object containing executable path and type information. .PARAMETER currentUrl The URL to open in the browser. .PARAMETER state The state object tracking browser positioning and process information. #> function open($browser, $currentUrl, $state) { Microsoft.PowerShell.Utility\Write-Verbose 'open()' # determine if this browser is the system default $state.IsDefaultBrowser = $browser -eq $defaultBrowser # enforce timing delays for proper window positioning enforceMinimumDelays $browser # initialize browser launch variables $startBrowser = $true $state.hadVisibleBrowser = $false $process = $null # find any existing browser process with main window $prcBefore = @(Microsoft.PowerShell.Management\Get-Process ` ([IO.Path]::GetFileNameWithoutExtension($browser.Path)) ` -ErrorAction SilentlyContinue) | Microsoft.PowerShell.Core\Where-Object -Property Path -EQ $browser.Path | Microsoft.PowerShell.Core\Where-Object -Property MainWindowHandle -NE 0 | Microsoft.PowerShell.Utility\Sort-Object { $PSItem.StartTime } -Descending | Microsoft.PowerShell.Utility\Select-Object -First 1 # check if existing browser window was found if ($state.PositioningDone -or (($prcBefore.Length -ge 1) -and ($null -ne $prcBefore[0]))) { Microsoft.PowerShell.Utility\Write-Verbose ('Found existing ' + 'webbrowser window') $state.hadVisibleBrowser = $true } # determine if we should skip launching new browser process if ((-not $NewWindow) -and (-not ($havePositioning -or $FullScreen)) -and (-not $urlSpecified)) { if ($state.hadVisibleBrowser) { Microsoft.PowerShell.Utility\Write-Verbose ('No url specified, ' + 'found existing webbrowser window') $startBrowser = $false $process = if ($state.FirstProcess) { $state.FirstProcess } else { $prcBefore[0] } } } # launch new browser process if needed if ($startBrowser) { # handle force parameter to ensure debug port availability if ($Force) { try { # try to get existing browser tabs with debug port $a = GenXdev.Webbrowser\Select-WebbrowserTab ` -Chrome:$Chrome -Edge:$Edge } catch { $a = @() } # close all browser instances if no debug port found if ($a.length -eq 0 -or ($a -is [string])) { Microsoft.PowerShell.Utility\Write-Verbose ('No browser ' + 'with open debugger port found, closing all browser ' + 'instances and starting a new one') $null = Microsoft.PowerShell.Management\Get-Process ` -Name ([IO.Path]::GetFileNameWithoutExtension($browser.Path)) ` -ErrorAction SilentlyContinue | Microsoft.PowerShell.Management\Stop-Process -Force ` -ErrorAction SilentlyContinue } } # check if any browser processes currently exist $currentProcesses = @((Microsoft.PowerShell.Management\Get-Process ` -Name ([IO.Path]::GetFileNameWithoutExtension($browser.Path)) ` -ErrorAction SilentlyContinue)) if ($currentProcesses.Count -eq 0) { $NewWindow = $false } # get browser-specific command line arguments $argumentList = constructArgumentList $browser $currentUrl $state # output verbose information about browser launch Microsoft.PowerShell.Utility\Write-Verbose ("$($browser.Name) --> " + "$($argumentList | Microsoft.PowerShell.Utility\ConvertTo-Json)") # start the browser process with constructed arguments $process = Microsoft.PowerShell.Management\Start-Process ` -FilePath ($browser.Path) -ArgumentList $argumentList -PassThru # wait briefly for process to initialize $null = $process.WaitForExit(2000) } # validate that we have a valid process if ($null -eq $process) { Microsoft.PowerShell.Utility\Write-Warning ('Could not start ' + "browser $($browser.Name)") return } # skip positioning if not needed or already done if ((-not $PassThru) -and ((-not ($havePositioning -or ($FullScreen -and -not $state.PositioningDone))) -or $state.PositioningDone)) { sendKeysIfSpecified $window Microsoft.PowerShell.Utility\Write-Verbose ('No positioning ' + 'required, done..') return } # return process object if passthru requested if ($PassThru) { # return first process if positioning done and process available if (($state.PositioningDone -or ((-not $FullScreen) -and (-not $havePositioning))) -and ($null -ne $state.FirstProcess) -and (-not $state.FirstProcess.HasExited) -and ($state.FirstProcess.MainWindowHandle -ne 0)) { Microsoft.PowerShell.Utility\Write-Verbose ('Returning ' + 'first process') Microsoft.PowerShell.Utility\Write-Output $state.FirstProcess return } # return current process if valid and has window if (($null -ne $process) -and (-not $process.HasExited) -and ($process.MainWindowHandle -ne 0)) { Microsoft.PowerShell.Utility\Write-Verbose 'Returning process' Microsoft.PowerShell.Utility\Write-Output $process if (-not $havePositioning) { return } } } # allow browser startup time and update process handle if needed enforceMinimumDelays $browser $browserFound = findProcess $browser $process $state $process = $browserFound.Process $window = $browserFound.Window # return process after lookup if passthru requested if (($PassThru -eq $true) -and ($null -ne $process)) { Microsoft.PowerShell.Utility\Write-Verbose ('Returning process ' + 'after process lookup') Microsoft.PowerShell.Utility\Write-Output $process } # skip positioning if not required or already completed if ((-not ($havePositioning -or ($FullScreen -and -not $state.PositioningDone))) -or $state.PositioningDone) { sendKeysIfSpecified $window Microsoft.PowerShell.Utility\Write-Verbose ('No positioning ' + 'required, done..') return } # mark positioning as completed and store first process $state.PositioningDone = $true $state.FirstProcess = $process # position browser window if we have a valid window handle if ($window.Length -eq 1) { $params = GenXdev.Helpers\Copy-IdenticalParamValues ` -BoundParameters $wbParams ` -FunctionName 'GenXdev.Windows\Set-WindowPosition' $params.WindowHelper = $window[0] $params.Monitor = $Monitor if ($params.ContainsKey('KeysToSend')) { $null = $params.Remove('KeysToSend') } $null = GenXdev.Windows\Set-WindowPosition @params } # wait for window positioning to complete Microsoft.PowerShell.Utility\Start-Sleep 2 } # initialize url processing index counter $index = -1 try { # iterate through each url that needs to be opened foreach ($currentUrl in $Url) { $index++ Microsoft.PowerShell.Utility\Write-Verbose "Opening $currentUrl" # use simple start-process for default browser without positioning if ($useStartProcess -or (($index -gt 0) -and ($state.IsDefaultBrowser))) { Microsoft.PowerShell.Utility\Write-Verbose 'Start-Process' # launch default browser with simple start-process method $process = Microsoft.PowerShell.Management\Start-Process $currentUrl ` -PassThru # return process if passthru requested for first launch if ($PassThru -and $useStartProcess -and ($index -eq 0)) { $browserFound = findProcess $defaultBrowser $process $state $process = $browserFound.Process $window = $browserFound.Window Microsoft.PowerShell.Utility\Write-Verbose ('Returning ' + 'process after Start-Process') Microsoft.PowerShell.Utility\Write-Output $process } continue } # handle opening url in all available browsers if ($All -eq $true) { # open current url in all installed browsers $browsers | Microsoft.PowerShell.Core\ForEach-Object { open $PSItem $currentUrl $state } continue } # handle chrome-specific browser selection elseif ($Chrome -eq $true) { # find and open chrome browser instances $browsers | Microsoft.PowerShell.Core\ForEach-Object { # check if this is a chrome browser if ($PSItem.Name -like '*Chrome*') { # open url in chrome open $PSItem $currentUrl $state } } } # handle edge-specific browser selection elseif ($Edge -eq $true) { # find and open edge browser instances $browsers | Microsoft.PowerShell.Core\ForEach-Object { # check if this is an edge browser if ($PSItem.Name -like '*Edge*') { # open url in edge open $PSItem $currentUrl $state } } } # handle chromium-based browser preference (edge or chrome) elseif ($Chromium -eq $true) { # check if default browser is already chromium-based if (($defaultBrowser.Name -like '*Chrome*') -or ($defaultBrowser.Name -like '*Edge*')) { # use default browser since it's already chromium-based open $defaultBrowser $currentUrl $state continue } # find available chromium-based browsers $browsers | Microsoft.PowerShell.Utility\Sort-Object { $PSItem.Name } ` -Descending | Microsoft.PowerShell.Core\ForEach-Object { # check if this is a chromium-based browser if (($PSItem.Name -like '*Chrome*') -or ($PSItem.Name -like '*Edge*')) { # open url in chromium-based browser open $PSItem $currentUrl $state } } } # handle firefox-specific browser selection if ($Firefox -eq $true) { # find and open firefox browser instances $browsers | Microsoft.PowerShell.Core\ForEach-Object { # check if this is a firefox browser if ($PSItem.Name -like '*Firefox*') { # open url in firefox open $PSItem $currentUrl $state } } } # use default browser when no specific browser requested if (($Chromium -ne $true) -and ($Chrome -ne $true) -and ($Edge -ne $true) -and ($Firefox -ne $true)) { # open url in system default browser open $defaultBrowser $currentUrl $state } } } finally { # handle fullscreen mode activation after all urls processed if ($FullScreen -eq $true) { Microsoft.PowerShell.Utility\Write-Verbose 'Setting fullscreen' # use browser window reference if available if ($null -ne $state.BrowserWindow) { Microsoft.PowerShell.Utility\Write-Verbose ('Changing focus ' + 'to browser window') try { $null = $state.BrowserWindow.Maximize() $null = $state.BrowserWindow.SetForeground() } catch { # ignore window manipulation errors } $tt = 0 $focusedWindowProcess = GenXdev.Windows\Get-CurrentFocusedProcess # wait for browser window to receive focus while (($tt++ -lt 20) -and (($null -eq $focusedWindowProcess) -or ($focusedWindowProcess.MainWindowHandle -ne $state.BrowserWindow.Handle))) { Microsoft.PowerShell.Utility\Write-Verbose ('have browser ' + 'window, sleeping 500ms') $null = [System.Threading.Thread]::Sleep(500) try { $null = $state.BrowserWindow.Maximize() $null = $state.BrowserWindow.SetForeground() } catch { # ignore window manipulation errors } $null = GenXdev.Windows\Set-ForegroundWindow ` ($state.BrowserWindow.Handle) $focusedWindowProcess = GenXdev.Windows\Get-CurrentFocusedProcess } } else { Microsoft.PowerShell.Utility\Write-Verbose ('Setting ' + 'fullscreen without having reference to browser window') $tt = 0 $focusedWindowProcess = GenXdev.Windows\Get-CurrentFocusedProcess $powershellWindow = GenXdev.Windows\Get-PowershellMainWindow # wait for powershell window focus before sending f11 while (($tt++ -lt 20) -and (($null -eq $focusedWindowProcess) -or ($null -eq $powerShellWindow) -or ($focusedWindowProcess.MainWindowHandle -ne $powerShellWindow.Handle))) { Microsoft.PowerShell.Utility\Write-Verbose ('no browser ' + 'window, sleeping 500ms') $null = [System.Threading.Thread]::Sleep(500) $focusedWindowProcess = GenXdev.Windows\Get-CurrentFocusedProcess $powershellWindow = GenXdev.Windows\Get-PowershellMainWindow } } $w = (GenXdev.Windows\Get-PowershellMainWindow); # send f11 key to activate fullscreen if browser has focus if ( ($w) -and ((GenXdev.Windows\Get-CurrentFocusedProcess).MainWindowHandle -ne $w.Handle)) { try { # create com object to send f11 key press $helper = Microsoft.PowerShell.Utility\New-Object ` -ComObject WScript.Shell $null = $helper.sendKeys('{F11}') Microsoft.PowerShell.Utility\Write-Verbose 'Sending F11' $null = [System.Threading.Thread]::Sleep(500) } catch { # ignore key sending errors } } } } } ######################################################################## end { # restore powershell window focus if requested if ($RestoreFocus) { # get reference to powershell main window $powerShellWindow = GenXdev.Windows\Get-PowershellMainWindow # restore focus to powershell window if it exists if ($null -ne $powerShellWindow) { # wait briefly before restoring focus $null = [System.Threading.Thread]::Sleep(500) # show and bring powershell window to foreground $null = $powerShellWindow.Show() $null = $powerShellWindow.SetForeground() # ensure powershell window receives focus $null = GenXdev.Windows\Set-ForegroundWindow ` ($powerShellWindow.Handle) } } } } ################################################################################ |