https://bugs.documentfoundation.org/show_bug.cgi?id=173639
Bug ID: 173639
Summary: Accessibility: ~900 AT-SPI events from up to 170
objects every time focus moves between two LibreOffice
windows (toolbars rebuilt, many sources already
defunct)
Product: LibreOffice
Version: 25.2.3.2 release
Hardware: ARM
OS: Linux (All)
Status: UNCONFIRMED
Severity: normal
Priority: medium
Component: UI
Assignee: [email protected]
Reporter: [email protected]
LibreOffice 25.2.3.2 (Debian 13.7 package 4:25.2.3-2+deb13u6, aarch64), gtk3
VCL plugin (SAL_USE_VCLPLUGIN=gtk3), at-spi2-core 2.56.2, X11 (Xvfb, no window
manager).
Steps to reproduce:
1. In one soffice process open a Writer document and a Calc document (two
windows).
2. Register an AT-SPI event listener for "object:" and "window:" events
(pyatspi).
3. Move X input focus from the Calc window to the Writer window (xdotool
windowfocus), wait 3 seconds, count the events. Then move it back.
Actual results:
Each focus change emits about 890 events from 84 to 167 distinct objects:
Calc -> Writer: 888 events from 84 objects: object:state-changed 390,
object:property-change 242, object:bounds-changed 146, object:children-changed
108, window:deactivate 1, window:activate 1
Writer -> Calc: 892 events from 167 objects: object:state-changed 390,
object:property-change 242, object:bounds-changed 149, object:children-changed
108, window:deactivate 1, window:activate 1
The sources are the toolbars, toolbar buttons and panels of both windows: the
toolbars of the window being activated and of the one being deactivated are
torn down and rebuilt (the children-changed events). About 60 of the sources
are already defunct by the time the event is delivered (calling GetApplication
on them raises "The application no longer exists"), and about 36 more have role
"invalid".
Expected results:
Activating a window should not rebuild its toolbars' accessible objects, and
objects that are destroyed should not emit events after their destruction. A
handful of state-changed:active/focused events and the
window:activate/deactivate pair would be the expected cost of a window switch.
Why it matters:
A screen reader or automation client pays for every one of those events on
every Alt-Tab between two LibreOffice windows; a client that re-reads what
changed re-reads two windows' toolbars each time. In our client that switch
costs about a second even after coalescing sources and dropping the defunct
ones.
Reproducer (python3-pyatspi + xdotool, with a Writer and a Calc window open):
import pyatspi, subprocess, time
from gi.repository import GLib
count = {"n": 0, "types": {}, "srcs": set()}
def spy(e):
count["n"] += 1
t = str(e.type); t = t.rsplit(":", 1)[0] if t.count(":") > 1 else t
count["types"][t] = count["types"].get(t, 0) + 1
count["srcs"].add(id(e.source))
for ev in ("object:", "window:"):
pyatspi.Registry.registerEventListener(spy, ev)
ctx = GLib.MainContext.default()
def pump(seconds):
end = time.time() + seconds
while time.time() < end:
while ctx.pending(): ctx.iteration(False)
time.sleep(0.01)
def wid(title):
return subprocess.run(["xdotool", "search", "--onlyvisible", "--name",
title], capture_output=True, text=True).stdout.split()[-1]
def focus(w):
subprocess.run(["xdotool", "windowraise", w]); subprocess.run(["xdotool",
"windowfocus", "--sync", w])
w_writer, w_calc = wid("LibreOffice Writer"), wid("LibreOffice Calc")
focus(w_calc); pump(2.5)
for label, w in (("Calc -> Writer", w_writer), ("Writer -> Calc", w_calc)):
count["n"] = 0; count["types"] = {}; count["srcs"] = set()
focus(w); pump(3.0)
print(label, count["n"], "events from", len(count["srcs"]), "objects:",
sorted(count["types"].items(), key=lambda kv: -kv[1]))
--
You are receiving this mail because:
You are the assignee for the bug.