au3WebDriver icon indicating copy to clipboard operation
au3WebDriver copied to clipboard

Webdriver BiDi support

Open Danp2 opened this issue 3 years ago • 10 comments

https://github.com/w3c/webdriver-bidi https://developer.chrome.com/blog/webdriver-bidi/ https://wiki.mozilla.org/WebDriver/RemoteProtocol/WebDriver_BiDi

Danp2 avatar Dec 26 '21 16:12 Danp2

Initial coding can be found here

Danp2 avatar Jul 24 '22 22:07 Danp2

Here is what I'm currently using to test the new bidi commands --

CLICK ME

#include "wd_core.au3"
#include "wd_capabilities.au3"
#include "wd_bidi.au3"

;~ $_WD_Debug = $_WD_DEBUG_Full
_WD_CapabilitiesDefine($_WD_KEYS__STANDARD_PRIMITIVE, 'webSocketUrl')
_WD_CapabilitiesDefine($_WD_KEYS__STANDARD_PRIMITIVE, '"moz:debuggerAddress"')

Global $url = 'https://google.com/'
Global $bHeadless = False
Global $sCapabilities = SetupGecko($bHeadless)
;~ Local $sCapabilities = SetupChrome($bHeadless)
Global $iWebDriver_PID = _WD_Startup()
Global $sSession = _WD_CreateSession($sCapabilities)

If @error <> $_WD_ERROR_Success Then
	ConsoleWrite("Session not created..." & @error)
	Exit
EndIf

_WD_Navigate($sSession, $url)

Global $sURL = _WD_BidiGetWebsocketURL($sSession)
Global $hWS  = _WD_BidiConnect($sURL)
Global $sContext = _WD_BidiGetContextID()
Global $oParams = Json_ObjCreate()
Global $sResult = _WD_BidiExecute('session.status', $oParams)
;### Debug CONSOLE ↓↓↓
ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $sResult = ' & $sResult & @CRLF & '>Error code: ' & @error & @CRLF)

Json_ObjPut($oParams, 'context', $sContext)
$sResult = _WD_BidiExecute('browsingContext.close', $oParams)
;### Debug CONSOLE ↓↓↓
ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $sResult = ' & $sResult & @CRLF & '>Error code: ' & @error & @CRLF)

$sResult = _WD_BidiDisconnect()
;### Debug CONSOLE ↓↓↓
ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $sResult = ' & $sResult & @CRLF & '>Error code: ' & @error & @CRLF)


Func SetupGecko($bHeadless)
	_WD_Option('Driver', 'geckodriver.exe')
	_WD_Option('DriverParams', '--log trace')
	_WD_Option('Port', 4444)

	_WD_CapabilitiesStartup()
	_WD_CapabilitiesAdd('alwaysMatch', 'firefox')
	_WD_CapabilitiesAdd('browserName', 'firefox')
	_WD_CapabilitiesAdd('acceptInsecureCerts', True)
	_WD_CapabilitiesAdd('webSocketUrl', True)
	_WD_CapabilitiesAdd('"moz:debuggerAddress"', True)

	If $bHeadless Then _WD_CapabilitiesAdd('args', '--headless')
	_WD_CapabilitiesDump(@ScriptLineNumber) ; dump current Capabilities setting to console - only for testing in this demo
	Local $sCapabilities = _WD_CapabilitiesGet()

	Return $sCapabilities
EndFunc   ;==>SetupGecko

Func SetupChrome($bHeadless)
	_WD_Option('Driver', 'chromedriver.exe')
	_WD_Option('Port', 9515)
	_WD_Option('DriverParams', '--verbose --log-path="' & @ScriptDir & '\chrome.log"')

;~ 	Local $sCapabilities = '{"capabilities": {"alwaysMatch": {"goog:chromeOptions": {"w3c": true, "excludeSwitches": [ "enable-automation"]}}}}'
	_WD_CapabilitiesStartup()
	_WD_CapabilitiesAdd('alwaysMatch', 'chrome')
	_WD_CapabilitiesAdd('w3c', True)
	_WD_CapabilitiesAdd('excludeSwitches', 'enable-automation')
	_WD_CapabilitiesAdd('webSocketUrl', True)

	If $bHeadless Then _WD_CapabilitiesAdd('args', '--headless')
	;~ _WD_CapabilitiesDump(@ScriptLineNumber) ; dump current Capabilities setting to console - only for testing in this demo
	Local $sCapabilities = _WD_CapabilitiesGet()
	Return $sCapabilities
EndFunc   ;==>SetupChrome

Works fine for me with Firefox. Chrome responds with "not supported"

Danp2 avatar Jul 25 '22 11:07 Danp2

WPT tracker

Danp2 avatar Aug 20 '22 15:08 Danp2

Found that calls to WinHttpWebSocketReceive would cause the process to hang when there wasn't pending data to receive. Switched to using websocat to perform all websocket activity.

Danp2 avatar Oct 03 '22 13:10 Danp2

Here is the latest revision of code I'm using to test the new bidi commands --

CLICK ME

#AutoIt3Wrapper_au3check_parameters=-d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6
#include <ButtonConstants.au3>
#include <EditConstants.au3>
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <GuiComboBoxEx.au3>
#include <GUIConstantsEx.au3>
#include <Misc.au3>
#include <GuiListView.au3>

#include "wd_core.au3"
#include "wd_helper.au3"
#include "wd_capabilities.au3"
#include "wd_bidi.au3"

Global $sSession

Global Const $aBrowsers[][2] = _
		[ _
		["Firefox", SetupGecko], _
		["Chrome", SetupChrome], _
		["MSEdge", SetupEdge], _
		["Opera", SetupOpera] _
		]

Global Const $aCommands[][3] = _
		[ _
			["Session Status", "session.status", "{}"], _
			["Session New", "session.new", "{}"], _
			["Session Subscribe", "session.subscribe", '{"events":["log.entryAdded"]}'], _
			["Session Unsubscribe", "session.unsubscribe", '{"events":["log.entryAdded"]}'] _
		]

Main()

Func Main()
	;~ $_WD_Debug = $_WD_DEBUG_Full
	_WD_CapabilitiesDefine($_WD_KEYS__STANDARD_PRIMITIVE, 'webSocketUrl')
	_WD_CapabilitiesDefine($_WD_KEYS__STANDARD_PRIMITIVE, '"moz:debuggerAddress"')

	Local $hGUI = GUICreate("BiDi Tester", 615, 325, -1, -1)
	GUICtrlCreateLabel("Command", 15, 15)
	Local $idCommands = GUICtrlCreateCombo("", 75, 12, 200, 20, $CBS_DROPDOWNLIST)
	Local $sData = _ArrayToString($aCommands, Default, Default, Default, "|", 0, 0)
	GUICtrlSetData($idCommands, $sData)
	GUICtrlSetData($idCommands, $aCommands[0][0])
	Local $iCommand = GUICtrlCreateInput($aCommands[0][1] & "|" & $aCommands[0][2], 24, 40, 409, 21)
	Local $iSend = GUICtrlCreateButton("Send", 448, 39, 75, 25)
	Local $iAsync = GUICtrlCreateCheckbox("Async?", 530, 40)

	Local $iLaunch = GUICtrlCreateButton("Launch", 15, 75, 75, 25)
	Local $iMenu = GUICtrlCreateContextMenu($iLaunch)
	For $i = 0 To UBound($aBrowsers, 1) - 1
		GUICtrlCreateMenuItem($aBrowsers[$i][0], $iMenu, Default, 1)
	Next

	Local $iShow = GUICtrlCreateButton("Show Maps", 95, 75, 75, 25)
	Local $iMenu2 = GUICtrlCreateContextMenu($iShow)
	Local $iEvents = GUICtrlCreateMenuItem("Events", $iMenu2, Default, 1)
	Local $iResults = GUICtrlCreateMenuItem("Results", $iMenu2, Default, 1)
    GUICtrlSetState($iEvents, $GUI_CHECKED)

	Local $iPollEvents = GUICtrlCreateCheckbox("Poll Events?", 175, 77)
	Local $iEdit = GUICtrlCreateEdit("", 15, 110, 585, 200)

	Local $iBrowser = 0, $iStartPopup = $iMenu + 1, $iEndPopup = $iStartPopup + UBound($aBrowsers, 1)
	Local $iSelect, $aCmd, $sCmd, $sOpt, $bAsync, $sResult, $mMap, $nMsg

    GUICtrlSetState($iStartPopup, $GUI_CHECKED)
	GUISetState(@SW_SHOW)

#forceref $iResults

	While 1
		$nMsg = GUIGetMsg()
		Switch $nMsg
			Case $GUI_EVENT_CLOSE
				_WD_BidiDisconnect()
				Exit

			Case $iSend
				$aCmd = StringSplit(GUICtrlRead($iCommand), '|')
				$sCmd = $aCmd[0] ? $aCmd[1] : ""
				$sOpt = ($aCmd[0] = 2) ? Json_Decode($aCmd[2]) : ""
				$bAsync = (GUICtrlRead($iAsync) = $GUI_CHECKED)

				$sResult = DoSend($sCmd, $sOpt, $bAsync)
				GUICtrlSetData($iEdit, $sResult & @CRLF, 1)
				
			Case $iLaunch
				DoLaunch($iBrowser)

			Case $idCommands
				$iSelect = _GUICtrlComboBox_GetCurSel($idCommands)
				GUICtrlSetData($iCommand,  $aCommands[$iSelect][1] & "|" & $aCommands[$iSelect][2])
			
			Case $iShow
				If BitAnd(GUICtrlRead($iEvents), $GUI_CHECKED) Then
					$mMap = __WD_BidiActions('maps', 'event')
					_Map_Display($mMap, 'events')
				Else
					$mMap = __WD_BidiActions('maps', 'result')
					_Map_Display($mMap, 'results')
				EndIf

			;~ Case $iEvents To $iResults
			;~ 	$iMap = $nMsg - $iEvents

			Case $iStartPopup To $iEndPopup
				$iBrowser = $nMsg - $iStartPopup

		EndSwitch

		DoPoll($iPollEvents, $iEdit)
	WEnd

	GUIDelete($hGUI)
EndFunc

Func DoLaunch($iBrowser)
	Local $bHeadless = False
	Local $sURL = 'https://google.com/'

	; Execute browser setup routine for user's browser selection
	Local $sCapabilities = Call($aBrowsers[$iBrowser][1], $bHeadless)

	_WD_Startup()
	$sSession = _WD_CreateSession($sCapabilities)
	_WD_Navigate($sSession, $sURL)
	_WD_BidiConnect($sSession)
EndFunc

Func DoSend($sCommand, $oParams, $bAsync)
	;~ Local $oParams = Json_ObjCreate()
	Local $sResult = _WD_BidiExecute($sCommand, $oParams, $bAsync)

	Return $sResult
EndFunc

Func DoPoll($iPollEvents, $iEdit)
	Local $_WD_DEBUG_Saved = $_WD_DEBUG ; save current DEBUG level
	If $_WD_DEBUG <> $_WD_DEBUG_Full Then $_WD_DEBUG = $_WD_DEBUG_None

	__WD_BidiActions('receive')

	If GUICtrlRead($iPollEvents) = $GUI_CHECKED Then
		Local $sResult = _WD_BidiGetEvent()
		If $sResult Then GUICtrlSetData($iEdit, $sResult & @CRLF, 1)
	Endif
	$_WD_DEBUG = $_WD_DEBUG_Saved ; restore DEBUG level
EndFunc



	;~ __TCPSendLine($_WD_BiDi_Socket, '{"id":2,"method":"session.subscribe","params":{"events":["log.entryAdded"]}}')

	; Prevent logging from __WD_BidiActions if not in Full debug mode

#cs
	While Not $bExitFlag



		If @error Then ExitLoop

		If $bLine Then
			;### Debug CONSOLE ↓↓↓
			ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $bLine = ' & $bLine & @CRLF & '>Error code: ' & @error & @CRLF)
		ElseIf _IsPressed("12", $hDLL) Then
			;~ __TCPSendLine($_WD_BiDi_Socket, '{"id":1,"method":"session.status","params":{}}')
			;~ $oParams = Json_ObjCreate()	
			;~ _WD_BidiExecute("session.status", $oParams)
			Local $sResult = __WD_BidiActions("receive", "event")

			If $sResult Then ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $sResult = ' & $sResult & @CRLF & '>Error code: ' & @error & @CRLF)
		EndIf

		Sleep(100)
	WEnd
#ce 
	


Func SetupGecko($bHeadless)
	_WD_Option('Driver', 'geckodriver.exe')
	_WD_Option('DriverParams', '--log trace')
	_WD_Option('Port', 4444)

	_WD_CapabilitiesStartup()
	_WD_CapabilitiesAdd('alwaysMatch', 'firefox')
	_WD_CapabilitiesAdd('browserName', 'firefox')
	_WD_CapabilitiesAdd('acceptInsecureCerts', True)
	_WD_CapabilitiesAdd('webSocketUrl', True)
	_WD_CapabilitiesAdd('"moz:debuggerAddress"', True)

	; enable experimental BiDi commands
    _WD_CapabilitiesAdd('prefs', 'remote.experimental.enabled', true)

	If $bHeadless Then _WD_CapabilitiesAdd('args', '--headless')
	_WD_CapabilitiesDump(@ScriptLineNumber) ; dump current Capabilities setting to console - only for testing in this demo
	Local $sCapabilities = _WD_CapabilitiesGet()

	Return $sCapabilities
EndFunc   ;==>SetupGecko

Func SetupChrome($bHeadless)
	_WD_Option('Driver', 'chromedriver.exe')
	_WD_Option('Port', 9515)
	_WD_Option('DriverParams', '--verbose --log-path="' & @ScriptDir & '\chrome.log"')

;~ 	Local $sCapabilities = '{"capabilities": {"alwaysMatch": {"goog:chromeOptions": {"w3c": true, "excludeSwitches": [ "enable-automation"]}}}}'
	_WD_CapabilitiesStartup()
	_WD_CapabilitiesAdd('alwaysMatch', 'chrome')
	_WD_CapabilitiesAdd('w3c', True)
	_WD_CapabilitiesAdd('webSocketUrl', True)
	_WD_CapabilitiesAdd('excludeSwitches', 'enable-automation')
	If $bHeadless Then _WD_CapabilitiesAdd('args', '--headless')
	_WD_CapabilitiesDump(@ScriptLineNumber) ; dump current Capabilities setting to console - only for testing in this demo
	Local $sCapabilities = _WD_CapabilitiesGet()
	Return $sCapabilities
EndFunc   ;==>SetupChrome

Func SetupEdge($bHeadless)
	_WD_Option('Driver', 'msedgedriver.exe')
	_WD_Option('Port', 9515)
	_WD_Option('DriverParams', '--verbose --log-path="' & @ScriptDir & '\msedge.log"')

;~ 	Local $sCapabilities = '{"capabilities": {"alwaysMatch": {"ms:edgeOptions": {"excludeSwitches": [ "enable-automation"]}}}}'
	_WD_CapabilitiesStartup()
	_WD_CapabilitiesAdd('alwaysMatch', 'msedge')
	_WD_CapabilitiesAdd('webSocketUrl', True)
	_WD_CapabilitiesAdd('excludeSwitches', 'enable-automation')
	If $bHeadless Then _WD_CapabilitiesAdd('args', '--headless')
	_WD_CapabilitiesDump(@ScriptLineNumber) ; dump current Capabilities setting to console - only for testing in this demo
	Local $sCapabilities = _WD_CapabilitiesGet()
	Return $sCapabilities
EndFunc   ;==>SetupEdge

Func SetupOpera($bHeadless)
	_WD_Option('Driver', 'operadriver.exe')
	_WD_Option('Port', 9515)
	_WD_Option('DriverParams', '--verbose --log-path="' & @ScriptDir & '\opera.log"')

;~ 	Local $sCapabilities = '{"capabilities": {"alwaysMatch":{"goog:chromeOptions": {"w3c":true, "excludeSwitches":["enable-automation"], "binary":"C:\\Users\\......\\AppData\\Local\\Programs\\Opera\\opera.exe"}}}}'
	_WD_CapabilitiesStartup()
	_WD_CapabilitiesAdd('alwaysMatch', 'opera')
	_WD_CapabilitiesAdd('w3c', True)
	_WD_CapabilitiesAdd('webSocketUrl', True)
	_WD_CapabilitiesAdd('excludeSwitches', 'enable-automation')
	; REMARK
	; using 32bit operadriver.exe requires to set 'binary' capabilities,
	; using 64bit operadriver.exe dosen't require to set this capability, but at the same time setting is not affecting the script
	; So this is good habit to setup for any case.
	_WD_CapabilitiesAdd('binary', _WD_GetBrowserPath("opera"))
	ConsoleWrite("wd_demo.au3: _WD_GetBrowserPath() > " & _WD_GetBrowserPath("opera") & @CRLF)

	If $bHeadless Then _WD_CapabilitiesAdd('args', '--headless')
	_WD_CapabilitiesDump(@ScriptLineNumber) ; dump current Capabilities setting to console - only for testing in this demo
	Local $sCapabilities = _WD_CapabilitiesGet()
	Return $sCapabilities
EndFunc   ;==>SetupOpera

; #FUNCTION# ====================================================================================================================
; Name ..........: _Map_Display
; Description ...: Displays the Key & Value pair in a ListView.
; Syntax ........: _Map_Display(Byref $mMap[, $sTitle = "Map Display"])
; Parameters ....: $mMap                - [in/out] Map to display.
;                  $sTitle              - [optional] Title for the Display window. Default is "Map Display".
; Return values .: Success: True
;                  Failure: False & @error is set to non-zero
; Author ........: Damon Harris (TheDcoder)
; Modified ......:
; Remarks .......: This function is intended to be used for Debugging. Its still in development, so it does not provide flexible
;                  functionality like _ArrayDisplay
; Related .......: _ArrayDisplay
; Link ..........:
; Example .......: Yes
; ===============================================================================================================================
Func _Map_Display(ByRef $mMap, $sTitle = "Map Display")
	If Not IsMap($mMap) Then
		Return SetError(1, 0, False)
	EndIf
	Local Const $esDataSepChar = Opt("GUIDataSeparatorChar") ; Get the GUIDataSeparatorChar
	Local $hGUI = GUICreate($sTitle, 300, 300, -1, -1, BitOR($WS_MINIMIZEBOX, $WS_CAPTION, $WS_POPUP, $WS_SYSMENU, $WS_SIZEBOX, $WS_MAXIMIZEBOX)) ; Create the $hGUI
	Local $idListView = GUICtrlCreateListView("Key|Value", 0, 0, 300, 264) ; Create the $idListView
	Local $idCopyButton = GUICtrlCreateButton("Copy Text", 3, 268, 146, 30) ; Create the $idCopyButton
	GUICtrlSetResizing(-1, $GUI_DOCKAUTO) ; Set Resizing
	Local $idExitButton = GUICtrlCreateButton("Exit Script", 149, 268, 148, 30) ; Create the $idExitButton
	GUICtrlSetResizing(-1, $GUI_DOCKAUTO) ; Set Resizing

	Local $aKeys = MapKeys($mMap) ; Get the $aKeys in the map
	;Local $iKeyCount = UBound($mMap) ; Get the $iKeyCount
	Local $sContents = "" ; Declare the variable in which the contents will be stored

	For $vKey In $aKeys ; Loop...
		$sContents = $vKey & $esDataSepChar
		Switch VarGetType($mMap[$vKey]) ; Switch to the Var Type
			Case "Map"
				$sContents &= '{Map}' ; Store the contents

			Case "Array"
				$sContents &= '{Array}' ; Store the contents

			Case "Object"
				$sContents &= '{Object}' ; Store the contents

			Case "DLLStruct"
				$sContents &= '{Struct}' ; Store the contents

			Case "Function"
				$sContents &= '{Function}' ; Store the contents

			Case "UserFunction"
				$sContents &= '{UDF}' ; Store the contents

			Case Else
				$sContents &= $mMap[$vKey] ; Store the contents
		EndSwitch
		GUICtrlCreateListViewItem($sContents, $idListView) ; Create the ListViewItem in $idListView
	Next

	_GUICtrlListView_SetColumnWidth($idListView, 0, $LVSCW_AUTOSIZE) ; Autosize the $idListView to fit the text
	GUISetState() ; Display the GUI
	Local $nMsg = 0

	While 1
		$nMsg = GUIGetMsg()
		Switch $nMsg
			Case $GUI_EVENT_CLOSE
				GUIDelete($hGUI) ; Delete the GUI
				Return True ; Return True

			Case $idExitButton
				Exit ; Exit

			Case $idCopyButton
				$sContents = GUICtrlRead(GUICtrlRead($idListView)) ; Copy the $sContents of the ListViewItem
				ClipPut($sContents) ; ClipPut them!
				MsgBox($MB_ICONINFORMATION, $sTitle, "The following has been copied to your clipboard: " & @CRLF & @CRLF & $sContents) ; Display a message
		EndSwitch
	WEnd
EndFunc   ;==>_Map_Display

Note that you can right click on the Launch and Show Maps buttons to alter their functionality.

Danp2 avatar Oct 03 '22 13:10 Danp2

Here is the latest revision of code I'm using to test the new bidi commands --

CLICK ME

;~ #AutoIt3Wrapper_au3check_parameters=-d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6
;~ #AutoIt3Wrapper_AutoIt3Dir="C:\AutoitPortable\3.3.16.0"
#include <ButtonConstants.au3>
#include <EditConstants.au3>
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <GuiComboBoxEx.au3>
#include <GuiListView.au3>
#include <GuiEdit.au3>

#include "wd_core.au3"
#include "wd_helper.au3"
#include "wd_capabilities.au3"
#include "wd_bidi.au3"

Global $sSession

Global Const $aBrowsers[][2] = _
        [ _
        ["Firefox", SetupGecko], _
        ["Chrome", SetupChrome], _
        ["MSEdge", SetupEdge], _
        ["Opera", SetupOpera] _
        ]

Global Const $aCommands[][2] = _
        [ _
            ["browsingContext.create", '{ "type": "tab" }'], _
            ["browsingContext.close", '{"context": "<context id>"}'], _
            ["browsingContext.getTree", "{}"], _
            ["browsingContext.navigate", '{"context": "<context id>", "url": "https://google.com"}'], _
            ["session.status",  "{}"], _
            ["session.new", "{}"], _
            ["session.subscribe", '{"events":["log.entryAdded"]}'], _
            ["session.unsubscribe", '{"events":["log.entryAdded"]}'] _
        ]

Opt('TrayIconDebug',1)

Main()

Func Main()
    $_WD_Debug = $_WD_DEBUG_Full
    _WD_BidiSetClient('websocat')
    _WD_CapabilitiesDefine($_WD_KEYS__STANDARD_PRIMITIVE, 'webSocketUrl')
    _WD_CapabilitiesDefine($_WD_KEYS__STANDARD_PRIMITIVE, '"moz:debuggerAddress"')

    Local $hGUI = GUICreate("BiDi Tester", 615, 325, -1, -1)
    GUICtrlCreateLabel("Command", 15, 15)
    Local $idCommands = GUICtrlCreateCombo("", 75, 12, 200, 20, $CBS_DROPDOWNLIST)
    Local $sData = _ArrayToString($aCommands, Default, Default, Default, "|", 0, 0)
    GUICtrlSetData($idCommands, $sData)
    GUICtrlSetData($idCommands, $aCommands[0][0])
    Local $iCommand = GUICtrlCreateInput($aCommands[0][0] &amp; "|" &amp; $aCommands[0][1], 24, 40, 409, 21)
    Local $iSend = GUICtrlCreateButton("Send", 448, 39, 75, 25)
    Local $iAsync = GUICtrlCreateCheckbox("Async?", 530, 40)

    Local $iLaunch = GUICtrlCreateButton("Launch", 15, 75, 75, 25)
    Local $iMenu = GUICtrlCreateContextMenu($iLaunch)
    For $i = 0 To UBound($aBrowsers, 1) - 1
        GUICtrlCreateMenuItem($aBrowsers[$i][0], $iMenu, Default, 1)
    Next

    Local $iShow = GUICtrlCreateButton("Show Maps", 95, 75, 75, 25)
    Local $iMenu2 = GUICtrlCreateContextMenu($iShow)
    Local $iEvents = GUICtrlCreateMenuItem("Events", $iMenu2, Default, 1)
    Local $iResults = GUICtrlCreateMenuItem("Results", $iMenu2, Default, 1)
    GUICtrlSetState($iEvents, $GUI_CHECKED)
    Local $iGetResult = GUICtrlCreateButton("Get Result", 175, 75, 75, 25)

    Local $iEdit = GUICtrlCreateEdit("", 15, 110, 585, 200)

    Local $iBrowser = 0, $iStartPopup = $iMenu + 1, $iEndPopup = $iStartPopup + UBound($aBrowsers, 1)
    Local $iSelect, $aCmd, $sCmd, $sOpt, $bAsync, $sResult, $mMap, $nMsg, $iLastID = 0, $sAnswer, $aPos
    Local $iDiff = 0, $hTimer = TimerInit() 

    GUICtrlSetState($iStartPopup, $GUI_CHECKED)
    GUISetState(@SW_SHOW)

#forceref $iResults

    While 1
        $nMsg = GUIGetMsg()
        Switch $nMsg
            Case $GUI_EVENT_CLOSE
                ExitLoop

            Case $iSend
                If _WD_BidiIsConnected() Then
                    $aCmd = StringSplit(GUICtrlRead($iCommand), '|')
                    $sCmd = $aCmd[0] ? $aCmd[1] : ""
                    $sOpt = ($aCmd[0] = 2) ? Json_Decode($aCmd[2]) : Json_ObjCreate()
                    $bAsync = (GUICtrlRead($iAsync) = $GUI_CHECKED)

                    $sResult = DoSend($sCmd, $sOpt, $bAsync)

                    If $bAsync Then
                        $iLastID = Number($sResult)
                        $sResult = "Request ID: " &amp; $sResult
                    EndIf
                Else
                    $sResult = "Not connected!"
                EndIf

                _GUICtrlEdit_AppendText($iEdit, $sResult &amp; @CRLF)

            Case $iLaunch
                DoLaunch($iBrowser)

            Case $idCommands
                $iSelect = _GUICtrlComboBox_GetCurSel($idCommands)
                GUICtrlSetData($iCommand,  $aCommands[$iSelect][0] &amp; "|" &amp; $aCommands[$iSelect][1])

            Case $iShow
                If BitAnd(GUICtrlRead($iEvents), $GUI_CHECKED) Then
                    $mMap = __WD_BidiActions('maps', 'event')
                    _Map_Display($mMap, 'events')
                Else
                    $mMap = __WD_BidiActions('maps', 'result')
                    _Map_Display($mMap, 'results')
                EndIf

            Case $iStartPopup To $iEndPopup
                $iBrowser = $nMsg - $iStartPopup

            Case $iGetResult
                $aPos = WinGetPos($hGUI)
                $sAnswer = InputBox("Result", "Enter the Request ID of the result to retrieve", $iLastID, Default, Default, Default, $aPos[0]+($aPos[2]/3), $aPos[1]+($aPos[3]/3))
                If @error = $_WD_ERROR_Success Then
                    $sResult = _WD_BidiGetResult(Number($sAnswer))

                    If @error Then _
                        $sResult = $aWD_ERROR_DESC[@error] &amp; " [" &amp; @error &amp; "]"

                    _GUICtrlEdit_AppendText($iEdit, $sResult &amp; @CRLF)
                EndIf
                
        EndSwitch


        If TimerDiff($hTimer) > $iDiff Then
            DoPoll($iEdit)
            $iDiff = TimerDiff($hTimer) + 500
        EndIf
    WEnd

    _WD_BidiDisconnect()
    GUIDelete($hGUI)
EndFunc

Func DoLaunch($iBrowser)
    Local $bHeadless = False
    Local $sURL = 'https://google.com/'

    ; Execute browser setup routine for user's browser selection
    Local $sCapabilities = Call($aBrowsers[$iBrowser][1], $bHeadless)

    _WD_Startup()
    $sSession = _WD_CreateSession($sCapabilities)

    If @error = $_WD_ERROR_Success Then
        _WD_Navigate($sSession, $sURL)
        _WD_BidiConnect($sSession)
    Endif
EndFunc

Func DoSend($sCommand, $oParams, $bAsync)
    Local $sResult = _WD_BidiExecute($sCommand, $oParams, $bAsync)

    Return $sResult
EndFunc

Func DoPoll($iEdit)
    Local $sResult
    Local $_WD_DEBUG_Saved = $_WD_DEBUG ; save current DEBUG level
    $_WD_DEBUG = $_WD_DEBUG_None

    If _WD_BidiIsConnected() Then
        $sResult = _WD_BidiGetEvent()
        If $sResult Then
            _GUICtrlEdit_AppendText($iEdit, $sResult &amp; @CRLF)
        EndIf

    EndIf

    $_WD_DEBUG = $_WD_DEBUG_Saved ; restore DEBUG level
EndFunc

Func SetupGecko($bHeadless)
    _WD_Option('Driver', 'geckodriver.exe')
    ;~ _WD_Option('DriverParams', '--log trace --websocket-port=60000 --allow-hosts 127.0.0.1 --allow-origins http://127.0.0.1:60000 http://localhost:60000')
    _WD_Option('DriverParams', '--log trace')
    _WD_Option('Port', 4444)

    _WD_CapabilitiesStartup()
    _WD_CapabilitiesAdd('alwaysMatch', 'firefox')
    _WD_CapabilitiesAdd('browserName', 'firefox')
    _WD_CapabilitiesAdd('acceptInsecureCerts', True)
    _WD_CapabilitiesAdd('webSocketUrl', True)
    _WD_CapabilitiesAdd('"moz:debuggerAddress"', True)

    ; enable experimental BiDi commands
    _WD_CapabilitiesAdd('prefs', 'remote.experimental.enabled', true)

    ;~ _WD_CapabilitiesAdd('prefs', 'remote.hosts.allowed', "127.0.0.1")
    ;~ _WD_CapabilitiesAdd('prefs', 'remote.origins.allowed', "http://127.0.0.1")

    If $bHeadless Then _WD_CapabilitiesAdd('args', '--headless')
    Local $sCapabilities = _WD_CapabilitiesGet()

    Return $sCapabilities
EndFunc   ;==>SetupGecko

Func SetupChrome($bHeadless)
    _WD_Option('Driver', 'chromedriver.exe')
    _WD_Option('Port', 9515)
    _WD_Option('DriverParams', '--verbose --log-path="' &amp; @ScriptDir &amp; '\chrome.log"')

    _WD_CapabilitiesStartup()
    _WD_CapabilitiesAdd('alwaysMatch', 'chrome')
    _WD_CapabilitiesAdd('w3c', True)
    _WD_CapabilitiesAdd('webSocketUrl', True)
    _WD_CapabilitiesAdd('excludeSwitches', 'enable-automation')
    If $bHeadless Then _WD_CapabilitiesAdd('args', '--headless')
    Local $sCapabilities = _WD_CapabilitiesGet()
    Return $sCapabilities
EndFunc   ;==>SetupChrome

Func SetupEdge($bHeadless)
    _WD_Option('Driver', 'msedgedriver.exe')
    _WD_Option('Port', 9515)
    _WD_Option('DriverParams', '--verbose --log-path="' &amp; @ScriptDir &amp; '\msedge.log"')

    _WD_CapabilitiesStartup()
    _WD_CapabilitiesAdd('alwaysMatch', 'msedge')
    _WD_CapabilitiesAdd('webSocketUrl', True)
    _WD_CapabilitiesAdd('excludeSwitches', 'enable-automation')
    If $bHeadless Then _WD_CapabilitiesAdd('args', '--headless')
    Local $sCapabilities = _WD_CapabilitiesGet()
    Return $sCapabilities
EndFunc   ;==>SetupEdge

Func SetupOpera($bHeadless)
    _WD_Option('Driver', 'operadriver.exe')
    _WD_Option('Port', 9515)
    _WD_Option('DriverParams', '--verbose --log-path="' &amp; @ScriptDir &amp; '\opera.log"')

    _WD_CapabilitiesStartup()
    _WD_CapabilitiesAdd('alwaysMatch', 'opera')
    _WD_CapabilitiesAdd('w3c', True)
    _WD_CapabilitiesAdd('webSocketUrl', True)
    _WD_CapabilitiesAdd('excludeSwitches', 'enable-automation')
    ; REMARK
    ; using 32bit operadriver.exe requires to set 'binary' capabilities,
    ; using 64bit operadriver.exe dosen't require to set this capability, but at the same time setting is not affecting the script
    ; So this is good habit to setup for any case.
    _WD_CapabilitiesAdd('binary', _WD_GetBrowserPath("opera"))
    ConsoleWrite("wd_demo.au3: _WD_GetBrowserPath() > " &amp; _WD_GetBrowserPath("opera") &amp; @CRLF)

    If $bHeadless Then _WD_CapabilitiesAdd('args', '--headless')
    Local $sCapabilities = _WD_CapabilitiesGet()
    Return $sCapabilities
EndFunc   ;==>SetupOpera

; #FUNCTION# ====================================================================================================================
; Name ..........: _Map_Display
; Description ...: Displays the Key &amp; Value pair in a ListView.
; Syntax ........: _Map_Display(Byref $mMap[, $sTitle = "Map Display"])
; Parameters ....: $mMap                - [in/out] Map to display.
;                  $sTitle              - [optional] Title for the Display window. Default is "Map Display".
; Return values .: Success: True
;                  Failure: False &amp; @error is set to non-zero
; Author ........: Damon Harris (TheDcoder)
; Modified ......:
; Remarks .......: This function is intended to be used for Debugging. Its still in development, so it does not provide flexible
;                  functionality like _ArrayDisplay
; Related .......: _ArrayDisplay
; Link ..........:
; Example .......: Yes
; ===============================================================================================================================
Func _Map_Display(ByRef $mMap, $sTitle = "Map Display")
    If Not IsMap($mMap) Then
        Return SetError(1, 0, False)
    EndIf
    Local Const $esDataSepChar = Opt("GUIDataSeparatorChar") ; Get the GUIDataSeparatorChar
    Local $hGUI = GUICreate($sTitle, 300, 300, -1, -1, BitOR($WS_MINIMIZEBOX, $WS_CAPTION, $WS_POPUP, $WS_SYSMENU, $WS_SIZEBOX, $WS_MAXIMIZEBOX)) ; Create the $hGUI
    Local $idListView = GUICtrlCreateListView("Key|Value", 0, 0, 300, 264) ; Create the $idListView
    Local $idCopyButton = GUICtrlCreateButton("Copy Text", 3, 268, 146, 30) ; Create the $idCopyButton
    GUICtrlSetResizing(-1, $GUI_DOCKAUTO) ; Set Resizing
    Local $idExitButton = GUICtrlCreateButton("Exit Script", 149, 268, 148, 30) ; Create the $idExitButton
    GUICtrlSetResizing(-1, $GUI_DOCKAUTO) ; Set Resizing

    Local $aKeys = MapKeys($mMap) ; Get the $aKeys in the map
    ;Local $iKeyCount = UBound($mMap) ; Get the $iKeyCount
    Local $sContents = "" ; Declare the variable in which the contents will be stored

    For $vKey In $aKeys ; Loop...
        $sContents = $vKey &amp; $esDataSepChar
        Switch VarGetType($mMap[$vKey]) ; Switch to the Var Type
            Case "Map"
                $sContents &amp;= '{Map}' ; Store the contents

            Case "Array"
                $sContents &amp;= '{Array}' ; Store the contents

            Case "Object"
                $sContents &amp;= '{Object}' ; Store the contents

            Case "DLLStruct"
                $sContents &amp;= '{Struct}' ; Store the contents

            Case "Function"
                $sContents &amp;= '{Function}' ; Store the contents

            Case "UserFunction"
                $sContents &amp;= '{UDF}' ; Store the contents

            Case Else
                $sContents &amp;= $mMap[$vKey] ; Store the contents
        EndSwitch
        GUICtrlCreateListViewItem($sContents, $idListView) ; Create the ListViewItem in $idListView
    Next

    _GUICtrlListView_SetColumnWidth($idListView, 0, $LVSCW_AUTOSIZE) ; Autosize the $idListView to fit the text
    GUISetState() ; Display the GUI
    Local $nMsg = 0

    While 1
        $nMsg = GUIGetMsg()
        Switch $nMsg
            Case $GUI_EVENT_CLOSE
                GUIDelete($hGUI) ; Delete the GUI
                Return True ; Return True

            Case $idExitButton
                Exit ; Exit

            Case $idCopyButton
                $sContents = GUICtrlRead(GUICtrlRead($idListView)) ; Copy the $sContents of the ListViewItem
                ClipPut($sContents) ; ClipPut them!
                MsgBox($MB_ICONINFORMATION, $sTitle, "The following has been copied to your clipboard: " &amp; @CRLF &amp; @CRLF &amp; $sContents) ; Display a message
        EndSwitch
    WEnd
EndFunc   ;==>_Map_Display

Note that you can right click on the Launch and Show Maps buttons to alter their functionality.

Danp2 avatar Nov 25 '22 22:11 Danp2

https://developer.chrome.com/articles/webdriver-bidi/

mlipok avatar Apr 25 '23 11:04 mlipok

I have been successful at creating a BiDi session in Firefox without the need of geckodriver. I don't believe that this feature is currently supported by Chrome or Edge.

Danp2 avatar Oct 23 '23 02:10 Danp2