diff options
Diffstat (limited to 'accessible/xpcom')
37 files changed, 5111 insertions, 0 deletions
diff --git a/accessible/xpcom/AccEventGen.py b/accessible/xpcom/AccEventGen.py new file mode 100755 index 0000000000..791b57827d --- /dev/null +++ b/accessible/xpcom/AccEventGen.py @@ -0,0 +1,256 @@ +#!/usr/bin/env python +# +# 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/. + +import os + +import buildconfig +import mozpack.path as mozpath +from xpidl import xpidl + +# Load the webidl configuration file. +glbl = {} +exec( + open( + mozpath.join(buildconfig.topsrcdir, "dom", "bindings", "Bindings.conf") + ).read(), + glbl, +) +webidlconfig = glbl["DOMInterfaces"] + +# Instantiate the parser. +p = xpidl.IDLParser() + + +def findIDL(includePath, interfaceFileName): + for d in includePath: + path = mozpath.join(d, interfaceFileName) + if os.path.exists(path): + return path + raise BaseException( + "No IDL file found for interface %s " + "in include path %r" % (interfaceFileName, includePath) + ) + + +def loadEventIDL(parser, includePath, eventname): + eventidl = "nsIAccessible%s.idl" % eventname + idlFile = findIDL(includePath, eventidl) + idl = p.parse(open(idlFile).read(), idlFile) + idl.resolve(includePath, p, webidlconfig) + return idl, idlFile + + +class Configuration: + def __init__(self, filename): + config = {} + exec(open(filename).read(), config) + self.simple_events = config.get("simple_events", []) + + +def firstCap(str): + return str[0].upper() + str[1:] + + +def writeAttributeParams(a): + return "%s a%s" % (a.realtype.nativeType("in"), firstCap(a.name)) + + +def print_header_file(fd, conf, incdirs): + idl_paths = set() + + fd.write("/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n") + fd.write( + "#ifndef _mozilla_a11y_generated_AccEvents_h_\n" + "#define _mozilla_a11y_generated_AccEvents_h_\n\n" + ) + fd.write('#include "nscore.h"\n') + fd.write('#include "nsCOMPtr.h"\n') + fd.write('#include "nsCycleCollectionParticipant.h"\n') + fd.write('#include "nsString.h"\n') + for e in conf.simple_events: + fd.write('#include "nsIAccessible%s.h"\n' % e) + for e in conf.simple_events: + idl, idl_path = loadEventIDL(p, incdirs, e) + idl_paths.add(idl_path) + for iface in filter(lambda p: p.kind == "interface", idl.productions): + classname = "xpcAcc%s" % e + baseinterfaces = interfaces(iface) + + fd.write("\nclass %s final : public %s\n" % (classname, iface.name)) + fd.write("{\n") + fd.write("public:\n") + + attributes = allAttributes(iface) + args = map(writeAttributeParams, attributes) + fd.write(" %s(%s) :\n" % (classname, ", ".join(args))) + + initializers = [] + for a in attributes: + initializers.append("m%s(a%s)" % (firstCap(a.name), firstCap(a.name))) + fd.write(" %s\n {}\n\n" % ", ".join(initializers)) + fd.write(" NS_DECL_CYCLE_COLLECTING_ISUPPORTS\n") + fd.write(" NS_DECL_CYCLE_COLLECTION_CLASS(%s)\n" % (classname)) + + for iface in filter(lambda i: i.name != "nsISupports", baseinterfaces): + fd.write(" NS_DECL_%s\n" % iface.name.upper()) + + fd.write("\nprivate:\n") + fd.write(" ~%s() {}\n\n" % classname) + for a in attributes: + fd.write(" %s\n" % attributeVariableTypeAndName(a)) + fd.write("};\n\n") + + fd.write("#endif\n") + + return idl_paths + + +def interfaceAttributeTypes(idl): + ifaces = filter(lambda p: p.kind == "interface", idl.productions) + attributes = [] + for i in ifaces: + ifaceAttributes = allAttributes(i) + attributes.extend(ifaceAttributes) + ifaceAttrs = filter(lambda a: a.realtype.nativeType("in").endswith("*"), attributes) + return map(lambda a: a.realtype.nativeType("in").strip(" *"), ifaceAttrs) + + +def print_cpp(idl, fd, conf, eventname): + for p in idl.productions: + if p.kind == "interface": + write_cpp(eventname, p, fd) + + +def print_cpp_file(fd, conf, incdirs): + idl_paths = set() + fd.write("/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n\n") + fd.write('#include "xpcAccEvents.h"\n') + + includes = [] + for e in conf.simple_events: + if e not in includes: + includes.append(("nsIAccessible%s" % e)) + + types = [] + for e in conf.simple_events: + idl, idl_path = loadEventIDL(p, incdirs, e) + idl_paths.add(idl_path) + types.extend(interfaceAttributeTypes(idl)) + + for c in types: + fd.write('#include "%s.h"\n' % c) + + fd.write("\n") + for e in conf.simple_events: + idl, idl_path = loadEventIDL(p, incdirs, e) + idl_paths.add(idl_path) + print_cpp(idl, fd, conf, e) + + return idl_paths + + +def attributeVariableTypeAndName(a): + if a.realtype.nativeType("in").endswith("*"): + l = [ + "nsCOMPtr<%s> m%s;" + % (a.realtype.nativeType("in").strip("* "), firstCap(a.name)) + ] + elif a.realtype.nativeType("in").count("nsAString"): + l = ["nsString m%s;" % firstCap(a.name)] + elif a.realtype.nativeType("in").count("nsACString"): + l = ["nsCString m%s;" % firstCap(a.name)] + else: + l = ["%sm%s;" % (a.realtype.nativeType("in"), firstCap(a.name))] + return ", ".join(l) + + +def writeAttributeGetter(fd, classname, a): + fd.write("NS_IMETHODIMP\n") + fd.write("%s::Get%s(" % (classname, firstCap(a.name))) + if a.realtype.nativeType("in").endswith("*"): + fd.write( + "%s** a%s" % (a.realtype.nativeType("in").strip("* "), firstCap(a.name)) + ) + elif a.realtype.nativeType("in").count("nsAString"): + fd.write("nsAString& a%s" % firstCap(a.name)) + elif a.realtype.nativeType("in").count("nsACString"): + fd.write("nsACString& a%s" % firstCap(a.name)) + else: + fd.write("%s*a%s" % (a.realtype.nativeType("in"), firstCap(a.name))) + fd.write(")\n") + fd.write("{\n") + if a.realtype.nativeType("in").endswith("*"): + fd.write(" NS_IF_ADDREF(*a%s = m%s);\n" % (firstCap(a.name), firstCap(a.name))) + elif a.realtype.nativeType("in").count("nsAString"): + fd.write(" a%s = m%s;\n" % (firstCap(a.name), firstCap(a.name))) + elif a.realtype.nativeType("in").count("nsACString"): + fd.write(" a%s = m%s;\n" % (firstCap(a.name), firstCap(a.name))) + else: + fd.write(" *a%s = m%s;\n" % (firstCap(a.name), firstCap(a.name))) + fd.write(" return NS_OK;\n") + fd.write("}\n\n") + + +def interfaces(iface): + interfaces = [] + while iface.base: + interfaces.append(iface) + iface = iface.idl.getName(xpidl.TypeId(iface.base), iface.location) + interfaces.append(iface) + interfaces.reverse() + return interfaces + + +def allAttributes(iface): + attributes = [] + for i in interfaces(iface): + attrs = filter(lambda m: isinstance(m, xpidl.Attribute), i.members) + attributes.extend(attrs) + + return attributes + + +def write_cpp(eventname, iface, fd): + classname = "xpcAcc%s" % eventname + attributes = allAttributes(iface) + ccattributes = filter( + lambda m: m.realtype.nativeType("in").endswith("*"), attributes + ) + fd.write("NS_IMPL_CYCLE_COLLECTION(%s" % classname) + for c in ccattributes: + fd.write(", m%s" % firstCap(c.name)) + fd.write(")\n\n") + + fd.write("NS_IMPL_CYCLE_COLLECTING_ADDREF(%s)\n" % classname) + fd.write("NS_IMPL_CYCLE_COLLECTING_RELEASE(%s)\n\n" % classname) + + fd.write("NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(%s)\n" % classname) + for baseiface in interfaces(iface): + fd.write(" NS_INTERFACE_MAP_ENTRY(%s)\n" % baseiface.name) + fd.write("NS_INTERFACE_MAP_END\n\n") + + for a in attributes: + writeAttributeGetter(fd, classname, a) + + +def get_conf(conf_file): + conf = Configuration(conf_file) + inc_dir = [ + mozpath.join(buildconfig.topsrcdir, "accessible", "interfaces"), + mozpath.join(buildconfig.topsrcdir, "xpcom", "base"), + ] + return conf, inc_dir + + +def gen_files(fd, conf_file): + deps = set() + conf, inc_dir = get_conf(conf_file) + deps.update(print_header_file(fd, conf, inc_dir)) + with open( + os.path.join(os.path.dirname(fd.name), "xpcAccEvents.cpp"), "w" + ) as cpp_fd: + deps.update(print_cpp_file(cpp_fd, conf, inc_dir)) + return deps diff --git a/accessible/xpcom/AccEvents.conf b/accessible/xpcom/AccEvents.conf new file mode 100644 index 0000000000..682559a3c6 --- /dev/null +++ b/accessible/xpcom/AccEvents.conf @@ -0,0 +1,20 @@ +""" -*- Mode: Python -*- + 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/. + + The name of the event which real interface should have nsIAccessible-prefix, + and should be in nsIAccessible<name>.idl file""" + +simple_events = [ + 'Event', + 'StateChangeEvent', + 'TextChangeEvent', + 'TextSelectionChangeEvent', + 'HideEvent', + 'CaretMoveEvent', + 'ObjectAttributeChangedEvent', + 'TableChangeEvent', + 'ScrollingEvent', + 'AnnouncementEvent' + ] diff --git a/accessible/xpcom/moz.build b/accessible/xpcom/moz.build new file mode 100644 index 0000000000..2ce13081a2 --- /dev/null +++ b/accessible/xpcom/moz.build @@ -0,0 +1,80 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +UNIFIED_SOURCES += [ + "nsAccessibleRelation.cpp", + "xpcAccessibilityService.cpp", + "xpcAccessible.cpp", + "xpcAccessibleApplication.cpp", + "xpcAccessibleDocument.cpp", + "xpcAccessibleGeneric.cpp", + "xpcAccessibleHyperLink.cpp", + "xpcAccessibleHyperText.cpp", + "xpcAccessibleImage.cpp", + "xpcAccessiblePivot.cpp", + "xpcAccessibleSelectable.cpp", + "xpcAccessibleTable.cpp", + "xpcAccessibleTableCell.cpp", + "xpcAccessibleTextLeafRange.cpp", + "xpcAccessibleTextRange.cpp", + "xpcAccessibleValue.cpp", +] + +SOURCES += [ + "!xpcAccEvents.cpp", +] + +EXPORTS += [ + "!xpcAccEvents.h", + "xpcAccessibilityService.h", +] + +LOCAL_INCLUDES += [ + "/accessible/base", + "/accessible/basetypes", + "/accessible/generic", +] + +if CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk": + LOCAL_INCLUDES += [ + "/accessible/atk", + ] +elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "windows": + LOCAL_INCLUDES += [ + "/accessible/windows/msaa", + ] +elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "cocoa": + LOCAL_INCLUDES += [ + "/accessible/ipc", + "/accessible/mac", + ] + UNIFIED_SOURCES += ["xpcAccessibleMacInterface.mm"] + EXPORTS += [ + "xpcAccessibleMacInterface.h", + ] +elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "android": + LOCAL_INCLUDES += [ + "/accessible/android", + ] +else: + LOCAL_INCLUDES += [ + "/accessible/other", + ] + + +GeneratedFile( + "xpcAccEvents.h", + "xpcAccEvents.cpp", + script="AccEventGen.py", + entry_point="gen_files", + inputs=[ + "AccEvents.conf", + ], +) + +FINAL_LIBRARY = "xul" + +include("/ipc/chromium/chromium-config.mozbuild") diff --git a/accessible/xpcom/nsAccessibleRelation.cpp b/accessible/xpcom/nsAccessibleRelation.cpp new file mode 100644 index 0000000000..d376c15e91 --- /dev/null +++ b/accessible/xpcom/nsAccessibleRelation.cpp @@ -0,0 +1,59 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. */ + +#include "nsAccessibleRelation.h" + +#include "Relation.h" +#include "xpcAccessibleDocument.h" + +#include "nsArrayUtils.h" +#include "nsComponentManagerUtils.h" + +using namespace mozilla::a11y; + +nsAccessibleRelation::nsAccessibleRelation(uint32_t aType, Relation* aRel) + : mType(aType) { + mTargets = do_CreateInstance(NS_ARRAY_CONTRACTID); + Accessible* targetAcc = nullptr; + while ((targetAcc = aRel->Next())) { + mTargets->AppendElement(static_cast<nsIAccessible*>(ToXPC(targetAcc))); + } +} + +nsAccessibleRelation::~nsAccessibleRelation() {} + +// nsISupports +NS_IMPL_ISUPPORTS(nsAccessibleRelation, nsIAccessibleRelation) + +// nsIAccessibleRelation +NS_IMETHODIMP +nsAccessibleRelation::GetRelationType(uint32_t* aType) { + NS_ENSURE_ARG_POINTER(aType); + *aType = mType; + return NS_OK; +} + +NS_IMETHODIMP +nsAccessibleRelation::GetTargetsCount(uint32_t* aCount) { + NS_ENSURE_ARG_POINTER(aCount); + *aCount = 0; + return mTargets->GetLength(aCount); +} + +NS_IMETHODIMP +nsAccessibleRelation::GetTarget(uint32_t aIndex, nsIAccessible** aTarget) { + NS_ENSURE_ARG_POINTER(aTarget); + nsresult rv = NS_OK; + nsCOMPtr<nsIAccessible> target = do_QueryElementAt(mTargets, aIndex, &rv); + target.forget(aTarget); + return rv; +} + +NS_IMETHODIMP +nsAccessibleRelation::GetTargets(nsIArray** aTargets) { + NS_ENSURE_ARG_POINTER(aTargets); + NS_ADDREF(*aTargets = mTargets); + return NS_OK; +} diff --git a/accessible/xpcom/nsAccessibleRelation.h b/accessible/xpcom/nsAccessibleRelation.h new file mode 100644 index 0000000000..f8776951f4 --- /dev/null +++ b/accessible/xpcom/nsAccessibleRelation.h @@ -0,0 +1,46 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. */ + +#ifndef _nsAccessibleRelation_H_ +#define _nsAccessibleRelation_H_ + +#include "nsIAccessibleRelation.h" + +#include "nsCOMPtr.h" +#include "nsTArray.h" +#include "nsIMutableArray.h" +#include "mozilla/Attributes.h" +#include "mozilla/a11y/RemoteAccessible.h" + +namespace mozilla { +namespace a11y { + +class Relation; + +/** + * Class represents an accessible relation. + */ +class nsAccessibleRelation final : public nsIAccessibleRelation { + public: + nsAccessibleRelation(uint32_t aType, Relation* aRel); + + NS_DECL_ISUPPORTS + NS_DECL_NSIACCESSIBLERELATION + + private: + nsAccessibleRelation(); + ~nsAccessibleRelation(); + + nsAccessibleRelation(const nsAccessibleRelation&); + nsAccessibleRelation& operator=(const nsAccessibleRelation&); + + uint32_t mType; + nsCOMPtr<nsIMutableArray> mTargets; +}; + +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/xpcom/xpcAccessibilityService.cpp b/accessible/xpcom/xpcAccessibilityService.cpp new file mode 100644 index 0000000000..a7b70b6f33 --- /dev/null +++ b/accessible/xpcom/xpcAccessibilityService.cpp @@ -0,0 +1,307 @@ +/* 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/. */ + +#include "xpcAccessibilityService.h" + +#include "mozilla/dom/Document.h" + +#include "xpcAccessiblePivot.h" +#include "nsAccessibilityService.h" +#include "xpcAccessibleApplication.h" +#include "xpcAccessibleDocument.h" +#include "xpcAccessibleTextLeafRange.h" + +#ifdef A11Y_LOG +# include "Logging.h" +#endif + +using namespace mozilla; +using namespace mozilla::a11y; +using namespace mozilla::dom; + +xpcAccessibilityService* xpcAccessibilityService::gXPCAccessibilityService = + nullptr; + +//////////////////////////////////////////////////////////////////////////////// +// nsISupports + +void xpcAccessibilityService::ShutdownCallback(nsITimer* aTimer, + void* aClosure) { + MaybeShutdownAccService(nsAccessibilityService::eXPCOM); + xpcAccessibilityService* xpcAccService = + reinterpret_cast<xpcAccessibilityService*>(aClosure); + + if (xpcAccService->mShutdownTimer) { + xpcAccService->mShutdownTimer->Cancel(); + xpcAccService->mShutdownTimer = nullptr; + } +} + +NS_IMETHODIMP_(MozExternalRefCountType) +xpcAccessibilityService::AddRef(void) { + MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(xpcAccessibilityService) + MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); + if (!nsAutoRefCnt::isThreadSafe) { + NS_ASSERT_OWNINGTHREAD(xpcAccessibilityService); + } + nsrefcnt count = ++mRefCnt; + NS_LOG_ADDREF(this, count, "xpcAccessibilityService", sizeof(*this)); + + // We want refcount to be > 1 because one reference is added in the XPCOM + // accessibility service getter. + if (mRefCnt > 1) { + if (mShutdownTimer) { + mShutdownTimer->Cancel(); + mShutdownTimer = nullptr; + } + + GetOrCreateAccService(nsAccessibilityService::eXPCOM); + } + + return count; +} + +NS_IMETHODIMP_(MozExternalRefCountType) +xpcAccessibilityService::Release(void) { + MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); + + if (!nsAutoRefCnt::isThreadSafe) { + NS_ASSERT_OWNINGTHREAD(xpcAccessibilityService); + } + + nsrefcnt count = --mRefCnt; + NS_LOG_RELEASE(this, count, "xpcAccessibilityService"); + + if (count == 0) { + if (!nsAutoRefCnt::isThreadSafe) { + NS_ASSERT_OWNINGTHREAD(xpcAccessibilityService); + } + + mRefCnt = 1; /* stabilize */ + delete (this); + return 0; + } + + // When ref count goes down to 1 (held internally as a static reference), + // it means that there are no more external references to the + // xpcAccessibilityService and we can attempt to shut down acceessiblity + // service. + if (count == 1 && !mShutdownTimer) { + NS_NewTimerWithFuncCallback( + getter_AddRefs(mShutdownTimer), ShutdownCallback, this, 100, + nsITimer::TYPE_ONE_SHOT, "xpcAccessibilityService::Release"); + } + + return count; +} + +NS_IMPL_QUERY_INTERFACE(xpcAccessibilityService, nsIAccessibilityService) + +NS_IMETHODIMP +xpcAccessibilityService::GetApplicationAccessible( + nsIAccessible** aAccessibleApplication) { + NS_ENSURE_ARG_POINTER(aAccessibleApplication); + + NS_IF_ADDREF(*aAccessibleApplication = XPCApplicationAcc()); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibilityService::GetAccessibleFor(nsINode* aNode, + nsIAccessible** aAccessible) { + NS_ENSURE_ARG_POINTER(aAccessible); + *aAccessible = nullptr; + if (!aNode) { + return NS_OK; + } + + nsAccessibilityService* accService = GetAccService(); + if (!accService) { + return NS_ERROR_SERVICE_NOT_AVAILABLE; + } + + DocAccessible* document = accService->GetDocAccessible(aNode->OwnerDoc()); + if (document) { + NS_IF_ADDREF(*aAccessible = ToXPC(document->GetAccessible(aNode))); + } + + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibilityService::GetAccessibleDescendantFor( + nsINode* aNode, nsIAccessible** aAccessible) { + NS_ENSURE_ARG_POINTER(aAccessible); + *aAccessible = nullptr; + if (!aNode) { + return NS_OK; + } + + nsAccessibilityService* accService = GetAccService(); + if (!accService) { + return NS_ERROR_SERVICE_NOT_AVAILABLE; + } + + DocAccessible* document = accService->GetDocAccessible(aNode->OwnerDoc()); + if (document) { + NS_IF_ADDREF(*aAccessible = + ToXPC(document->GetAccessibleOrDescendant(aNode))); + } + + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibilityService::GetStringRole(uint32_t aRole, nsAString& aString) { + nsAccessibilityService* accService = GetAccService(); + if (!accService) { + return NS_ERROR_SERVICE_NOT_AVAILABLE; + } + + accService->GetStringRole(aRole, aString); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibilityService::GetStringStates(uint32_t aState, uint32_t aExtraState, + nsISupports** aStringStates) { + nsAccessibilityService* accService = GetAccService(); + if (!accService) { + return NS_ERROR_SERVICE_NOT_AVAILABLE; + } + + accService->GetStringStates(aState, aExtraState, aStringStates); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibilityService::GetStringEventType(uint32_t aEventType, + nsAString& aString) { + nsAccessibilityService* accService = GetAccService(); + if (!accService) { + return NS_ERROR_SERVICE_NOT_AVAILABLE; + } + + accService->GetStringEventType(aEventType, aString); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibilityService::GetStringRelationType(uint32_t aRelationType, + nsAString& aString) { + nsAccessibilityService* accService = GetAccService(); + if (!accService) { + return NS_ERROR_SERVICE_NOT_AVAILABLE; + } + + accService->GetStringRelationType(aRelationType, aString); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibilityService::GetAccessibleFromCache(nsINode* aNode, + nsIAccessible** aAccessible) { + NS_ENSURE_ARG_POINTER(aAccessible); + *aAccessible = nullptr; + if (!aNode) { + return NS_OK; + } + + nsAccessibilityService* accService = GetAccService(); + if (!accService) { + return NS_ERROR_SERVICE_NOT_AVAILABLE; + } + + // Search for an accessible in each of our per document accessible object + // caches. If we don't find it, and the given node is itself a document, check + // our cache of document accessibles (document cache). Note usually shutdown + // document accessibles are not stored in the document cache, however an + // "unofficially" shutdown document (i.e. not from DocManager) can still + // exist in the document cache. + LocalAccessible* accessible = accService->FindAccessibleInCache(aNode); + if (!accessible && aNode->IsDocument()) { + accessible = mozilla::a11y::GetExistingDocAccessible(aNode->AsDocument()); + } + + NS_IF_ADDREF(*aAccessible = ToXPC(accessible)); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibilityService::CreateAccessiblePivot(nsIAccessible* aRoot, + nsIAccessiblePivot** aPivot) { + NS_ENSURE_ARG_POINTER(aPivot); + NS_ENSURE_ARG(aRoot); + *aPivot = nullptr; + + xpcAccessiblePivot* pivot = new xpcAccessiblePivot(aRoot); + NS_ADDREF(*aPivot = pivot); + + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibilityService::CreateTextLeafPoint( + nsIAccessible* aAccessible, int32_t aOffset, + nsIAccessibleTextLeafPoint** aPoint) { + NS_ENSURE_ARG_POINTER(aPoint); + NS_ENSURE_ARG(aAccessible); + *aPoint = nullptr; + + RefPtr<xpcAccessibleTextLeafPoint> point = + new xpcAccessibleTextLeafPoint(aAccessible, aOffset); + point.forget(aPoint); + + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibilityService::SetLogging(const nsACString& aModules) { +#ifdef A11Y_LOG + logging::Enable(PromiseFlatCString(aModules)); +#endif + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibilityService::IsLogged(const nsAString& aModule, bool* aIsLogged) { + NS_ENSURE_ARG_POINTER(aIsLogged); + *aIsLogged = false; + +#ifdef A11Y_LOG + *aIsLogged = logging::IsEnabled(aModule); +#endif + + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibilityService::GetConsumers(nsAString& aString) { + nsAccessibilityService* accService = GetAccService(); + if (!accService) { + return NS_ERROR_SERVICE_NOT_AVAILABLE; + } + + accService->GetConsumers(aString); + return NS_OK; +} + +//////////////////////////////////////////////////////////////////////////////// +// NS_GetAccessibilityService +//////////////////////////////////////////////////////////////////////////////// + +nsresult NS_GetAccessibilityService(nsIAccessibilityService** aResult) { + NS_ENSURE_TRUE(aResult, NS_ERROR_NULL_POINTER); + *aResult = nullptr; + + if (!GetOrCreateAccService(nsAccessibilityService::eXPCOM)) { + return NS_ERROR_SERVICE_NOT_AVAILABLE; + } + + xpcAccessibilityService* service = new xpcAccessibilityService(); + xpcAccessibilityService::gXPCAccessibilityService = service; + NS_ADDREF(*aResult = service); + + return NS_OK; +} diff --git a/accessible/xpcom/xpcAccessibilityService.h b/accessible/xpcom/xpcAccessibilityService.h new file mode 100644 index 0000000000..04c23e7075 --- /dev/null +++ b/accessible/xpcom/xpcAccessibilityService.h @@ -0,0 +1,68 @@ +/* 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/. */ + +#ifndef mozilla_a11y_xpcAccessibilityService_h_ +#define mozilla_a11y_xpcAccessibilityService_h_ + +#include "nsIAccessibilityService.h" +#include "nsITimer.h" + +class xpcAccessibilityService : public nsIAccessibilityService { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIACCESSIBILITYSERVICE + + /** + * Return true if xpc accessibility service is in use. + */ + static bool IsInUse() { + // When ref count goes down to 1 (held internally as a static reference), + // it means that there are no more external references and thus it is not in + // use. + return gXPCAccessibilityService ? gXPCAccessibilityService->mRefCnt > 1 + : false; + } + + protected: + virtual ~xpcAccessibilityService() { + if (mShutdownTimer) { + mShutdownTimer->Cancel(); + mShutdownTimer = nullptr; + } + gXPCAccessibilityService = nullptr; + } + + private: + // xpcAccessibilityService creation is controlled by friend + // NS_GetAccessibilityService, keep constructor private. + xpcAccessibilityService() = default; + + nsCOMPtr<nsITimer> mShutdownTimer; + + /** + * Reference for xpc accessibility service instance. + */ + static xpcAccessibilityService* gXPCAccessibilityService; + + /** + * Used to shutdown nsAccessibilityService if xpcom accessible service is not + * in use any more. + */ + static void ShutdownCallback(nsITimer* aTimer, void* aClosure); + + friend nsresult NS_GetAccessibilityService(nsIAccessibilityService** aResult); +}; + +// for component registration +// {3b265b69-f813-48ff-880d-d88d101af404} +#define NS_ACCESSIBILITY_SERVICE_CID \ + { \ + 0x3b265b69, 0xf813, 0x48ff, { \ + 0x88, 0x0d, 0xd8, 0x8d, 0x10, 0x1a, 0xf4, 0x04 \ + } \ + } + +extern nsresult NS_GetAccessibilityService(nsIAccessibilityService** aResult); + +#endif diff --git a/accessible/xpcom/xpcAccessible.cpp b/accessible/xpcom/xpcAccessible.cpp new file mode 100644 index 0000000000..37901e3ec2 --- /dev/null +++ b/accessible/xpcom/xpcAccessible.cpp @@ -0,0 +1,665 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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/. */ + +#include "AccAttributes.h" +#include "nsAccUtils.h" +#include "nsComponentManagerUtils.h" +#include "nsIAccessibleRelation.h" +#include "nsIAccessibleRole.h" +#include "nsAccessibleRelation.h" +#include "Relation.h" +#include "RootAccessible.h" +#include "xpcAccessibleDocument.h" + +#include "nsIMutableArray.h" +#include "nsPersistentProperties.h" + +#ifdef MOZ_WIDGET_COCOA +# include "xpcAccessibleMacInterface.h" +#endif + +using namespace mozilla::a11y; + +NS_IMETHODIMP +xpcAccessible::GetParent(nsIAccessible** aParent) { + NS_ENSURE_ARG_POINTER(aParent); + *aParent = nullptr; + if (!IntlGeneric()) return NS_ERROR_FAILURE; + + Accessible* parent = IntlGeneric()->Parent(); + NS_IF_ADDREF(*aParent = ToXPC(parent)); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessible::GetNextSibling(nsIAccessible** aNextSibling) { + NS_ENSURE_ARG_POINTER(aNextSibling); + *aNextSibling = nullptr; + if (!IntlGeneric()) return NS_ERROR_FAILURE; + + NS_IF_ADDREF(*aNextSibling = ToXPC(IntlGeneric()->NextSibling())); + + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessible::GetPreviousSibling(nsIAccessible** aPreviousSibling) { + NS_ENSURE_ARG_POINTER(aPreviousSibling); + *aPreviousSibling = nullptr; + if (!IntlGeneric()) return NS_ERROR_FAILURE; + + NS_IF_ADDREF(*aPreviousSibling = ToXPC(IntlGeneric()->PrevSibling())); + + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessible::GetFirstChild(nsIAccessible** aFirstChild) { + NS_ENSURE_ARG_POINTER(aFirstChild); + *aFirstChild = nullptr; + + if (!IntlGeneric()) return NS_ERROR_FAILURE; + + NS_IF_ADDREF(*aFirstChild = ToXPC(IntlGeneric()->FirstChild())); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessible::GetLastChild(nsIAccessible** aLastChild) { + NS_ENSURE_ARG_POINTER(aLastChild); + *aLastChild = nullptr; + + if (!IntlGeneric()) return NS_ERROR_FAILURE; + + NS_IF_ADDREF(*aLastChild = ToXPC(IntlGeneric()->LastChild())); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessible::GetChildCount(int32_t* aChildCount) { + NS_ENSURE_ARG_POINTER(aChildCount); + + if (!IntlGeneric()) return NS_ERROR_FAILURE; + + *aChildCount = IntlGeneric()->ChildCount(); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessible::GetChildAt(int32_t aChildIndex, nsIAccessible** aChild) { + NS_ENSURE_ARG_POINTER(aChild); + *aChild = nullptr; + + if (!IntlGeneric()) return NS_ERROR_FAILURE; + + // If child index is negative, then return last child. + // XXX: do we really need this? + if (aChildIndex < 0) aChildIndex = IntlGeneric()->ChildCount() - 1; + + Accessible* child = IntlGeneric()->ChildAt(aChildIndex); + if (!child) return NS_ERROR_INVALID_ARG; + + NS_ADDREF(*aChild = ToXPC(child)); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessible::GetChildren(nsIArray** aChildren) { + NS_ENSURE_ARG_POINTER(aChildren); + *aChildren = nullptr; + + if (!IntlGeneric()) return NS_ERROR_FAILURE; + + nsresult rv = NS_OK; + nsCOMPtr<nsIMutableArray> children = + do_CreateInstance(NS_ARRAY_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + uint32_t childCount = IntlGeneric()->ChildCount(); + for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) { + Accessible* child = IntlGeneric()->ChildAt(childIdx); + children->AppendElement(static_cast<nsIAccessible*>(ToXPC(child))); + } + + children.forget(aChildren); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessible::GetIndexInParent(int32_t* aIndexInParent) { + NS_ENSURE_ARG_POINTER(aIndexInParent); + *aIndexInParent = -1; + if (!IntlGeneric()) return NS_ERROR_FAILURE; + + *aIndexInParent = IntlGeneric()->IndexInParent(); + + return *aIndexInParent != -1 ? NS_OK : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +xpcAccessible::GetUniqueID(int64_t* aUniqueID) { + NS_ENSURE_ARG_POINTER(aUniqueID); + + if (!IntlGeneric()) return NS_ERROR_FAILURE; + + if (IntlGeneric()->IsLocal()) { + *aUniqueID = reinterpret_cast<uintptr_t>(Intl()->UniqueID()); + } else if (IntlGeneric()->IsRemote()) { + *aUniqueID = IntlGeneric()->AsRemote()->ID(); + } + + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessible::GetDOMNode(nsINode** aDOMNode) { + NS_ENSURE_ARG_POINTER(aDOMNode); + *aDOMNode = nullptr; + + if (!Intl()) return NS_ERROR_FAILURE; + + nsCOMPtr<nsINode> node = Intl()->GetNode(); + node.forget(aDOMNode); + + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessible::GetId(nsAString& aID) { + if (!IntlGeneric()) { + return NS_ERROR_FAILURE; + } + + RemoteAccessible* proxy = IntlGeneric()->AsRemote(); + if (!proxy) { + return NS_ERROR_FAILURE; + } + + nsString id; + proxy->DOMNodeID(id); + aID.Assign(id); + + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessible::GetDocument(nsIAccessibleDocument** aDocument) { + NS_ENSURE_ARG_POINTER(aDocument); + *aDocument = nullptr; + + if (!Intl()) return NS_ERROR_FAILURE; + + NS_IF_ADDREF(*aDocument = ToXPCDocument(Intl()->Document())); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessible::GetRootDocument(nsIAccessibleDocument** aRootDocument) { + NS_ENSURE_ARG_POINTER(aRootDocument); + *aRootDocument = nullptr; + + if (!Intl()) return NS_ERROR_FAILURE; + + NS_IF_ADDREF(*aRootDocument = ToXPCDocument(Intl()->RootAccessible())); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessible::GetRole(uint32_t* aRole) { + NS_ENSURE_ARG_POINTER(aRole); + *aRole = nsIAccessibleRole::ROLE_NOTHING; + + if (!IntlGeneric()) return NS_ERROR_FAILURE; + + *aRole = IntlGeneric()->Role(); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessible::GetState(uint32_t* aState, uint32_t* aExtraState) { + NS_ENSURE_ARG_POINTER(aState); + + Accessible* acc = IntlGeneric(); + if (acc) { + nsAccUtils::To32States(acc->State(), aState, aExtraState); + } else { + nsAccUtils::To32States(states::DEFUNCT, aState, aExtraState); + } + + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessible::GetName(nsAString& aName) { + aName.Truncate(); + + if (!IntlGeneric()) return NS_ERROR_FAILURE; + + nsAutoString name; + IntlGeneric()->Name(name); + + aName.Assign(name); + + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessible::GetDescription(nsAString& aDescription) { + if (!IntlGeneric()) return NS_ERROR_FAILURE; + + nsAutoString desc; + IntlGeneric()->Description(desc); + + aDescription.Assign(desc); + + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessible::GetLanguage(nsAString& aLanguage) { + if (!IntlGeneric()) return NS_ERROR_FAILURE; + + nsAutoString lang; + IntlGeneric()->Language(lang); + + aLanguage.Assign(lang); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessible::GetValue(nsAString& aValue) { + if (!IntlGeneric()) return NS_ERROR_FAILURE; + + nsAutoString value; + IntlGeneric()->Value(value); + + aValue.Assign(value); + + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessible::GetAccessKey(nsAString& aAccessKey) { + aAccessKey.Truncate(); + + if (!IntlGeneric()) return NS_ERROR_FAILURE; + + IntlGeneric()->AccessKey().ToString(aAccessKey); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessible::GetKeyboardShortcut(nsAString& aKeyBinding) { + aKeyBinding.Truncate(); + if (!IntlGeneric()) return NS_ERROR_FAILURE; + + if (IntlGeneric()->IsRemote()) { + return NS_ERROR_NOT_IMPLEMENTED; + } + Intl()->KeyboardShortcut().ToString(aKeyBinding); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessible::GetAttributes(nsIPersistentProperties** aAttributes) { + NS_ENSURE_ARG_POINTER(aAttributes); + *aAttributes = nullptr; + + if (!IntlGeneric()) { + return NS_ERROR_FAILURE; + } + + RefPtr<nsPersistentProperties> props = new nsPersistentProperties(); + + RefPtr<AccAttributes> attributes = IntlGeneric()->Attributes(); + + nsAutoString unused; + for (auto iter : *attributes) { + nsAutoString name; + iter.NameAsString(name); + + nsAutoString value; + iter.ValueAsString(value); + + props->SetStringProperty(NS_ConvertUTF16toUTF8(name), value, unused); + } + + props.forget(aAttributes); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessible::GetCache(nsIPersistentProperties** aCachedFields) { + NS_ENSURE_ARG_POINTER(aCachedFields); + *aCachedFields = nullptr; + + if (!IntlGeneric()) { + return NS_ERROR_FAILURE; + } + + RefPtr<nsPersistentProperties> props = new nsPersistentProperties(); + if (RemoteAccessible* remoteAcc = IntlGeneric()->AsRemote()) { + if (RefPtr<AccAttributes> cachedFields = remoteAcc->mCachedFields) { + nsAutoString unused; + for (auto iter : *cachedFields) { + nsAutoString name; + iter.NameAsString(name); + + nsAutoString value; + iter.ValueAsString(value); + + props->SetStringProperty(NS_ConvertUTF16toUTF8(name), value, unused); + } + } + } + + props.forget(aCachedFields); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessible::GetNativeInterface(nsISupports** aNativeInterface) { +#ifdef MOZ_WIDGET_COCOA + NS_ENSURE_ARG_POINTER(aNativeInterface); + + // We don't cache or store this instance anywhere so each get returns a + // different instance. So `acc.nativeInterface != acc.nativeInterface`. This + // just seems simpler and more robust for now. + nsCOMPtr<nsISupports> macIface = static_cast<nsIAccessibleMacInterface*>( + new xpcAccessibleMacInterface(IntlGeneric())); + macIface.swap(*aNativeInterface); + + return NS_OK; +#else + return NS_ERROR_NOT_IMPLEMENTED; +#endif +} + +NS_IMETHODIMP +xpcAccessible::GetBounds(int32_t* aX, int32_t* aY, int32_t* aWidth, + int32_t* aHeight) { + NS_ENSURE_ARG_POINTER(aX); + *aX = 0; + NS_ENSURE_ARG_POINTER(aY); + *aY = 0; + NS_ENSURE_ARG_POINTER(aWidth); + *aWidth = 0; + NS_ENSURE_ARG_POINTER(aHeight); + *aHeight = 0; + + if (!IntlGeneric()) return NS_ERROR_FAILURE; + + LayoutDeviceIntRect rect = IntlGeneric()->Bounds(); + rect.GetRect(aX, aY, aWidth, aHeight); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessible::GetBoundsInCSSPixels(int32_t* aX, int32_t* aY, int32_t* aWidth, + int32_t* aHeight) { + NS_ENSURE_ARG_POINTER(aX); + *aX = 0; + NS_ENSURE_ARG_POINTER(aY); + *aY = 0; + NS_ENSURE_ARG_POINTER(aWidth); + *aWidth = 0; + NS_ENSURE_ARG_POINTER(aHeight); + *aHeight = 0; + + if (!IntlGeneric()) { + return NS_ERROR_FAILURE; + } + + nsIntRect rect = IntlGeneric()->BoundsInCSSPixels(); + rect.GetRect(aX, aY, aWidth, aHeight); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessible::GroupPosition(int32_t* aGroupLevel, + int32_t* aSimilarItemsInGroup, + int32_t* aPositionInGroup) { + NS_ENSURE_ARG_POINTER(aGroupLevel); + NS_ENSURE_ARG_POINTER(aSimilarItemsInGroup); + NS_ENSURE_ARG_POINTER(aPositionInGroup); + + GroupPos groupPos = IntlGeneric()->GroupPosition(); + + *aGroupLevel = groupPos.level; + *aSimilarItemsInGroup = groupPos.setSize; + *aPositionInGroup = groupPos.posInSet; + + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessible::GetRelationByType(uint32_t aType, + nsIAccessibleRelation** aRelation) { + NS_ENSURE_ARG_POINTER(aRelation); + *aRelation = nullptr; + + NS_ENSURE_ARG(aType <= static_cast<uint32_t>(RelationType::LAST)); + + if (!IntlGeneric()) return NS_ERROR_FAILURE; + + Relation rel = + IntlGeneric()->RelationByType(static_cast<RelationType>(aType)); + NS_ADDREF(*aRelation = new nsAccessibleRelation(aType, &rel)); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessible::GetRelations(nsIArray** aRelations) { + NS_ENSURE_ARG_POINTER(aRelations); + *aRelations = nullptr; + + if (!IntlGeneric()) return NS_ERROR_FAILURE; + + nsCOMPtr<nsIMutableArray> relations = do_CreateInstance(NS_ARRAY_CONTRACTID); + NS_ENSURE_TRUE(relations, NS_ERROR_OUT_OF_MEMORY); + + for (uint32_t type = 0; type <= static_cast<uint32_t>(RelationType::LAST); + ++type) { + nsCOMPtr<nsIAccessibleRelation> relation; + nsresult rv = GetRelationByType(type, getter_AddRefs(relation)); + + if (NS_SUCCEEDED(rv) && relation) { + uint32_t targets = 0; + relation->GetTargetsCount(&targets); + if (targets) relations->AppendElement(relation); + } + } + + NS_ADDREF(*aRelations = relations); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessible::GetFocusedChild(nsIAccessible** aChild) { + NS_ENSURE_ARG_POINTER(aChild); + *aChild = nullptr; + + if (!IntlGeneric()) return NS_ERROR_FAILURE; + + NS_IF_ADDREF(*aChild = ToXPC(IntlGeneric()->FocusedChild())); + + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessible::GetChildAtPoint(int32_t aX, int32_t aY, + nsIAccessible** aAccessible) { + NS_ENSURE_ARG_POINTER(aAccessible); + *aAccessible = nullptr; + + if (!IntlGeneric()) return NS_ERROR_FAILURE; + + NS_IF_ADDREF(*aAccessible = ToXPC(IntlGeneric()->ChildAtPoint( + aX, aY, Accessible::EWhichChildAtPoint::DirectChild))); + + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessible::GetDeepestChildAtPoint(int32_t aX, int32_t aY, + nsIAccessible** aAccessible) { + NS_ENSURE_ARG_POINTER(aAccessible); + *aAccessible = nullptr; + + if (!IntlGeneric()) return NS_ERROR_FAILURE; + + NS_IF_ADDREF(*aAccessible = ToXPC(IntlGeneric()->ChildAtPoint( + aX, aY, Accessible::EWhichChildAtPoint::DeepestChild))); + + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessible::GetDeepestChildAtPointInProcess(int32_t aX, int32_t aY, + nsIAccessible** aAccessible) { + NS_ENSURE_ARG_POINTER(aAccessible); + *aAccessible = nullptr; + + Accessible* generic = IntlGeneric(); + if (!generic || generic->IsRemote()) { + return NS_ERROR_FAILURE; + } + + NS_IF_ADDREF(*aAccessible = ToXPC(Intl()->LocalChildAtPoint( + aX, aY, Accessible::EWhichChildAtPoint::DeepestChild))); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessible::SetSelected(bool aSelect) { + if (!IntlGeneric()) return NS_ERROR_FAILURE; + + IntlGeneric()->SetSelected(aSelect); + + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessible::TakeSelection() { + if (!IntlGeneric()) return NS_ERROR_FAILURE; + + IntlGeneric()->TakeSelection(); + + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessible::TakeFocus() { + if (!IntlGeneric()) return NS_ERROR_FAILURE; + + IntlGeneric()->TakeFocus(); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessible::GetActionCount(uint8_t* aActionCount) { + NS_ENSURE_ARG_POINTER(aActionCount); + *aActionCount = 0; + if (!IntlGeneric()) return NS_ERROR_FAILURE; + + *aActionCount = IntlGeneric()->ActionCount(); + + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessible::GetActionName(uint8_t aIndex, nsAString& aName) { + aName.Truncate(); + + if (!IntlGeneric()) { + return NS_ERROR_FAILURE; + } + + if (aIndex >= IntlGeneric()->ActionCount()) { + return NS_ERROR_INVALID_ARG; + } + + nsAutoString name; + IntlGeneric()->ActionNameAt(aIndex, name); + + aName.Assign(name); + + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessible::GetActionDescription(uint8_t aIndex, nsAString& aDescription) { + aDescription.Truncate(); + + if (!IntlGeneric()) { + return NS_ERROR_FAILURE; + } + + if (aIndex >= IntlGeneric()->ActionCount()) { + return NS_ERROR_INVALID_ARG; + } + + nsAutoString description; + IntlGeneric()->ActionDescriptionAt(aIndex, description); + + aDescription.Assign(description); + + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessible::DoAction(uint8_t aIndex) { + if (!IntlGeneric()) return NS_ERROR_FAILURE; + + return IntlGeneric()->DoAction(aIndex) ? NS_OK : NS_ERROR_INVALID_ARG; +} + +NS_IMETHODIMP +xpcAccessible::ScrollTo(uint32_t aHow) { + if (!IntlGeneric()) return NS_ERROR_FAILURE; + + IntlGeneric()->ScrollTo(aHow); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessible::ScrollToPoint(uint32_t aCoordinateType, int32_t aX, int32_t aY) { + if (!IntlGeneric()) return NS_ERROR_FAILURE; + + IntlGeneric()->ScrollToPoint(aCoordinateType, aX, aY); + + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessible::Announce(const nsAString& aAnnouncement, uint16_t aPriority) { + if (RemoteAccessible* proxy = IntlGeneric()->AsRemote()) { +#if defined(XP_WIN) + return NS_ERROR_NOT_IMPLEMENTED; +#else + nsString announcement(aAnnouncement); + proxy->Announce(announcement, aPriority); +#endif + } else { + Intl()->Announce(aAnnouncement, aPriority); + } + + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessible::GetComputedARIARole(nsAString& aRole) { + if (!IntlGeneric()) { + return NS_ERROR_FAILURE; + } + + nsStaticAtom* ariaRole = IntlGeneric()->ComputedARIARole(); + if (ariaRole) { + ariaRole->ToString(aRole); + } + + return NS_OK; +} diff --git a/accessible/xpcom/xpcAccessible.h b/accessible/xpcom/xpcAccessible.h new file mode 100644 index 0000000000..02168ecf5d --- /dev/null +++ b/accessible/xpcom/xpcAccessible.h @@ -0,0 +1,112 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_a11y_xpcAccessible_h_ +#define mozilla_a11y_xpcAccessible_h_ + +#include "nsIAccessible.h" + +class nsIAccessible; + +namespace mozilla { +namespace a11y { + +class Accessible; +class LocalAccessible; + +/** + * XPCOM nsIAccessible interface implementation, used by xpcAccessibleGeneric + * class. + */ +class xpcAccessible : public nsIAccessible { + public: + // nsIAccessible + NS_IMETHOD GetParent(nsIAccessible** aParent) final; + NS_IMETHOD GetNextSibling(nsIAccessible** aNextSibling) final; + NS_IMETHOD GetPreviousSibling(nsIAccessible** aPreviousSibling) final; + NS_IMETHOD GetFirstChild(nsIAccessible** aFirstChild) final; + NS_IMETHOD GetLastChild(nsIAccessible** aLastChild) final; + NS_IMETHOD GetChildCount(int32_t* aChildCount) final; + NS_IMETHOD GetChildAt(int32_t aChildIndex, nsIAccessible** aChild) final; + NS_IMETHOD GetChildren(nsIArray** aChildren) final; + NS_IMETHOD GetIndexInParent(int32_t* aIndexInParent) final; + + NS_IMETHOD GetUniqueID(int64_t* aUniqueID) final; + NS_IMETHOD GetDOMNode(nsINode** aDOMNode) final; + NS_IMETHOD GetId(nsAString& aID) final; + NS_IMETHOD GetDocument(nsIAccessibleDocument** aDocument) final; + NS_IMETHOD GetRootDocument(nsIAccessibleDocument** aRootDocument) final; + + NS_IMETHOD GetRole(uint32_t* aRole) final; + NS_IMETHOD GetState(uint32_t* aState, uint32_t* aExtraState) final; + + NS_IMETHOD GetDescription(nsAString& aDescription) final; + NS_IMETHOD GetName(nsAString& aName) final; + NS_IMETHOD GetLanguage(nsAString& aLanguage) final; + NS_IMETHOD GetValue(nsAString& aValue) final; + + NS_IMETHOD GetAccessKey(nsAString& aAccessKey) final; + NS_IMETHOD GetKeyboardShortcut(nsAString& aKeyBinding) final; + + NS_IMETHOD GetAttributes(nsIPersistentProperties** aAttributes) final; + + NS_IMETHOD GetCache(nsIPersistentProperties** aCachedFields) final; + + NS_IMETHOD GetNativeInterface(nsISupports** aNativeInterface) final; + + NS_IMETHOD GetBounds(int32_t* aX, int32_t* aY, int32_t* aWidth, + int32_t* aHeight) final; + NS_IMETHOD GetBoundsInCSSPixels(int32_t* aX, int32_t* aY, int32_t* aWidth, + int32_t* aHeight) final; + NS_IMETHOD GroupPosition(int32_t* aGroupLevel, int32_t* aSimilarItemsInGroup, + int32_t* aPositionInGroup) final; + NS_IMETHOD GetRelationByType(uint32_t aType, + nsIAccessibleRelation** aRelation) final; + NS_IMETHOD GetRelations(nsIArray** aRelations) final; + + NS_IMETHOD GetFocusedChild(nsIAccessible** aChild) final; + NS_IMETHOD GetChildAtPoint(int32_t aX, int32_t aY, + nsIAccessible** aAccessible) final; + NS_IMETHOD GetDeepestChildAtPoint(int32_t aX, int32_t aY, + nsIAccessible** aAccessible) final; + NS_IMETHOD GetDeepestChildAtPointInProcess(int32_t aX, int32_t aY, + nsIAccessible** aAccessible) final; + + NS_IMETHOD SetSelected(bool aSelect) final; + NS_IMETHOD TakeSelection() final; + NS_IMETHOD TakeFocus() final; + + NS_IMETHOD GetActionCount(uint8_t* aActionCount) final; + NS_IMETHOD GetActionName(uint8_t aIndex, nsAString& aName) final; + NS_IMETHOD GetActionDescription(uint8_t aIndex, + nsAString& aDescription) final; + NS_IMETHOD DoAction(uint8_t aIndex) final; + + MOZ_CAN_RUN_SCRIPT + NS_IMETHOD ScrollTo(uint32_t aHow) final; + NS_IMETHOD ScrollToPoint(uint32_t aCoordinateType, int32_t aX, + int32_t aY) final; + + NS_IMETHOD Announce(const nsAString& aAnnouncement, uint16_t aPriority) final; + + NS_IMETHOD GetComputedARIARole(nsAString& aRole) final; + + protected: + xpcAccessible() {} + virtual ~xpcAccessible() {} + + private: + LocalAccessible* Intl(); + Accessible* IntlGeneric(); + + xpcAccessible(const xpcAccessible&) = delete; + xpcAccessible& operator=(const xpcAccessible&) = delete; +}; + +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/xpcom/xpcAccessibleApplication.cpp b/accessible/xpcom/xpcAccessibleApplication.cpp new file mode 100644 index 0000000000..133c255778 --- /dev/null +++ b/accessible/xpcom/xpcAccessibleApplication.cpp @@ -0,0 +1,60 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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/. */ + +#include "xpcAccessibleApplication.h" + +#include "ApplicationAccessible.h" + +using namespace mozilla::a11y; + +//////////////////////////////////////////////////////////////////////////////// +// nsISupports + +NS_IMPL_ISUPPORTS_INHERITED(xpcAccessibleApplication, xpcAccessibleGeneric, + nsIAccessibleApplication) + +//////////////////////////////////////////////////////////////////////////////// +// nsIAccessibleApplication + +NS_IMETHODIMP +xpcAccessibleApplication::GetAppName(nsAString& aName) { + aName.Truncate(); + + if (!Intl()) return NS_ERROR_FAILURE; + + Intl()->AppName(aName); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleApplication::GetAppVersion(nsAString& aVersion) { + aVersion.Truncate(); + + if (!Intl()) return NS_ERROR_FAILURE; + + Intl()->AppVersion(aVersion); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleApplication::GetPlatformName(nsAString& aName) { + aName.Truncate(); + + if (!Intl()) return NS_ERROR_FAILURE; + + Intl()->PlatformName(aName); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleApplication::GetPlatformVersion(nsAString& aVersion) { + aVersion.Truncate(); + + if (!Intl()) return NS_ERROR_FAILURE; + + Intl()->PlatformVersion(aVersion); + return NS_OK; +} diff --git a/accessible/xpcom/xpcAccessibleApplication.h b/accessible/xpcom/xpcAccessibleApplication.h new file mode 100644 index 0000000000..432d0a07bb --- /dev/null +++ b/accessible/xpcom/xpcAccessibleApplication.h @@ -0,0 +1,47 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_a11y_xpcAccessibleApplication_h_ +#define mozilla_a11y_xpcAccessibleApplication_h_ + +#include "nsIAccessibleApplication.h" +#include "ApplicationAccessible.h" +#include "xpcAccessibleGeneric.h" + +namespace mozilla { +namespace a11y { + +/** + * XPCOM wrapper around ApplicationAccessible class. + */ +class xpcAccessibleApplication : public xpcAccessibleGeneric, + public nsIAccessibleApplication { + public: + explicit xpcAccessibleApplication(Accessible* aIntl) + : xpcAccessibleGeneric(aIntl) {} + + NS_DECL_ISUPPORTS_INHERITED + + // nsIAccessibleApplication + NS_IMETHOD GetAppName(nsAString& aName) final; + NS_IMETHOD GetAppVersion(nsAString& aVersion) final; + NS_IMETHOD GetPlatformName(nsAString& aName) final; + NS_IMETHOD GetPlatformVersion(nsAString& aVersion) final; + + protected: + virtual ~xpcAccessibleApplication() { Shutdown(); } + + private: + ApplicationAccessible* Intl() { return mIntl->AsLocal()->AsApplication(); } + + xpcAccessibleApplication(const xpcAccessibleApplication&) = delete; + xpcAccessibleApplication& operator=(const xpcAccessibleApplication&) = delete; +}; + +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/xpcom/xpcAccessibleDocument.cpp b/accessible/xpcom/xpcAccessibleDocument.cpp new file mode 100644 index 0000000000..d616e476b2 --- /dev/null +++ b/accessible/xpcom/xpcAccessibleDocument.cpp @@ -0,0 +1,179 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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/. */ + +#include "xpcAccessibleDocument.h" +#include "xpcAccessibleImage.h" +#include "xpcAccessibleTable.h" +#include "xpcAccessibleTableCell.h" + +#include "nsAccUtils.h" +#include "DocAccessible-inl.h" + +using namespace mozilla; +using namespace mozilla::a11y; + +//////////////////////////////////////////////////////////////////////////////// +// nsISupports + +NS_IMPL_QUERY_INTERFACE_INHERITED(xpcAccessibleDocument, xpcAccessibleHyperText, + nsIAccessibleDocument) +NS_IMPL_ADDREF_INHERITED(xpcAccessibleDocument, xpcAccessibleHyperText) +NS_IMETHODIMP_(MozExternalRefCountType) xpcAccessibleDocument::Release(void) { + nsrefcnt r = xpcAccessibleHyperText::Release(); + NS_LOG_RELEASE(this, r, "xpcAccessibleDocument"); + + // The only reference to the xpcAccessibleDocument is in DocManager's cache. + if (r == 1 && !!mIntl && mCache.Count() == 0) { + if (mIntl->IsLocal()) { + GetAccService()->RemoveFromXPCDocumentCache(mIntl->AsLocal()->AsDoc()); + } else { + GetAccService()->RemoveFromRemoteXPCDocumentCache( + mIntl->AsRemote()->AsDoc()); + } + } + return r; +} + +//////////////////////////////////////////////////////////////////////////////// +// nsIAccessibleDocument + +NS_IMETHODIMP +xpcAccessibleDocument::GetURL(nsAString& aURL) { + if (!mIntl) return NS_ERROR_FAILURE; + + nsAccUtils::DocumentURL(mIntl, aURL); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleDocument::GetTitle(nsAString& aTitle) { + if (!Intl()) return NS_ERROR_FAILURE; + + nsAutoString title; + Intl()->Title(title); + aTitle = title; + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleDocument::GetMimeType(nsAString& aType) { + if (!mIntl) return NS_ERROR_FAILURE; + + nsAccUtils::DocumentMimeType(mIntl, aType); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleDocument::GetDocType(nsAString& aType) { + if (!Intl()) return NS_ERROR_FAILURE; + + Intl()->DocType(aType); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleDocument::GetDOMDocument(dom::Document** aDOMDocument) { + NS_ENSURE_ARG_POINTER(aDOMDocument); + *aDOMDocument = nullptr; + + if (!Intl()) return NS_ERROR_FAILURE; + + if (Intl()->DocumentNode()) NS_ADDREF(*aDOMDocument = Intl()->DocumentNode()); + + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleDocument::GetWindow(mozIDOMWindowProxy** aDOMWindow) { + NS_ENSURE_ARG_POINTER(aDOMWindow); + *aDOMWindow = nullptr; + + if (!Intl()) return NS_ERROR_FAILURE; + + NS_IF_ADDREF(*aDOMWindow = Intl()->DocumentNode()->GetWindow()); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleDocument::GetParentDocument(nsIAccessibleDocument** aDocument) { + NS_ENSURE_ARG_POINTER(aDocument); + *aDocument = nullptr; + + if (!Intl()) return NS_ERROR_FAILURE; + + NS_IF_ADDREF(*aDocument = ToXPCDocument(Intl()->ParentDocument())); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleDocument::GetChildDocumentCount(uint32_t* aCount) { + NS_ENSURE_ARG_POINTER(aCount); + *aCount = 0; + + if (!Intl()) return NS_ERROR_FAILURE; + + *aCount = Intl()->ChildDocumentCount(); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleDocument::GetChildDocumentAt(uint32_t aIndex, + nsIAccessibleDocument** aDocument) { + NS_ENSURE_ARG_POINTER(aDocument); + *aDocument = nullptr; + + if (!Intl()) return NS_ERROR_FAILURE; + + NS_IF_ADDREF(*aDocument = ToXPCDocument(Intl()->GetChildDocumentAt(aIndex))); + return *aDocument ? NS_OK : NS_ERROR_INVALID_ARG; +} + +//////////////////////////////////////////////////////////////////////////////// +// xpcAccessibleDocument + +xpcAccessibleGeneric* xpcAccessibleDocument::GetAccessible( + Accessible* aAccessible) { + if (aAccessible->IsLocal() && + ToXPCDocument(aAccessible->AsLocal()->Document()) != this) { + NS_ERROR( + "This XPCOM document is not related with given internal accessible!"); + return nullptr; + } + + if (aAccessible->IsRemote() && + ToXPCDocument(aAccessible->AsRemote()->Document()) != this) { + NS_ERROR( + "This XPCOM document is not related with given internal accessible!"); + return nullptr; + } + + if (aAccessible->IsDoc()) return this; + + return mCache.LookupOrInsertWith(aAccessible, [&]() -> xpcAccessibleGeneric* { + if (aAccessible->IsImage()) { + return new xpcAccessibleImage(aAccessible); + } + if (aAccessible->IsTable()) { + return new xpcAccessibleTable(aAccessible); + } + if (aAccessible->IsTableCell()) { + return new xpcAccessibleTableCell(aAccessible); + } + if (aAccessible->IsHyperText()) { + return new xpcAccessibleHyperText(aAccessible); + } + + return new xpcAccessibleGeneric(aAccessible); + }); +} + +void xpcAccessibleDocument::Shutdown() { + for (auto iter = mCache.Iter(); !iter.Done(); iter.Next()) { + iter.Data()->Shutdown(); + iter.Remove(); + } + xpcAccessibleGeneric::Shutdown(); +} diff --git a/accessible/xpcom/xpcAccessibleDocument.h b/accessible/xpcom/xpcAccessibleDocument.h new file mode 100644 index 0000000000..8e9bf2b413 --- /dev/null +++ b/accessible/xpcom/xpcAccessibleDocument.h @@ -0,0 +1,136 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_a11y_xpcAccessibleDocument_h_ +#define mozilla_a11y_xpcAccessibleDocument_h_ + +#include "nsIAccessibleDocument.h" + +#include "DocAccessible.h" +#include "nsAccessibilityService.h" +#include "xpcAccessibleApplication.h" +#include "xpcAccessibleHyperText.h" + +namespace mozilla { +namespace a11y { + +/** + * XPCOM wrapper around DocAccessible class. + */ +class xpcAccessibleDocument : public xpcAccessibleHyperText, + public nsIAccessibleDocument { + public: + explicit xpcAccessibleDocument(Accessible* aIntl) + : xpcAccessibleHyperText(aIntl), + mCache(kDefaultCacheLength), + mRemote(aIntl->IsRemote()) { + // XXX: Once there is a base doc class that both remote and local + // accessibles inherit, we can add the type to the prototype and remove this + // assert. + MOZ_ASSERT(aIntl->IsDoc()); + } + + NS_DECL_ISUPPORTS_INHERITED + + // nsIAccessibleDocument + NS_IMETHOD GetURL(nsAString& aURL) final; + NS_IMETHOD GetTitle(nsAString& aTitle) final; + NS_IMETHOD GetMimeType(nsAString& aType) final; + NS_IMETHOD GetDocType(nsAString& aType) final; + NS_IMETHOD GetDOMDocument(dom::Document** aDOMDocument) final; + NS_IMETHOD GetWindow(mozIDOMWindowProxy** aDOMWindow) final; + NS_IMETHOD GetParentDocument(nsIAccessibleDocument** aDocument) final; + NS_IMETHOD GetChildDocumentCount(uint32_t* aCount) final; + NS_IMETHOD GetChildDocumentAt(uint32_t aIndex, + nsIAccessibleDocument** aDocument) final; + + /** + * Return XPCOM wrapper for the internal accessible. + */ + xpcAccessibleGeneric* GetAccessible(Accessible* aAccessible); + + virtual void Shutdown() override; + + protected: + virtual ~xpcAccessibleDocument() {} + + private: + DocAccessible* Intl() { + if (LocalAccessible* acc = mIntl->AsLocal()) { + return acc->AsDoc(); + } + + return nullptr; + } + + void NotifyOfShutdown(Accessible* aAccessible) { + xpcAccessibleGeneric* xpcAcc = mCache.Get(aAccessible); + if (xpcAcc) { + xpcAcc->Shutdown(); + } + + mCache.Remove(aAccessible); + if (mCache.Count() == 0 && mRefCnt == 1) { + if (mIntl->IsLocal()) { + GetAccService()->RemoveFromXPCDocumentCache(mIntl->AsLocal()->AsDoc()); + } else { + GetAccService()->RemoveFromRemoteXPCDocumentCache( + mIntl->AsRemote()->AsDoc()); + } + } + } + + friend class DocManager; + friend class DocAccessible; + friend class RemoteAccessible; + friend class xpcAccessibleGeneric; + + xpcAccessibleDocument(const xpcAccessibleDocument&) = delete; + xpcAccessibleDocument& operator=(const xpcAccessibleDocument&) = delete; + + nsTHashMap<nsPtrHashKey<const void>, xpcAccessibleGeneric*> mCache; + bool mRemote; +}; + +inline xpcAccessibleGeneric* ToXPC(Accessible* aAccessible) { + if (!aAccessible) return nullptr; + + if (aAccessible->IsApplication()) return XPCApplicationAcc(); + + xpcAccessibleDocument* xpcDoc = + aAccessible->IsLocal() + ? GetAccService()->GetXPCDocument(aAccessible->AsLocal()->Document()) + : GetAccService()->GetXPCDocument( + aAccessible->AsRemote()->Document()); + return xpcDoc ? xpcDoc->GetAccessible(aAccessible) : nullptr; +} + +inline xpcAccessibleHyperText* ToXPCText(Accessible* aAccessible) { + if (!aAccessible || !aAccessible->IsHyperText()) { + return nullptr; + } + + xpcAccessibleDocument* xpcDoc = + aAccessible->IsLocal() + ? GetAccService()->GetXPCDocument(aAccessible->AsLocal()->Document()) + : nsAccessibilityService::GetXPCDocument( + aAccessible->AsRemote()->Document()); + return static_cast<xpcAccessibleHyperText*>( + xpcDoc ? xpcDoc->GetAccessible(aAccessible) : nullptr); +} + +inline xpcAccessibleDocument* ToXPCDocument(DocAccessible* aAccessible) { + return GetAccService()->GetXPCDocument(aAccessible); +} + +inline xpcAccessibleDocument* ToXPCDocument(DocAccessibleParent* aAccessible) { + return GetAccService()->GetXPCDocument(aAccessible); +} + +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/xpcom/xpcAccessibleGeneric.cpp b/accessible/xpcom/xpcAccessibleGeneric.cpp new file mode 100644 index 0000000000..aaef5ac14c --- /dev/null +++ b/accessible/xpcom/xpcAccessibleGeneric.cpp @@ -0,0 +1,61 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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/. */ + +#include "xpcAccessibleGeneric.h" + +#include "xpcAccessibleDocument.h" + +using namespace mozilla::a11y; + +//////////////////////////////////////////////////////////////////////////////// +// nsISupports + +NS_INTERFACE_MAP_BEGIN(xpcAccessibleGeneric) + NS_INTERFACE_MAP_ENTRY(nsIAccessible) + NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAccessibleSelectable, + mSupportedIfaces & eSelectable) + NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAccessibleValue, + mSupportedIfaces & eValue) + NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAccessibleHyperLink, + mSupportedIfaces & eHyperLink) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAccessible) +NS_INTERFACE_MAP_END + +NS_IMPL_ADDREF(xpcAccessibleGeneric) +NS_IMPL_RELEASE(xpcAccessibleGeneric) + +xpcAccessibleGeneric::~xpcAccessibleGeneric() { + if (!mIntl) { + return; + } + + xpcAccessibleDocument* xpcDoc = nullptr; + if (mIntl->IsLocal()) { + LocalAccessible* acc = mIntl->AsLocal(); + if (!acc->IsDoc() && !acc->IsApplication()) { + xpcDoc = GetAccService()->GetXPCDocument(acc->Document()); + xpcDoc->NotifyOfShutdown(acc); + } + } else { + RemoteAccessible* proxy = mIntl->AsRemote(); + if (!proxy->IsDoc()) { + xpcDoc = GetAccService()->GetXPCDocument(proxy->Document()); + xpcDoc->NotifyOfShutdown(proxy); + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// nsIAccessible + +LocalAccessible* xpcAccessibleGeneric::ToInternalAccessible() { + return mIntl->AsLocal(); +} + +//////////////////////////////////////////////////////////////////////////////// +// xpcAccessibleGeneric + +void xpcAccessibleGeneric::Shutdown() { mIntl = nullptr; } diff --git a/accessible/xpcom/xpcAccessibleGeneric.h b/accessible/xpcom/xpcAccessibleGeneric.h new file mode 100644 index 0000000000..65df7e6ec7 --- /dev/null +++ b/accessible/xpcom/xpcAccessibleGeneric.h @@ -0,0 +1,96 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_a11y_xpcAccessibleGeneric_h_ +#define mozilla_a11y_xpcAccessibleGeneric_h_ + +#include "xpcAccessible.h" +#include "xpcAccessibleHyperLink.h" +#include "xpcAccessibleSelectable.h" +#include "xpcAccessibleValue.h" + +#include "LocalAccessible.h" +#include "mozilla/a11y/Accessible.h" +#include "mozilla/a11y/RemoteAccessible.h" + +namespace mozilla { +namespace a11y { + +/** + * XPCOM wrapper around Accessible class. + */ +class xpcAccessibleGeneric : public xpcAccessible, + public xpcAccessibleHyperLink, + public xpcAccessibleSelectable, + public xpcAccessibleValue { + public: + explicit xpcAccessibleGeneric(Accessible* aInternal) + : mIntl(aInternal), mSupportedIfaces(0) { + if (aInternal->IsSelect()) mSupportedIfaces |= eSelectable; + if (aInternal->HasNumericValue()) mSupportedIfaces |= eValue; + if (aInternal->IsLink()) mSupportedIfaces |= eHyperLink; + } + + NS_DECL_ISUPPORTS + + // nsIAccessible + LocalAccessible* ToInternalAccessible() final; + Accessible* ToInternalGeneric() final { return mIntl; } + + // xpcAccessibleGeneric + virtual void Shutdown(); + + protected: + virtual ~xpcAccessibleGeneric(); + + Accessible* mIntl; + + enum { + eSelectable = 1 << 0, + eValue = 1 << 1, + eHyperLink = 1 << 2, + eText = 1 << 3 + }; + uint8_t mSupportedIfaces; + + private: + friend class LocalAccessible; + friend class xpcAccessible; + friend class xpcAccessibleHyperLink; + friend class xpcAccessibleSelectable; + friend class xpcAccessibleValue; + + xpcAccessibleGeneric(const xpcAccessibleGeneric&) = delete; + xpcAccessibleGeneric& operator=(const xpcAccessibleGeneric&) = delete; +}; + +inline LocalAccessible* xpcAccessible::Intl() { + if (!static_cast<xpcAccessibleGeneric*>(this)->mIntl) { + return nullptr; + } + return static_cast<xpcAccessibleGeneric*>(this)->mIntl->AsLocal(); +} + +inline Accessible* xpcAccessible::IntlGeneric() { + return static_cast<xpcAccessibleGeneric*>(this)->mIntl; +} + +inline Accessible* xpcAccessibleHyperLink::Intl() { + return static_cast<xpcAccessibleGeneric*>(this)->mIntl; +} + +inline Accessible* xpcAccessibleSelectable::Intl() { + return static_cast<xpcAccessibleGeneric*>(this)->mIntl; +} + +inline Accessible* xpcAccessibleValue::Intl() { + return static_cast<xpcAccessibleGeneric*>(this)->mIntl; +} + +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/xpcom/xpcAccessibleHyperLink.cpp b/accessible/xpcom/xpcAccessibleHyperLink.cpp new file mode 100644 index 0000000000..113e8d3297 --- /dev/null +++ b/accessible/xpcom/xpcAccessibleHyperLink.cpp @@ -0,0 +1,92 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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/. */ + +#include "xpcAccessibleHyperLink.h" + +#include "xpcAccessibleDocument.h" + +using namespace mozilla::a11y; + +NS_IMETHODIMP +xpcAccessibleHyperLink::GetStartIndex(int32_t* aStartIndex) { + NS_ENSURE_ARG_POINTER(aStartIndex); + *aStartIndex = 0; + + if (!Intl()) return NS_ERROR_FAILURE; + + *aStartIndex = static_cast<int32_t>(Intl()->StartOffset()); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleHyperLink::GetEndIndex(int32_t* aEndIndex) { + NS_ENSURE_ARG_POINTER(aEndIndex); + *aEndIndex = 0; + + if (!Intl()) return NS_ERROR_FAILURE; + + *aEndIndex = static_cast<int32_t>(Intl()->EndOffset()); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleHyperLink::GetAnchorCount(int32_t* aAnchorCount) { + NS_ENSURE_ARG_POINTER(aAnchorCount); + *aAnchorCount = 0; + + if (!Intl()) return NS_ERROR_FAILURE; + + *aAnchorCount = Intl()->AnchorCount(); + + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleHyperLink::GetURI(int32_t aIndex, nsIURI** aURI) { + NS_ENSURE_ARG_POINTER(aURI); + + if (!Intl()) { + return NS_ERROR_FAILURE; + } + + if (aIndex < 0 || aIndex >= static_cast<int32_t>(Intl()->AnchorCount())) { + return NS_ERROR_INVALID_ARG; + } + + RefPtr<nsIURI>(Intl()->AnchorURIAt(aIndex)).forget(aURI); + + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleHyperLink::GetAnchor(int32_t aIndex, nsIAccessible** aAccessible) { + NS_ENSURE_ARG_POINTER(aAccessible); + *aAccessible = nullptr; + + if (!Intl()) return NS_ERROR_FAILURE; + + if (aIndex < 0) return NS_ERROR_INVALID_ARG; + + if (aIndex >= static_cast<int32_t>(Intl()->AnchorCount())) { + return NS_ERROR_INVALID_ARG; + } + + NS_IF_ADDREF(*aAccessible = ToXPC(Intl()->AnchorAt(aIndex))); + + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleHyperLink::GetValid(bool* aValid) { + NS_ENSURE_ARG_POINTER(aValid); + *aValid = false; + + if (!Intl()) return NS_ERROR_FAILURE; + + *aValid = Intl()->IsLinkValid(); + + return NS_OK; +} diff --git a/accessible/xpcom/xpcAccessibleHyperLink.h b/accessible/xpcom/xpcAccessibleHyperLink.h new file mode 100644 index 0000000000..85e686eb54 --- /dev/null +++ b/accessible/xpcom/xpcAccessibleHyperLink.h @@ -0,0 +1,48 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_a11y_xpcAccessibleHyperLink_h_ +#define mozilla_a11y_xpcAccessibleHyperLink_h_ + +#include "nsIAccessibleHyperLink.h" + +#include "mozilla/a11y/Accessible.h" + +class nsIAccessible; + +namespace mozilla { +namespace a11y { + +class LocalAccessible; + +/** + * XPCOM nsIAccessibleHyperLink implementation, used by xpcAccessibleGeneric + * class. + */ +class xpcAccessibleHyperLink : public nsIAccessibleHyperLink { + public: + NS_IMETHOD GetAnchorCount(int32_t* aAnchorCount) final; + NS_IMETHOD GetStartIndex(int32_t* aStartIndex) final; + NS_IMETHOD GetEndIndex(int32_t* aEndIndex) final; + NS_IMETHOD GetURI(int32_t aIndex, nsIURI** aURI) final; + NS_IMETHOD GetAnchor(int32_t aIndex, nsIAccessible** aAccessible) final; + NS_IMETHOD GetValid(bool* aValid) final; + + protected: + xpcAccessibleHyperLink() {} + virtual ~xpcAccessibleHyperLink() {} + + private: + xpcAccessibleHyperLink(const xpcAccessibleHyperLink&) = delete; + xpcAccessibleHyperLink& operator=(const xpcAccessibleHyperLink&) = delete; + + Accessible* Intl(); +}; + +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/xpcom/xpcAccessibleHyperText.cpp b/accessible/xpcom/xpcAccessibleHyperText.cpp new file mode 100644 index 0000000000..a1e86ac6cd --- /dev/null +++ b/accessible/xpcom/xpcAccessibleHyperText.cpp @@ -0,0 +1,477 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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/. */ + +#include "xpcAccessibleHyperText.h" + +#include "TextRange.h" +#include "AccAttributes.h" +#include "nsComponentManagerUtils.h" +#include "nsPersistentProperties.h" +#include "xpcAccessibleDocument.h" +#include "xpcAccessibleTextRange.h" + +#include "nsIMutableArray.h" + +using namespace mozilla::a11y; + +//////////////////////////////////////////////////////////////////////////////// +// nsISupports + +NS_INTERFACE_MAP_BEGIN(xpcAccessibleHyperText) + NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAccessibleText, + mSupportedIfaces & eText) + NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAccessibleEditableText, + mSupportedIfaces & eText) + NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAccessibleHyperText, + mSupportedIfaces & eText) +NS_INTERFACE_MAP_END_INHERITING(xpcAccessibleGeneric) + +NS_IMPL_ADDREF_INHERITED(xpcAccessibleHyperText, xpcAccessibleGeneric) +NS_IMPL_RELEASE_INHERITED(xpcAccessibleHyperText, xpcAccessibleGeneric) + +//////////////////////////////////////////////////////////////////////////////// +// nsIAccessibleText + +NS_IMETHODIMP +xpcAccessibleHyperText::GetCharacterCount(int32_t* aCharacterCount) { + NS_ENSURE_ARG_POINTER(aCharacterCount); + *aCharacterCount = 0; + + if (!mIntl) return NS_ERROR_FAILURE; + + *aCharacterCount = static_cast<int32_t>(Intl()->CharacterCount()); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleHyperText::GetText(int32_t aStartOffset, int32_t aEndOffset, + nsAString& aText) { + aText.Truncate(); + + if (!mIntl) return NS_ERROR_FAILURE; + + Intl()->TextSubstring(aStartOffset, aEndOffset, aText); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleHyperText::GetTextBeforeOffset( + int32_t aOffset, AccessibleTextBoundary aBoundaryType, + int32_t* aStartOffset, int32_t* aEndOffset, nsAString& aText) { + NS_ENSURE_ARG_POINTER(aStartOffset); + NS_ENSURE_ARG_POINTER(aEndOffset); + *aStartOffset = *aEndOffset = 0; + aText.Truncate(); + + if (!mIntl) return NS_ERROR_FAILURE; + + Intl()->TextBeforeOffset(aOffset, aBoundaryType, aStartOffset, aEndOffset, + aText); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleHyperText::GetTextAtOffset(int32_t aOffset, + AccessibleTextBoundary aBoundaryType, + int32_t* aStartOffset, + int32_t* aEndOffset, nsAString& aText) { + NS_ENSURE_ARG_POINTER(aStartOffset); + NS_ENSURE_ARG_POINTER(aEndOffset); + *aStartOffset = *aEndOffset = 0; + aText.Truncate(); + + if (!mIntl) return NS_ERROR_FAILURE; + + Intl()->TextAtOffset(aOffset, aBoundaryType, aStartOffset, aEndOffset, aText); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleHyperText::GetTextAfterOffset(int32_t aOffset, + AccessibleTextBoundary aBoundaryType, + int32_t* aStartOffset, + int32_t* aEndOffset, + nsAString& aText) { + NS_ENSURE_ARG_POINTER(aStartOffset); + NS_ENSURE_ARG_POINTER(aEndOffset); + *aStartOffset = *aEndOffset = 0; + aText.Truncate(); + + Intl()->TextAfterOffset(aOffset, aBoundaryType, aStartOffset, aEndOffset, + aText); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleHyperText::GetCharacterAtOffset(int32_t aOffset, + char16_t* aCharacter) { + NS_ENSURE_ARG_POINTER(aCharacter); + *aCharacter = L'\0'; + + if (!mIntl) return NS_ERROR_FAILURE; + + *aCharacter = Intl()->CharAt(aOffset); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleHyperText::GetTextAttributes( + bool aIncludeDefAttrs, int32_t aOffset, int32_t* aStartOffset, + int32_t* aEndOffset, nsIPersistentProperties** aAttributes) { + NS_ENSURE_ARG_POINTER(aStartOffset); + NS_ENSURE_ARG_POINTER(aEndOffset); + NS_ENSURE_ARG_POINTER(aAttributes); + *aStartOffset = *aEndOffset = 0; + *aAttributes = nullptr; + + if (!mIntl) return NS_ERROR_FAILURE; + + RefPtr<AccAttributes> attributes = Intl()->TextAttributes( + aIncludeDefAttrs, aOffset, aStartOffset, aEndOffset); + RefPtr<nsPersistentProperties> props = new nsPersistentProperties(); + nsAutoString unused; + for (auto iter : *attributes) { + nsAutoString name; + iter.NameAsString(name); + + nsAutoString value; + iter.ValueAsString(value); + + props->SetStringProperty(NS_ConvertUTF16toUTF8(name), value, unused); + } + + props.forget(aAttributes); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleHyperText::GetDefaultTextAttributes( + nsIPersistentProperties** aAttributes) { + NS_ENSURE_ARG_POINTER(aAttributes); + *aAttributes = nullptr; + + if (!mIntl) return NS_ERROR_FAILURE; + + RefPtr<AccAttributes> attributes = Intl()->DefaultTextAttributes(); + RefPtr<nsPersistentProperties> props = new nsPersistentProperties(); + nsAutoString unused; + for (auto iter : *attributes) { + nsAutoString name; + iter.NameAsString(name); + + nsAutoString value; + iter.ValueAsString(value); + + props->SetStringProperty(NS_ConvertUTF16toUTF8(name), value, unused); + } + + props.forget(aAttributes); + + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleHyperText::GetCharacterExtents(int32_t aOffset, int32_t* aX, + int32_t* aY, int32_t* aWidth, + int32_t* aHeight, + uint32_t aCoordType) { + NS_ENSURE_ARG_POINTER(aX); + NS_ENSURE_ARG_POINTER(aY); + NS_ENSURE_ARG_POINTER(aWidth); + NS_ENSURE_ARG_POINTER(aHeight); + *aX = *aY = *aWidth = *aHeight; + + if (!mIntl) return NS_ERROR_FAILURE; + + LayoutDeviceIntRect rect = Intl()->CharBounds(aOffset, aCoordType); + rect.GetRect(aX, aY, aWidth, aHeight); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleHyperText::GetRangeExtents(int32_t aStartOffset, + int32_t aEndOffset, int32_t* aX, + int32_t* aY, int32_t* aWidth, + int32_t* aHeight, uint32_t aCoordType) { + NS_ENSURE_ARG_POINTER(aX); + NS_ENSURE_ARG_POINTER(aY); + NS_ENSURE_ARG_POINTER(aWidth); + NS_ENSURE_ARG_POINTER(aHeight); + *aX = *aY = *aWidth = *aHeight = 0; + + if (!mIntl) return NS_ERROR_FAILURE; + + LayoutDeviceIntRect rect = + Intl()->TextBounds(aStartOffset, aEndOffset, aCoordType); + rect.GetRect(aX, aY, aWidth, aHeight); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleHyperText::GetOffsetAtPoint(int32_t aX, int32_t aY, + uint32_t aCoordType, + int32_t* aOffset) { + NS_ENSURE_ARG_POINTER(aOffset); + *aOffset = -1; + + if (!mIntl) return NS_ERROR_FAILURE; + + *aOffset = Intl()->OffsetAtPoint(aX, aY, aCoordType); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleHyperText::GetCaretOffset(int32_t* aCaretOffset) { + NS_ENSURE_ARG_POINTER(aCaretOffset); + *aCaretOffset = -1; + + if (!mIntl) return NS_ERROR_FAILURE; + + *aCaretOffset = Intl()->CaretOffset(); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleHyperText::SetCaretOffset(int32_t aCaretOffset) { + if (!mIntl) return NS_ERROR_FAILURE; + + Intl()->SetCaretOffset(aCaretOffset); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleHyperText::GetCaretRect(int32_t* aX, int32_t* aY, int32_t* aWidth, + int32_t* aHeight) { + NS_ENSURE_ARG_POINTER(aX); + NS_ENSURE_ARG_POINTER(aY); + NS_ENSURE_ARG_POINTER(aWidth); + NS_ENSURE_ARG_POINTER(aHeight); + *aX = *aY = *aWidth = *aHeight; + + if (!mIntl) { + return NS_ERROR_FAILURE; + } + if (mIntl->IsRemote()) { + return NS_ERROR_NOT_IMPLEMENTED; + } + + nsIWidget* widget; + LayoutDeviceIntRect rect = IntlLocal()->GetCaretRect(&widget); + rect.GetRect(aX, aY, aWidth, aHeight); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleHyperText::GetSelectionCount(int32_t* aSelectionCount) { + NS_ENSURE_ARG_POINTER(aSelectionCount); + *aSelectionCount = 0; + + if (!mIntl) return NS_ERROR_FAILURE; + + *aSelectionCount = Intl()->SelectionCount(); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleHyperText::GetSelectionBounds(int32_t aSelectionNum, + int32_t* aStartOffset, + int32_t* aEndOffset) { + NS_ENSURE_ARG_POINTER(aStartOffset); + NS_ENSURE_ARG_POINTER(aEndOffset); + *aStartOffset = *aEndOffset = 0; + + if (!mIntl) return NS_ERROR_FAILURE; + + if (aSelectionNum < 0) return NS_ERROR_INVALID_ARG; + + if (aSelectionNum >= Intl()->SelectionCount()) { + return NS_ERROR_INVALID_ARG; + } + + Intl()->SelectionBoundsAt(aSelectionNum, aStartOffset, aEndOffset); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleHyperText::SetSelectionBounds(int32_t aSelectionNum, + int32_t aStartOffset, + int32_t aEndOffset) { + if (!mIntl) return NS_ERROR_FAILURE; + + if (aSelectionNum < 0) return NS_ERROR_INVALID_ARG; + + Intl()->SetSelectionBoundsAt(aSelectionNum, aStartOffset, aEndOffset); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleHyperText::AddSelection(int32_t aStartOffset, int32_t aEndOffset) { + if (!mIntl) return NS_ERROR_FAILURE; + + Intl()->AddToSelection(aStartOffset, aEndOffset); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleHyperText::RemoveSelection(int32_t aSelectionNum) { + if (!mIntl) return NS_ERROR_FAILURE; + + Intl()->RemoveFromSelection(aSelectionNum); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleHyperText::ScrollSubstringTo(int32_t aStartOffset, + int32_t aEndOffset, + uint32_t aScrollType) { + if (!mIntl) return NS_ERROR_FAILURE; + + Intl()->ScrollSubstringTo(aStartOffset, aEndOffset, aScrollType); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleHyperText::ScrollSubstringToPoint(int32_t aStartOffset, + int32_t aEndOffset, + uint32_t aCoordinateType, + int32_t aX, int32_t aY) { + if (!mIntl) return NS_ERROR_FAILURE; + + Intl()->ScrollSubstringToPoint(aStartOffset, aEndOffset, aCoordinateType, aX, + aY); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleHyperText::GetSelectionRanges(nsIArray** aRanges) { + NS_ENSURE_ARG_POINTER(aRanges); + *aRanges = nullptr; + + nsresult rv = NS_OK; + nsCOMPtr<nsIMutableArray> xpcRanges = + do_CreateInstance(NS_ARRAY_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + AutoTArray<TextRange, 1> ranges; + Intl()->SelectionRanges(&ranges); + uint32_t len = ranges.Length(); + for (uint32_t idx = 0; idx < len; idx++) { + xpcRanges->AppendElement(new xpcAccessibleTextRange(ranges[idx])); + } + + xpcRanges.forget(aRanges); + return NS_OK; +} + +//////////////////////////////////////////////////////////////////////////////// +// nsIAccessibleEditableText + +NS_IMETHODIMP +xpcAccessibleHyperText::SetTextContents(const nsAString& aText) { + if (!mIntl) return NS_ERROR_FAILURE; + + Intl()->ReplaceText(aText); + + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleHyperText::InsertText(const nsAString& aText, int32_t aOffset) { + if (!mIntl) return NS_ERROR_FAILURE; + + Intl()->InsertText(aText, aOffset); + + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleHyperText::CopyText(int32_t aStartOffset, int32_t aEndOffset) { + if (!mIntl) return NS_ERROR_FAILURE; + + Intl()->CopyText(aStartOffset, aEndOffset); + + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleHyperText::CutText(int32_t aStartOffset, int32_t aEndOffset) { + if (!mIntl) return NS_ERROR_FAILURE; + + Intl()->CutText(aStartOffset, aEndOffset); + + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleHyperText::DeleteText(int32_t aStartOffset, int32_t aEndOffset) { + if (!mIntl) return NS_ERROR_FAILURE; + + Intl()->DeleteText(aStartOffset, aEndOffset); + + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleHyperText::PasteText(int32_t aOffset) { + if (!mIntl) return NS_ERROR_FAILURE; + + Intl()->PasteText(aOffset); + + return NS_OK; +} + +//////////////////////////////////////////////////////////////////////////////// +// nsIAccessibleHyperText + +NS_IMETHODIMP +xpcAccessibleHyperText::GetLinkCount(int32_t* aLinkCount) { + NS_ENSURE_ARG_POINTER(aLinkCount); + *aLinkCount = 0; + + if (!mIntl) return NS_ERROR_FAILURE; + + *aLinkCount = static_cast<int32_t>(Intl()->LinkCount()); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleHyperText::GetLinkAt(int32_t aIndex, + nsIAccessibleHyperLink** aLink) { + NS_ENSURE_ARG_POINTER(aLink); + *aLink = nullptr; + + if (!mIntl) return NS_ERROR_FAILURE; + + NS_IF_ADDREF(*aLink = ToXPC(Intl()->LinkAt(aIndex))); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleHyperText::GetLinkIndex(nsIAccessibleHyperLink* aLink, + int32_t* aIndex) { + NS_ENSURE_ARG_POINTER(aLink); + NS_ENSURE_ARG_POINTER(aIndex); + *aIndex = -1; + + if (!mIntl) return NS_ERROR_FAILURE; + + nsCOMPtr<nsIAccessible> xpcLink(do_QueryInterface(aLink)); + Accessible* accLink = xpcLink->ToInternalGeneric(); + *aIndex = Intl()->LinkIndexOf(accLink); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleHyperText::GetLinkIndexAtOffset(int32_t aOffset, + int32_t* aLinkIndex) { + NS_ENSURE_ARG_POINTER(aLinkIndex); + *aLinkIndex = -1; // API says this magic value means 'not found' + + if (!mIntl) return NS_ERROR_FAILURE; + + *aLinkIndex = Intl()->LinkIndexAtOffset(aOffset); + return NS_OK; +} diff --git a/accessible/xpcom/xpcAccessibleHyperText.h b/accessible/xpcom/xpcAccessibleHyperText.h new file mode 100644 index 0000000000..06cdba2d7a --- /dev/null +++ b/accessible/xpcom/xpcAccessibleHyperText.h @@ -0,0 +1,57 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_a11y_xpcAccessibleHyperText_h_ +#define mozilla_a11y_xpcAccessibleHyperText_h_ + +#include "nsIAccessibleText.h" +#include "nsIAccessibleHyperText.h" +#include "nsIAccessibleEditableText.h" + +#include "HyperTextAccessible.h" +#include "xpcAccessibleGeneric.h" + +namespace mozilla { +namespace a11y { + +class xpcAccessibleHyperText : public xpcAccessibleGeneric, + public nsIAccessibleText, + public nsIAccessibleEditableText, + public nsIAccessibleHyperText { + public: + explicit xpcAccessibleHyperText(Accessible* aIntl) + : xpcAccessibleGeneric(aIntl) { + if (aIntl->IsHyperText() && aIntl->IsTextRole()) mSupportedIfaces |= eText; + } + + NS_DECL_ISUPPORTS_INHERITED + + NS_DECL_NSIACCESSIBLETEXT + NS_DECL_NSIACCESSIBLEHYPERTEXT + NS_DECL_NSIACCESSIBLEEDITABLETEXT + + protected: + virtual ~xpcAccessibleHyperText() {} + + private: + HyperTextAccessibleBase* Intl() { return mIntl->AsHyperTextBase(); } + + HyperTextAccessible* IntlLocal() { + if (LocalAccessible* acc = mIntl->AsLocal()) { + return acc->AsHyperText(); + } + + return nullptr; + } + + xpcAccessibleHyperText(const xpcAccessibleHyperText&) = delete; + xpcAccessibleHyperText& operator=(const xpcAccessibleHyperText&) = delete; +}; + +} // namespace a11y +} // namespace mozilla + +#endif // mozilla_a11y_xpcAccessibleHyperText_h_ diff --git a/accessible/xpcom/xpcAccessibleImage.cpp b/accessible/xpcom/xpcAccessibleImage.cpp new file mode 100644 index 0000000000..137ebe6791 --- /dev/null +++ b/accessible/xpcom/xpcAccessibleImage.cpp @@ -0,0 +1,49 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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/. */ + +#include "xpcAccessibleImage.h" + +using namespace mozilla::a11y; + +//////////////////////////////////////////////////////////////////////////////// +// nsISupports + +NS_IMPL_ISUPPORTS_INHERITED(xpcAccessibleImage, xpcAccessibleGeneric, + nsIAccessibleImage) + +//////////////////////////////////////////////////////////////////////////////// +// nsIAccessibleImage + +NS_IMETHODIMP +xpcAccessibleImage::GetImagePosition(uint32_t aCoordType, int32_t* aX, + int32_t* aY) { + NS_ENSURE_ARG_POINTER(aX); + *aX = 0; + NS_ENSURE_ARG_POINTER(aY); + *aY = 0; + + if (!mIntl) return NS_ERROR_FAILURE; + + LayoutDeviceIntPoint point = mIntl->Position(aCoordType); + *aX = point.x; + *aY = point.y; + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleImage::GetImageSize(int32_t* aWidth, int32_t* aHeight) { + NS_ENSURE_ARG_POINTER(aWidth); + *aWidth = 0; + NS_ENSURE_ARG_POINTER(aHeight); + *aHeight = 0; + + if (!mIntl) return NS_ERROR_FAILURE; + + LayoutDeviceIntSize size = mIntl->Size(); + *aWidth = size.width; + *aHeight = size.height; + return NS_OK; +} diff --git a/accessible/xpcom/xpcAccessibleImage.h b/accessible/xpcom/xpcAccessibleImage.h new file mode 100644 index 0000000000..ca70ab35f8 --- /dev/null +++ b/accessible/xpcom/xpcAccessibleImage.h @@ -0,0 +1,40 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_a11y_xpcAccessibleImage_h_ +#define mozilla_a11y_xpcAccessibleImage_h_ + +#include "nsIAccessibleImage.h" + +#include "xpcAccessibleGeneric.h" + +namespace mozilla { +namespace a11y { + +class xpcAccessibleImage : public xpcAccessibleGeneric, + public nsIAccessibleImage { + public: + explicit xpcAccessibleImage(Accessible* aIntl) + : xpcAccessibleGeneric(aIntl) {} + + NS_DECL_ISUPPORTS_INHERITED + + NS_IMETHOD GetImagePosition(uint32_t aCoordType, int32_t* aX, + int32_t* aY) final; + NS_IMETHOD GetImageSize(int32_t* aWidth, int32_t* aHeight) final; + + protected: + virtual ~xpcAccessibleImage() {} + + private: + xpcAccessibleImage(const xpcAccessibleImage&) = delete; + xpcAccessibleImage& operator=(const xpcAccessibleImage&) = delete; +}; + +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/xpcom/xpcAccessibleMacInterface.h b/accessible/xpcom/xpcAccessibleMacInterface.h new file mode 100644 index 0000000000..18b5955087 --- /dev/null +++ b/accessible/xpcom/xpcAccessibleMacInterface.h @@ -0,0 +1,104 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_a11y_xpcAccessibleMacInterface_h_ +#define mozilla_a11y_xpcAccessibleMacInterface_h_ + +#include "mozilla/a11y/Accessible.h" +#include "nsIAccessibleMacInterface.h" + +class nsIAccessibleMacInterface; + +namespace mozilla { +namespace a11y { + +class xpcAccessibleMacNSObjectWrapper : public nsIAccessibleMacNSObjectWrapper { + public: + explicit xpcAccessibleMacNSObjectWrapper(id aTextMarker); + + NS_DECL_ISUPPORTS + NS_DECL_NSIACCESSIBLEMACNSOBJECTWRAPPER + + protected: + virtual ~xpcAccessibleMacNSObjectWrapper(); + + id mNativeObject; +}; + +class xpcAccessibleMacInterface : public xpcAccessibleMacNSObjectWrapper, + public nsIAccessibleMacInterface { + public: + // Construct an xpcAccessibleMacInterface using this + // native object that conforms to the NSAccessibility protocol. + explicit xpcAccessibleMacInterface(id aNativeObj) + : xpcAccessibleMacNSObjectWrapper(aNativeObj) {} + + // Construct an xpcAccessibleMacInterface using the native object + // associated with this accessible. + explicit xpcAccessibleMacInterface(Accessible* aObj); + + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSIACCESSIBLEMACINTERFACE + + // Convert an NSObject (which can be anything, string, number, array, etc.) + // into a properly typed js value populated in the aResult handle. + static nsresult NSObjectToJsValue(id aObj, JSContext* aCx, + JS::MutableHandleValue aResult); + + protected: + virtual ~xpcAccessibleMacInterface() {} + + // Return true if our native object responds to this selector and + // if it implements isAccessibilitySelectorAllowed check that it returns true + // too. + bool SupportsSelector(SEL aSelector); + + // Convert a js value to an NSObject. This is called recursively for arrays. + // If the conversion fails, aResult is set to an error and nil is returned. + id JsValueToNSObject(JS::HandleValue aValue, JSContext* aCx, + nsresult* aResult); + + // Convert a js value to an NSValue NSObject. This is called + // by JsValueToNSObject when encountering a JS object with + // a "value" and "valueType" property. + id JsValueToNSValue(JS::HandleObject aObject, JSContext* aCx, + nsresult* aResult); + + // Convert a js value to a specified NSObject. This is called + // by JsValueToNSObject when encountering a JS object with + // a "object" and "objcetType" property. + id JsValueToSpecifiedNSObject(JS::HandleObject aObject, JSContext* aCx, + nsresult* aResult); + + private: + xpcAccessibleMacInterface(const xpcAccessibleMacInterface&) = delete; + xpcAccessibleMacInterface& operator=(const xpcAccessibleMacInterface&) = + delete; +}; + +class xpcAccessibleMacEvent : public nsIAccessibleMacEvent { + public: + explicit xpcAccessibleMacEvent(id aNativeObj, id aData); + + NS_DECL_ISUPPORTS + NS_DECL_NSIACCESSIBLEMACEVENT; + + // This sends notifications via nsIObserverService to be consumed by our + // mochitests. aNativeObj is a NSAccessibility protocol object, + // and aNotification is a NSString. + static void FireEvent(id aNativeObj, id aNotification, id aUserInfo); + + protected: + virtual ~xpcAccessibleMacEvent(); + + id mNativeObject; + id mData; +}; + +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/xpcom/xpcAccessibleMacInterface.mm b/accessible/xpcom/xpcAccessibleMacInterface.mm new file mode 100644 index 0000000000..e05937360b --- /dev/null +++ b/accessible/xpcom/xpcAccessibleMacInterface.mm @@ -0,0 +1,581 @@ +/* clang-format off */ +/* -*- Mode: Objective-C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* clang-format on */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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/. */ + +#include "xpcAccessibleMacInterface.h" + +#include "nsCocoaUtils.h" +#include "nsContentUtils.h" +#include "nsIObserverService.h" +#include "nsISimpleEnumerator.h" +#include "nsIXPConnect.h" +#include "mozilla/dom/ToJSValue.h" +#include "mozilla/Services.h" +#include "nsString.h" +#include "js/PropertyAndElement.h" // JS_Enumerate, JS_GetElement, JS_GetProperty, JS_GetPropertyById, JS_HasOwnProperty, JS_SetUCProperty + +#import "mozAccessible.h" + +using namespace mozilla::a11y; + +// xpcAccessibleMacNSObjectWrapper + +NS_IMPL_ISUPPORTS(xpcAccessibleMacNSObjectWrapper, + nsIAccessibleMacNSObjectWrapper) + +xpcAccessibleMacNSObjectWrapper::xpcAccessibleMacNSObjectWrapper(id aNativeObj) + : mNativeObject(aNativeObj) { + [mNativeObject retain]; +} + +xpcAccessibleMacNSObjectWrapper::~xpcAccessibleMacNSObjectWrapper() { + [mNativeObject release]; +} + +id xpcAccessibleMacNSObjectWrapper::GetNativeObject() { return mNativeObject; } + +// xpcAccessibleMacInterface + +NS_IMPL_ISUPPORTS_INHERITED(xpcAccessibleMacInterface, + xpcAccessibleMacNSObjectWrapper, + nsIAccessibleMacInterface) + +xpcAccessibleMacInterface::xpcAccessibleMacInterface(Accessible* aObj) + : xpcAccessibleMacNSObjectWrapper(GetNativeFromGeckoAccessible(aObj)) {} + +NS_IMETHODIMP +xpcAccessibleMacInterface::GetAttributeNames( + nsTArray<nsString>& aAttributeNames) { + NS_OBJC_BEGIN_TRY_BLOCK_RETURN + + if (!mNativeObject || [mNativeObject isExpired]) { + return NS_ERROR_NOT_AVAILABLE; + } + + for (NSString* name in [mNativeObject accessibilityAttributeNames]) { + nsAutoString attribName; + nsCocoaUtils::GetStringForNSString(name, attribName); + aAttributeNames.AppendElement(attribName); + } + + return NS_OK; + + NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE) +} + +NS_IMETHODIMP +xpcAccessibleMacInterface::GetParameterizedAttributeNames( + nsTArray<nsString>& aAttributeNames) { + NS_OBJC_BEGIN_TRY_BLOCK_RETURN + + if (!mNativeObject || [mNativeObject isExpired]) { + return NS_ERROR_NOT_AVAILABLE; + } + + for (NSString* name in + [mNativeObject accessibilityParameterizedAttributeNames]) { + nsAutoString attribName; + nsCocoaUtils::GetStringForNSString(name, attribName); + aAttributeNames.AppendElement(attribName); + } + + return NS_OK; + + NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE) +} + +NS_IMETHODIMP +xpcAccessibleMacInterface::GetActionNames(nsTArray<nsString>& aActionNames) { + NS_OBJC_BEGIN_TRY_BLOCK_RETURN + + if (!mNativeObject || [mNativeObject isExpired]) { + return NS_ERROR_NOT_AVAILABLE; + } + + for (NSString* name in [mNativeObject accessibilityActionNames]) { + nsAutoString actionName; + nsCocoaUtils::GetStringForNSString(name, actionName); + aActionNames.AppendElement(actionName); + } + + return NS_OK; + + NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE) +} + +NS_IMETHODIMP +xpcAccessibleMacInterface::PerformAction(const nsAString& aActionName) { + NS_OBJC_BEGIN_TRY_BLOCK_RETURN + + if (!mNativeObject || [mNativeObject isExpired]) { + return NS_ERROR_NOT_AVAILABLE; + } + + NSString* actionName = nsCocoaUtils::ToNSString(aActionName); + [mNativeObject accessibilityPerformAction:actionName]; + + return NS_OK; + + NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE) +} + +NS_IMETHODIMP +xpcAccessibleMacInterface::GetAttributeValue(const nsAString& aAttributeName, + JSContext* aCx, + JS::MutableHandleValue aResult) { + NS_OBJC_BEGIN_TRY_BLOCK_RETURN + + if (!mNativeObject || [mNativeObject isExpired]) { + return NS_ERROR_NOT_AVAILABLE; + } + + NSString* attribName = nsCocoaUtils::ToNSString(aAttributeName); + return NSObjectToJsValue( + [mNativeObject accessibilityAttributeValue:attribName], aCx, aResult); + + NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE) +} + +NS_IMETHODIMP +xpcAccessibleMacInterface::IsAttributeSettable(const nsAString& aAttributeName, + bool* aIsSettable) { + NS_ENSURE_ARG_POINTER(aIsSettable); + + NSString* attribName = nsCocoaUtils::ToNSString(aAttributeName); + if ([mNativeObject + respondsToSelector:@selector(accessibilityIsAttributeSettable:)]) { + *aIsSettable = [mNativeObject accessibilityIsAttributeSettable:attribName]; + return NS_OK; + } + + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +xpcAccessibleMacInterface::SetAttributeValue(const nsAString& aAttributeName, + JS::HandleValue aAttributeValue, + JSContext* aCx) { + nsresult rv = NS_OK; + id obj = JsValueToNSObject(aAttributeValue, aCx, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + NSString* attribName = nsCocoaUtils::ToNSString(aAttributeName); + if ([mNativeObject respondsToSelector:@selector(accessibilitySetValue: + forAttribute:)]) { + // The NSObject has an attribute setter, call that. + [mNativeObject accessibilitySetValue:obj forAttribute:attribName]; + return NS_OK; + } + + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +xpcAccessibleMacInterface::GetParameterizedAttributeValue( + const nsAString& aAttributeName, JS::HandleValue aParameter, JSContext* aCx, + JS::MutableHandleValue aResult) { + nsresult rv = NS_OK; + id paramObj = JsValueToNSObject(aParameter, aCx, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + NSString* attribName = nsCocoaUtils::ToNSString(aAttributeName); + return NSObjectToJsValue([mNativeObject accessibilityAttributeValue:attribName + forParameter:paramObj], + aCx, aResult); +} + +bool xpcAccessibleMacInterface::SupportsSelector(SEL aSelector) { + // return true if we have this selector, and if isAccessibilitySelectorAllowed + // is implemented too whether it is "allowed". + return [mNativeObject respondsToSelector:aSelector] && + (![mNativeObject respondsToSelector:@selector + (isAccessibilitySelectorAllowed:selector:)] || + [mNativeObject isAccessibilitySelectorAllowed:aSelector]); +} + +nsresult xpcAccessibleMacInterface::NSObjectToJsValue( + id aObj, JSContext* aCx, JS::MutableHandleValue aResult) { + if (!aObj) { + aResult.set(JS::NullValue()); + } else if ([aObj isKindOfClass:[NSString class]]) { + nsAutoString strVal; + nsCocoaUtils::GetStringForNSString((NSString*)aObj, strVal); + if (!mozilla::dom::ToJSValue(aCx, strVal, aResult)) { + return NS_ERROR_FAILURE; + } + } else if ([aObj isKindOfClass:[NSNumber class]]) { + // If the type being held by the NSNumber is a BOOL set js value + // to boolean. Otherwise use a double value. + if (strcmp([(NSNumber*)aObj objCType], @encode(BOOL)) == 0) { + if (!mozilla::dom::ToJSValue(aCx, [(NSNumber*)aObj boolValue], aResult)) { + return NS_ERROR_FAILURE; + } + } else { + if (!mozilla::dom::ToJSValue(aCx, [(NSNumber*)aObj doubleValue], + aResult)) { + return NS_ERROR_FAILURE; + } + } + } else if ([aObj isKindOfClass:[NSValue class]] && + strcmp([(NSValue*)aObj objCType], @encode(NSPoint)) == 0) { + NSPoint point = [(NSValue*)aObj pointValue]; + return NSObjectToJsValue( + @[ + [NSNumber numberWithDouble:point.x], + [NSNumber numberWithDouble:point.y] + ], + aCx, aResult); + } else if ([aObj isKindOfClass:[NSValue class]] && + strcmp([(NSValue*)aObj objCType], @encode(NSSize)) == 0) { + NSSize size = [(NSValue*)aObj sizeValue]; + return NSObjectToJsValue( + @[ + [NSNumber numberWithDouble:size.width], + [NSNumber numberWithDouble:size.height] + ], + aCx, aResult); + } else if ([aObj isKindOfClass:[NSValue class]] && + strcmp([(NSValue*)aObj objCType], @encode(NSRange)) == 0) { + NSRange range = [(NSValue*)aObj rangeValue]; + return NSObjectToJsValue(@[ @(range.location), @(range.length) ], aCx, + aResult); + } else if ([aObj isKindOfClass:[NSValue class]] && + strcmp([(NSValue*)aObj objCType], @encode(NSRect)) == 0) { + NSRect rect = [(NSValue*)aObj rectValue]; + return NSObjectToJsValue(@{ + @"origin" : [NSValue valueWithPoint:rect.origin], + @"size" : [NSValue valueWithSize:rect.size] + }, + aCx, aResult); + } else if ([aObj isKindOfClass:[NSArray class]]) { + NSArray* objArr = (NSArray*)aObj; + + JS::RootedVector<JS::Value> v(aCx); + if (!v.resize([objArr count])) { + return NS_ERROR_FAILURE; + } + for (size_t i = 0; i < [objArr count]; ++i) { + nsresult rv = NSObjectToJsValue(objArr[i], aCx, v[i]); + NS_ENSURE_SUCCESS(rv, rv); + } + + JSObject* arrayObj = JS::NewArrayObject(aCx, v); + if (!arrayObj) { + return NS_ERROR_FAILURE; + } + aResult.setObject(*arrayObj); + } else if ([aObj isKindOfClass:[NSDictionary class]]) { + JS::RootedObject obj(aCx, JS_NewPlainObject(aCx)); + for (NSString* key in aObj) { + nsAutoString strKey; + nsCocoaUtils::GetStringForNSString(key, strKey); + JS::RootedValue value(aCx); + nsresult rv = NSObjectToJsValue(aObj[key], aCx, &value); + NS_ENSURE_SUCCESS(rv, rv); + JS_SetUCProperty(aCx, obj, strKey.get(), strKey.Length(), value); + } + aResult.setObject(*obj); + } else if ([aObj isKindOfClass:[NSAttributedString class]]) { + NSAttributedString* attrStr = (NSAttributedString*)aObj; + __block NSMutableArray* attrRunArray = [[NSMutableArray alloc] init]; + + [attrStr + enumerateAttributesInRange:NSMakeRange(0, [attrStr length]) + options: + NSAttributedStringEnumerationLongestEffectiveRangeNotRequired + usingBlock:^(NSDictionary* attributes, NSRange range, + BOOL* stop) { + NSString* str = + [[attrStr string] substringWithRange:range]; + if (!str || !attributes) { + return; + } + + NSMutableDictionary* attrRun = + [attributes mutableCopy]; + attrRun[@"string"] = str; + + [attrRunArray addObject:attrRun]; + }]; + + // The attributed string is represented in js as an array of objects. + // Each object represents a run of text where the "string" property is the + // string value and all the AX* properties are the attributes. + return NSObjectToJsValue(attrRunArray, aCx, aResult); + } else if (CFGetTypeID(aObj) == CGColorGetTypeID()) { + const CGFloat* components = CGColorGetComponents((CGColorRef)aObj); + NSString* hexString = [NSString + stringWithFormat:@"#%02x%02x%02x", (int)(components[0] * 0xff), + (int)(components[1] * 0xff), + (int)(components[2] * 0xff)]; + return NSObjectToJsValue(hexString, aCx, aResult); + } else if ([aObj respondsToSelector:@selector(isAccessibilityElement)]) { + // We expect all of our accessibility objects to implement + // isAccessibilityElement at the very least. If it is implemented we will + // assume its an accessibility object. + nsCOMPtr<nsIAccessibleMacInterface> obj = + new xpcAccessibleMacInterface(aObj); + return nsContentUtils::WrapNative( + aCx, obj, &NS_GET_IID(nsIAccessibleMacInterface), aResult); + } else { + // If this is any other kind of NSObject, just wrap it and return it. + // It will be opaque and immutable on the JS side, but it can be + // brought back to us in an argument. + nsCOMPtr<nsIAccessibleMacNSObjectWrapper> obj = + new xpcAccessibleMacNSObjectWrapper(aObj); + return nsContentUtils::WrapNative( + aCx, obj, &NS_GET_IID(nsIAccessibleMacNSObjectWrapper), aResult); + } + + return NS_OK; +} + +id xpcAccessibleMacInterface::JsValueToNSObject(JS::HandleValue aValue, + JSContext* aCx, + nsresult* aResult) { + *aResult = NS_OK; + if (aValue.isInt32()) { + return [NSNumber numberWithInteger:aValue.toInt32()]; + } else if (aValue.isBoolean()) { + return [NSNumber numberWithBool:aValue.toBoolean()]; + } else if (aValue.isString()) { + nsAutoJSString temp; + if (!temp.init(aCx, aValue)) { + NS_WARNING("cannot init string with given value"); + *aResult = NS_ERROR_FAILURE; + return nil; + } + return nsCocoaUtils::ToNSString(temp); + } else if (aValue.isObject()) { + JS::Rooted<JSObject*> obj(aCx, aValue.toObjectOrNull()); + + bool isArray; + JS::IsArrayObject(aCx, obj, &isArray); + if (isArray) { + // If this is an array, we construct an NSArray and insert the js + // array's elements by recursively calling this function. + uint32_t len; + JS::GetArrayLength(aCx, obj, &len); + NSMutableArray* array = [NSMutableArray arrayWithCapacity:len]; + for (uint32_t i = 0; i < len; i++) { + JS::RootedValue v(aCx); + JS_GetElement(aCx, obj, i, &v); + [array addObject:JsValueToNSObject(v, aCx, aResult)]; + NS_ENSURE_SUCCESS(*aResult, nil); + } + return array; + } + + bool hasValueType; + bool hasValue; + JS_HasOwnProperty(aCx, obj, "valueType", &hasValueType); + JS_HasOwnProperty(aCx, obj, "value", &hasValue); + if (hasValueType && hasValue) { + // A js object representin an NSValue looks like this: + // { valueType: "NSRange", value: [1, 3] } + return JsValueToNSValue(obj, aCx, aResult); + } + + bool hasObjectType; + bool hasObject; + JS_HasOwnProperty(aCx, obj, "objectType", &hasObjectType); + JS_HasOwnProperty(aCx, obj, "object", &hasObject); + if (hasObjectType && hasObject) { + // A js object representing an NSDictionary looks like this: + // { objectType: "NSDictionary", value: {k: v, k: v, ...} } + return JsValueToSpecifiedNSObject(obj, aCx, aResult); + } + + // This may be another nsIAccessibleMacInterface instance. + // If so, return the wrapped NSObject. + nsCOMPtr<nsIXPConnect> xpc = nsIXPConnect::XPConnect(); + + nsCOMPtr<nsIXPConnectWrappedNative> wrappedObj; + nsresult rv = + xpc->GetWrappedNativeOfJSObject(aCx, obj, getter_AddRefs(wrappedObj)); + NS_ENSURE_SUCCESS(rv, nil); + nsCOMPtr<nsIAccessibleMacNSObjectWrapper> macObjIface = + do_QueryInterface(wrappedObj->Native()); + return macObjIface->GetNativeObject(); + } + + *aResult = NS_ERROR_FAILURE; + return nil; +} + +id xpcAccessibleMacInterface::JsValueToNSValue(JS::HandleObject aObject, + JSContext* aCx, + nsresult* aResult) { + *aResult = NS_ERROR_FAILURE; + JS::RootedValue valueTypeValue(aCx); + if (!JS_GetProperty(aCx, aObject, "valueType", &valueTypeValue)) { + NS_WARNING("Could not get valueType"); + return nil; + } + + JS::RootedValue valueValue(aCx); + if (!JS_GetProperty(aCx, aObject, "value", &valueValue)) { + NS_WARNING("Could not get value"); + return nil; + } + + nsAutoJSString valueType; + if (!valueTypeValue.isString() || !valueType.init(aCx, valueTypeValue)) { + NS_WARNING("valueType is not a string"); + return nil; + } + + bool isArray; + JS::IsArrayObject(aCx, valueValue, &isArray); + if (!isArray) { + NS_WARNING("value is not an array"); + return nil; + } + + JS::Rooted<JSObject*> value(aCx, valueValue.toObjectOrNull()); + + if (valueType.EqualsLiteral("NSRange")) { + uint32_t len; + JS::GetArrayLength(aCx, value, &len); + if (len != 2) { + NS_WARNING("Expected a 2 member array"); + return nil; + } + + JS::RootedValue locationValue(aCx); + JS_GetElement(aCx, value, 0, &locationValue); + JS::RootedValue lengthValue(aCx); + JS_GetElement(aCx, value, 1, &lengthValue); + if (!locationValue.isInt32() || !lengthValue.isInt32()) { + NS_WARNING("Expected an array of integers"); + return nil; + } + + *aResult = NS_OK; + return [NSValue valueWithRange:NSMakeRange(locationValue.toInt32(), + lengthValue.toInt32())]; + } + + return nil; +} + +id xpcAccessibleMacInterface::JsValueToSpecifiedNSObject( + JS::HandleObject aObject, JSContext* aCx, nsresult* aResult) { + *aResult = NS_ERROR_FAILURE; + JS::RootedValue objectTypeValue(aCx); + if (!JS_GetProperty(aCx, aObject, "objectType", &objectTypeValue)) { + NS_WARNING("Could not get objectType"); + return nil; + } + + JS::RootedValue objectValue(aCx); + if (!JS_GetProperty(aCx, aObject, "object", &objectValue)) { + NS_WARNING("Could not get object"); + return nil; + } + + nsAutoJSString objectType; + if (!objectTypeValue.isString()) { + NS_WARNING("objectType is not a string"); + return nil; + } + + if (!objectType.init(aCx, objectTypeValue)) { + NS_WARNING("cannot init string with object type"); + return nil; + } + + bool isObject = objectValue.isObjectOrNull(); + if (!isObject) { + NS_WARNING("object is not a JSON object"); + return nil; + } + + JS::Rooted<JSObject*> object(aCx, objectValue.toObjectOrNull()); + + if (objectType.EqualsLiteral("NSDictionary")) { + JS::Rooted<JS::IdVector> ids(aCx, JS::IdVector(aCx)); + if (!JS_Enumerate(aCx, object, &ids)) { + NS_WARNING("Unable to get keys from dictionary object"); + return nil; + } + + NSMutableDictionary* dict = [[NSMutableDictionary alloc] init]; + + for (size_t i = 0, n = ids.length(); i < n; i++) { + nsresult rv = NS_OK; + // get current key + JS::RootedValue currentKey(aCx); + JS_IdToValue(aCx, ids[i], ¤tKey); + id unwrappedKey = JsValueToNSObject(currentKey, aCx, &rv); + NS_ENSURE_SUCCESS(rv, nil); + MOZ_ASSERT([unwrappedKey isKindOfClass:[NSString class]]); + + // get associated value for current key + JS::RootedValue currentValue(aCx); + JS_GetPropertyById(aCx, object, ids[i], ¤tValue); + id unwrappedValue = JsValueToNSObject(currentValue, aCx, &rv); + NS_ENSURE_SUCCESS(rv, nil); + dict[unwrappedKey] = unwrappedValue; + } + + *aResult = NS_OK; + return dict; + } + + return nil; +} + +NS_IMPL_ISUPPORTS(xpcAccessibleMacEvent, nsIAccessibleMacEvent) + +xpcAccessibleMacEvent::xpcAccessibleMacEvent(id aNativeObj, id aData) + : mNativeObject(aNativeObj), mData(aData) { + [mNativeObject retain]; + [mData retain]; +} + +xpcAccessibleMacEvent::~xpcAccessibleMacEvent() { + [mNativeObject release]; + [mData release]; +} + +NS_IMETHODIMP +xpcAccessibleMacEvent::GetMacIface(nsIAccessibleMacInterface** aMacIface) { + RefPtr<xpcAccessibleMacInterface> macIface = + new xpcAccessibleMacInterface(mNativeObject); + macIface.forget(aMacIface); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleMacEvent::GetData(JSContext* aCx, JS::MutableHandleValue aData) { + return xpcAccessibleMacInterface::NSObjectToJsValue(mData, aCx, aData); +} + +void xpcAccessibleMacEvent::FireEvent(id aNativeObj, id aNotification, + id aUserInfo) { + if (nsCOMPtr<nsIObserverService> obsService = + services::GetObserverService()) { + nsCOMPtr<nsISimpleEnumerator> observers; + // Get all observers for the mac event topic. + obsService->EnumerateObservers(NS_ACCESSIBLE_MAC_EVENT_TOPIC, + getter_AddRefs(observers)); + if (observers) { + bool hasObservers = false; + observers->HasMoreElements(&hasObservers); + // If we have observers, notify them. + if (hasObservers) { + nsCOMPtr<nsIAccessibleMacEvent> xpcIface = + new xpcAccessibleMacEvent(aNativeObj, aUserInfo); + nsAutoString notificationStr; + nsCocoaUtils::GetStringForNSString(aNotification, notificationStr); + obsService->NotifyObservers(xpcIface, NS_ACCESSIBLE_MAC_EVENT_TOPIC, + notificationStr.get()); + } + } + } +} diff --git a/accessible/xpcom/xpcAccessiblePivot.cpp b/accessible/xpcom/xpcAccessiblePivot.cpp new file mode 100644 index 0000000000..862cb88bdd --- /dev/null +++ b/accessible/xpcom/xpcAccessiblePivot.cpp @@ -0,0 +1,155 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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/. */ + +#include "xpcAccessiblePivot.h" +#include "xpcAccessibleDocument.h" + +#include "Pivot.h" + +using namespace mozilla::a11y; + +using mozilla::DebugOnly; + +/** + * An object that stores a given traversal rule during the pivot movement. + */ +class xpcPivotRule : public PivotRule { + public: + explicit xpcPivotRule(nsIAccessibleTraversalRule* aRule) : mRule(aRule) {} + ~xpcPivotRule() {} + + virtual uint16_t Match(Accessible* aAcc) override; + + private: + nsCOMPtr<nsIAccessibleTraversalRule> mRule; +}; + +//////////////////////////////////////////////////////////////////////////////// +// xpcAccessiblePivot + +xpcAccessiblePivot::xpcAccessiblePivot(nsIAccessible* aRoot) : mRoot(aRoot) { + NS_ASSERTION(aRoot, "A root accessible is required"); +} + +xpcAccessiblePivot::~xpcAccessiblePivot() {} + +//////////////////////////////////////////////////////////////////////////////// +// nsISupports + +NS_IMPL_CYCLE_COLLECTION(xpcAccessiblePivot, mRoot) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(xpcAccessiblePivot) + NS_INTERFACE_MAP_ENTRY(nsIAccessiblePivot) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAccessiblePivot) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(xpcAccessiblePivot) +NS_IMPL_CYCLE_COLLECTING_RELEASE(xpcAccessiblePivot) + +//////////////////////////////////////////////////////////////////////////////// +// nsIAccessiblePivot + +NS_IMETHODIMP +xpcAccessiblePivot::Next(nsIAccessible* aAnchor, + nsIAccessibleTraversalRule* aRule, bool aIncludeStart, + uint8_t aArgc, nsIAccessible** aResult) { + NS_ENSURE_ARG(aResult); + NS_ENSURE_ARG(aRule); + + Accessible* root = Root(); + Accessible* anchor = aAnchor->ToInternalGeneric(); + NS_ENSURE_TRUE(root && anchor, NS_ERROR_NOT_IN_TREE); + + Pivot pivot(Root()); + xpcPivotRule rule(aRule); + Accessible* result = + pivot.Next(anchor, rule, (aArgc > 0) ? aIncludeStart : false); + NS_IF_ADDREF(*aResult = ToXPC(result)); + + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessiblePivot::Prev(nsIAccessible* aAnchor, + nsIAccessibleTraversalRule* aRule, bool aIncludeStart, + uint8_t aArgc, nsIAccessible** aResult) { + NS_ENSURE_ARG(aResult); + NS_ENSURE_ARG(aRule); + + Accessible* root = Root(); + Accessible* anchor = aAnchor->ToInternalGeneric(); + NS_ENSURE_TRUE(root && anchor, NS_ERROR_NOT_IN_TREE); + + Pivot pivot(Root()); + xpcPivotRule rule(aRule); + Accessible* result = + pivot.Prev(anchor, rule, (aArgc > 0) ? aIncludeStart : false); + NS_IF_ADDREF(*aResult = ToXPC(result)); + + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessiblePivot::First(nsIAccessibleTraversalRule* aRule, + nsIAccessible** aResult) { + NS_ENSURE_ARG(aResult); + NS_ENSURE_ARG(aRule); + + Accessible* root = Root(); + NS_ENSURE_TRUE(root, NS_ERROR_NOT_IN_TREE); + + Pivot pivot(root); + xpcPivotRule rule(aRule); + Accessible* result = pivot.First(rule); + NS_IF_ADDREF(*aResult = ToXPC(result)); + + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessiblePivot::Last(nsIAccessibleTraversalRule* aRule, + nsIAccessible** aResult) { + NS_ENSURE_ARG(aResult); + NS_ENSURE_ARG(aRule); + + Accessible* root = Root(); + NS_ENSURE_TRUE(root, NS_ERROR_NOT_IN_TREE); + + Pivot pivot(root); + xpcPivotRule rule(aRule); + Accessible* result = pivot.Last(rule); + NS_IF_ADDREF(*aResult = ToXPC(result)); + + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessiblePivot::AtPoint(int32_t aX, int32_t aY, + nsIAccessibleTraversalRule* aRule, + nsIAccessible** aResult) { + NS_ENSURE_ARG_POINTER(aResult); + NS_ENSURE_ARG_POINTER(aRule); + + Accessible* root = Root(); + NS_ENSURE_TRUE(root, NS_ERROR_NOT_IN_TREE); + + xpcPivotRule rule(aRule); + Pivot pivot(root); + + Accessible* result = pivot.AtPoint(aX, aY, rule); + NS_IF_ADDREF(*aResult = ToXPC(result)); + + return NS_OK; +} + +uint16_t xpcPivotRule::Match(Accessible* aAcc) { + uint16_t matchResult = nsIAccessibleTraversalRule::FILTER_IGNORE; + + DebugOnly<nsresult> rv = mRule->Match(ToXPC(aAcc), &matchResult); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + + return matchResult; +} diff --git a/accessible/xpcom/xpcAccessiblePivot.h b/accessible/xpcom/xpcAccessiblePivot.h new file mode 100644 index 0000000000..db5fdbdc3d --- /dev/null +++ b/accessible/xpcom/xpcAccessiblePivot.h @@ -0,0 +1,47 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef _xpcAccessiblePivot_H_ +#define _xpcAccessiblePivot_H_ + +#include "nsIAccessiblePivot.h" + +#include "Accessible.h" +#include "nsCycleCollectionParticipant.h" +#include "mozilla/Attributes.h" +#include "xpcAccessible.h" + +namespace mozilla::a11y { +/** + * Class represents an accessible pivot. + */ +class xpcAccessiblePivot final : public nsIAccessiblePivot { + public: + explicit xpcAccessiblePivot(nsIAccessible* aRoot); + + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(xpcAccessiblePivot, + nsIAccessiblePivot) + + NS_DECL_NSIACCESSIBLEPIVOT + + private: + ~xpcAccessiblePivot(); + xpcAccessiblePivot() = delete; + xpcAccessiblePivot(const xpcAccessiblePivot&) = delete; + void operator=(const xpcAccessiblePivot&) = delete; + + Accessible* Root() { return mRoot ? mRoot->ToInternalGeneric() : nullptr; } + + /* + * The root accessible. + */ + RefPtr<nsIAccessible> mRoot; +}; + +} // namespace mozilla::a11y + +#endif diff --git a/accessible/xpcom/xpcAccessibleSelectable.cpp b/accessible/xpcom/xpcAccessibleSelectable.cpp new file mode 100644 index 0000000000..b3c2b424f0 --- /dev/null +++ b/accessible/xpcom/xpcAccessibleSelectable.cpp @@ -0,0 +1,119 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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/. */ + +#include "nsComponentManagerUtils.h" +#include "nsIAccessible.h" +#include "nsIMutableArray.h" +#include "xpcAccessibleDocument.h" +#include "xpcAccessibleSelectable.h" + +using namespace mozilla::a11y; + +NS_IMETHODIMP +xpcAccessibleSelectable::GetSelectedItems(nsIArray** aSelectedItems) { + NS_ENSURE_ARG_POINTER(aSelectedItems); + *aSelectedItems = nullptr; + + if (!Intl()) return NS_ERROR_FAILURE; + MOZ_ASSERT(Intl()->IsSelect(), "Called on non selectable widget!"); + + AutoTArray<Accessible*, 10> items; + Intl()->SelectedItems(&items); + + uint32_t itemCount = items.Length(); + if (itemCount == 0) return NS_OK; + + nsresult rv = NS_OK; + nsCOMPtr<nsIMutableArray> xpcItems = + do_CreateInstance(NS_ARRAY_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + for (uint32_t idx = 0; idx < itemCount; idx++) { + xpcItems->AppendElement(static_cast<nsIAccessible*>(ToXPC(items[idx]))); + } + + NS_ADDREF(*aSelectedItems = xpcItems); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleSelectable::GetSelectedItemCount(uint32_t* aSelectionCount) { + NS_ENSURE_ARG_POINTER(aSelectionCount); + *aSelectionCount = 0; + + if (!Intl()) return NS_ERROR_FAILURE; + MOZ_ASSERT(Intl()->IsSelect(), "Called on non selectable widget!"); + + *aSelectionCount = Intl()->SelectedItemCount(); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleSelectable::GetSelectedItemAt(uint32_t aIndex, + nsIAccessible** aSelected) { + NS_ENSURE_ARG_POINTER(aSelected); + *aSelected = nullptr; + + if (!Intl()) return NS_ERROR_FAILURE; + MOZ_ASSERT(Intl()->IsSelect(), "Called on non selectable widget!"); + + *aSelected = ToXPC(Intl()->GetSelectedItem(aIndex)); + if (*aSelected) { + NS_ADDREF(*aSelected); + return NS_OK; + } + + return NS_ERROR_INVALID_ARG; +} + +NS_IMETHODIMP +xpcAccessibleSelectable::IsItemSelected(uint32_t aIndex, bool* aIsSelected) { + NS_ENSURE_ARG_POINTER(aIsSelected); + *aIsSelected = false; + + if (!Intl()) return NS_ERROR_FAILURE; + MOZ_ASSERT(Intl()->IsSelect(), "Called on non selectable widget!"); + + *aIsSelected = Intl()->IsItemSelected(aIndex); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleSelectable::AddItemToSelection(uint32_t aIndex) { + if (!Intl()) return NS_ERROR_FAILURE; + MOZ_ASSERT(Intl()->IsSelect(), "Called on non selectable widget!"); + + return Intl()->AddItemToSelection(aIndex) ? NS_OK : NS_ERROR_INVALID_ARG; +} + +NS_IMETHODIMP +xpcAccessibleSelectable::RemoveItemFromSelection(uint32_t aIndex) { + if (!Intl()) return NS_ERROR_FAILURE; + MOZ_ASSERT(Intl()->IsSelect(), "Called on non selectable widget!"); + + return Intl()->RemoveItemFromSelection(aIndex) ? NS_OK : NS_ERROR_INVALID_ARG; +} + +NS_IMETHODIMP +xpcAccessibleSelectable::SelectAll(bool* aIsMultiSelect) { + NS_ENSURE_ARG_POINTER(aIsMultiSelect); + *aIsMultiSelect = false; + + if (!Intl()) return NS_ERROR_FAILURE; + MOZ_ASSERT(Intl()->IsSelect(), "Called on non selectable widget!"); + + *aIsMultiSelect = Intl()->SelectAll(); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleSelectable::UnselectAll() { + if (!Intl()) return NS_ERROR_FAILURE; + MOZ_ASSERT(Intl()->IsSelect(), "Called on non selectable widget!"); + + Intl()->UnselectAll(); + return NS_OK; +} diff --git a/accessible/xpcom/xpcAccessibleSelectable.h b/accessible/xpcom/xpcAccessibleSelectable.h new file mode 100644 index 0000000000..706690df2e --- /dev/null +++ b/accessible/xpcom/xpcAccessibleSelectable.h @@ -0,0 +1,50 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_a11y_xpcAccessibleSelectable_h_ +#define mozilla_a11y_xpcAccessibleSelectable_h_ + +#include "nsIAccessibleSelectable.h" + +class nsIAccessible; +class nsIArray; + +namespace mozilla { +namespace a11y { + +class Accessible; + +/** + * XPCOM nsIAccessibleSelectable inteface implementation, used by + * xpcAccessibleGeneric class. + */ +class xpcAccessibleSelectable : public nsIAccessibleSelectable { + public: + // nsIAccessibleSelectable + NS_IMETHOD GetSelectedItems(nsIArray** aSelectedItems) final; + NS_IMETHOD GetSelectedItemCount(uint32_t* aSelectedItemCount) final; + NS_IMETHOD GetSelectedItemAt(uint32_t aIndex, nsIAccessible** aItem) final; + NS_IMETHOD IsItemSelected(uint32_t aIndex, bool* aIsSelected) final; + NS_IMETHOD AddItemToSelection(uint32_t aIndex) final; + NS_IMETHOD RemoveItemFromSelection(uint32_t aIndex) final; + NS_IMETHOD SelectAll(bool* aIsMultiSelect) final; + NS_IMETHOD UnselectAll() final; + + protected: + xpcAccessibleSelectable() {} + virtual ~xpcAccessibleSelectable() {} + + private: + xpcAccessibleSelectable(const xpcAccessibleSelectable&) = delete; + xpcAccessibleSelectable& operator=(const xpcAccessibleSelectable&) = delete; + + Accessible* Intl(); +}; + +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/xpcom/xpcAccessibleTable.cpp b/accessible/xpcom/xpcAccessibleTable.cpp new file mode 100644 index 0000000000..b7de1cd85c --- /dev/null +++ b/accessible/xpcom/xpcAccessibleTable.cpp @@ -0,0 +1,362 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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/. */ + +#include "xpcAccessibleTable.h" + +#include "mozilla/a11y/TableAccessible.h" + +#include "nsIMutableArray.h" +#include "nsComponentManagerUtils.h" +#include "xpcAccessibleDocument.h" + +using namespace mozilla::a11y; + +static const uint32_t XPC_TABLE_DEFAULT_SIZE = 40; + +//////////////////////////////////////////////////////////////////////////////// +// nsISupports + +NS_IMPL_ISUPPORTS_INHERITED(xpcAccessibleTable, xpcAccessibleHyperText, + nsIAccessibleTable) + +//////////////////////////////////////////////////////////////////////////////// +// nsIAccessibleTable + +NS_IMETHODIMP +xpcAccessibleTable::GetCaption(nsIAccessible** aCaption) { + NS_ENSURE_ARG_POINTER(aCaption); + *aCaption = nullptr; + if (!Intl()) return NS_ERROR_FAILURE; + + NS_IF_ADDREF(*aCaption = ToXPC(Intl()->Caption())); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleTable::GetColumnCount(int32_t* aColumnCount) { + NS_ENSURE_ARG_POINTER(aColumnCount); + *aColumnCount = 0; + + if (!Intl()) return NS_ERROR_FAILURE; + + *aColumnCount = Intl()->ColCount(); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleTable::GetRowCount(int32_t* aRowCount) { + NS_ENSURE_ARG_POINTER(aRowCount); + *aRowCount = 0; + + if (!Intl()) return NS_ERROR_FAILURE; + + *aRowCount = Intl()->RowCount(); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleTable::GetCellAt(int32_t aRowIdx, int32_t aColIdx, + nsIAccessible** aCell) { + NS_ENSURE_ARG_POINTER(aCell); + *aCell = nullptr; + + if (!Intl()) return NS_ERROR_FAILURE; + + if (aRowIdx < 0 || static_cast<uint32_t>(aRowIdx) >= Intl()->RowCount() || + aColIdx < 0 || static_cast<uint32_t>(aColIdx) >= Intl()->ColCount()) { + return NS_ERROR_INVALID_ARG; + } + + NS_IF_ADDREF(*aCell = ToXPC(Intl()->CellAt(aRowIdx, aColIdx))); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleTable::GetCellIndexAt(int32_t aRowIdx, int32_t aColIdx, + int32_t* aCellIdx) { + NS_ENSURE_ARG_POINTER(aCellIdx); + *aCellIdx = -1; + + if (!Intl()) return NS_ERROR_FAILURE; + + if (aRowIdx < 0 || static_cast<uint32_t>(aRowIdx) >= Intl()->RowCount() || + aColIdx < 0 || static_cast<uint32_t>(aColIdx) >= Intl()->ColCount()) { + return NS_ERROR_INVALID_ARG; + } + + *aCellIdx = Intl()->CellIndexAt(aRowIdx, aColIdx); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleTable::GetColumnExtentAt(int32_t aRowIdx, int32_t aColIdx, + int32_t* aColumnExtent) { + NS_ENSURE_ARG_POINTER(aColumnExtent); + *aColumnExtent = -1; + + if (!Intl()) return NS_ERROR_FAILURE; + + if (aRowIdx < 0 || static_cast<uint32_t>(aRowIdx) >= Intl()->RowCount() || + aColIdx < 0 || static_cast<uint32_t>(aColIdx) >= Intl()->ColCount()) { + return NS_ERROR_INVALID_ARG; + } + + *aColumnExtent = Intl()->ColExtentAt(aRowIdx, aColIdx); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleTable::GetRowExtentAt(int32_t aRowIdx, int32_t aColIdx, + int32_t* aRowExtent) { + NS_ENSURE_ARG_POINTER(aRowExtent); + *aRowExtent = -1; + + if (!Intl()) return NS_ERROR_FAILURE; + + if (aRowIdx < 0 || static_cast<uint32_t>(aRowIdx) >= Intl()->RowCount() || + aColIdx < 0 || static_cast<uint32_t>(aColIdx) >= Intl()->ColCount()) { + return NS_ERROR_INVALID_ARG; + } + + *aRowExtent = Intl()->RowExtentAt(aRowIdx, aColIdx); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleTable::GetColumnDescription(int32_t aColIdx, + nsAString& aDescription) { + if (!Intl()) return NS_ERROR_FAILURE; + + if (aColIdx < 0 || static_cast<uint32_t>(aColIdx) >= Intl()->ColCount()) { + return NS_ERROR_INVALID_ARG; + } + + nsAutoString description; + Intl()->ColDescription(aColIdx, description); + aDescription.Assign(description); + + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleTable::GetRowDescription(int32_t aRowIdx, + nsAString& aDescription) { + if (!Intl()) return NS_ERROR_FAILURE; + + if (aRowIdx < 0 || static_cast<uint32_t>(aRowIdx) >= Intl()->ColCount()) { + return NS_ERROR_INVALID_ARG; + } + + nsAutoString description; + Intl()->RowDescription(aRowIdx, description); + aDescription.Assign(description); + + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleTable::IsColumnSelected(int32_t aColIdx, bool* aIsSelected) { + NS_ENSURE_ARG_POINTER(aIsSelected); + *aIsSelected = false; + + if (!Intl()) return NS_ERROR_FAILURE; + + if (aColIdx < 0 || static_cast<uint32_t>(aColIdx) >= Intl()->ColCount()) { + return NS_ERROR_INVALID_ARG; + } + + *aIsSelected = Intl()->IsColSelected(aColIdx); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleTable::IsRowSelected(int32_t aRowIdx, bool* aIsSelected) { + NS_ENSURE_ARG_POINTER(aIsSelected); + *aIsSelected = false; + + if (!Intl()) return NS_ERROR_FAILURE; + + if (aRowIdx < 0 || static_cast<uint32_t>(aRowIdx) >= Intl()->RowCount()) { + return NS_ERROR_INVALID_ARG; + } + + *aIsSelected = Intl()->IsRowSelected(aRowIdx); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleTable::IsCellSelected(int32_t aRowIdx, int32_t aColIdx, + bool* aIsSelected) { + NS_ENSURE_ARG_POINTER(aIsSelected); + *aIsSelected = false; + + if (!Intl()) return NS_ERROR_FAILURE; + + if (aRowIdx < 0 || static_cast<uint32_t>(aRowIdx) >= Intl()->RowCount() || + aColIdx < 0 || static_cast<uint32_t>(aColIdx) >= Intl()->ColCount()) { + return NS_ERROR_INVALID_ARG; + } + + *aIsSelected = Intl()->IsCellSelected(aRowIdx, aColIdx); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleTable::GetSelectedCellCount(uint32_t* aSelectedCellCount) { + NS_ENSURE_ARG_POINTER(aSelectedCellCount); + *aSelectedCellCount = 0; + + if (!Intl()) return NS_ERROR_FAILURE; + + *aSelectedCellCount = Intl()->SelectedCellCount(); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleTable::GetSelectedColumnCount(uint32_t* aSelectedColumnCount) { + NS_ENSURE_ARG_POINTER(aSelectedColumnCount); + *aSelectedColumnCount = 0; + + if (!Intl()) return NS_ERROR_FAILURE; + + *aSelectedColumnCount = Intl()->SelectedColCount(); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleTable::GetSelectedRowCount(uint32_t* aSelectedRowCount) { + NS_ENSURE_ARG_POINTER(aSelectedRowCount); + *aSelectedRowCount = 0; + + if (!Intl()) return NS_ERROR_FAILURE; + + *aSelectedRowCount = Intl()->SelectedRowCount(); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleTable::GetSelectedCells(nsIArray** aSelectedCells) { + NS_ENSURE_ARG_POINTER(aSelectedCells); + *aSelectedCells = nullptr; + + if (!Intl()) return NS_ERROR_FAILURE; + + nsresult rv = NS_OK; + nsCOMPtr<nsIMutableArray> selCells = + do_CreateInstance(NS_ARRAY_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + AutoTArray<Accessible*, XPC_TABLE_DEFAULT_SIZE> cellsArray; + Intl()->SelectedCells(&cellsArray); + + uint32_t totalCount = cellsArray.Length(); + for (uint32_t idx = 0; idx < totalCount; idx++) { + Accessible* cell = cellsArray.ElementAt(idx); + selCells->AppendElement(static_cast<nsIAccessible*>(ToXPC(cell))); + } + + NS_ADDREF(*aSelectedCells = selCells); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleTable::GetSelectedCellIndices(nsTArray<uint32_t>& aCellsArray) { + if (!Intl()) return NS_ERROR_FAILURE; + + Intl()->SelectedCellIndices(&aCellsArray); + + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleTable::GetSelectedColumnIndices(nsTArray<uint32_t>& aColsArray) { + if (!Intl()) return NS_ERROR_FAILURE; + + Intl()->SelectedColIndices(&aColsArray); + + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleTable::GetSelectedRowIndices(nsTArray<uint32_t>& aRowsArray) { + if (!Intl()) return NS_ERROR_FAILURE; + + Intl()->SelectedRowIndices(&aRowsArray); + + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleTable::GetColumnIndexAt(int32_t aCellIdx, int32_t* aColIdx) { + NS_ENSURE_ARG_POINTER(aColIdx); + *aColIdx = -1; + + if (!Intl()) return NS_ERROR_FAILURE; + + if (aCellIdx < 0 || static_cast<uint32_t>(aCellIdx) >= + Intl()->RowCount() * Intl()->ColCount()) { + return NS_ERROR_INVALID_ARG; + } + + *aColIdx = Intl()->ColIndexAt(aCellIdx); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleTable::GetRowIndexAt(int32_t aCellIdx, int32_t* aRowIdx) { + NS_ENSURE_ARG_POINTER(aRowIdx); + *aRowIdx = -1; + + if (!Intl()) return NS_ERROR_FAILURE; + + if (aCellIdx < 0 || static_cast<uint32_t>(aCellIdx) >= + Intl()->RowCount() * Intl()->ColCount()) { + return NS_ERROR_INVALID_ARG; + } + + *aRowIdx = Intl()->RowIndexAt(aCellIdx); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleTable::GetRowAndColumnIndicesAt(int32_t aCellIdx, int32_t* aRowIdx, + int32_t* aColIdx) { + NS_ENSURE_ARG_POINTER(aRowIdx); + *aRowIdx = -1; + NS_ENSURE_ARG_POINTER(aColIdx); + *aColIdx = -1; + + if (!Intl()) return NS_ERROR_FAILURE; + + if (aCellIdx < 0 || static_cast<uint32_t>(aCellIdx) >= + Intl()->RowCount() * Intl()->ColCount()) { + return NS_ERROR_INVALID_ARG; + } + + Intl()->RowAndColIndicesAt(aCellIdx, aRowIdx, aColIdx); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleTable::GetSummary(nsAString& aSummary) { + if (!Intl()) return NS_ERROR_FAILURE; + + nsAutoString summary; + Intl()->Summary(summary); + aSummary.Assign(summary); + + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleTable::IsProbablyForLayout(bool* aResult) { + NS_ENSURE_ARG_POINTER(aResult); + *aResult = false; + if (!Intl()) return NS_ERROR_FAILURE; + + *aResult = Intl()->IsProbablyLayoutTable(); + return NS_OK; +} diff --git a/accessible/xpcom/xpcAccessibleTable.h b/accessible/xpcom/xpcAccessibleTable.h new file mode 100644 index 0000000000..1691ce9e7d --- /dev/null +++ b/accessible/xpcom/xpcAccessibleTable.h @@ -0,0 +1,74 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_a11y_xpcAccessibleTable_h_ +#define mozilla_a11y_xpcAccessibleTable_h_ + +#include "nsIAccessibleTable.h" +#include "xpcAccessibleHyperText.h" + +namespace mozilla { +namespace a11y { +class TableAccessible; + +/** + * XPCOM wrapper around TableAccessible class. + */ +class xpcAccessibleTable : public xpcAccessibleHyperText, + public nsIAccessibleTable { + public: + explicit xpcAccessibleTable(Accessible* aIntl) + : xpcAccessibleHyperText(aIntl) {} + + NS_DECL_ISUPPORTS_INHERITED + + // nsIAccessibleTable + NS_IMETHOD GetCaption(nsIAccessible** aCaption) final; + NS_IMETHOD GetSummary(nsAString& aSummary) final; + NS_IMETHOD GetColumnCount(int32_t* aColumnCount) final; + NS_IMETHOD GetRowCount(int32_t* aRowCount) final; + NS_IMETHOD GetCellAt(int32_t aRowIndex, int32_t aColumnIndex, + nsIAccessible** aCell) final; + NS_IMETHOD GetCellIndexAt(int32_t aRowIndex, int32_t aColumnIndex, + int32_t* aCellIndex) final; + NS_IMETHOD GetColumnIndexAt(int32_t aCellIndex, int32_t* aColumnIndex) final; + NS_IMETHOD GetRowIndexAt(int32_t aCellIndex, int32_t* aRowIndex) final; + NS_IMETHOD GetRowAndColumnIndicesAt(int32_t aCellIndex, int32_t* aRowIndex, + int32_t* aColumnIndex) final; + NS_IMETHOD GetColumnExtentAt(int32_t row, int32_t column, + int32_t* aColumnExtent) final; + NS_IMETHOD GetRowExtentAt(int32_t row, int32_t column, + int32_t* aRowExtent) final; + NS_IMETHOD GetColumnDescription(int32_t aColIdx, + nsAString& aDescription) final; + NS_IMETHOD GetRowDescription(int32_t aRowIdx, nsAString& aDescription) final; + NS_IMETHOD IsColumnSelected(int32_t aColIdx, bool* _retval) final; + NS_IMETHOD IsRowSelected(int32_t aRowIdx, bool* _retval) final; + NS_IMETHOD IsCellSelected(int32_t aRowIdx, int32_t aColIdx, + bool* _retval) final; + NS_IMETHOD GetSelectedCellCount(uint32_t* aSelectedCellCount) final; + NS_IMETHOD GetSelectedColumnCount(uint32_t* aSelectedColumnCount) final; + NS_IMETHOD GetSelectedRowCount(uint32_t* aSelectedRowCount) final; + NS_IMETHOD GetSelectedCells(nsIArray** aSelectedCell) final; + NS_IMETHOD GetSelectedCellIndices(nsTArray<uint32_t>& aCellsArray) final; + NS_IMETHOD GetSelectedColumnIndices(nsTArray<uint32_t>& aColsArray) final; + NS_IMETHOD GetSelectedRowIndices(nsTArray<uint32_t>& aRowsArray) final; + NS_IMETHOD IsProbablyForLayout(bool* aIsForLayout) final; + + protected: + virtual ~xpcAccessibleTable() {} + + private: + TableAccessible* Intl() { return mIntl->AsTable(); } + + xpcAccessibleTable(const xpcAccessibleTable&) = delete; + xpcAccessibleTable& operator=(const xpcAccessibleTable&) = delete; +}; + +} // namespace a11y +} // namespace mozilla + +#endif // mozilla_a11y_xpcAccessibleTable_h_ diff --git a/accessible/xpcom/xpcAccessibleTableCell.cpp b/accessible/xpcom/xpcAccessibleTableCell.cpp new file mode 100644 index 0000000000..37da0d4852 --- /dev/null +++ b/accessible/xpcom/xpcAccessibleTableCell.cpp @@ -0,0 +1,140 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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/. */ + +#include "xpcAccessibleTableCell.h" + +#include "mozilla/a11y/TableAccessible.h" +#include "mozilla/a11y/TableCellAccessible.h" +#include "nsIAccessibleTable.h" + +#include "nsComponentManagerUtils.h" +#include "nsIMutableArray.h" +#include "xpcAccessibleDocument.h" + +using namespace mozilla; +using namespace mozilla::a11y; + +//////////////////////////////////////////////////////////////////////////////// +// nsISupports + +NS_IMPL_ISUPPORTS_INHERITED(xpcAccessibleTableCell, xpcAccessibleHyperText, + nsIAccessibleTableCell) + +//////////////////////////////////////////////////////////////////////////////// +// nsIAccessibleTableCell + +NS_IMETHODIMP +xpcAccessibleTableCell::GetTable(nsIAccessibleTable** aTable) { + NS_ENSURE_ARG_POINTER(aTable); + *aTable = nullptr; + + if (!Intl()) return NS_ERROR_FAILURE; + + TableAccessible* table = Intl()->Table(); + if (!table) return NS_ERROR_FAILURE; + + nsCOMPtr<nsIAccessibleTable> xpcTable = do_QueryInterface( + static_cast<nsIAccessible*>(ToXPC(table->AsAccessible()))); + xpcTable.forget(aTable); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleTableCell::GetColumnIndex(int32_t* aColIdx) { + NS_ENSURE_ARG_POINTER(aColIdx); + *aColIdx = -1; + + if (!Intl()) return NS_ERROR_FAILURE; + + *aColIdx = Intl()->ColIdx(); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleTableCell::GetRowIndex(int32_t* aRowIdx) { + NS_ENSURE_ARG_POINTER(aRowIdx); + *aRowIdx = -1; + + if (!Intl()) return NS_ERROR_FAILURE; + + *aRowIdx = Intl()->RowIdx(); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleTableCell::GetColumnExtent(int32_t* aExtent) { + NS_ENSURE_ARG_POINTER(aExtent); + *aExtent = -1; + + if (!Intl()) return NS_ERROR_FAILURE; + + *aExtent = Intl()->ColExtent(); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleTableCell::GetRowExtent(int32_t* aExtent) { + NS_ENSURE_ARG_POINTER(aExtent); + *aExtent = -1; + + if (!Intl()) return NS_ERROR_FAILURE; + + *aExtent = Intl()->RowExtent(); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleTableCell::GetColumnHeaderCells(nsIArray** aHeaderCells) { + NS_ENSURE_ARG_POINTER(aHeaderCells); + *aHeaderCells = nullptr; + + if (!Intl()) return NS_ERROR_FAILURE; + + AutoTArray<Accessible*, 10> headerCells; + Intl()->ColHeaderCells(&headerCells); + + nsCOMPtr<nsIMutableArray> cells = do_CreateInstance(NS_ARRAY_CONTRACTID); + NS_ENSURE_TRUE(cells, NS_ERROR_FAILURE); + + for (uint32_t idx = 0; idx < headerCells.Length(); idx++) { + cells->AppendElement(static_cast<nsIAccessible*>(ToXPC(headerCells[idx]))); + } + + NS_ADDREF(*aHeaderCells = cells); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleTableCell::GetRowHeaderCells(nsIArray** aHeaderCells) { + NS_ENSURE_ARG_POINTER(aHeaderCells); + *aHeaderCells = nullptr; + + if (!Intl()) return NS_ERROR_FAILURE; + + AutoTArray<Accessible*, 10> headerCells; + Intl()->RowHeaderCells(&headerCells); + + nsCOMPtr<nsIMutableArray> cells = do_CreateInstance(NS_ARRAY_CONTRACTID); + NS_ENSURE_TRUE(cells, NS_ERROR_FAILURE); + + for (uint32_t idx = 0; idx < headerCells.Length(); idx++) { + cells->AppendElement(static_cast<nsIAccessible*>(ToXPC(headerCells[idx]))); + } + + NS_ADDREF(*aHeaderCells = cells); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleTableCell::IsSelected(bool* aSelected) { + NS_ENSURE_ARG_POINTER(aSelected); + *aSelected = false; + + if (!Intl()) return NS_ERROR_FAILURE; + + *aSelected = Intl()->Selected(); + return NS_OK; +} diff --git a/accessible/xpcom/xpcAccessibleTableCell.h b/accessible/xpcom/xpcAccessibleTableCell.h new file mode 100644 index 0000000000..8a8ea2bb53 --- /dev/null +++ b/accessible/xpcom/xpcAccessibleTableCell.h @@ -0,0 +1,52 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_a11y_xpcom_xpcAccessibletableCell_h_ +#define mozilla_a11y_xpcom_xpcAccessibletableCell_h_ + +#include "nsIAccessibleTable.h" + +#include "xpcAccessibleHyperText.h" + +namespace mozilla { +namespace a11y { +class TableCellAccessible; + +/** + * XPCOM wrapper around TableAccessibleCell class. + */ +class xpcAccessibleTableCell : public xpcAccessibleHyperText, + public nsIAccessibleTableCell { + public: + explicit xpcAccessibleTableCell(Accessible* aIntl) + : xpcAccessibleHyperText(aIntl) {} + + NS_DECL_ISUPPORTS_INHERITED + + // nsIAccessibleTableCell + NS_IMETHOD GetTable(nsIAccessibleTable** aTable) final; + NS_IMETHOD GetColumnIndex(int32_t* aColIdx) final; + NS_IMETHOD GetRowIndex(int32_t* aRowIdx) final; + NS_IMETHOD GetColumnExtent(int32_t* aExtent) final; + NS_IMETHOD GetRowExtent(int32_t* aExtent) final; + NS_IMETHOD GetColumnHeaderCells(nsIArray** aHeaderCells) final; + NS_IMETHOD GetRowHeaderCells(nsIArray** aHeaderCells) final; + NS_IMETHOD IsSelected(bool* aSelected) final; + + protected: + virtual ~xpcAccessibleTableCell() {} + + private: + TableCellAccessible* Intl() { return mIntl->AsTableCell(); } + + xpcAccessibleTableCell(const xpcAccessibleTableCell&) = delete; + xpcAccessibleTableCell& operator=(const xpcAccessibleTableCell&) = delete; +}; + +} // namespace a11y +} // namespace mozilla + +#endif // mozilla_a11y_xpcom_xpcAccessibletableCell_h_ diff --git a/accessible/xpcom/xpcAccessibleTextLeafRange.cpp b/accessible/xpcom/xpcAccessibleTextLeafRange.cpp new file mode 100644 index 0000000000..8f493deea5 --- /dev/null +++ b/accessible/xpcom/xpcAccessibleTextLeafRange.cpp @@ -0,0 +1,83 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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/. */ + +#include "xpcAccessibleTextLeafRange.h" + +#include "nsIAccessible.h" +#include "TextLeafRange.h" +#include "xpcAccessibleDocument.h" + +using namespace mozilla; +using namespace mozilla::a11y; + +// xpcAccessibleTextLeafPoint + +NS_IMPL_ADDREF(xpcAccessibleTextLeafPoint) +NS_IMPL_RELEASE(xpcAccessibleTextLeafPoint) + +NS_IMPL_QUERY_INTERFACE(xpcAccessibleTextLeafPoint, nsIAccessibleTextLeafPoint) + +xpcAccessibleTextLeafPoint::xpcAccessibleTextLeafPoint( + nsIAccessible* aAccessible, int32_t aOffset) + : mAccessible(nullptr), mOffset(0) { + // When constructing a text point it will actualize the offset + // and adjust the accessible to the appropriate leaf. These + // might differ from the given constructor arguments. + if (aAccessible) { + TextLeafPoint point(aAccessible->ToInternalGeneric(), aOffset); + mAccessible = ToXPC(point.mAcc); + mOffset = point.mOffset; + } +} + +NS_IMETHODIMP xpcAccessibleTextLeafPoint::GetAccessible( + nsIAccessible** aAccessible) { + NS_ENSURE_ARG_POINTER(aAccessible); + RefPtr<nsIAccessible> acc = mAccessible; + acc.forget(aAccessible); + return NS_OK; +} + +NS_IMETHODIMP xpcAccessibleTextLeafPoint::SetAccessible( + nsIAccessible* aAccessible) { + mAccessible = aAccessible; + return NS_OK; +} + +NS_IMETHODIMP xpcAccessibleTextLeafPoint::GetOffset(int32_t* aOffset) { + NS_ENSURE_ARG_POINTER(aOffset); + *aOffset = mOffset; + return NS_OK; +} +NS_IMETHODIMP xpcAccessibleTextLeafPoint::SetOffset(int32_t aOffset) { + mOffset = aOffset; + return NS_OK; +} + +NS_IMETHODIMP xpcAccessibleTextLeafPoint::FindBoundary( + AccessibleTextBoundary aBoundaryType, uint32_t aDirection, uint32_t aFlags, + nsIAccessibleTextLeafPoint** aPoint) { + TextLeafPoint thisPoint = ToPoint(); + if (!thisPoint) { + return NS_ERROR_FAILURE; + } + + TextLeafPoint result = thisPoint.FindBoundary( + aBoundaryType, static_cast<nsDirection>(aDirection), + static_cast<TextLeafPoint::BoundaryFlags>(aFlags)); + RefPtr<xpcAccessibleTextLeafPoint> point = new xpcAccessibleTextLeafPoint( + result ? ToXPC(result.mAcc) : nullptr, result ? result.mOffset : 0); + point.forget(aPoint); + return NS_OK; +} + +TextLeafPoint xpcAccessibleTextLeafPoint::ToPoint() { + if (mAccessible) { + return TextLeafPoint(mAccessible->ToInternalGeneric(), mOffset); + } + + return TextLeafPoint(); +} diff --git a/accessible/xpcom/xpcAccessibleTextLeafRange.h b/accessible/xpcom/xpcAccessibleTextLeafRange.h new file mode 100644 index 0000000000..8d61d2edc1 --- /dev/null +++ b/accessible/xpcom/xpcAccessibleTextLeafRange.h @@ -0,0 +1,45 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_a11y_xpcAccessibleTextLeafRange_h_ +#define mozilla_a11y_xpcAccessibleTextLeafRange_h_ + +#include "nsIAccessibleTextLeafRange.h" + +namespace mozilla { +namespace a11y { + +class TextLeafPoint; + +class xpcAccessibleTextLeafPoint final : public nsIAccessibleTextLeafPoint { + public: + xpcAccessibleTextLeafPoint(nsIAccessible* aAccessible, int32_t aOffset); + + NS_DECL_ISUPPORTS + NS_DECL_NSIACCESSIBLETEXTLEAFPOINT + + private: + xpcAccessibleTextLeafPoint() = delete; + + ~xpcAccessibleTextLeafPoint() {} + + xpcAccessibleTextLeafPoint& operator=(const xpcAccessibleTextLeafPoint&) = + delete; + + TextLeafPoint ToPoint(); + + // We can't hold a strong reference to an Accessible, but XPCOM needs strong + // references. Thus, instead of holding a TextLeafPoint here, we hold + // an nsIAccessible references and create the TextLeafPoint for each call + // using ToPoint(). + RefPtr<nsIAccessible> mAccessible; + int32_t mOffset; +}; + +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/xpcom/xpcAccessibleTextRange.cpp b/accessible/xpcom/xpcAccessibleTextRange.cpp new file mode 100644 index 0000000000..1ecfdd6c2b --- /dev/null +++ b/accessible/xpcom/xpcAccessibleTextRange.cpp @@ -0,0 +1,125 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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/. */ + +#include "xpcAccessibleTextRange.h" + +#include "TextRange-inl.h" + +#include "nsQueryObject.h" +#include "xpcAccessibleDocument.h" + +using namespace mozilla; +using namespace mozilla::a11y; + +// nsISupports and cycle collection + +NS_INTERFACE_MAP_BEGIN(xpcAccessibleTextRange) + NS_INTERFACE_MAP_ENTRY(nsIAccessibleTextRange) + NS_INTERFACE_MAP_ENTRY(xpcAccessibleTextRange) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAccessibleTextRange) +NS_INTERFACE_MAP_END + +NS_IMPL_ADDREF(xpcAccessibleTextRange) +NS_IMPL_RELEASE(xpcAccessibleTextRange) + +a11y::TextRange xpcAccessibleTextRange::Range() { + return a11y::TextRange(mRoot->ToInternalGeneric(), + mStartContainer->ToInternalGeneric(), mStartOffset, + mEndContainer->ToInternalGeneric(), mEndOffset); +} + +void xpcAccessibleTextRange::SetRange(TextRange& aRange) { + mRoot = ToXPCText(aRange.Root()); + mStartContainer = ToXPCText(aRange.StartContainer()); + mStartOffset = aRange.StartOffset(); + mEndContainer = ToXPCText(aRange.EndContainer()); + mEndOffset = aRange.EndOffset(); +} + +// nsIAccessibleTextRange + +NS_IMETHODIMP +xpcAccessibleTextRange::GetStartContainer(nsIAccessibleText** aAnchor) { + NS_ENSURE_ARG_POINTER(aAnchor); + NS_IF_ADDREF(*aAnchor = mStartContainer); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleTextRange::GetStartOffset(int32_t* aOffset) { + NS_ENSURE_ARG_POINTER(aOffset); + *aOffset = mStartOffset; + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleTextRange::GetEndContainer(nsIAccessibleText** aAnchor) { + NS_ENSURE_ARG_POINTER(aAnchor); + NS_IF_ADDREF(*aAnchor = mEndContainer); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleTextRange::GetEndOffset(int32_t* aOffset) { + NS_ENSURE_ARG_POINTER(aOffset); + *aOffset = mEndOffset; + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleTextRange::GetContainer(nsIAccessible** aContainer) { + NS_ENSURE_ARG_POINTER(aContainer); + NS_IF_ADDREF(*aContainer = ToXPC(Range().Container())); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleTextRange::Compare(nsIAccessibleTextRange* aOtherRange, + bool* aResult) { + RefPtr<xpcAccessibleTextRange> xpcRange(do_QueryObject(aOtherRange)); + if (!xpcRange || !aResult) return NS_ERROR_INVALID_ARG; + + *aResult = (Range() == xpcRange->Range()); + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleTextRange::CompareEndPoints(uint32_t aEndPoint, + nsIAccessibleTextRange* aOtherRange, + uint32_t aOtherRangeEndPoint, + int32_t* aResult) { + RefPtr<xpcAccessibleTextRange> xpcRange(do_QueryObject(aOtherRange)); + if (!xpcRange || !aResult) return NS_ERROR_INVALID_ARG; + + TextRange thisRange = Range(); + TextRange otherRange = xpcRange->Range(); + TextPoint p = (aEndPoint == EndPoint_Start) ? thisRange.StartPoint() + : thisRange.EndPoint(); + TextPoint otherPoint = (aOtherRangeEndPoint == EndPoint_Start) + ? otherRange.StartPoint() + : otherRange.EndPoint(); + + if (p == otherPoint) { + *aResult = 0; + } else { + *aResult = p < otherPoint ? -1 : 1; + } + + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleTextRange::Crop(nsIAccessible* aContainer, bool* aSuccess) { + Accessible* container = aContainer->ToInternalGeneric(); + NS_ENSURE_TRUE(container, NS_ERROR_INVALID_ARG); + + TextRange range = Range(); + *aSuccess = range.Crop(container); + if (*aSuccess) { + SetRange(range); + } + return NS_OK; +} diff --git a/accessible/xpcom/xpcAccessibleTextRange.h b/accessible/xpcom/xpcAccessibleTextRange.h new file mode 100644 index 0000000000..01ca228ade --- /dev/null +++ b/accessible/xpcom/xpcAccessibleTextRange.h @@ -0,0 +1,78 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_a11y_xpcAccessibleTextRange_h_ +#define mozilla_a11y_xpcAccessibleTextRange_h_ + +#include <utility> + +#include "TextRange.h" +#include "nsIAccessibleTextRange.h" +#include "xpcAccessibleHyperText.h" + +namespace mozilla { +namespace a11y { + +class TextRange; + +#define NS_ACCESSIBLETEXTRANGE_IMPL_IID \ + { /* 133c8bf4-4913-4355-bd50-426bd1d6e1ad */ \ + 0xb17652d9, 0x4f54, 0x4c56, { \ + 0xbb, 0x62, 0x6d, 0x5b, 0xf1, 0xef, 0x91, 0x0c \ + } \ + } + +class xpcAccessibleTextRange final : public nsIAccessibleTextRange { + public: + explicit xpcAccessibleTextRange(TextRange& aRange) { SetRange(aRange); } + + NS_DECL_ISUPPORTS + + NS_IMETHOD GetStartContainer(nsIAccessibleText** aAnchor) final; + NS_IMETHOD GetStartOffset(int32_t* aOffset) final; + NS_IMETHOD GetEndContainer(nsIAccessibleText** aAnchor) final; + NS_IMETHOD GetEndOffset(int32_t* aOffset) final; + NS_IMETHOD GetContainer(nsIAccessible** aContainer) final; + NS_IMETHOD Compare(nsIAccessibleTextRange* aOtherRange, bool* aResult) final; + NS_IMETHOD CompareEndPoints(uint32_t aEndPoint, + nsIAccessibleTextRange* aOtherRange, + uint32_t aOtherRangeEndPoint, + int32_t* aResult) final; + NS_IMETHOD Crop(nsIAccessible* aContainer, bool* aSuccess) final; + + NS_DECLARE_STATIC_IID_ACCESSOR(NS_ACCESSIBLETEXTRANGE_IMPL_IID) + + private: + xpcAccessibleTextRange() {} + + ~xpcAccessibleTextRange() {} + + friend class xpcAccessibleHyperText; + + xpcAccessibleTextRange& operator=(const xpcAccessibleTextRange&) = delete; + + void SetRange(TextRange& aRange); + + TextRange Range(); + + // We can't hold a strong reference to an Accessible, but XPCOM needs strong + // references. Thus, instead of holding a TextRange here, we hold + // xpcAccessibleHyperText references and create the TextRange for each call + // using Range(). + RefPtr<xpcAccessibleHyperText> mRoot; + RefPtr<xpcAccessibleHyperText> mStartContainer; + int32_t mStartOffset; + RefPtr<xpcAccessibleHyperText> mEndContainer; + int32_t mEndOffset; +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(xpcAccessibleTextRange, + NS_ACCESSIBLETEXTRANGE_IMPL_IID) + +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/xpcom/xpcAccessibleValue.cpp b/accessible/xpcom/xpcAccessibleValue.cpp new file mode 100644 index 0000000000..b409879883 --- /dev/null +++ b/accessible/xpcom/xpcAccessibleValue.cpp @@ -0,0 +1,99 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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/. */ + +#include "xpcAccessibleGeneric.h" +#include "LocalAccessible.h" +#include "LocalAccessible-inl.h" + +using namespace mozilla; +using namespace mozilla::a11y; + +NS_IMETHODIMP +xpcAccessibleValue::GetMaximumValue(double* aValue) { + NS_ENSURE_ARG_POINTER(aValue); + *aValue = 0; + + if (!Intl()) return NS_ERROR_FAILURE; + + if (Intl()->IsLocal() && Intl()->AsLocal()->IsDefunct()) { + return NS_ERROR_FAILURE; + } + + double value = Intl()->MaxValue(); + + if (!std::isnan(value)) *aValue = value; + + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleValue::GetMinimumValue(double* aValue) { + NS_ENSURE_ARG_POINTER(aValue); + *aValue = 0; + + if (!Intl()) return NS_ERROR_FAILURE; + + if (Intl()->IsLocal() && Intl()->AsLocal()->IsDefunct()) { + return NS_ERROR_FAILURE; + } + + double value = Intl()->MinValue(); + + if (!std::isnan(value)) *aValue = value; + + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleValue::GetCurrentValue(double* aValue) { + NS_ENSURE_ARG_POINTER(aValue); + *aValue = 0; + + if (!Intl()) return NS_ERROR_FAILURE; + + if (Intl()->IsLocal() && Intl()->AsLocal()->IsDefunct()) { + return NS_ERROR_FAILURE; + } + + double value = Intl()->CurValue(); + + if (!std::isnan(value)) *aValue = value; + + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleValue::SetCurrentValue(double aValue) { + if (!Intl()) return NS_ERROR_FAILURE; + + if (Intl()->IsLocal() && Intl()->AsLocal()->IsDefunct()) { + return NS_ERROR_FAILURE; + } + + if (!Intl()->SetCurValue(aValue)) { + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +NS_IMETHODIMP +xpcAccessibleValue::GetMinimumIncrement(double* aValue) { + NS_ENSURE_ARG_POINTER(aValue); + *aValue = 0; + + if (!Intl()) return NS_ERROR_FAILURE; + + if (Intl()->IsLocal() && Intl()->AsLocal()->IsDefunct()) { + return NS_ERROR_FAILURE; + } + + double value = Intl()->Step(); + + if (!std::isnan(value)) *aValue = value; + + return NS_OK; +} diff --git a/accessible/xpcom/xpcAccessibleValue.h b/accessible/xpcom/xpcAccessibleValue.h new file mode 100644 index 0000000000..812278ab56 --- /dev/null +++ b/accessible/xpcom/xpcAccessibleValue.h @@ -0,0 +1,42 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_a11y_xpcAccessibleValue_h_ +#define mozilla_a11y_xpcAccessibleValue_h_ + +#include "nsIAccessibleValue.h" + +namespace mozilla { +namespace a11y { + +class LocalAccessible; + +/** + * XPCOM nsIAccessibleValue interface implementation, used by + * xpcAccessibleGeneric class. + */ +class xpcAccessibleValue : public nsIAccessibleValue { + public: + NS_IMETHOD GetMaximumValue(double* aValue) final; + NS_IMETHOD GetMinimumValue(double* aValue) final; + NS_IMETHOD GetCurrentValue(double* aValue) final; + NS_IMETHOD SetCurrentValue(double aValue) final; + NS_IMETHOD GetMinimumIncrement(double* aMinIncrement) final; + + protected: + xpcAccessibleValue() {} + virtual ~xpcAccessibleValue() {} + + private: + Accessible* Intl(); + + xpcAccessibleValue(const xpcAccessibleValue&) = delete; + xpcAccessibleValue& operator=(const xpcAccessibleValue&) = delete; +}; + +} // namespace a11y +} // namespace mozilla +#endif |