From 9c941915832b12498e407d75459fdd7d14911e03 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Tue, 7 Apr 2026 16:07:32 -0400 Subject: [PATCH] Hell of a speed boost for Steam. --- .../scripts/apps/steamwebhelper/script.py | 28 +++++ .../apps/steamwebhelper/script_utilities.py | 14 +++ tests/test_steam_selection_regressions.py | 110 ++++++++++++++++++ 3 files changed, 152 insertions(+) diff --git a/src/cthulhu/scripts/apps/steamwebhelper/script.py b/src/cthulhu/scripts/apps/steamwebhelper/script.py index 09123b2..c4e5d21 100644 --- a/src/cthulhu/scripts/apps/steamwebhelper/script.py +++ b/src/cthulhu/scripts/apps/steamwebhelper/script.py @@ -118,8 +118,19 @@ class Script(Chromium.Script): self._presentSteamNotification(event.any_data) return + if self._handleSteamVirtualizedListMutation(event): + return True + super().onChildrenAdded(event) + def onChildrenRemoved(self, event): + """Callback for object:children-changed:removed accessibility events.""" + + if self._handleSteamVirtualizedListMutation(event): + return True + + return super().onChildrenRemoved(event) + def getStructuralNavigation(self): types = self.getEnabledStructuralNavigationTypes() enable = settingsManager.getSetting('structuralNavigationEnabled') @@ -165,6 +176,23 @@ class Script(Chromium.Script): self._logSteamNavigationEvent("active-descendant-changed", event) return super().onActiveDescendantChanged(event) + def _handleSteamVirtualizedListMutation(self, event): + if not event or not self.utilities.isSteamVirtualizedList(event.source): + return False + + focus = cthulhu_state.locusOfFocus + if not focus or AXObject.is_dead(focus): + return False + + if not AXObject.find_ancestor(focus, lambda x: x == event.source): + return False + + AXObject.clear_cache_now("children-changed event.") + self.utilities.clearSteamVirtualizedListCaches() + msg = "STEAM: Skipping generic web cache dump for virtualized list churn near focus" + debug.printMessage(debug.LEVEL_INFO, msg, True) + return True + def _trySteamButtonActivation(self, keyboardEvent) -> bool: if keyboardEvent.event_string not in ["Return", "KP_Enter"]: return False diff --git a/src/cthulhu/scripts/apps/steamwebhelper/script_utilities.py b/src/cthulhu/scripts/apps/steamwebhelper/script_utilities.py index b2959f3..a651c74 100644 --- a/src/cthulhu/scripts/apps/steamwebhelper/script_utilities.py +++ b/src/cthulhu/scripts/apps/steamwebhelper/script_utilities.py @@ -41,6 +41,20 @@ class Utilities(ChromiumUtilities): super().clearCachedObjects() self._steamInferredButtonLabels = {} + def clearSteamVirtualizedListCaches(self) -> None: + self.clearContentCache() + self._steamInferredButtonLabels = {} + + def isSteamVirtualizedList(self, obj) -> bool: + if not (obj and self.inDocumentContent(obj)): + return False + + if not AXUtilities.is_list(obj): + return False + + className = AXObject.get_attribute(obj, "class") or "" + return "ReactVirtualized__Grid__innerScrollContainer" in className + def displayedLabel(self, obj): label = super().displayedLabel(obj) if label or not self._shouldInferSteamButtonLabel(obj): diff --git a/tests/test_steam_selection_regressions.py b/tests/test_steam_selection_regressions.py index 7728ea5..e48df2a 100644 --- a/tests/test_steam_selection_regressions.py +++ b/tests/test_steam_selection_regressions.py @@ -95,6 +95,116 @@ class SteamReturnActivationTests(unittest.TestCase): performAction.assert_called_once_with(button) +class SteamVirtualizedListMutationTests(unittest.TestCase): + def test_children_added_skips_generic_web_cache_dump_for_virtualized_list_churn(self): + testScript = steam_script.Script.__new__(steam_script.Script) + source = object() + focus = object() + event = mock.Mock( + type="object:children-changed:add", + source=source, + any_data=object(), + detail1=0, + ) + chromiumCalls = [] + + testScript.utilities = mock.Mock() + testScript.utilities.isSteamVirtualizedList.return_value = True + testScript.utilities.clearSteamVirtualizedListCaches = mock.Mock() + + def chromiumOnChildrenAdded(self, addedEvent): + chromiumCalls.append((self, addedEvent)) + return False + + with ( + mock.patch.object(steam_script.cthulhu_state, "locusOfFocus", focus), + mock.patch.object(steam_script.AXObject, "is_dead", return_value=False), + mock.patch.object(steam_script.AXObject, "find_ancestor", return_value=source), + mock.patch.object(steam_script.AXObject, "clear_cache_now") as clearCache, + mock.patch.object(steam_script.Script, "_isSteamNotification", return_value=False), + mock.patch.object( + steam_script.Chromium.Script, + "onChildrenAdded", + new=chromiumOnChildrenAdded, + ), + ): + self.assertTrue(testScript.onChildrenAdded(event)) + + clearCache.assert_called_once_with("children-changed event.") + testScript.utilities.clearSteamVirtualizedListCaches.assert_called_once_with() + self.assertEqual(chromiumCalls, []) + + def test_children_removed_skips_generic_web_cache_dump_for_virtualized_list_churn(self): + testScript = steam_script.Script.__new__(steam_script.Script) + source = object() + focus = object() + event = mock.Mock( + type="object:children-changed:remove", + source=source, + any_data=object(), + detail1=0, + ) + chromiumCalls = [] + + testScript.utilities = mock.Mock() + testScript.utilities.isSteamVirtualizedList.return_value = True + testScript.utilities.clearSteamVirtualizedListCaches = mock.Mock() + + def chromiumOnChildrenRemoved(self, removedEvent): + chromiumCalls.append((self, removedEvent)) + return False + + with ( + mock.patch.object(steam_script.cthulhu_state, "locusOfFocus", focus), + mock.patch.object(steam_script.AXObject, "is_dead", return_value=False), + mock.patch.object(steam_script.AXObject, "find_ancestor", return_value=source), + mock.patch.object(steam_script.AXObject, "clear_cache_now") as clearCache, + mock.patch.object( + steam_script.Chromium.Script, + "onChildrenRemoved", + new=chromiumOnChildrenRemoved, + ), + ): + self.assertTrue(testScript.onChildrenRemoved(event)) + + clearCache.assert_called_once_with("children-changed event.") + testScript.utilities.clearSteamVirtualizedListCaches.assert_called_once_with() + self.assertEqual(chromiumCalls, []) + + def test_children_removed_defers_to_generic_handler_when_focus_is_dead(self): + testScript = steam_script.Script.__new__(steam_script.Script) + source = object() + focus = object() + event = mock.Mock( + type="object:children-changed:remove", + source=source, + any_data=object(), + detail1=0, + ) + chromiumCalls = [] + + testScript.utilities = mock.Mock() + testScript.utilities.isSteamVirtualizedList.return_value = True + + def chromiumOnChildrenRemoved(self, removedEvent): + chromiumCalls.append((self, removedEvent)) + return False + + with ( + mock.patch.object(steam_script.cthulhu_state, "locusOfFocus", focus), + mock.patch.object(steam_script.AXObject, "is_dead", return_value=True), + mock.patch.object( + steam_script.Chromium.Script, + "onChildrenRemoved", + new=chromiumOnChildrenRemoved, + ), + ): + self.assertFalse(testScript.onChildrenRemoved(event)) + + self.assertEqual(chromiumCalls, [(testScript, event)]) + testScript.utilities.clearSteamVirtualizedListCaches.assert_not_called() + + class SteamLabelRecoveryTests(unittest.TestCase): def test_displayed_label_recovers_friends_list_tab_text_from_parent_context(self): testScript = mock.Mock(generatorCache={})