@@ -222,6 +222,51 @@ async def build_hierarchical_tree(
222222 "idToUrl" : id_to_url ,
223223 }
224224
225+ async def get_accessibility_node_name (page : "StagehandPage" , node : AccessibilityNode ) -> str :
226+ """Gets the name of an accessibility node."""
227+ name = node .get ("name" )
228+ if name and name .get ("value" ):
229+ return name
230+
231+ nodeId = node .get ("nodeId" )
232+ if not nodeId :
233+ return None
234+
235+ nodeId = int (nodeId )
236+
237+ args = {"backendNodeId" : nodeId }
238+ response = await page .send_cdp ("DOM.resolveNode" , args )
239+ object_id = response .get ("object" , {}).get ("objectId" )
240+
241+ xpath = await get_xpath_by_resolved_object_id (page ._cdp_client , object_id )
242+ if not xpath :
243+ return None
244+
245+ locator = page .locator (f"xpath={ xpath } " )
246+ if not locator :
247+ return None
248+
249+ name_value = await locator .get_attribute ("name" )
250+ if name_value :
251+ return {"value" : name_value , "type" : "string" }
252+
253+ aria_label_value = await locator .get_attribute ("aria-label" )
254+ if aria_label_value :
255+ return {"value" : aria_label_value , "type" : "string" }
256+
257+ id_value = await locator .get_attribute ("id" )
258+ if id_value :
259+ return {"value" : id_value , "type" : "string" }
260+
261+ placeholder_value = await locator .get_attribute ("placeholder" )
262+ if placeholder_value :
263+ return {"value" : placeholder_value , "type" : "string" }
264+
265+ title_value = await locator .get_attribute ("title" )
266+ if title_value :
267+ return {"value" : title_value , "type" : "string" }
268+
269+ return None
225270
226271async def get_accessibility_tree (
227272 page : "StagehandPage" ,
@@ -254,6 +299,27 @@ async def get_accessibility_tree(
254299 "value" : new_role ,
255300 } # Create role if missing
256301
302+ # Process accessibility node names in parallel after the main loop
303+ async def get_node_name_with_timeout (node_data ):
304+ try :
305+ if "ignored" in node_data and not node_data ["ignored" ]:
306+ node_name = await asyncio .wait_for (get_accessibility_node_name (page , node_data ), timeout = 0.5 )
307+ if node_name and ("name" not in node_data or (not node_data ["name" ].get ("value" ))):
308+ node_data ["name" ] = node_name
309+ except asyncio .TimeoutError :
310+ logger .debug (
311+ message = f"Timeout getting accessibility node name for node { node_data .get ('nodeId' )} " ,
312+ auxiliary = {"error" : {"value" : "Timeout after 0.5 seconds" , "type" : "string" }},
313+ )
314+ except Exception as e :
315+ logger .debug (
316+ message = f"Error getting accessibility node name for node { node_data .get ('nodeId' )} " ,
317+ auxiliary = {"error" : {"value" : str (e ), "type" : "string" }},
318+ )
319+
320+ # Run all node name retrievals in parallel
321+ await asyncio .gather (* [get_node_name_with_timeout (node_data ) for node_data in nodes ], return_exceptions = True )
322+
257323 hierarchical_tree = await build_hierarchical_tree (nodes , page , logger )
258324
259325 end_time = time .time ()
0 commit comments