"""Bounded native metadata shared by JSON and XML hierarchy observations.""" import json import math from typing import Any def _finite_number(value: Any) -> bool: # Android ranges are finite floats. Reject malformed oversized JSON integers # before conversion, which otherwise raises OverflowError in math.isfinite. return type(value) in (int, float) and -1e308 <= value <= 1e308 and math.isfinite(value) def native_control_state(node: dict[str, Any]) -> dict[str, Any]: state: dict[str, Any] = {} for key in ("range", "actions"): value = node.get(key) if isinstance(value, str) and len(value) <= 8192: try: value = json.loads(value) except ValueError: value = None if key == "actions" and isinstance(value, list): state[key] = [item for item in value[:32] if isinstance(item, str) and len(item) <= 64] elif key == "range" and isinstance(value, dict): numbers = [value.get(k) for k in ("min", "max", "current")] if all(_finite_number(v) for v in numbers): if numbers[0] <= numbers[2] <= numbers[1]: state[key] = {k: value[k] for k in ("min", "max", "current")} state[key]["type"] = value.get("type") for key in ( "visible", "enabled", "scrollable", "editable", "password", "checked", "checkable", "selected", "focused", ): value = node.get(key) if isinstance(value, bool) or value in ("true", "false"): state[key] = value is True or value == "true" for key in ("selection_start", "selection_end"): value = node.get(key, node.get(key.replace("_", "-"))) if isinstance(value, str) and len(value) < 12: digits = value[1:] if value.startswith("-") else value if digits and digits.isascii() and digits.isdecimal(): value = int(value) if isinstance(value, int) and not isinstance(value, bool) and -1 <= value <= 2**31 - 1: state[key] = value return state def informative_native_control(state: dict[str, Any]) -> bool: return bool( state.get("range") or state.get("scrollable") or set(state.get("actions", [])) & { "click", "long_click", "set_progress", "set_text", "set_selection", "ime_enter", } )