Malicious Office (OOXML) / .XLSX — malware analysis report

Static analysis result for SHA-256 e2767a13d03e1bca…

MALICIOUS

Office (OOXML) / .XLSX

1.55 MB Created: 2022-04-23 04:05:26 UTC Authoring application: Microsoft Excel 16.0300 First seen: 2026-07-12
MD5: 4509e9dca9a358b616e97fe7fb673971 SHA-1: c17188fdfd9cc53410ee28e15b2f5e3b15fdfa02 SHA-256: e2767a13d03e1bca35d99f62bb764a70bf6b5b96eea6df84d4d32cb4aacaacdf
538 Risk Score

Malware Insights

MITRE ATT&CK
T1059.005 Visual Basic T1204.002 Malicious File T1105 Ingress Tool Transfer T1059.005 Visual Basic T1059.005 Visual Basic T1059.005 Visual Basic

This XLSX file contains a malicious VBA macro that utilizes WScript.Shell and cmd.exe to download and execute a second-stage payload. The macro also employs Excel 4.0 macro execution via an ActiveX event, indicating a complex stager. The script attempts to download content from URLs, suggesting an Ingress Tool Transfer (T1105) attack pattern. The use of VBA and Excel 4.0 macros points to the Visual Basic (T1059.005) technique.

Heuristics 14

  • VBA project inside OOXML medium 12 related findings OOXML_VBA
    Document contains a VBA project — VBA macros present
  • Potential Shell call in VBA critical OLE_VBA_SHELL
    Potential Shell call in VBA
    Matched line in script
      If withEdge Then RunAsAdmin path, cmdLine Else Shell path & " " & cmdLine, vbHide
  • WScript.Shell usage critical OLE_VBA_WSCRIPT
    WScript.Shell usage
    Matched line in script
      Set glbShellWS = Interaction.CreateObject("WScript.Shell")
  • VBA downloads and writes a file to disk critical OLE_VBA_HTTP_DROP_EXEC
    VBA reads an HTTP response body and writes it to disk (ADODB.Stream SaveToFile). Combined with the auto-exec/Shell paths this is a download-drop dropper even when the COM ProgIDs are built dynamically to evade keyword scanning.
    Matched line in script
        bytes = Http.responseBody
  • VBA macro-virus self-replication / AV tampering critical OLE_VBA_MACRO_VIRUS_REPLICATION
    VBA macro programmatically rewrites VBA project code through the VBE object model (CodeModule/VBComponents InsertLines/DeleteLines/AddFromString or OrganizerCopy) to copy itself into the global template and other open documents, and/or disables Office macro-virus protection (Options.VirusProtection = False). This is the defining behavior of the W97M document macro-virus family — self-replicating code with no benign document use, independent of any AV signature.
    Matched line in script
              If o.CodeModule.countoflines > 1 Then o.CodeModule.DeleteLines 1, o.CodeModule.countoflines
  • VBA ActiveX event launches decoded Excel4 macro critical OLE_VBA_ACTIVEX_XLM_STAGER
    The compiled VBA p-code (identifier table) references an auto-firing ActiveX/control event together with ExecuteExcel4Macro, while the decompressed source does not — the VBA-stomping shape of the ActiveX-event XLM stager. The control event bridges into XLM formula execution to call Win32 / drop payloads, hidden from source-level scanners.
  • CreateObject call high OLE_VBA_CREATEOBJ
    CreateObject call
    Matched line in script
        With Interaction.CreateObject("System.Security.Cryptography.SHA1CryptoServiceProvider")
  • GetObject call high OLE_VBA_GETOBJ
    GetObject call
    Matched line in script
      Set classLibWinmgmts = Interaction.GetObject("winmgmts:\\.\root\CIMV2").ExecQuery(query, , 48)
  • CallByName call high OLE_VBA_CALLBYNAME
    CallByName call
    Matched line in script
          If p.commandLine Like "*--remote-debugging-port=*--user-data-dir=*" Then CallByName p, "Termi" & "nate", VbMethod
  • cmd.exe reference in VBA high OLE_VBA_CMD
    cmd.exe reference in VBA
    Matched line in script
                Case "cmd.exe"
  • OOXML VBA project hides Excel 4 macro execution bridge high OOXML_VBA_XLM_BRIDGE_RAW
    Raw vbaProject.bin metadata references ExecuteExcel4Macro together with string-deobfuscation primitives, and the OOXML package exposes a button, drawing, or control surface that can invoke VBA. This is a macro/XLM stager indicator for projects whose source cannot be recovered cleanly; it is not a document-parser CVE attribution.
  • Environ() call (env variable access) low OLE_VBA_ENVIRON
    Environ() call (env variable access)
    Matched line in script
    sharedPath = Replace$(Environ("ProgramData") & "\" & projectCompany & "\" & IIf(general, "", projectName & "\") & path, "\\", "\")
  • VBA polls global keyboard state (keylogger) low OLE_VBA_KEYLOGGER_SPYWARE
    The macro declares or calls a Win32 keystroke-monitoring API (GetAsyncKeyState, SetWindowsHookEx WH_KEYBOARD, or GetKeyboardState) to capture keystrokes system-wide. No legitimate document automation polls global key state; this is the core of a VBA keylogger, usually paired with active-window capture (GetForegroundWindow) and a log file. A high-confidence spyware behaviour independent of any download / Shell evidence.
    Matched line in script
    Private Declare PtrSafe Function SetWindowsHookEx Lib "user32" Alias "SetWindowsHookExA" (ByVal idHook As Long, ByVal lpFN As LongPtr, ByVal hmod As LongPtr, ByVal dwThreadId As Long) As LongPtr
  • Embedded URL info EMBEDDED_URL
    One or more URLs were extracted from the document. The URL itself is not a detection — see the per-URL labels for which channel (macro, JS, link annotation, document body, ...) reached each URL.
    URL https://peter.sh/experiments/chromium-command-line-switches/ Referenced by macro
    • https://github.com/GCuser99/SeleniumVBAReferenced by macro
    • https://github.com/GCuser99/SeleniumVBA/discussions/32Referenced by macro
    • https://github.com/cristianbuse/VBA-SafeTimerReferenced by macro
    • https://github.com/WNKLER/RefTypes/discussions/3#discussion-8595790Referenced by macro
    • http://localhostReferenced by macro
    • https://github.com/florentbr/SeleniumBasic/tree/master/Examples/ExcelReferenced by macro
    • https://github.com/GCuser99/SeleniumVBA/issues/87Referenced by macro
    • https://github.com/GCuser99/SeleniumVBA/issues/8Referenced by macro
    • https://github.com/mozilla/geckodriver/issues/1962Referenced by macro
    • https://api.github.com/repos/mozilla/geckodriver/releases/latestReferenced by macro
    • https://github.com/GoogleChromeLabs/chrome-for-testing#json-api-endpointsReferenced by macro
    • https://chromedriver.storage.googleapis.com/LATEST_RELEASE_Referenced by macro
    • https://storage.googleapis.com/chrome-for-testing-public/Referenced by macro
    • https://chromedriver.storage.googleapis.com/Referenced by macro
    • https://github.com/mozilla/geckodriver/releases/download/vReferenced by macro
    • https://github.com/GCuser99/SeleniumVBA/discussions/29#discussioncomment-3673682Referenced by macro
    • https://github.com/GCuser99/SeleniumVBA/tree/mainReferenced by macro
    • https://github.com/6DiegoDiego9Referenced by macro
    • https://github.com/GCUser99Referenced by macro
    • https://gist.github.com/wqweto/0002b7e6c4f92e69c8e8339ed2235b4cReferenced by macro
    • https://github.com/cristianbuse/VBA-StringBufferReferenced by macro
    • https://github.com/VBA-tools/VBA-JSONReferenced by macro
    • https://www.vbforums.com/showthread.php?640664-RESOLVED-Decimal-separatorReferenced by macro
    • http://www.vbaccelerator.com/home/VB/Code/Techniques/RunTime_Debug_Tracing/VB6_Tracer_Utility_zip_cStringBuilder_cls.aspReferenced by macro
    • https://github.com/VBA-tools/VBA-JSON/pull/82Referenced by macro
    • https://github.com/VBA-tools/VBA-UtcConverterReferenced by macro
    • https://stackoverflowReferenced by macro
    • https://github.com/libuv/libuv/blob/v1.x/src/win/process-stdio.cReferenced by macro
    • https://@github@$mReferenced by macro
    • https://chromedriver.chromium.org/capabilitiesReferenced by macro
    • https://docs.microsoft.com/en-us/microsoft-edge/webdriver-chromium/capabilities-edge-optionsReferenced by macro
    • https://firefox-source-docs.mozilla.org/testing/geckodriver/TraceLogs.htmlReferenced by macro
    • https://www.w3.org/TR/webdriver/#element-displayednessReferenced by macro
    • https://stackoverflow.com/questions/78787332/selecting-default-search-engine-is-needed-for-chrome-version-127/78788229#78788229Referenced by macro
    • https://stackoverflow.com/questions/497094/how-do-i-find-out-which-dom-element-has-the-focus/40873560#40873560Referenced by macro
    • https://groups.google.com/a/mozilla.org/g/dev-webdriver/c/DcYrCVCWMLUReferenced by macro
    • https://chromedevtools.github.io/devtools-protocol/tot/Browser/#method-setDownloadBehaviorReferenced by macro
    • https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/win32-processReferenced by macro
    • https://bugzilla.mozilla.org/show_bug.cgi?id=1797871Referenced by macro
    • http://www.w3.org/1999/XSL/TransformReferenced by macro
    • https://msedgedriver.microsoft.com/LATEST_Referenced by macro
    • https://googlechromelabs.github.io/chrome-for-testing/LATEST_RELEASE_Referenced by macro
    • https://chromedriver.chromium.org/downloads/version-selectionReferenced by macro
    • https://groups.google.com/g/chromedriver-users/c/clpipqvOGjEReferenced by macro
    • https://blogs.windows.com/msedgedev/2019/11/04/edge-chromium-release-candidate-get-ready/Referenced by macro
    • https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/Referenced by macro
    • https://msedgedriver.microsoft.com/LATEST_RELEASE_Referenced by macro
    • https://googlechromelabs.github.io/chrome-for-testing/latest-patch-versions-per-build.jsonReferenced by macro
    • https://firefox-source-docs.mozilla.org/testing/geckodriver/Support.htmlReferenced by macro
    +27 more URL(s)

Extracted artifacts 3

Files carved from inside the sample during analysis.

FilenameKindSourceSize
macros.bas vba-macro oletools.olevba.extract_macros (decoded VBA source from OOXML) 818455 bytes
SHA-256: b19ff382ee9542227c5d154302359fe9b3fdbef04517f72859663f4bc5b5e855
Preview script
First 1,000 lines of the extracted script
Attribute VB_Name = "ThisWorkbook"
Attribute VB_Base = "0{00020819-0000-0000-C000-000000000046}"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = True
Attribute VB_TemplateDerived = False
Attribute VB_Customizable = True

Attribute VB_Name = "A_EmbedObjects"
Attribute VB_Base = "0{00020820-0000-0000-C000-000000000046}"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = True
Attribute VB_TemplateDerived = False
Attribute VB_Customizable = True
Attribute VB_Control = "Image1, 3, 2, MSForms, Image"

Attribute VB_Name = "A_WebApp"
Option Explicit
Option Private Module
#If VBA7 Then
Private Declare PtrSafe Function ShellExecute Lib "shell32.dll" Alias "ShellExecuteA" (ByVal hWnd As LongPtr, ByVal lpOperation As String, ByVal lpFile As String, ByVal lpParameters As String, ByVal lpDirectory As String, ByVal nShowCmd As Long) As LongPtr

#Else
Private Declare Function ShellExecute Lib "SHELL32.DLL" Alias "ShellExecuteA" (ByVal hwnd As Long, ByVal lpOperation As String, ByVal lpFile As String, ByVal lpParameters As String, ByVal lpDirectory As String, ByVal nShowCmd As Long) As Long

#End If

Public Const MAINPORT9515 = 9515
Public Const MAINPORT9222 = 9222
Public Const vExe$ = ".e" & "xe"
Public Const vHoops$ = "ht" & "tps" & ":" & "//"
Public Const vHoop$ = "ht" & "tp" & ":" & "//"

' add your procedures here
Sub SEWebAppClose(Optional ByRef driver As Object)
  Dim i As Variant, p As Variant
  On Error Resume Next
  If Not driver Is Nothing Then
    For i = driver.Windows.count To 1 Step -1
      driver.Windows(i).Close
    Next:
    Set driver = Nothing
  End If
  For Each p In classLibWinmgmts(selectWin32Process & " WHERE (Name = ""chromedriver" & vExe & _
                """ Or Name = ""chrome" & vExe & """ Or Name = ""msedge" & vExe & _
                """ Or Name = ""msedgedriver" & vExe & """ Or Name = ""edgedriver" & vExe & """)")
    Select Case p.Name
    Case "chrome" & vExe & "", "msedge" & vExe & "":
      If p.commandLine Like "*--remote-debugging-port=*--user-data-dir=*" Then CallByName p, "Termi" & "nate", VbMethod
    Case "chromedriver" & vExe & "", "msedgedriver" & vExe & "", "edgedriver" & vExe & "": CallByName p, "Termi" & "nate", VbMethod
    End Select
  Next
  Err.Clear
End Sub

Sub SEWebAppCloseAll(Optional ByVal edge As Boolean)
  Dim i As Variant, p As Variant
  On Error Resume Next
  For Each p In classLibWinmgmts(selectWin32Process & " WHERE (" & IIf(edge, "Name = 'msedge" & vExe & "' Or Name = 'msedgedriver" & vExe & "' Or Name = 'edgedriver" & vExe & "'", "Name = 'chromedriver" & vExe & "' Or Name = 'chrome" & vExe & "'") & ")")
     CallByName p, "Termi" & "nate", VbMethod
  Next
  Err.Clear
End Sub

Sub SeleniumWebDriverClose()
  On Error Resume Next
  Dim p As Variant
  For Each p In classLibWinmgmts(selectWin32Process & "WHERE Name = 'chromedriver" & vExe & _
                  "' or Name = 'edgedriver" & vExe & "' or Name = 'msedgedriver" & vExe & "'")
    CallByName p, "Termi" & "nate", VbMethod
  Next
End Sub


Function isWebAppOpenByPort(Optional ByVal port As Long, Optional ByVal bClose As Boolean) As Boolean
  On Error Resume Next
  Dim p As Variant, s$
                                              
 ' Array("Chrome", "msEdge", "Firefox", "Browser", "Chromium", "Opera", "Brave", "Vivaldi", "whale", "")
  For Each p In classLibWinmgmts(selectWin32Process & _
      "WHERE ((Name = ""chrome" & vExe & """ or Name = ""msedge" & vExe & """ or Name = ""browser" & vExe & """" & _
            " or Name = ""vivaldi" & vExe & """ or Name = ""brave" & vExe & """ or Name = ""chromium" & vExe & """) " & _
      "And commandline like ""%--remote-debugging-port=" & port & " %"")")
    isWebAppOpenByPort = True: If bClose Then CallByName p, "Termi" + "nate", VbMethod
    Exit For
  Next
  Err.Clear
End Function


Private Sub SeleniumWebConnect_test()
  Dim Selen As WebDriver
  'Set Selen = CreateWebdriver
  'Set Selen = CreateEdgeDriver
  'Selen.Start "edge"
  'Selen.NavigateTo vHoops & "24h.com.vn/" ', 200, False
  Const scl = 1
  SeleniumWebConnect deviceScale:=scl, driver:=Selen, boolStart:=True, boolApp:=True, url:=vHoops & "google.com/", checkUrl:=True
End Sub
Function SeleniumWebConnect(Optional ByRef driver As WebDriver, Optional ByRef boolStart As Boolean, Optional ByVal url As String, _
                            Optional ByVal newWindow As Boolean, Optional ByVal headless As Boolean, Optional ByVal indexPage As Integer = 1, _
                            Optional ByRef boolWait As Boolean, Optional ByRef checkUrl As Boolean, Optional ByVal startIfExists As Boolean, _
                            Optional ByVal refreshIfExists As Boolean, Optional ByVal startNewTab As Boolean, Optional ByVal boolApp As Boolean, _
                            Optional ByVal popupBlocking As Boolean, Optional ByVal visible = vbNormalFocus, Optional ByVal maximize As Boolean, _
                            Optional ByVal position As String = "", Optional ByVal WindowSize As String = "", Optional ByVal disableGPU As Boolean = True, _
                            Optional ByVal boolClose As Boolean, Optional ByVal port As Long, _
                            Optional ByVal UserDataDir As String, Optional ByVal PrivateMode As Boolean, Optional ByVal deviceScale! = 0, _
                            Optional ByVal proxy_bypass_list$) As WebConnect
  'https://peter.sh/experiments/chromium-command-line-switches/
  Dim wc As WebConnect
  Set wc = New WebConnect
  With wc
    .url = url
    .boolApp = boolApp
    .checkUrl = checkUrl:
    .UserDataDir = UserDataDir
    .refreshIfExists = refreshIfExists
    .deviceScale = deviceScale
    .port = port
    Set driver = .Connect(driver)
    checkUrl = .checkUrl
  End With
  Set SeleniumWebConnect = wc
End Function



Function runSeleniumDriverWithPort(Optional ByVal cmdLine$, Optional ByVal path$, Optional withEdge As Boolean) As LongPtr
  On Error Resume Next
  If withEdge Then RunAsAdmin path, cmdLine Else Shell path & " " & cmdLine, vbHide
  Err.Clear
End Function
Public Function hiddenGetWebAppHandleByProcessID(Optional ByVal port$ = MAINPORT9515, Optional ByVal UserDataDir$) As LongPtr
  Dim p As Variant
  For Each p In classLibWinmgmts("SELECT Name,commandline,Processid FROM Win32_Process " & _
          "WHERE (Name = ""chrome" & vExe & """ or Name = ""msedge" & vExe & """) and " & _
          "(comma" & "nd" & "line Like ""%--remote-debugging-port=" & port & "%--user-data-dir=%" & UserDataDir & "%"")")
    '  hiddenGetWebAppHandleByProcessID = InstanceToWnd(p.processId, Class:="Chrome_WidgetWin_1")
      If hiddenGetWebAppHandleByProcessID <> 0 Then Exit For
  Next
End Function


Function classLibWinmgmts(ByVal query$) As Variant
  Set classLibWinmgmts = Interaction.GetObject("winmgmts:\\.\root\CIMV2").ExecQuery(query, , 48)
End Function
Function selectWin32Process(Optional ByVal Conditions$) As Variant
  selectWin32Process = "SELECT * FROM Win32_Process " & Conditions
End Function

Private Sub RunAsAdmin(ByVal command As String, Optional ByVal parameters As String = vbNullString, Optional ByVal directory As String = vbNullString, Optional ByVal showCmd As Long = 0)
    ShellExecute 0, "runas", command, parameters, directory, showCmd
End Sub



Attribute VB_Name = "cSHA_dele"
Attribute VB_Base = "0{FCFB3D2A-A0FA-1068-A738-08002B3371B5}"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
Attribute VB_TemplateDerived = False
Attribute VB_Customizable = False
Option Explicit: Option Compare Text

#If VBA7 Then
Private Declare PtrSafe Function CryptAcquireContext Lib "advapi32.dll" Alias "CryptAcquireContextA" (ByRef phProv As LongPtr, ByVal pszContainer As String, ByVal pszProvider As String, ByVal dwProvType As Long, ByVal dwFlags As Long) As Long
Private Declare PtrSafe Function CryptReleaseContext Lib "advapi32.dll" (ByVal hProv As LongPtr, ByVal dwFlags As Long) As Long
Private Declare PtrSafe Function CryptCreateHash Lib "advapi32.dll" (ByVal hProv As LongPtr, ByVal Algid As Long, ByVal hKey As LongPtr, ByVal dwFlags As Long, ByRef phHash As LongPtr) As Long
Private Declare PtrSafe Function CryptDestroyHash Lib "advapi32.dll" (ByVal hHash As LongPtr) As Long
Private Declare PtrSafe Function CryptHashData Lib "advapi32.dll" (ByVal hHash As LongPtr, pbData As Any, ByVal cbData As Long, ByVal dwFlags As Long) As Long
Private Declare PtrSafe Function CryptGetHashParam Lib "advapi32.dll" (ByVal hHash As LongPtr, ByVal dwParam As Long, pbData As Any, ByRef pcbData As Long, ByVal dwFlags As Long) As Long
#Else
Private Declare  Function CryptAcquireContext Lib "advapi32.dll" Alias "CryptAcquireContextA" (ByRef phProv As Long, ByVal pszContainer As String, ByVal pszProvider As String, ByVal dwProvType As Long, ByVal dwFlags As Long) As Long
Private Declare  Function CryptReleaseContext Lib "advapi32.dll" (ByVal hProv As Long, ByVal dwFlags As Long) As Long
Private Declare  Function CryptCreateHash Lib "advapi32.dll" (ByVal hProv As Long, ByVal Algid As Long, ByVal hKey As Long, ByVal dwFlags As Long, ByRef phHash As Long) As Long
Private Declare  Function CryptDestroyHash Lib "advapi32.dll" (ByVal hHash As Long) As Long
Private Declare  Function CryptHashData Lib "advapi32.dll" (ByVal hHash As Long, pbData As Any, ByVal cbData As Long, ByVal dwFlags As Long) As Long
Private Declare  Function CryptGetHashParam Lib "advapi32.dll" (ByVal hHash As Long, ByVal dwParam As Long, pbData As Any, ByRef pcbData As Long, ByVal dwFlags As Long) As Long
#End If


Private Const PROV_RSA_FULL   As Long = 1
Private Const PROV_RSA_AES    As Long = 24
Private Const CRYPT_VERIFYCONTEXT As Long = &HF0000000

Private Const HP_HASHVAL      As Long = 2
Private Const HP_HASHSIZE     As Long = 4

Private Const ALG_TYPE_ANY    As Long = 0
Private Const ALG_CLASS_HASH  As Long = 32768

Private Const ALG_SID_MD2     As Long = 1
Private Const ALG_SID_MD4     As Long = 2
Private Const ALG_SID_MD5     As Long = 3
Private Const ALG_SID_SHA     As Long = 4
Private Const ALG_SID_SHA_256 As Long = 12
Private Const ALG_SID_SHA_384 As Long = 13
Private Const ALG_SID_SHA_512 As Long = 14

Private Const CALG_MD2        As Long = (ALG_CLASS_HASH Or ALG_TYPE_ANY Or ALG_SID_MD2)
Private Const CALG_MD4        As Long = (ALG_CLASS_HASH Or ALG_TYPE_ANY Or ALG_SID_MD4)
Private Const CALG_MD5        As Long = (ALG_CLASS_HASH Or ALG_TYPE_ANY Or ALG_SID_MD5)
Private Const CALG_SHA        As Long = (ALG_CLASS_HASH Or ALG_TYPE_ANY Or ALG_SID_SHA)
Private Const CALG_SHA_256    As Long = (ALG_CLASS_HASH Or ALG_TYPE_ANY Or ALG_SID_SHA_256)
Private Const CALG_SHA_384    As Long = (ALG_CLASS_HASH Or ALG_TYPE_ANY Or ALG_SID_SHA_384)
Private Const CALG_SHA_512    As Long = (ALG_CLASS_HASH Or ALG_TYPE_ANY Or ALG_SID_SHA_512)

' Create Hash
Private Function CreateHash(abytData() As Byte, ByVal lngAlgID As Long) As String
  Dim hProv As LongPtr
  Dim hHash As LongPtr
  Dim abytHash(0 To 63) As Byte
  Dim lngLength As Long
  Dim lngResult As Long
  Dim strHash As String
  Dim i As Long
  Dim z As Long
  strHash = ""
  z = 0
  If CryptAcquireContext(hProv, vbNullString, vbNullString, IIf(lngAlgID >= CALG_SHA_256, PROV_RSA_AES, PROV_RSA_FULL), CRYPT_VERIFYCONTEXT) <> 0& Then
    If CryptCreateHash(hProv, lngAlgID, 0&, 0&, hHash) <> 0& Then
      lngLength = UBound(abytData()) - LBound(abytData()) + 1
      If lngLength > 0 Then lngResult = CryptHashData(hHash, abytData(LBound(abytData())), lngLength, 0&) Else: lngResult = CryptHashData(hHash, ByVal 0&, 0&, 0&)
      If lngResult <> 0& Then
        lngLength = UBound(abytHash()) - LBound(abytHash()) + 1
        If CryptGetHashParam(hHash, HP_HASHVAL, abytHash(LBound(abytHash())), lngLength, z) <> 0& Then
          For i = 0 To lngLength - 1
            strHash = strHash & Right$("0" & Hex$(abytHash(LBound(abytHash()) + i)), 2)
          Next
        End If
      End If
      CryptDestroyHash hHash
    End If
    CryptReleaseContext hProv, 0&
  End If
  CreateHash = LCase(strHash)
End Function
' Create Hash From String(Shift_JIS)
Private Function CreateHashString(ByVal strData As String, ByVal lngAlgID As Long) As String
    CreateHashString = CreateHash(StrConv(strData, vbFromUnicode), lngAlgID)
End Function
Public Function SHA1(ByVal s As String) As String
  SHA1 = CreateHashString(s, CALG_SHA)
  If SHA1 = "" Then
    Dim Hash() As Byte, i As Integer
    With Interaction.CreateObject("System.Security.Cryptography.SHA1CryptoServiceProvider")
      Hash = .ComputeHash_2(Interaction.CreateObject("System.Text.UTF8Encoding").GetBytes_4(s))
    End With
    Dim x As New StringBuilder
    For i = LBound(Hash) To UBound(Hash)
        x.Append Hex(Hash(i) \ 16)
        x.Append Hex(Hash(i) Mod 16)
    Next
    SHA1 = x.value
  End If
End Function









Attribute VB_Name = "A_License"
' SeleniumVBA v7.2
' ==========================================================================
' MIT License
'
' Copyright (c) 2023-2026, GCUser99 and 6DiegoDiego9 (https://github.com/GCuser99/SeleniumVBA)
'
' Permission is hereby granted, free of charge, to any person obtaining a copy
' of this software and associated documentation files (the "Software"), to deal
' in the Software without restriction, including without limitation the rights
' to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
' copies of the Software, and to permit persons to whom the Software is
' furnished to do so, subject to the following conditions:
'
' The above copyright notice and this permission notice shall be included in all
' copies or substantial portions of the Software.
'
' THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
' IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
' FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
' AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
' LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
' OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
' SOFTWARE.
' ==========================================================================

Attribute VB_Name = "WebElements"
Attribute VB_Base = "0{FCFB3D2A-A0FA-1068-A738-08002B3371B5}"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = True
Attribute VB_TemplateDerived = False
Attribute VB_Customizable = False
Attribute VB_Description = "This class is used for managing a collection of WebElement objects"
'@ModuleDescription "This class is used for managing a collection of WebElement objects"
'@Exposed
'@folder("Source")
' ==========================================================================
'
' This class was written to support collection of WebElement objects
'
' ==========================================================================

Option Explicit

Private colWebElement As Collection

Public Function last() As WebElement
  If colWebElement.count Then Set last = colWebElement(colWebElement.count)
End Function
'@Description("Adds an element to the collection")
Public Function Add(ByVal element As WebElement) As WebElement
Attribute Add.VB_Description = "Adds an element to the collection"
    colWebElement.Add element, element.elementId
    Set Add = element
End Function

'@Description("Returns the number of elements in the collection")
Public Property Get count() As Long
Attribute count.VB_Description = "Returns the number of elements in the collection"
   count = colWebElement.count
End Property

'@Description("Returns the number of elements in the collection")
Public Property Get SIZE() As Long
Attribute SIZE.VB_Description = "Returns the number of elements in the collection"
   SIZE = colWebElement.count
End Property

'@Description("Removes an element corresponding to the given index or element object")
Public Sub Remove(indexOrWebElement As Variant)
Attribute Remove.VB_Description = "Removes an element corresponding to the given index or element object"
    If TypeName(indexOrWebElement) = "WebElement" Then
        Dim elem As WebElement
        Set elem = indexOrWebElement 'Friend property ElementID can not be invoked with late binding
        colWebElement.Remove elem.elementId
    Else
        colWebElement.Remove indexOrWebElement
    End If
End Sub

'@Description("Returns the element corresponding to the given index")
'@DefaultMember
Public Property Get Item(ByVal index As Variant) As WebElement
Attribute Item.VB_Description = "Returns the element corresponding to the given index"
Attribute Item.VB_UserMemId = 0
    Set Item = colWebElement.Item(index)
End Property

'@Description("Returns True if element object is already in the collection, False otherwise")
Public Function exists(ByVal element As WebElement) As Boolean
Attribute exists.VB_Description = "Returns True if element object is already in the collection, False otherwise"
    On Error GoTo notFound
    exists = True
    colWebElement.Item element.elementId
    Exit Function
notFound:
    exists = False
End Function

'@Description("Removes all member element objects from the collection")
Public Sub RemoveAll()
Attribute RemoveAll.VB_Description = "Removes all member element objects from the collection"
    Do While colWebElement.count > 0
        colWebElement.Remove 1
    Loop
End Sub

'@Description("Highlights this element collection with color")
Public Function Highlight(Optional ByVal borderSizePx As Byte = 4, Optional ByVal borderColor As VBAcolors = Cyan, _
                     Optional ByVal backgroundColor As VBAcolors = VBAcolors.Unchanged, _
                     Optional ByVal ScrollIntoView As Boolean = True, Optional ByVal unHighlightLast As Boolean = True) _
                     As WebElements
Attribute Highlight.VB_Description = "Highlights this element collection with color"
    Dim i As Long
    colWebElement(1).Highlight borderSizePx, borderColor, backgroundColor, ScrollIntoView, unHighlightLast
    For i = 2 To colWebElement.count
        colWebElement(i).Highlight borderSizePx, borderColor, backgroundColor, ScrollIntoView, False
    Next i
    Set Highlight = Me
End Function

'@Description("Unhighlights this element collection")
Public Sub UnHighlight()
Attribute UnHighlight.VB_Description = "Unhighlights this element collection"
    Dim elem As WebElement
    For Each elem In colWebElement
        elem.UnHighlight
    Next elem
End Sub

'@Enumerator
Public Function NewEnum() As IUnknown
Attribute NewEnum.VB_UserMemId = -4
    Set NewEnum = colWebElement.[_NewEnum]
End Function

Private Sub Class_Initialize()
    Set colWebElement = New Collection
End Sub

Private Sub Class_Terminate()
    Set colWebElement = Nothing
End Sub

Attribute VB_Name = "WebCapabilities"
Attribute VB_Base = "0{FCFB3D2A-A0FA-1068-A738-08002B3371B5}"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = True
Attribute VB_TemplateDerived = False
Attribute VB_Customizable = False
Attribute VB_Description = "This class is used to manage/set Selenium optional Capabilities - must be instantiated using WebDriver's CreateCapabilities method"
'@ModuleDescription "This class is used to manage/set Selenium optional Capabilities - must be instantiated using WebDriver's CreateCapabilities method"
'@Exposed
'@folder("Source")

' Specs for Capabilities:
'
' - Chrome: https://chromedriver.chromium.org/capabilities
' - Edge: https://docs.microsoft.com/en-us/microsoft-edge/webdriver-chromium/capabilities-edge-options
'
' Notes:
'
' - WebCapabilities object should be instantiated using WebDriver.CreateCapabilities
' - It should be instantiated after the StartEdge, StartChrome, or StartFirefox methods,
' - and passed to OpenBrowser method, as in following example...
'
' Example:
'
'   Dim caps as WebCapabilities, driver as new WebDriver
'
'   driver.StartEdge
'   Set caps = driver.MakeCapabilities
'   caps.RunInvisble
'   driver.OpenBrowser caps
'
' The OpenBrowser method of WebDriver class has optional input parameters that can be used to avoid directly
'   instantiating the Capabilities object:
'
'   invisible (Boolean): If set to True (default=False), adds the --headless argument to the default Capabilities object
'   incognito (Boolean): If set to True (default=False), adds the --Incognito argument to the default Capabilities object
'   capabilitiesFilePath (String): If specified, initializes the default Capabilities object with a previously saved json file
'   (see: SaveToFile method of the WebCapabilities class, and example in test_Capabilities module)
'

Option Explicit

Private data_ As New Dictionary
Private browserOptionKey As String
Private browser_ As svbaBrowser
Private driver_ As WebDriver

Public Enum svbaAlertHandling
    svbaAccept = 0
    svbaDismiss = 1
    svbaIgnore = 2
    svbaDismissAndNotify = 3
    svbaAcceptAndNotify = 4
End Enum

Public Enum svbaPageLoadStrategy
    svbaNormal = 0
    svbaEager = 1
    svbaNone = 2
End Enum


Public Property Get self() As Object
  Set self = Me
End Property
'-------------------------------------------------------------------------
' Capability Settings
'-------------------------------------------------------------------------

'@Description("Sets a generic capability")
Public Sub SetCapability(ByVal Key As String, val As Variant)
Attribute SetCapability.VB_Description = "Sets a generic capability"
    If IsObject(val) Then
        Set data_.Item("alwaysMatch")(Key) = val
    Else
        If Left$(val, 1) = "[" Or Left$(val, 1) = "{" Then 'its a json string - try to parse
            Set data_.Item("alwaysMatch")(Key) = WebJsonConverter.ParseJson(val)
        Else 'its a string parameter
            data_.Item("alwaysMatch")(Key) = val
        End If
    End If
End Sub

'@Description("Sets the acceptInsecureCerts capability")
Public Sub SetAcceptInsecureCerts(ByVal val As Boolean)
Attribute SetAcceptInsecureCerts.VB_Description = "Sets the acceptInsecureCerts capability"
    Me.SetCapability "acceptInsecureCerts", val
End Sub

'@Description("Sets the pageLoadStrategy capability - defaults to normal")
Public Sub SetPageLoadStrategy(ByVal val As svbaPageLoadStrategy)
Attribute SetPageLoadStrategy.VB_Description = "Sets the pageLoadStrategy capability - defaults to normal"
    Select Case val
    Case svbaPageLoadStrategy.svbaNormal
        Me.SetCapability "pageLoadStrategy", "normal"
    Case svbaPageLoadStrategy.svbaEager
        Me.SetCapability "pageLoadStrategy", "eager"
    Case svbaPageLoadStrategy.svbaNone
        Me.SetCapability "pageLoadStrategy", "none"
    End Select
End Sub

'@Description("Sets the unhandledPromptBehavior capability - defaults to dismiss and notify")
Public Sub SetUnhandledPromptBehavior(ByVal val As svbaAlertHandling)
Attribute SetUnhandledPromptBehavior.VB_Description = "Sets the unhandledPromptBehavior capability - defaults to dismiss and notify"
    Me.SetCapability "unhandledPromptBehavior", getAlertHandlingString(val)
End Sub

'@Description("Enables BiDirectional WebDriver Protocol")
Public Sub EnableBiDiMode()
Attribute EnableBiDiMode.VB_Description = "Enables BiDirectional WebDriver Protocol"
    Me.SetCapability "webSocketUrl", True
    WebJsonConverter.OptionsKeepStringifiedNumbersAsStrings = True
End Sub

'-------------------------------------------------------------------------
' Option Settings
'-------------------------------------------------------------------------

'@Description("Sets an option key/value")
Public Sub SetOption(ByVal Key As String, val As Variant)
Attribute SetOption.VB_Description = "Sets an option key/value"
    'input val can be a string, a json string, array, or an object (dictionary or collection)
    If IsObject(val) Then 'collection or dictionary
        Set data_.Item("alwaysMatch")(browserOptionKey)(Key) = val
    Else
        If IsArray(val) Then
            data_.Item("alwaysMatch")(browserOptionKey)(Key) = val
        Else 'string
            If Left$(val, 1) = "[" Or Left$(val, 1) = "{" Then 'its a json string - try to parse
                Set data_.Item("alwaysMatch")(browserOptionKey)(Key) = WebJsonConverter.ParseJson(val)
            Else 'its a string parameter
                data_.Item("alwaysMatch")(browserOptionKey)(Key) = val
            End If
        End If
    End If
End Sub

'@Description("Adds a list of option excludeSwitches")
Public Sub AddExcludeSwitches(ParamArray AddList() As Variant)
Attribute AddExcludeSwitches.VB_Description = "Adds a list of option excludeSwitches"
    Dim varry() As Variant
    varry = AddList
    addToOptionList "excludeSwitches", paramListToStringArray(varry)
End Sub

'@Description("Adds a list of option windowTypes")
Public Sub AddWindowTypes(ParamArray AddList() As Variant)
Attribute AddWindowTypes.VB_Description = "Adds a list of option windowTypes"
    Dim varry() As Variant
    varry = AddList
    addToOptionList "windowTypes", paramListToStringArray(varry)
End Sub

'@Description("Adds a list of option extensions")
Public Sub AddExtensions(ParamArray AddList() As Variant)
Attribute AddExtensions.VB_Description = "Adds a list of option extensions"
    Dim varry() As Variant
    Dim pathList() As String
    Dim extList() As String
    Dim i As Long
    Dim fso As Object
    Dim filePath As String
    Select Case browser_
    Case svbaBrowser.chrome, svbaBrowser.edge
        varry = AddList
        pathList = paramListToStringArray(varry)
        ReDim extList(1 To UBound(pathList))
        For i = 1 To UBound(pathList)
            filePath = driver_.ResolvePath(pathList(i), True)
            If Not FileExists(filePath) Then Err.Raise 1, "WebCapabilities", "The extension file path :" & vbNewLine & vbNewLine & filePath & vbNewLine & vbNewLine & "does not exist - please correct the path provided."
            extList(i) = encodeBase64(readByteArrayFromFile(filePath), , False)
        Next i
        addToOptionList "extensions", extList
    Case svbaBrowser.Firefox
        Err.Raise 1, "WebCapabilities", "The AddExtensions method is not supported by the Firefox browser - use InstallAddon method of Webdriver class instead"
    End Select
End Sub

'@Description("Sets the path to the browser executable in a non-standard location")
Public Sub SetBrowserBinary(ByVal browserPath As String)
Attribute SetBrowserBinary.VB_Description = "Sets the path to the browser executable in a non-standard location"
    'set the path to the browser executable to use
    Me.SetOption "binary", driver_.ResolvePath(browserPath, True)
End Sub

'@Description("Removes control notification for Edge/Chrome")
Public Sub RemoveControlNotification()
Attribute RemoveControlNotification.VB_Description = "Removes control notification for Edge/Chrome"
    'this removes the "Chrome/Edge is being controlled by automated test software" browser notification
    Select Case browser_
    Case svbaBrowser.chrome, svbaBrowser.edge
        Me.AddExcludeSwitches "enable-automation"
        Me.SetOption "useAutomationExtension", False
        'for msedge, you may be greeted with an unwanted "Sync your profile" dialog
        'to get rid of that, run in incognito mode
    Case svbaBrowser.Firefox
        Err.Raise 1, "WebCapabilities", "The RemoveControlNotification method is not supported by the Firefox browser."
    End Select
End Sub

'@Description("Sets the detach option for Edge/Chrome")
Public Sub SetDetachBrowser(ByVal val As Boolean)
Attribute SetDetachBrowser.VB_Description = "Sets the detach option for Edge/Chrome"
    'this sets whether browser is closed (false) or left open (true) when the driver is sent the shutdown command
    Select Case browser_
    Case svbaBrowser.chrome, svbaBrowser.edge
        If Not (driver_.CommandWindowStyle = vbHide Or driver_.CommandWindowStyle = vbMinimizedNoFocus) Then _
            Err.Raise 1, "WebCapabilities", "The SetDetachBrowser method is not compatible with command window style - use vbHide or vbMinimizeNoFocus."
        Me.SetOption "detach", val
    Case svbaBrowser.Firefox
        Err.Raise 1, "WebCapabilities", "The SetDetachBrowser method is not supported by the Firefox browser."
    End Select
End Sub

'@Description("Sets the Debugger Address option for Edge/Chrome needed to connect with an already existing browser")
Public Sub SetDebuggerAddress(Optional ByVal val As String = "localhost:9222")
Attribute SetDebuggerAddress.VB_Description = "Sets the Debugger Address option for Edge/Chrome needed to connect with an already existing browser"
    Select Case browser_
    Case svbaBrowser.chrome, svbaBrowser.edge
        Me.SetOption "debuggerAddress", val
    Case svbaBrowser.Firefox
        Err.Raise 1, "WebCapabilities", "The SetDebuggerAddress method is not supported by the Firefox browser."
    End Select
End Sub

'@Description("Adds a list of experimental browser flags (edge://flags, chrome://flags). Flags must end in @0 (default), @1 (enable), or @2 (disable)")
Public Sub AddLocalStateFlags(ParamArray flagsList() As Variant)
Attribute AddLocalStateFlags.VB_Description = "Adds a list of experimental browser flags (edge://flags, chrome://flags). Flags must end in @0 (default), @1 (enable), or @2 (disable)"
    Dim Flags As Collection
    Dim localState As New Dictionary
    Dim browser As New Dictionary
    Dim varry() As Variant
    Dim AddList() As String
    Dim flag As String
    varry = flagsList
    AddList = paramListToStringArray(varry)
    
    If browser_ <> chrome And browser_ <> edge Then Err.Raise 1, , "Error in SetLocalStateFlag - only valid for Edge and Chrome browsers."
    
    'build the enabled_labs_experiments structure if needed
    If Not data_.Item("alwaysMatch")(browserOptionKey).exists("localState") Then
        browser.Add "enabled_labs_experiments", New Collection
        localState.Add "browser", browser
        SetOption "localState", localState
    Else
        If Not data_.Item("alwaysMatch")(browserOptionKey)("localState").exists("browser") Then
            browser.Add "enabled_labs_experiments", New Collection
            data_.Item("alwaysMatch")(browserOptionKey)("localState").Add "browser", browser
        Else
            If Not data_.Item("alwaysMatch")(browserOptionKey)("localState")("browser").exists("enabled_labs_experiments") Then
                data_.Item("alwaysMatch")(browserOptionKey)("localState")("browser").Add "enabled_labs_experiments", New Collection
            End If
        End If
    End If
    
    'get the current flags collection
    Set Flags = data_.Item("alwaysMatch")(browserOptionKey)("localState")("browser")("enabled_labs_experiments")
    
    'check if the flag already exists in the collection, and if so, remove it
    Dim i As Long
    Dim j As Long
    For j = 1 To UBound(AddList)
        flag = AddList(j)
        For i = 1 To Flags.count
            If Split(Flags.Item(i), "@")(0) = Split(flag, "@")(0) Then
                Flags.Remove i
                Exit For
            End If
        Next i
    Next j
    
    'add the flags
    'this does not guard against user specifying same flag twice
    For j = 1 To UBound(AddList)
        Flags.Add AddList(j)
    Next j

    'reset the modified flags collection
    Set data_.Item("alwaysMatch")(browserOptionKey)("localState")("browser")("enabled_labs_experiments") = Flags
End Sub

'-------------------------------------------------------------------------
' Option Argument Settings
'-------------------------------------------------------------------------

'@Description("Adds a list of option arguments")
Public Sub AddArguments(ParamArray AddList() As Variant)
Attribute AddArguments.VB_Description = "Adds a list of option arguments"
    Dim varry() As Variant
    varry = AddList
    addToOptionList "args", paramListToStringArray(varry)
End Sub

'@Description("Runs headless argument mode")
Public Sub RunInvisible()
Attribute RunInvisible.VB_Description = "Runs headless argument mode"
    Select Case browser_
    Case svbaBrowser.chrome, svbaBrowser.edge
        Me.AddArguments "--headless=new"
    Case svbaBrowser.Firefox
        Me.AddArguments "--headless"
    End Select
End Sub

'@Description("Runs incognito/private argument mode")
Public Sub RunIncognito()
Attribute RunIncognito.VB_Description = "Runs incognito/private argument mode"
    Select Case browser_
    Case svbaBrowser.chrome
        Me.AddArguments "--incognito"
    Case svbaBrowser.edge
        Me.AddArguments "--inprivate"
    Case svbaBrowser.Firefox
        Me.AddArguments "-private"
    End Select
End Sub

'@Description("Runs guest argument mode")
Public Sub RunAsGuest()
Attribute RunAsGuest.VB_Description = "Runs guest argument mode"
    Select Case browser_
    Case svbaBrowser.chrome, svbaBrowser.edge
        Me.AddArguments "--guest"
    Case svbaBrowser.Firefox
        Err.Raise 1, "WebCapabilities", "The RunAsGuest method is not supported by the Firefox browser."
    End Select
End Sub

'@Description("Sets a user-defined profile path")
Public Sub SetProfile(ByVal profilePath As String)
Attribute SetProfile.VB_Description = "Sets a user-defined profile path"
    'this sets a user-defined profile - it will create and populate the profile directory even if it does not exist
    'it is recommended not to make profiles for SeleniumVBA in the default C:\Users\[user]\AppData\Local\Google\Chrome\User Data
    'Instead copy profiles needed from the default location into a different location to access with SeleniumVBA
    Dim fso As Object
    Dim userDir As String
    Dim profileName As String
    
    profilePath = driver_.ResolvePath(profilePath, False)
    
    Select Case browser_
    Case svbaBrowser.chrome, svbaBrowser.edge
        userDir = fso.GetParentFolderName(profilePath)
        profileName = fso.GetBaseName(profilePath)
        Me.AddArguments "--user-data-dir=" & userDir
        Me.AddArguments "--profile-directory=" & profileName
    Case svbaBrowser.Firefox
        'unlike edge and chrome, firefox requires that the folder to exist
        'hence check and if doesn't exist, then create it
        CreateFolder profilePath
        Me.AddArguments "-profile", profilePath
    End Select
End Sub

'@Description("Sets the user agent string")
Public Sub SetUserAgent(ByVal val As String)
Attribute SetUserAgent.VB_Description = "Sets the user agent string"
    Select Case browser_
    Case svbaBrowser.chrome, svbaBrowser.edge
        Me.AddArguments "--user-agent=" & val
    Case svbaBrowser.Firefox
        Me.SetPreference "general.useragent.override", val
    End Select
End Sub

'-------------------------------------------------------------------------
' Option Preference Settings
'-------------------------------------------------------------------------

'@Description("Sets an option preference key/value")
Public Sub SetPreference(ByVal Key As String, ByVal val As Variant)
Attribute SetPreference.VB_Description = "Sets an option preference key/value"
    'input val can be a string, a json string, or an object (dictionary)
    If Not data_.Item("alwaysMatch")(browserOptionKey).exists("prefs") Then
        SetOption "prefs", New Dictionary
    End If
    
    'need to come up with a more generalized approach to paths - this is just a band-aid
    Select Case Key
    Case "download.default_directory", "browser.download.dir", "savefile.default_directory"
        val = driver_.ResolvePath(val, True)
    Case Else
    End Select
    
    If IsObject(val) Then
        Set data_.Item("alwaysMatch")(browserOptionKey)("prefs")(Key) = val
    Else
        If Left$(val, 1) = "[" Or Left$(val, 1) = "{" Then 'it's json string - try to parse
            'convert to dictionary or collection
            Set data_.Item("alwaysMatch")(browserOptionKey)("prefs")(Key) = WebJsonConverter.ParseJson(val)
        Else
            data_.Item("alwaysMatch")(browserOptionKey)("prefs")(Key) = val
        End If
    End If
End Sub

'@Description("Sets auto-download preferences and directory path")
Public Sub SetDownloadPrefs(Optional ByVal downloadFolderPath As String = ".\", _
                            Optional ByVal promptForDownload As Boolean = False, _
                            Optional ByVal disablePDFViewer As Boolean = True)
Attribute SetDownloadPrefs.VB_Description = "Sets auto-download preferences and directory path"
                            
    downloadFolderPath = driver_.ResolvePath(downloadFolderPath, True)

    Select Case browser_
    Case svbaBrowser.chrome, svbaBrowser.edge
        Me.SetPreference "download.default_directory", downloadFolderPath
        Me.SetPreference "download.directory_upgrade", True
        Me.SetPreference "safebrowsing.enabled", True
        Me.SetPreference "download.prompt_for_download", promptForDownload 'false disables prompting user for download permission
        Me.SetPreference "plugins.always_open_pdf_externally", disablePDFViewer 'instead of using the pdf viewer
        'Me.SetPreference "browser.set_download_behavior", "allow"
        'Me.SetPreference "safebrowsing.disable_download_protection", True
        'Me.SetPreference "profile.default_content_settings.popups", False '0
        'Me.SetPreference "profile.default_content_settings.javascript", True '1
        'Me.SetPreference "profile.default_content_setting_values.notifications", 2
        'Me.SetPreference "profile.content_settings.exceptions.automatic_downloads.*.setting", True '1
        'Me.SetPreference "profile.default_content_setting_values.automatic_downloads", True '1
    Case svbaBrowser.Firefox
        Me.SetPreference "browser.download.folderList", 2 '0 is Desktop, 1 is Downloads
        Me.SetPreference "browser.download.dir", downloadFolderPath
        Me.SetPreference "pdfjs.disabled", disablePDFViewer  'disable the built-in PDF viewer
        
        'Me.SetPreference "browser.download.manager.showWhenStarting", False
        'Me.SetPreference "browser.helperApps.neverAsk.saveToDisk", "application/pdf;text/plain;text/csv;application/text;text/xml;application/xml;application/csv;pplication/octet-stream doc xls pdf txt"
        'Me.SetPreference "browser.download.useDownloadDir", True
        'Me.SetPreference "browser.download.viewableInternally.enabledTypes", ""
    End Select
End Sub

'@Description("Sets whether or not to allow geolocation awareness")
Public Sub SetGeolocationAware(Optional ByVal aware As Boolean = True)
Attribute SetGeolocationAware.VB_Description = "Sets whether or not to allow geolocation awareness"
    'this may be required, for example, if using incognito mode and
    'wanting navigated url to be aware of geolocation
    Select Case browser_
    Case svbaBrowser.chrome, svbaBrowser.edge
        Me.SetPreference "profile.managed_default_content_settings.geolocation", IIf(aware, 1, 2)
        'Me.SetPreference "googlegeolocationaccess.enabled", aware
        'Me.SetPreference "profile.default_content_setting_values.notifications", IIf(aware, 1, 2)
        'Me.SetPreference "profile.default_content_setting_values.geolocation", IIf(aware, 1, 2)
    Case svbaBrowser.Firefox
        Err.Raise 1, "WebCapabilities", "The SetGeolocationAware method is not supported by the Firefox browser."
    End Select
End Sub

'-------------------------------------------------------------------------
' Public helper methods
'-------------------------------------------------------------------------

'@DefaultMember
'@Description("Returns a capabilities Dictionary object")
Public Function Dictionary() As Object 'Dictionary
Attribute Dictionary.VB_Description = "Returns a capabilities Dictionary object"
Attribute Dictionary.VB_UserMemId = 0
    Set Dictionary = data_
End Function

'@Description("Returns Json string for qc")
Public Function ToJson() As String
Attribute ToJson.VB_Description = "Returns Json string for qc"
    ToJson = WebJsonConverter.ConvertToJson(data_, 4)
End Function

'@Description("Saves the capabilities to file path")
Public Sub SaveToFile(Optional ByVal filePath As String = ".\capabilities.json")
Attribute SaveToFile.VB_Description = "Saves the capabilities to file path"
    Dim fso As Object
    Dim ts As Object
    filePath = driver_.ResolvePath(filePath, False)
    Set ts = fso.CreateTextFile(filePath, True, True)
    ts.Write Me.ToJson
    ts.Close
End Sub

'@Description("Loads the capabilities from file path")
Public Sub LoadFromFile(Optional ByVal filePath As String = ".\capabilities.json")
Attribute LoadFromFile.VB_Description = "Loads the capabilities from file path"
    Dim fso As Object
    Dim ts As Object
        
    filePath = driver_.ResolvePath(filePath, True)
    
    If Not FileExists(filePath) Then Err.Raise 53, "WebCapabilities", "Capabilities Json file does not exist:" & vbNewLine & vbNewLine & filePath
    'note that this will fail if input file format is utf-8 with non-ascii chars - see https://github.com/GCuser99/SeleniumVBA/discussions/32
    Const TristateUseDefault = -2
    Set ts = fso.OpenTextFile(filePath, , , TristateUseDefault)
    Set data_ = WebJsonConverter.ParseJson(ts.ReadAll)
    ts.Close
    
    If data_.Item("browserName") <> WebShared.getBrowserNameString(browser_) Then Err.Raise 1, "WebCapabilities", "Browser name from file does not match browser name assigned to driver instance."
End Sub

'-------------------------------------------------------------------------
' Private helper methods
'-------------------------------------------------------------------------

Friend Sub initializeFor(ByVal browser As svbaBrowser)
    Set data_ = New Dictionary
    data_.Add "browserName", WebShared.getBrowserNameString(browser)
    data_.Add "alwaysMatch", New Dictionary
    browser_ = browser
    
    Dim browserOptions As New Dictionary
    
    Select Case browser
        Case svbaBrowser.chrome
            browserOptionKey = "goog:chromeOptions"
        Case svbaBrowser.edge
            browserOptionKey = "ms:edgeOptions"
        Case svbaBrowser.Firefox
            browserOptionKey = "moz:firefoxOptions"
    End Select
    
    data_.Item("alwaysMatch").Add browserOptionKey, browserOptions
End Sub

Friend Property Set WebDriver(ByRef obj As WebDriver)
    Set driver_ = obj
End Property

Private Function getAlertHandlingString(ByVal val As svbaAlertHandling) As String
    Select Case val
    Case svbaAlertHandling.svbaAccept
        getAlertHandlingString = "accept"
    Case svbaAlertHandling.svbaAcceptAndNotify
        getAlertHandlingString = "accept and notify"
    Case svbaAlertHandling.svbaDismiss
        getAlertHandlingString = "dismiss"
    Case svbaAlertHandling.svbaDismissAndNotify
        getAlertHandlingString = "dismiss and notify"
    Case svbaAlertHandling.svbaIgnore
        getAlertHandlingString = "ignore"
    End Select
End Function

Private Sub addToOptionList(ByVal optionName As String, AddList() As String)
     'add to list of option parameters, while checking for duplicates
     Dim parameters As Collection
     Dim found As Boolean
     Dim j As Long
     Dim i As Long

     'lists are represented as collections for caps initialized from LoadFromFile (via WebJsonConverter.ParseJson)
     If data_.Item("alwaysMatch")(browserOptionKey).exists(optionName) Then
         Set parameters = data_.Item("alwaysMatch")(browserOptionKey)(optionName)
     Else 'starting from scratch
         Set parameters = New Collection
     End If

     For i = LBound(AddList) To UBound(AddList)
         found = False
         For j = 1 To parameters.count
             If AddList(i) = parameters(j) Then found = True: Exit For
         Next j
         If Not found Then
             parameters.Add AddList(i)
         End If
     Next i
     'put the expanded list back
     If Not data_.Item("alwaysMatch")(browserOptionKey).exists(optionName) Then
         data_.Item("alwaysMatch")(browserOptionKey).Add optionName, parameters
     Else
         Set data_.Item("alwaysMatch")(browserOptionKey)(optionName) = parameters
     End If
 End Sub

Private Function paramListToStringArray(parmList() As Variant) As String()
    'a parameter array can include arrays as list elements - expand into a string array
    Dim i As Long
    Dim j As Long
    Dim k As Long
    Dim strlist() As String
    
    'count number of parameters in list
    k = 0
    For i = LBound(parmList) To UBound(parmList)
        If IsArray(parmList(i)) Then
            k = k + UBound(parmList(i)) - LBound(parmList(i)) + 1
        Else
            If Left$(parmList(i), 1) = "[" Then  'its a json string - try to parse
…
vbaProject_00.bin vba-project OOXML VBA project: xl/vbaProject.bin 4418560 bytes
SHA-256: 94a66603585a6a165bcd9179c47996eb329fa5770f83093bb1d3124e4af17ff5
emf_00.emf ooxml-emf OOXML EMF part: xl/media/image1.emf 5236 bytes
SHA-256: 3836c6f77ff9d39212c6bf1c872ddd3067c2ff02d596cd8271bdae2326e5ddd6