From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- accessible/tests/browser/windows/a11y_setup.py | 145 +++++++++++++++++++++ .../browser/windows/a11y_setup_requirements.txt | 1 + accessible/tests/browser/windows/ia2/browser.toml | 9 ++ .../tests/browser/windows/ia2/browser_role.js | 39 ++++++ accessible/tests/browser/windows/ia2/head.js | 18 +++ accessible/tests/browser/windows/uia/browser.toml | 11 ++ .../browser/windows/uia/browser_controlType.js | 30 +++++ .../windows/uia/browser_elementFromPoint.js | 34 +++++ accessible/tests/browser/windows/uia/head.js | 18 +++ 9 files changed, 305 insertions(+) create mode 100644 accessible/tests/browser/windows/a11y_setup.py create mode 100644 accessible/tests/browser/windows/a11y_setup_requirements.txt create mode 100644 accessible/tests/browser/windows/ia2/browser.toml create mode 100644 accessible/tests/browser/windows/ia2/browser_role.js create mode 100644 accessible/tests/browser/windows/ia2/head.js create mode 100644 accessible/tests/browser/windows/uia/browser.toml create mode 100644 accessible/tests/browser/windows/uia/browser_controlType.js create mode 100644 accessible/tests/browser/windows/uia/browser_elementFromPoint.js create mode 100644 accessible/tests/browser/windows/uia/head.js (limited to 'accessible/tests/browser/windows') diff --git a/accessible/tests/browser/windows/a11y_setup.py b/accessible/tests/browser/windows/a11y_setup.py new file mode 100644 index 0000000000..d6dc19f0fb --- /dev/null +++ b/accessible/tests/browser/windows/a11y_setup.py @@ -0,0 +1,145 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +"""Python environment for Windows a11y browser tests. +""" + +import ctypes +import os +from ctypes import POINTER, byref +from ctypes.wintypes import BOOL, HWND, LPARAM, POINT # noqa: F401 + +import comtypes.client +import psutil +from comtypes import COMError, IServiceProvider + +user32 = ctypes.windll.user32 +oleacc = ctypes.oledll.oleacc +oleaccMod = comtypes.client.GetModule("oleacc.dll") +IAccessible = oleaccMod.IAccessible +del oleaccMod +OBJID_CLIENT = -4 +CHILDID_SELF = 0 +NAVRELATION_EMBEDS = 0x1009 +# This is the path if running locally. +ia2Tlb = os.path.join( + os.getcwd(), + "..", + "..", + "..", + "accessible", + "interfaces", + "ia2", + "IA2Typelib.tlb", +) +if not os.path.isfile(ia2Tlb): + # This is the path if running in CI. + ia2Tlb = os.path.join(os.getcwd(), "ia2Typelib.tlb") +ia2Mod = comtypes.client.GetModule(ia2Tlb) +del ia2Tlb +# Shove all the IAccessible* interfaces and IA2_* constants directly +# into our namespace for convenience. +globals().update((k, getattr(ia2Mod, k)) for k in ia2Mod.__all__) +# We use this below. The linter doesn't understand our globals() update hack. +IAccessible2 = ia2Mod.IAccessible2 +del ia2Mod + +uiaMod = comtypes.client.GetModule("UIAutomationCore.dll") +globals().update((k, getattr(uiaMod, k)) for k in uiaMod.__all__) +uiaClient = comtypes.CoCreateInstance( + uiaMod.CUIAutomation._reg_clsid_, + interface=uiaMod.IUIAutomation, + clsctx=comtypes.CLSCTX_INPROC_SERVER, +) +TreeScope_Descendants = uiaMod.TreeScope_Descendants +UIA_AutomationIdPropertyId = uiaMod.UIA_AutomationIdPropertyId +del uiaMod + + +def AccessibleObjectFromWindow(hwnd, objectID=OBJID_CLIENT): + p = POINTER(IAccessible)() + oleacc.AccessibleObjectFromWindow( + hwnd, objectID, byref(IAccessible._iid_), byref(p) + ) + return p + + +def getFirefoxHwnd(): + """Search all top level windows for the Firefox instance being + tested. + We search by window class name and window title prefix. + """ + # We can compare the grandparent process ids to find the Firefox started by + # the test harness. + commonPid = psutil.Process().parent().ppid() + # We need something mutable to store the result from the callback. + found = [] + + @ctypes.WINFUNCTYPE(BOOL, HWND, LPARAM) + def callback(hwnd, lParam): + name = ctypes.create_unicode_buffer(100) + user32.GetClassNameW(hwnd, name, 100) + if name.value != "MozillaWindowClass": + return True + pid = ctypes.wintypes.DWORD() + user32.GetWindowThreadProcessId(hwnd, byref(pid)) + if psutil.Process(pid.value).parent().ppid() != commonPid: + return True # Not the Firefox being tested. + found.append(hwnd) + return False + + user32.EnumWindows(callback, LPARAM(0)) + if not found: + raise LookupError("Couldn't find Firefox HWND") + return found[0] + + +def toIa2(obj): + serv = obj.QueryInterface(IServiceProvider) + return serv.QueryService(IAccessible2._iid_, IAccessible2) + + +def getDocIa2(): + """Get the IAccessible2 for the document being tested.""" + hwnd = getFirefoxHwnd() + root = AccessibleObjectFromWindow(hwnd) + doc = root.accNavigate(NAVRELATION_EMBEDS, 0) + try: + child = toIa2(doc.accChild(1)) + if "id:default-iframe-id;" in child.attributes: + # This is an iframe or remoteIframe test. + doc = child.accChild(1) + except COMError: + pass # No child. + return toIa2(doc) + + +def findIa2ByDomId(root, id): + search = f"id:{id};" + # Child ids begin at 1. + for i in range(1, root.accChildCount + 1): + child = toIa2(root.accChild(i)) + if search in child.attributes: + return child + descendant = findIa2ByDomId(child, id) + if descendant: + return descendant + + +def getDocUia(): + """Get the IUIAutomationElement for the document being tested.""" + # We start with IAccessible2 because there's no efficient way to + # find the document we want with UIA. + ia2 = getDocIa2() + return uiaClient.ElementFromIAccessible(ia2, CHILDID_SELF) + + +def findUiaByDomId(root, id): + cond = uiaClient.CreatePropertyCondition(UIA_AutomationIdPropertyId, id) + # FindFirst ignores elements in the raw tree, so we have to use + # FindFirstBuildCache to override that, even though we don't want to cache + # anything. + request = uiaClient.CreateCacheRequest() + request.TreeFilter = uiaClient.RawViewCondition + return root.FindFirstBuildCache(TreeScope_Descendants, cond, request) diff --git a/accessible/tests/browser/windows/a11y_setup_requirements.txt b/accessible/tests/browser/windows/a11y_setup_requirements.txt new file mode 100644 index 0000000000..ea131a6f92 --- /dev/null +++ b/accessible/tests/browser/windows/a11y_setup_requirements.txt @@ -0,0 +1 @@ +comtypes==1.2.0 diff --git a/accessible/tests/browser/windows/ia2/browser.toml b/accessible/tests/browser/windows/ia2/browser.toml new file mode 100644 index 0000000000..d72b5f8a2d --- /dev/null +++ b/accessible/tests/browser/windows/ia2/browser.toml @@ -0,0 +1,9 @@ +[DEFAULT] +subsuite = "a11y" +skip-if = [ + "os != 'win'", + "headless", +] +support-files = ["head.js"] + +["browser_role.js"] diff --git a/accessible/tests/browser/windows/ia2/browser_role.js b/accessible/tests/browser/windows/ia2/browser_role.js new file mode 100644 index 0000000000..08e44c280f --- /dev/null +++ b/accessible/tests/browser/windows/ia2/browser_role.js @@ -0,0 +1,39 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const ROLE_SYSTEM_DOCUMENT = 15; +const ROLE_SYSTEM_GROUPING = 20; +const IA2_ROLE_PARAGRAPH = 1054; + +addAccessibleTask( + ` +

p

+ `, + async function (browser, docAcc) { + let role = await runPython(` + global doc + doc = getDocIa2() + return doc.accRole(CHILDID_SELF) + `); + is(role, ROLE_SYSTEM_DOCUMENT, "doc has correct MSAA role"); + role = await runPython(`doc.role()`); + is(role, ROLE_SYSTEM_DOCUMENT, "doc has correct IA2 role"); + ok( + await runPython(` + global p + p = findIa2ByDomId(doc, "p") + firstChild = toIa2(doc.accChild(1)) + return p == firstChild + `), + "doc's first child is p" + ); + role = await runPython(`p.accRole(CHILDID_SELF)`); + is(role, ROLE_SYSTEM_GROUPING, "p has correct MSAA role"); + role = await runPython(`p.role()`); + is(role, IA2_ROLE_PARAGRAPH, "p has correct IA2 role"); + }, + { chrome: true, topLevel: true, iframe: true, remoteIframe: true } +); diff --git a/accessible/tests/browser/windows/ia2/head.js b/accessible/tests/browser/windows/ia2/head.js new file mode 100644 index 0000000000..afc50984bd --- /dev/null +++ b/accessible/tests/browser/windows/ia2/head.js @@ -0,0 +1,18 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +// Load the shared-head file first. +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/accessible/tests/browser/shared-head.js", + this +); + +// Loading and common.js from accessible/tests/mochitest/ for all tests, as +// well as promisified-events.js. +loadScripts( + { name: "common.js", dir: MOCHITESTS_DIR }, + { name: "promisified-events.js", dir: MOCHITESTS_DIR } +); diff --git a/accessible/tests/browser/windows/uia/browser.toml b/accessible/tests/browser/windows/uia/browser.toml new file mode 100644 index 0000000000..f7974d69c7 --- /dev/null +++ b/accessible/tests/browser/windows/uia/browser.toml @@ -0,0 +1,11 @@ +[DEFAULT] +subsuite = "a11y" +skip-if = [ + "os != 'win'", + "headless", +] +support-files = ["head.js"] + +["browser_controlType.js"] + +["browser_elementFromPoint.js"] diff --git a/accessible/tests/browser/windows/uia/browser_controlType.js b/accessible/tests/browser/windows/uia/browser_controlType.js new file mode 100644 index 0000000000..16db892581 --- /dev/null +++ b/accessible/tests/browser/windows/uia/browser_controlType.js @@ -0,0 +1,30 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +/* eslint-disable camelcase */ +const UIA_ButtonControlTypeId = 50000; +const UIA_DocumentControlTypeId = 50030; +/* eslint-enable camelcase */ + +addAccessibleTask( + ` + + `, + async function (browser, docAcc) { + let controlType = await runPython(` + global doc + doc = getDocUia() + return doc.CurrentControlType + `); + is(controlType, UIA_DocumentControlTypeId, "doc has correct control type"); + controlType = await runPython(` + button = findUiaByDomId(doc, "button") + return button.CurrentControlType + `); + is(controlType, UIA_ButtonControlTypeId, "button has correct control type"); + }, + { chrome: true, topLevel: true, iframe: true, remoteIframe: true } +); diff --git a/accessible/tests/browser/windows/uia/browser_elementFromPoint.js b/accessible/tests/browser/windows/uia/browser_elementFromPoint.js new file mode 100644 index 0000000000..e2fda4ab30 --- /dev/null +++ b/accessible/tests/browser/windows/uia/browser_elementFromPoint.js @@ -0,0 +1,34 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +addAccessibleTask( + ` +