-
Notifications
You must be signed in to change notification settings - Fork 1
Home
hanamichi77777 edited this page Jul 15, 2026
·
19 revisions
````markdown
# 1 Implementation Procedure for SeleniumVBA (ver.7.2 or later)
(1) To enable WebSocket communication from VBA, please import the one standard module starting with "BiDi_" and the two class modules starting with "BiDi" into the SeleniumVBA file you wish to use.
<img width="582" height="449" alt="WebDriver BiDi" src="https://github.com/user-attachments/assets/efc66c94-8806-4a69-85c0-7c679a9b8ebf" />
(2) Add the following description to the main routine of your standard module. That is all. Introduction has become significantly easier from ver. 7.2.
```vb:VBA
Dim driver As WebDriver: Set driver = New WebDriver
With driver
' Start
.StartChrome
' Browser startup settings (for both Chrome and Edge)
Dim caps As WebCapabilities: Set caps = .CreateCapabilities
caps.EnableBiDiMode ' WebDriver BiDi becomes enabled
' Open
.OpenBrowser caps
' Perform WebSocket communication to use WebDriver BiDi
Dim bidi As New BiDiCommandWrapper
bidi.ConnectTo .GetWebSocketUrl
' Typing "bidi." will display BiDi-specific methods
bidi.
```
(3) Set the VBE error trapping to "Break on Unhandled Errors."

# 2 Wrapper Functions
In this article, I will introduce particularly powerful and practical major methods from the VBA class `BiDiCommandWrapper`, which fuses the WebDriver BiDi protocol and CDP (Chrome DevTools Protocol).
---
## 📋 Key Method List
| Category | Method Name | Overview (Detailed Version) |
| :--- | :--- | :--- |
| **Navigation** | `ExecuteNavigateAndGetStatus` | Checks HTTP status after URL transition. An SPA synchronization engine waits until communication (Net) and rendering (DOM) stop, suppressing errors immediately after transition. |
| **Element Operation** | `ExecuteClickByXPath` | Scrolls into the display area, focuses, and clicks. It automatically waits for communication to stabilize after the action, enabling extremely stable operation. |
| | `ExecuteInputValueByXPath` | Simulates input using `execCommand`. This ensures state updates in React, etc., solving the problem where values are not reflected. |
| | `ExecuteSelectValueByXPath` | Dropdown operation. Allows selection by display text as well as Value. It forcibly triggers events after selection to ensure display changes are triggered. |
| | `ExecuteRegisterAutoClickerByXPath` | Utilizes CDP to station a "silent observer" inside the browser. It clicks elements as soon as they appear without waiting for VBA communication. This is useful for high-speed SPA redirects or transient pop-ups. |
| **Special/Hierarchical** | `GetIframeContextIdByUrl` | **Identifies iframe by URL.** By passing the obtained ID to other methods, you can operate directly inside the frame without using SwitchTo. |
| | `ExecuteClickShadowBySelector` | Recursively searches and penetrates hidden **Shadow DOM** using an array of CSS selectors. This enables operation of modern sites with Web Components structures. |
| **SPA Synchronization** | `ArmContentSignal` | Opens a one-shot completion gate when the subtree of an **existing element is rewritten**. This prevents the SPA wait from ending before delayed rendering begins. |
| | `ArmNetworkSignal` | Opens a one-shot completion gate when a matching API response arrives. This is useful when a specific response provides reliable evidence that an operation has completed. |
| | `ArmVisibilitySignal` | Opens a one-shot completion gate when a new or hidden element becomes visible. This is useful for result panels, dialogs, and dynamically inserted controls. |
| **Management/Optimization** | `ExecuteEnableResourceBlocking` | Blocks the loading of images, ads, etc. Minimizes network load and dramatically improves execution speed. |
| | `ExecuteWebExtensionInstall` | **Installs Chrome extensions dynamically.** Allows integration of ad blockers or custom tools to extend browser functionality through the program. |
| **AI Collaboration** | `StartDiscoveryLog` | Records browser communication, errors, DOM changes, and SPA signal activity in detail. Outputs "AI-Ready" logs that LLMs such as Gemini or ChatGPT can use to diagnose causes and propose suitable wait conditions. |
---
## 🛠 Detailed Explanation of Each Method
### 1. Navigation
#### ExecuteNavigateAndGetStatus(targetUrl, [waitNetworkIdle], [contextId])
Navigates to the specified URL and returns success/failure (e.g., 200: Success, 500: Failure) based on the HTTP status code. **The internal SPA synchronization engine, which waits until both "network communication" and "DOM changes" stop**, dramatically suppresses element-not-found errors immediately after navigation.
* **targetUrl** (String): Destination URL.
* **waitNetworkIdle** (Boolean): Whether to wait until communication and DOM are completely stationary (Default: True).
* **contextId** (String): Used to specify a target tab or frame.
```VBA
Dim status As Long
status = bidi.ExecuteNavigateAndGetStatus("https://example.com/dashboard")
If status = 200 Then
Debug.Print "Transition and SPA rendering successful"
End If
```
### 2. Element Operation (Modern Framework Support)
#### ExecuteClickByXPath(xpath, [searchTimeoutMs], [waitNetworkIdle], ..., [contextId])
Ensures execution in the order of "Auto-scroll to view" → "Focus" → "Click". Because it automatically performs synchronous waiting until any asynchronous communication triggered by the operation ends, extremely stable continuous operation is possible.
* **xpath** (String): XPath to identify the target element.
* **searchTimeoutMs** (Long): Maximum wait time for the element to appear (milliseconds).
* **waitNetworkIdle** (Boolean): Whether to wait for communication updates after clicking.
```VBA
' Reliable click including scroll and communication wait
bidi.ExecuteClickByXPath "//button[@id='submit-order']"
```
#### ExecuteInputValueByXPath(xpath, valueToSet, [searchTimeoutMs], [waitNetworkIdle], ..., [contextId])
By internally utilizing JavaScript's `execCommand('insertText')`, it generates events equivalent to a human typing on a keyboard. This solves the issue where frameworks like React or Vue do not recognize simple `.value` overwrites because their internal state is not updated.
```VBA
' Input that ensures validation in React, etc. is triggered
bidi.ExecuteInputValueByXPath "//input[@id='username']", "vba_user"
```
#### ExecuteSelectValueByXPath(xpath, valueOrText, [selectByText], ..., [contextId])
Operates a dropdown (`select` element). Items can be selected not only by the internal Value but also by the text visible on the screen. It automatically triggers the necessary events after selection.
```VBA
' Select item by displayed text (March 2026)
bidi.ExecuteSelectValueByXPath _
"//select[@name='calselect']", _
"2026年03月", _
True
```
#### ExecuteRegisterAutoClickerByXPath(xpath, [timeoutMs])
Registers a persistent JavaScript-based `MutationObserver` via the CDP command `Page.addScriptToEvaluateOnNewDocument`. This script is injected and executed every time a new document or frame is initialized, even before the VBA code has a chance to send a "Find Element" command.
```VBA
' Example: Handle a "Save Successful" popup that might appear after any action
bidi.ExecuteRegisterAutoClickerByXPath "//button[text()='Dismiss']"
' Proceed with standard automation without worrying about the popup timing
bidi.ExecuteClickByXPath "//input[@id='save-records']"
```
### 3. Special/Hierarchical Operation (Shadow DOM & iframe)
#### ExecuteClickShadowBySelector(selectorsArray, [searchTimeoutMs], ..., [contextId])
**Penetrates and clicks through Shadow DOM (encapsulated internal structures)** that cannot be reached by normal XPath searches. You pass an array of CSS selectors required to reach the target as an argument.
```VBA
' Follow the Shadow Hosts in order to specify the final button
Dim path(2) As String
path(0) = "settings-ui" ' 1st host
path(1) = "settings-main" ' 2nd host
path(2) = "#confirm-button" ' Final target
bidi.ExecuteClickShadowBySelector path
```
#### GetIframeContextIdByUrl(partialUrl, [timeoutMs])
Identifies an iframe by a part of its URL and obtains an operation ID (`contextId`). By passing this ID to the `contextId` argument of any other method, you can operate directly within that frame.
```VBA
Dim conID As String
conID = bidi.GetIframeContextIdByUrl("login_frame")
' Click an element inside the frame by specifying the ID
bidi.ExecuteClickByXPath "//button[@id='submit']", , , , , conID
```
### 4. SPA Synchronization Signals
Ordinary SPA synchronization waits until network activity and DOM changes become quiet. However, some sites temporarily appear quiet before delayed rendering begins. This interval is called the **settle-to-render gap**.
The following three methods add explicit completion evidence to the next SPA synchronization wait.
| API | Completion Evidence | Trigger Type |
| :--- | :--- | :--- |
| `ArmContentSignal(xpath)` | An existing element's subtree is rewritten | Edge |
| `ArmNetworkSignal(pattern)` | A matching API response arrives | Event |
| `ArmVisibilitySignal(xpath)` | A new or hidden element becomes visible | Level, latched |
When a signal is detected, the DOM-quiet timer is reset. The wrapper then observes a complete `minStableMs` quiet period after the signal. This prevents the wait from ending during the settle-to-render gap without requiring a larger fixed wait threshold.
Signals are one-shot. They are consumed when the next SPA-idle wait ends, regardless of whether the operation succeeds or times out. No separate signal timeout is added; the operation's existing `maxTimeoutMs` remains the single deadline.
#### ArmContentSignal(xpath)
Monitors the subtree of an existing element and opens the completion gate when that subtree is rewritten.
This is suitable for existing tables, lists, grids, or result containers whose contents are replaced after an operation.
```VBA
' Evidence: the existing table body is rewritten
bidi.ArmContentSignal "//*[@id='table-body']"
' The signal applies to this click's synchronization wait
bidi.ExecuteClickByXPath yearTabXPath
```
The target element must already exist when the signal is armed. A missing target or invalid XPath raises an error immediately instead of silently degrading to an ungated wait.
#### ArmNetworkSignal(pattern)
Opens the completion gate when a matching API response is received through the WebDriver BiDi `network.responseCompleted` event.
The pattern uses the same notation as `AddIdleIgnoreNetworkPattern`. Matching is performed before Discovery Log or recorder filtering, so a filtered response can still be used as completion evidence.
```VBA
' Evidence: the API response responsible for loading the results has arrived
bidi.ArmNetworkSignal "rpcids=BVAT3"
bidi.ExecuteClickByXPath searchButtonXPath
```
This method is useful when a specific API response provides clearer completion evidence than general network-idle detection.
#### ArmVisibilitySignal(xpath)
Opens the completion gate when an element matching the XPath becomes visible.
This is suitable for result panels, completion messages, dialogs, buttons, and other controls that appear after an asynchronous operation.
```VBA
' Evidence: the results panel becomes visible
bidi.ArmVisibilitySignal "//*[@id='search-results']"
bidi.ExecuteClickByXPath searchButtonXPath
```
The target must not already be visible when the signal is armed. An already-visible target or invalid XPath raises an error immediately, preventing old page state from being treated as evidence for the next operation.
#### Combining Multiple Signals
Multiple signal types can be armed together. They are combined with **AND semantics**, meaning that all armed signals must be detected before the SPA synchronization wait can complete.
```VBA
' Require both the API response and the resulting visible panel
bidi.ArmNetworkSignal "GetShoppingResults"
bidi.ArmVisibilitySignal "//*[@id='flight-results']"
bidi.ExecuteClickByXPath searchButtonXPath
```
This is useful when both server-side completion and browser-side rendering must be confirmed.
#### DisarmSignal
Clears all currently armed signals.
```VBA
bidi.DisarmSignal
```
#### Signal Diagnostics
Signal activity is recorded in the diagnostic timeline.
```text
[SPA-SIGNAL-ARM]
[SPA-SIGNAL-HIT]
[SPA-SIGNAL-ARM-FAIL]
[SPA-SIGNAL-LOST]
[SPA-SIGNAL-DISARM]
```
The `[SPA-WAIT-BEGIN]` and `[SPA-WAIT-END]` lines also include signal status.
```text
signal=net:hit,dom:miss
```
The `quietAtHit=` value shows how much apparent quiet time was discarded when the signal reset the DOM-quiet timer. These records help determine whether the selected signal is appropriate for the target SPA operation.
### 5. Management, Debugging, & AI Collaboration
#### ExecuteEnableResourceBlocking(patterns)
Blocks images, ads, tracking communications, etc., at the browser level. By eliminating unnecessary communication, execution speed is dramatically improved.
```VBA
' Accelerate by blocking images and ad domains
Dim blockList
blockList = Array( _
"*.png", _
"*.jpg", _
"*google-analytics.com*", _
"*doubleclick*" _
)
bidi.ExecuteEnableResourceBlocking blockList
```
#### ExecuteWebExtensionInstall(extensionPath)
Installs a Chrome extension from the specified path into the current browser session. This allows for dynamic integration of ad blockers, security enhancements, or specific business auxiliary tools within the automation process.
* **extensionPath** (String): File path to the extension.
```VBA
' Settings to enable Chrome extensions
caps.AddArguments "--remote-debugging-pipe"
caps.AddArguments "--enable-unsafe-extension-debugging"
' Dynamically install a Chrome extension
bidi.ExecuteWebExtensionInstall _
Environ("LOCALAPPDATA") & _
"\Google\Chrome\User Data\Default\Extensions\" & _
"aapbdbdomjkkjkaonfhkkikfgjllcleb\2.0.16_0"
```
#### StartDiscoveryLog([excludeImagesAndCss])
Records events occurring inside the browser in an "AI-decipherable format." By feeding the saved logs to Gemini or ChatGPT, you can achieve "AI-Ready" debugging where error causes and suitable synchronization conditions can be analyzed.
The Discovery Log records network responses, DOM changes, suppressed background noise, SPA stability margins, and signal activity. This makes it easier to determine:
* Which API responses should be monitored with `ArmNetworkSignal`
* Which containers should be monitored with `ArmContentSignal`
* Which elements are suitable for `ArmVisibilitySignal`
* Which background communications should be ignored
* Which resources may be safely blocked
* Whether the current wait thresholds are appropriate
```VBA
' Start log recording (excluding noisy image/CSS logs)
bidi.StartDiscoveryLog excludeImagesAndCss:=True
' (Execute operations...)
' Save the log. Passing this to AI supports diagnosis and wait-condition design.
bidi.StopAndSaveDiscoveryLog "C:\temp\discovery_log.txt"
```
In other words, the Discovery Log is not merely an execution log. It is a diagnostic tool for discovering what should be waited for when automating unknown third-party SPA sites.
# 3 Operation from a launched browser
Based on experiments with the following code, I confirmed that WebDriver BiDi can be enabled and events can be detected even from an already launched instance of Edge.
```VBA
'=================================================
' Automatically operate a browser in a logged-in state via remote debugging.
' Start from an already launched state via a shortcut.
'=================================================
' Set the shortcut target as follows:
' "C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe" --remote-debugging-port=9222 --user-data-dir="C:\EdgeDebugProfile"
'=================================================
Dim caps As SeleniumVBA.WebCapabilities
Set driver = SeleniumVBA.New_WebDriver
driver.StartEdge
Set caps = driver.CreateCapabilities(initializeFromSettingsFile:=False)
caps.SetDebuggerAddress "localhost:9222"
' Enable BiDi (Required to be True for this program)
caps.EnableBiDiMode
driver.OpenBrowser caps
' (Continued...)
```
# 4 The JavaScript-First Strategy
In the `BiDiCommandWrapper` class, we adopt a strategy of "delegating to JavaScript" via `script.callFunction` rather than firing repetitive, native BiDi commands.
This approach compensates for VBA’s single-threaded nature and minimizes communication overhead. Below are the three primary advantages of offloading processing to JavaScript.
### 1. Drastic Reduction in Round-trips (Preventing VBA Freezes)
The greatest challenge in VBA automation is the "Excel is not responding" state caused by synchronous communication latency.
A typical sequence—Find Element → Get Coordinates → Scroll → Click—requires at least four round-trips if executed using individual BiDi commands.
By wrapping this logic into a single JavaScript snippet and sending it via `script.callFunction`, the entire process is completed internally by the browser. VBA only needs to send one command and wait for a single "success" response, significantly reducing the risk of UI freezing.
### 2. Minimizing Data "Translation" Costs
The raw data returned by WebDriver BiDi often consists of deeply nested and complex JSON structures. Parsing these in VBA is CPU-intensive and leads to overly complicated code.
To avoid this, we let the JavaScript side process the data and return a simplified string, such as:
```JavaScript
JSON.stringify({status: 'ok', value: isVisible})
```
Consequently, the VBA side only needs to handle a simple string—often using basic regular expressions—making the overall processing remarkably lightweight.
### 3. Implementing Advanced Logic Beyond BiDi Standards
The current WebDriver BiDi specification is still evolving and lacks high-level features that are essential for stable automation, such as "Is the element visible to the user?" (Visibility) or "Has network activity ceased?" (Network Idle).
Advanced monitoring—such as using `MutationObserver` to track DOM changes or hooking into `fetch`/XHR to monitor traffic—can only be performed by JavaScript within the browser context.
We utilize BiDi primarily as a "high-speed tunnel" to inject JavaScript, allowing the browser’s native capabilities to handle the complex logic.
### Summary
By offloading logic to JavaScript, SeleniumVBA remains shielded from communication bottlenecks and complex parsing logic. This ensures a stable, responsive, and robust browser control experience within the VBA environment.