feat(cua-driver-rs)(windows)(#1620): auto-inject Chromium anti-throttling flags in launch_app#1624
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughThis PR consolidates HTML test fixtures from both Swift and Rust ports into a shared canonical directory, updates Windows platform driver behavior for hidden Chromium browsers and UIA interactions, and documents fixture coverage and browser compatibility gaps. ChangesCanonical fixture consolidation and documentation
Windows platform improvements
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes The PR spans fixture creation (straightforward HTML/documentation), fixture reorganization (systematic pointer/symlink changes across two port directories), and two independent Windows driver features (Chromium detection+flag injection with comprehensive tests, and UIA selection+invocation logic with moderate complexity). While the total file count is moderately high, each section is coherent and reviewable in sequence without extensive cross-file reasoning. Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@libs/cua-driver-fixtures/gesture_panels.html`:
- Around line 98-105: The drag event history array dragEvents is persistent
across runs; reset it at the start of each drag by reinitializing dragEvents =
[] inside the src.addEventListener('dragstart', ...) handler before pushing the
first 'dragstart', then update
document.getElementById('drag-status').textContent as before so each drag run is
deterministic and doesn't include stale events.
In `@libs/cua-driver-fixtures/README.md`:
- Around line 30-43: Add a language tag to the fenced code block that contains
the directory tree beginning with "libs/cua-driver/Tests/integration/" (the
README.md snippet showing fixtures/assets and cua-driver-rs paths); change the
opening triple backticks to include a language token such as "text" (e.g.,
```text) so markdownlint MD040 is satisfied while leaving the block content
unchanged.
In `@libs/cua-driver-rs/crates/platform-windows/src/tools/impl_.rs`:
- Around line 837-843: The Chromium anti-throttling flags are skipped for
launches that supply a non-AUMID bundle_id because the current condition only
looks at launch_path_opt, path_opt, or name_opt; update the conditional so
launches with a bundle_id also enter the Chromium-injection branch.
Specifically, in the block that checks if launch_path_opt.is_some() ||
path_opt.is_some() || name_opt.is_some(), include the bundle_id option (or
ensure name_opt is set from bundle_id) so that when let Some(t) =
target.as_deref() and is_chromium_browser_target(t) is true you still call
inject_chromium_anti_throttling_flags(&mut extra_args); keep the same
target/is_chromium_browser_target and inject_chromium_anti_throttling_flags
calls but broaden the presence check to include bundle_id.
In `@libs/cua-driver-rs/crates/platform-windows/src/uia/windows_enum.rs`:
- Around line 182-211: The inserted helper is now between the existing doc
comment and pub fn try_invoke_in_window_at_point, so the long doc got attached
to is_coord_independent_action and try_invoke_in_window_at_point is left
undocumented and stale; fix by moving is_coord_independent_action (and its short
doc) either before the big doc block or after pub fn
try_invoke_in_window_at_point so the /// block correctly documents
try_invoke_in_window_at_point, and update the doc text to mention that the
routine accepts InvokePattern OR ExpandCollapsePattern and that it filters by
coord-independent control types (reference the functions
is_coord_independent_action and try_invoke_in_window_at_point and the
Invoke/Expand patterns in the updated wording).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: beb8adff-e4d3-49b5-a02b-eef5e127dc66
📒 Files selected for processing (17)
libs/cua-driver-fixtures/README.mdlibs/cua-driver-fixtures/form_all_inputs.htmllibs/cua-driver-fixtures/gesture_panels.htmllibs/cua-driver-fixtures/interactive.htmllibs/cua-driver-fixtures/test_page.htmllibs/cua-driver-rs/crates/platform-windows/src/tools/impl_.rslibs/cua-driver-rs/crates/platform-windows/src/uia/windows_enum.rslibs/cua-driver-rs/tests/integration/fixtures/interactive.htmllibs/cua-driver-rs/tests/integration/fixtures/interactive.htmllibs/cua-driver-rs/tests/integration/v2/assets/test_page.htmllibs/cua-driver-rs/tests/integration/v2/assets/test_page.htmllibs/cua-driver/Tests/integration/assets/test_page.htmllibs/cua-driver/Tests/integration/assets/test_page.htmllibs/cua-driver/Tests/integration/fixtures/form_all_inputs.htmllibs/cua-driver/Tests/integration/fixtures/form_all_inputs.htmllibs/cua-driver/Tests/integration/fixtures/interactive.htmllibs/cua-driver/Tests/integration/fixtures/interactive.html
| var dragEvents = []; | ||
| var src = document.getElementById('drag-source'); | ||
| var tgt = document.getElementById('drag-target'); | ||
| src.addEventListener('dragstart', function(e) { | ||
| dragEvents.push('dragstart'); | ||
| e.dataTransfer.setData('text/plain', 'DRAG ME'); | ||
| document.getElementById('drag-status').textContent = 'drag: ' + dragEvents.join(' → '); | ||
| }); |
There was a problem hiding this comment.
Reset drag event history per drag run.
dragEvents keeps growing across multiple drags, so later assertions may include stale events. Reinitialize it in dragstart to keep each probe deterministic.
Suggested diff
src.addEventListener('dragstart', function(e) {
+ dragEvents = [];
dragEvents.push('dragstart');
e.dataTransfer.setData('text/plain', 'DRAG ME');
document.getElementById('drag-status').textContent = 'drag: ' + dragEvents.join(' → ');
});📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| var dragEvents = []; | |
| var src = document.getElementById('drag-source'); | |
| var tgt = document.getElementById('drag-target'); | |
| src.addEventListener('dragstart', function(e) { | |
| dragEvents.push('dragstart'); | |
| e.dataTransfer.setData('text/plain', 'DRAG ME'); | |
| document.getElementById('drag-status').textContent = 'drag: ' + dragEvents.join(' → '); | |
| }); | |
| var dragEvents = []; | |
| var src = document.getElementById('drag-source'); | |
| var tgt = document.getElementById('drag-target'); | |
| src.addEventListener('dragstart', function(e) { | |
| dragEvents = []; | |
| dragEvents.push('dragstart'); | |
| e.dataTransfer.setData('text/plain', 'DRAG ME'); | |
| document.getElementById('drag-status').textContent = 'drag: ' + dragEvents.join(' → '); | |
| }); |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@libs/cua-driver-fixtures/gesture_panels.html` around lines 98 - 105, The drag
event history array dragEvents is persistent across runs; reset it at the start
of each drag by reinitializing dragEvents = [] inside the
src.addEventListener('dragstart', ...) handler before pushing the first
'dragstart', then update document.getElementById('drag-status').textContent as
before so each drag run is deterministic and doesn't include stale events.
| ``` | ||
| libs/cua-driver/Tests/integration/ | ||
| ├── fixtures/ | ||
| │ ├── interactive.html → ../../../../cua-driver-fixtures/interactive.html | ||
| │ └── form_all_inputs.html → ../../../../cua-driver-fixtures/form_all_inputs.html | ||
| └── assets/ | ||
| └── test_page.html → ../../../../cua-driver-fixtures/test_page.html | ||
|
|
||
| libs/cua-driver-rs/tests/integration/ | ||
| ├── fixtures/ | ||
| │ └── interactive.html → ../../../../cua-driver-fixtures/interactive.html | ||
| └── v2/assets/ | ||
| └── test_page.html → ../../../../../cua-driver-fixtures/test_page.html | ||
| ``` |
There was a problem hiding this comment.
Add a language tag to the fenced code block.
The block starting at Line 30 is missing a fence language, which triggers markdownlint MD040.
Suggested diff
-```
+```text
libs/cua-driver/Tests/integration/
├── fixtures/
│ ├── interactive.html → ../../../../cua-driver-fixtures/interactive.html
│ └── form_all_inputs.html → ../../../../cua-driver-fixtures/form_all_inputs.html
└── assets/
└── test_page.html → ../../../../cua-driver-fixtures/test_page.html
libs/cua-driver-rs/tests/integration/
├── fixtures/
│ └── interactive.html → ../../../../cua-driver-fixtures/interactive.html
└── v2/assets/
└── test_page.html → ../../../../../cua-driver-fixtures/test_page.html</details>
<!-- suggestion_start -->
<details>
<summary>📝 Committable suggestion</summary>
> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
```suggestion
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)
[warning] 30-30: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@libs/cua-driver-fixtures/README.md` around lines 30 - 43, Add a language tag
to the fenced code block that contains the directory tree beginning with
"libs/cua-driver/Tests/integration/" (the README.md snippet showing
fixtures/assets and cua-driver-rs paths); change the opening triple backticks to
include a language token such as "text" (e.g., ```text) so markdownlint MD040 is
satisfied while leaving the block content unchanged.
| if launch_path_opt.is_some() || path_opt.is_some() || name_opt.is_some() { | ||
| if let Some(t) = target.as_deref() { | ||
| if is_chromium_browser_target(t) { | ||
| inject_chromium_anti_throttling_flags(&mut extra_args); | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Include non-AUMID bundle_id launches in the Chromium injection path.
Line 837 excludes bundle_id, even though this tool documents non-AUMID bundle_id as a name alias. That means launch_app({ "bundle_id": "chrome" }) still goes through the ShellExecuteEx path without the anti-throttling flags, so hidden Chromium launches via that supported entry point keep the renderer-occlusion behavior this PR is meant to fix.
🔧 Suggested fix
- if launch_path_opt.is_some() || path_opt.is_some() || name_opt.is_some() {
+ let uses_shell_execute_target = launch_path_opt.is_some()
+ || path_opt.is_some()
+ || name_opt.is_some()
+ || bundle_id_opt
+ .as_deref()
+ .is_some_and(|s| !crate::launch_uwp::is_aumid(s));
+ if uses_shell_execute_target {
if let Some(t) = target.as_deref() {
if is_chromium_browser_target(t) {
inject_chromium_anti_throttling_flags(&mut extra_args);
}
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if launch_path_opt.is_some() || path_opt.is_some() || name_opt.is_some() { | |
| if let Some(t) = target.as_deref() { | |
| if is_chromium_browser_target(t) { | |
| inject_chromium_anti_throttling_flags(&mut extra_args); | |
| } | |
| } | |
| } | |
| let uses_shell_execute_target = launch_path_opt.is_some() | |
| || path_opt.is_some() | |
| || name_opt.is_some() | |
| || bundle_id_opt | |
| .as_deref() | |
| .is_some_and(|s| !crate::launch_uwp::is_aumid(s)); | |
| if uses_shell_execute_target { | |
| if let Some(t) = target.as_deref() { | |
| if is_chromium_browser_target(t) { | |
| inject_chromium_anti_throttling_flags(&mut extra_args); | |
| } | |
| } | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@libs/cua-driver-rs/crates/platform-windows/src/tools/impl_.rs` around lines
837 - 843, The Chromium anti-throttling flags are skipped for launches that
supply a non-AUMID bundle_id because the current condition only looks at
launch_path_opt, path_opt, or name_opt; update the conditional so launches with
a bundle_id also enter the Chromium-injection branch. Specifically, in the block
that checks if launch_path_opt.is_some() || path_opt.is_some() ||
name_opt.is_some(), include the bundle_id option (or ensure name_opt is set from
bundle_id) so that when let Some(t) = target.as_deref() and
is_chromium_browser_target(t) is true you still call
inject_chromium_anti_throttling_flags(&mut extra_args); keep the same
target/is_chromium_browser_target and inject_chromium_anti_throttling_flags
calls but broaden the presence check to include bundle_id.
| /// Returns `true` when the element's control type has a *coord-independent* | ||
| /// primary action — i.e. a UIA `Invoke()` on it does something semantically | ||
| /// equivalent to "click the element" regardless of where inside its bounding | ||
| /// rectangle the click was requested. | ||
| /// | ||
| /// Used by the `x, y` click path to decide whether to take the UIA Invoke | ||
| /// route or fall through to PostMessage with the literal coords. The split | ||
| /// matters for canvases, panes, and custom-drawn surfaces where Invoke would | ||
| /// fire `mousedown` at the element centre — losing the caller's pixel | ||
| /// precision (see #1621). | ||
| fn is_coord_independent_action(elem: &IUIAutomationElement) -> bool { | ||
| let ct: UIA_CONTROLTYPE_ID = match unsafe { elem.CurrentControlType() } { | ||
| Ok(t) => t, | ||
| Err(_) => return false, | ||
| }; | ||
| matches!( | ||
| ct, | ||
| UIA_ButtonControlTypeId | ||
| | UIA_MenuItemControlTypeId | ||
| | UIA_HyperlinkControlTypeId | ||
| | UIA_TabItemControlTypeId | ||
| | UIA_ListItemControlTypeId | ||
| | UIA_CheckBoxControlTypeId | ||
| | UIA_RadioButtonControlTypeId | ||
| | UIA_SplitButtonControlTypeId | ||
| | UIA_TreeItemControlTypeId | ||
| ) | ||
| } | ||
|
|
||
| pub fn try_invoke_in_window_at_point(hwnd: isize, sx: i32, sy: i32) -> bool { |
There was a problem hiding this comment.
New helper inserted between try_invoke_in_window_at_point's doc block and its definition — doc now misattributed and stale.
The pre-existing /// block at lines 142–181 was the doc for try_invoke_in_window_at_point. Rust attaches /// to the immediately following item, so by inserting is_coord_independent_action (line 192) between that doc and pub fn try_invoke_in_window_at_point (line 211), two things happen:
- The entire 142–191 block now documents
is_coord_independent_action, which is jarring (most of it describes a windowed UIA invoke walk, not a control-type classifier), andtry_invoke_in_window_at_pointis left undocumented. - The wording inside that block is now stale anyway — line 144 (
fire 'Invoke' on the deepest descendant ... AND which supports 'InvokePattern') and lines 178–181 (exposes 'InvokePattern'. Smallest-area approximates "deepest"...) no longer reflect the new behavior, which accepts InvokePattern OR ExpandCollapsePattern and filters by coord-independent control type.
Move the new helper out of the way (e.g. place is_coord_independent_action and its doc before the existing 142–181 block, or after try_invoke_in_window_at_point) and refresh the stale wording to mention ExpandCollapse + the coord-independent filter.
📝 Suggested re-ordering (place helper before the doc block)
+/// Returns `true` when the element's control type has a *coord-independent*
+/// primary action — i.e. a UIA `Invoke()` on it does something semantically
+/// equivalent to "click the element" regardless of where inside its bounding
+/// rectangle the click was requested.
+///
+/// Used by the `x, y` click path to decide whether to take the UIA Invoke
+/// route or fall through to PostMessage with the literal coords. The split
+/// matters for canvases, panes, and custom-drawn surfaces where Invoke would
+/// fire `mousedown` at the element centre — losing the caller's pixel
+/// precision (see `#1621`).
+fn is_coord_independent_action(elem: &IUIAutomationElement) -> bool {
+ let ct: UIA_CONTROLTYPE_ID = match unsafe { elem.CurrentControlType() } {
+ Ok(t) => t,
+ Err(_) => return false,
+ };
+ matches!(
+ ct,
+ UIA_ButtonControlTypeId
+ | UIA_MenuItemControlTypeId
+ | UIA_HyperlinkControlTypeId
+ | UIA_TabItemControlTypeId
+ | UIA_ListItemControlTypeId
+ | UIA_CheckBoxControlTypeId
+ | UIA_RadioButtonControlTypeId
+ | UIA_SplitButtonControlTypeId
+ | UIA_TreeItemControlTypeId
+ )
+}
+
/// Hit-test screen point `(sx, sy)` against the UIA subtree rooted at
-/// `hwnd` and fire `Invoke` on the deepest descendant whose bounding
-/// rect contains the point AND which supports `InvokePattern`. Returns
-/// `true` iff such an element was found and `Invoke()` succeeded.
+/// `hwnd` and activate the deepest descendant whose bounding rect contains
+/// the point AND which supports `InvokePattern` or `ExpandCollapsePattern`
+/// AND whose control type has a coord-independent primary action (see
+/// `is_coord_independent_action`). Returns `true` iff such an element was
+/// found and `Invoke()` / `Expand()` succeeded.
...
-/// Returns `true` when the element's control type has a *coord-independent*
-/// primary action — i.e. a UIA `Invoke()` on it does something semantically
-/// equivalent to "click the element" regardless of where inside its bounding
-/// rectangle the click was requested.
-///
-/// Used by the `x, y` click path to decide whether to take the UIA Invoke
-/// route or fall through to PostMessage with the literal coords. The split
-/// matters for canvases, panes, and custom-drawn surfaces where Invoke would
-/// fire `mousedown` at the element centre — losing the caller's pixel
-/// precision (see `#1621`).
-fn is_coord_independent_action(elem: &IUIAutomationElement) -> bool {
- ...
-}
-
pub fn try_invoke_in_window_at_point(hwnd: isize, sx: i32, sy: i32) -> bool {Also refresh lines 178–181 to reference Invoke/Expand and the coord-independent filter rather than just InvokePattern.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@libs/cua-driver-rs/crates/platform-windows/src/uia/windows_enum.rs` around
lines 182 - 211, The inserted helper is now between the existing doc comment and
pub fn try_invoke_in_window_at_point, so the long doc got attached to
is_coord_independent_action and try_invoke_in_window_at_point is left
undocumented and stale; fix by moving is_coord_independent_action (and its short
doc) either before the big doc block or after pub fn
try_invoke_in_window_at_point so the /// block correctly documents
try_invoke_in_window_at_point, and update the doc text to mention that the
routine accepts InvokePattern OR ExpandCollapsePattern and that it filters by
coord-independent control types (reference the functions
is_coord_independent_action and try_invoke_in_window_at_point and the
Invoke/Expand patterns in the updated wording).
…ling flags in launch_app `launch_app` uses `SW_SHOWNOACTIVATE` so launched windows don't steal focus. For Chromium-based browsers (Edge / Chrome / Brave / Vivaldi / Opera / Chromium / Arc / Thorium / Iridium / Yandex) this triggers occlusion-based renderer throttling: the renderer process is suspended for the *entire* tab lifetime, the UIA tree exposes only browser chrome, and `PrintWindow` returns a blank body. Downstream tools (`get_window_state`, `screenshot`, `click`, `type_text`) all fail silently against the page content. ## Fix When `launch_app` resolves a target naming a Chromium-based browser, auto-prepend three flags to `additional_arguments`: ``` --disable-features=CalculateNativeWinOcclusion ← root cause --disable-backgrounding-occluded-windows ← backstop 1 (process priority) --disable-renderer-backgrounding ← backstop 2 (renderer throttle) ``` `CalculateNativeWinOcclusion` is the root cause; the two `--disable-backgrounding-*` flags backstop the same effect through the process-priority and renderer-throttling layers because Chromium suspends renderers on multiple signals. Injecting all three matches the flag set documented at Chromium's `chrome://flags`. Two helpers, both pure logic: - **`is_chromium_browser_target(target)`** — matches the executable basename (case-insensitive, with/without `.exe`) against the known Chromium browser names. Handles bare names (`"msedge"`), full paths (`r"C:\...\msedge.exe"`), forward-slash paths, and round-tripped launch paths with trailing arguments (`r#""C:\...\chrome.exe" --profile-directory=..."#`). Uses `split_launchable_target` to peel args off launch_path-style targets. - **`inject_chromium_anti_throttling_flags(extra_args)`** — prepends the three flags. Idempotent: if `--disable-features=` already exists in the caller's args, merges `CalculateNativeWinOcclusion` into it (Chromium has subtle merging rules across duplicate `--disable-features` entries — collapsing into one entry avoids ambiguity). The boolean flags are only inserted when absent. ## Where the injection runs After target resolution in `LaunchAppTool::run`, gated on the target having been resolved from `launch_path` / `path` / `name` (i.e. the ShellExecuteExW path). UWP/AUMID routing is skipped because the packaged Edge channel routes differently and the modern Edge ships as a desktop install that hits the ShellExecuteExW path here. ## Tests 8 new unit tests under `chromium_flag_injection_tests`: - `detects_bare_browser_names` — all 10 known names match (case-insensitive) - `detects_full_paths` — both `C:\...` and `C:/...` separators - `detects_launch_path_with_trailing_args` — `"<exe>" <args>` round-trip - `does_not_match_non_chromium_apps` — firefox, notepad, explorer, code, soffice - `injects_three_flags_into_empty_args` — base case - `merges_into_existing_disable_features_list` — `--disable-features=Foo,Bar` + injection = single `--disable-features=Foo,Bar,CalculateNativeWinOcclusion` - `idempotent_when_all_flags_already_present` — second call is a no-op - `preserves_user_url_argument_after_flags` — URL stays in args after injection All 8 pass on the VM (13.77s test compile, 0.00s test execution). ## E2E verification (against #1619's canonical `test_page.html`) ``` launch_app(path='msedge', additional_arguments=['file:///C:/...test_page.html']) → pid 6708, returned without page DOM get_window_state (after Chromium lazy-builds the tree on first AT probe) → 33 elements, includes: Document "CUA Driver Test Page v2" Button "Click Me" id=clicker actions=[invoke] screenshot → fully painted page (Button + Text Input + Checkbox + Dropdown all visible), not a blanked body ``` Edge launched non-foreground via `SW_SHOWNOACTIVATE`; renderer was NOT occlusion-throttled; DOM constructed, exposed via UIA, and painted — exactly the regression-prevention case the fix targets. Closes #1620. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
46f72c9 to
3c19a7a
Compare
Summary
launch_appusesSW_SHOWNOACTIVATEso launched windows don't steal focus. For Chromium-based browsers (Edge / Chrome / Brave / Vivaldi / Opera / Chromium / Arc / Thorium / Iridium / Yandex) that triggers occlusion-based renderer throttling — the renderer process is suspended for the entire tab lifetime, the UIA tree exposes only browser chrome, andPrintWindowreturns a blank body. Downstream tools (get_window_state,screenshot,click,type_text) all fail silently against the page content.This PR makes
launch_appauto-inject three flags whenever the resolved target names a Chromium-based browser:Callers don't need to know about the Chromium quirk; the driver handles it.
Implementation
Two pure-logic helpers + a small dispatch hook in
LaunchAppTool::run:is_chromium_browser_target(target)— case-insensitive basename match against the known Chromium browser names. Handles bare names ("msedge"), full paths (r"C:\...\msedge.exe"), forward-slash paths, and round-tripped launch paths with trailing arguments (r#""C:\...\chrome.exe" --profile-directory="..."#). Usessplit_launchable_targetto peel args off launch_path-style targets.inject_chromium_anti_throttling_flags(extra_args)— prepends the three flags. Idempotent: if--disable-features=already exists, mergesCalculateNativeWinOcclusioninto it (Chromium has subtle merging rules across duplicate--disable-features=entries — collapsing into one entry avoids ambiguity). Boolean flags only inserted when absent.The injection runs after target resolution in
LaunchAppTool::run, gated on the target coming fromlaunch_path/path/name(the ShellExecuteExW path). UWP/AUMID routing is skipped; the modern Edge ships as a desktop install that goes through the ShellExecuteExW path here.Tests
8 new unit tests under
chromium_flag_injection_testscovering:.exe)C:\...\msedge.exe)--disable-features=Foo,BarAll 8 pass on the VM (13.77s test compile, 0.00s test exec).
E2E verification
Against
libs/cua-driver-fixtures/test_page.html(the canonical Swift harness, now shared per #1619):Edge launched non-foreground via
SW_SHOWNOACTIVATE; renderer was NOT occlusion-throttled; DOM constructed, exposed via UIA, and painted — the regression-prevention case the fix targets.Related
test_page.htmlthis verifies against)cua-driver-uiaworker)Closes #1620.
Test plan
cargo test -p platform-windows --lib chromium_flag_injection_tests— 8 tests pass on the VMcargo build --release -p cua-driverclean (24.79s)launch_app(path='msedge', additional_arguments=['file:///...'])without flags → page DOM exposed via UIA + screenshot shows rendered content🤖 Generated with Claude Code
Summary by CodeRabbit
Documentation
New Features
Improvements