Skip to content

Commit

Permalink
Merge pull request #9 from Igalia/poll-active-tab
Browse files Browse the repository at this point in the history
Poll for active tab (and non-busy state)
  • Loading branch information
alice authored Jul 19, 2024
2 parents f31b0fb + b73654b commit 41ad1e2
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 39 deletions.
86 changes: 48 additions & 38 deletions tools/wptrunner/wptrunner/executors/executoratspi.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,33 @@
from gi.repository import Atspi
import json
import threading
import time

def find_active_tab(root):
import sys


def poll_for_active_tab(root, logger, product):
active_tab = find_active_tab(root, product)
while not active_tab:
time.sleep(0.01)
active_tab = find_active_tab(root, product)

return active_tab


def find_active_tab(root, product):
stack = [root]
while stack:
node = stack.pop()

if Atspi.Accessible.get_role_name(node) == "frame":
## Helper: list of string relations, get targets for relation?
relationset = Atspi.Accessible.get_relation_set(node)
for relation in relationset:
if relation.get_relation_type() == Atspi.RelationType.EMBEDS:
return relation.get_target(0)
active_tab = relation.get_target(0)
if is_ready(active_tab, product):
return active_tab
else:
return None
continue

for i in range(Atspi.Accessible.get_child_count(node)):
Expand All @@ -25,6 +40,24 @@ def find_active_tab(root):
return None


def is_ready(active_tab, product):
# Firefox uses the "BUSY" state to indicate the page is not ready.
if product == "firefox":
state_set = Atspi.Accessible.get_state_set(active_tab)
return not Atspi.StateSet.contains(state_set, Atspi.StateType.BUSY)

# Chromium family browsers do not use "BUSY", but you can
# tell if the document can be queried by Title attribute. If the 'Title'
# attribute is not here, we need to query for a new accessible object.
# TODO: eventually we should test this against the actual title of the
# page being tested.
document = Atspi.Accessible.get_document_iface(active_tab)
document_attributes = Atspi.Document.get_document_attributes(document)
if "Title" in document_attributes and document_attributes["Title"]:
return True
return False


def serialize_node(node):
node_dictionary = {}
node_dictionary["API"] = "atspi"
Expand Down Expand Up @@ -63,54 +96,31 @@ def find_browser(name):


class AtspiExecutorImpl:
def start_atspi_listener(self):
self._event_listener = Atspi.EventListener.new(self.handle_event)
self._event_listener.register("document:load-complete")
Atspi.event_main()

def handle_event(self, e):
app = Atspi.Accessible.get_application(e.source)
app_name = Atspi.Accessible.get_name(app)
if self.full_app_name == app_name and e.any_data:
self.load_complete = True
self._event_listener.deregister("document:load-complete")
Atspi.event_quit()

def setup(self, product_name):
def setup(self, product_name, logger):
self.logger = logger
self.product_name = product_name
self.full_app_name = ""
self.root = None
self.found_browser = False
self.load_complete = False

self.atspi_listener_thread = threading.Thread(target=self.start_atspi_listener)

(self.root, self.full_app_name) = find_browser(self.product_name)
if self.root:
self.found_browser = True
self.atspi_listener_thread.start()
else:
print(
f"Cannot find root accessibility node for {self.product_name} - did you turn on accessibility?"
if not self.root:
self.logger.error(
f"Couldn't find browser {self.product_name} in accessibility API ATSPI. Accessibility API queries will not succeeded."
)

def get_accessibility_api_node(self, dom_id):
if not self.found_browser:
if not self.root:
raise Exception(
f"Couldn't find browser {self.product_name}. Did you turn on accessibility?"
f"Couldn't find browser {self.product_name} in accessibility API ATSPI. Did you turn on accessibility?"
)

if not self.load_complete:
self.atspi_listener_thread.join()

active_tab = find_active_tab(self.root)
if not active_tab:
raise Exception(
f"Could not find the test page within the browser. Did you turn on accessiblity?"
)
active_tab = poll_for_active_tab(self.root, self.logger, self.product_name)

node = find_node(active_tab, dom_id)
if not node:
raise Exception(f"Couldn't find node with id {dom_id}.")
raise Exception(
f"Couldn't find node with id={dom_id} in accessibility API ATSPI."
)

return json.dumps(serialize_node(node))
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def setup(self):
self.impl = None
if linux:
self.impl = AtspiExecutorImpl()
self.impl.setup(self.product_name)
self.impl.setup(self.product_name, self.logger)
if mac:
self.impl = AXAPIExecutorImpl()
self.impl.setup(self.product_name)
Expand Down

0 comments on commit 41ad1e2

Please sign in to comment.