Repository navigation
fix(snapshot): list a browser's own controls in the UI tree - #614
Open
tanishqbaweja wants to merge 1 commit into
Open
tanishqbaweja wants to merge 1 commit into
tanishqbaweja wants to merge 1 commit into
Conversation
Snapshot answers with the UI tree. For a browser window, get_nodes() walked the window without a semantic parent and built its tree afterwards from the page's DOM lists alone, so nothing else the walk found reached the answer: the tabs, the address bar, the toolbar buttons and the browser's own dialogs were captured, numbered for label= and boxed on the annotated screenshot, but not listed. A Firefox window, whose page has no RootWebArea and is walked like the rest of the window, got its window line alone. use_dom=True is documented to give "web page elements instead of browser UI", yet without it a browser window's tree was the same page-only tree. get_nodes() now gives every window its semantic node before the walk. Without use_dom the walk attaches a browser's own controls to it, as it does for any app, and the page's elements and text follow in page order, as before. With use_dom=True the walk attaches nothing to a browser window, so its tree is still the page alone. Other windows are unchanged.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Snapshot answers with the UI tree (
UI Tree:), so an element left out of the tree is left out of the answer, and with the defaultuse_vision=Falsethe tree is the only list of elements the agent gets. For a browser window,Tree.get_nodes()walks the window with no semantic parent (window_sem_nodewas created onlyif not is_browser) and builds the window's tree afterwards from the page's DOM lists alone. Everything else the walk finds is captured, numbered forlabel=and boxed on the annotated screenshot, but not listed:RootWebArea(as the IA2 fallback's comment says), so it is walked like the rest of the window, and the IA2 fallback that fills the DOM lists runs only withuse_dom=True. Its tree is the window line alone.Snapshot's description says "Set use_dom=True for browser content to get web page elements instead of browser UI", and the README says DOM mode is "filtering out browser UI elements". Without
use_dom, though, a browser window's tree is the same page-only tree. Before 8c6c670 replaced it with the UI tree, Snapshot printed the flat list ofinteractive_nodes, which still holds these controls.Reproduction
At 106b50c on Windows 11 (build 22631) with Python 3.14.9, with mocks only. A script built two browser windows out of MagicMock UIA elements that hold the cached properties the walk reads: a toolbar ("App bar") with Back and the address bar, above a page with a text and a link. The Chrome window's page has the AutomationId
RootWebArea; the Firefox window's has none. The script ran them through the realTree.get_state()andtree_traversal(), withControlFromHandle, the cache requests and the children lookup patched, and printed the labels andsemantic_tree_to_string(), which Snapshot prints underUI Tree:. Output onmain:Fix
tree/service.py,get_nodes(): every window gets its semantic node before the walk.use_dom, the walk attaches a browser's own controls to it, as it does for any app. The page's elements and text are then added from the DOM lists, in page order, as before. The walk entersRootWebAreawithout a semantic parent, so nothing is listed twice.use_dom=True, the walk attaches nothing to a browser window, so its tree is still the page alone.With the change, the reproduction prints the same labels and the same
use_dom=Truetree. The two other trees become:The browser's controls come first, as the window lays them out above the page. If you would rather keep the default tree to the page too, the
use_domdescription would need to change, and the Firefox window would still need the elements its walk finds.Tests
New
tests/test_tree_browser_controls.py, mocks only: the same two windows go through the realTree.get_state()andtree_traversal().test_browser_controls_are_listed_above_the_page: the Chrome window withoutuse_dom. The labels are the address bar, Back and the link, and the tree lists the toolbar with Back and the address bar, then the link and the text.test_page_without_root_web_area_is_listed: the Firefox window withoutuse_dom. The tree lists the toolbar and the link.test_use_dom_lists_the_page_alone: the Chrome window withuse_dom=True. The labels and the tree hold the page alone, as onmain.I ran each file on its own, in the project's environment:
On
main, the first two new tests fail at the tree, after their label check passes:At index 2 diff: ' ├── (200,310) link "More info" [action: click]' != ' ├── tool bar "App bar"'andRight contains 4 more items, first extra item: ' ├── tool bar "App bar"'.test_use_dom_lists_the_page_alonepasses on both. All 3 pass on this branch.These pass on
mainand on this branch with the same counts:tests/test_tree_service.py(50 passed),tests/test_tree_closed_window.py(4),tests/test_tree_retry_backoff.py(3),tests/test_tree_walk_z_order.py(3),tests/test_uia_timeouts.py(17) andtests/test_tree_views.py(29). I did not run the whole suite.ruff checkandruff format --checkare clean on the new test file. Ontree/service.py,ruff checkreports the same two W293 lines (650 and 749) onmainand on this branch, and the file is not formatted onmaineither; on the lines this change edits,ruff format --diffshows only what it shows there onmain(the single quotes of the window node and the longtree_traversal()call).Not tested: a real browser. Everything ran against mocks. To check it on a real machine, open a page in Chrome or Edge and call Snapshot without
use_dom: with this change the UI tree should list the browser's tabs and toolbar (the address bar, Back) above the page's elements, and onmainonly the page's. Withuse_dom=True, both should list the page alone. In Firefox, Snapshot withoutuse_domshould show only the window line onmain.