QtBase  v6.3.1
qaccessiblecache.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qaccessiblecache_p.h"
41 #include <QtCore/qdebug.h>
42 #include <QtCore/qloggingcategory.h>
43 
44 #ifndef QT_NO_ACCESSIBILITY
45 
47 
48 Q_LOGGING_CATEGORY(lcAccessibilityCache, "qt.accessibility.cache");
49 
56 static QAccessibleCache *accessibleCache = nullptr;
57 
58 static void cleanupAccessibleCache()
59 {
60  delete accessibleCache;
61  accessibleCache = nullptr;
62 }
63 
65 {
66  for (QAccessible::Id id: idToInterface.keys())
67  deleteInterface(id);
68 }
69 
71 {
72  if (!accessibleCache) {
73  accessibleCache = new QAccessibleCache;
74  qAddPostRoutine(cleanupAccessibleCache);
75  }
76  return accessibleCache;
77 }
78 
79 /*
80  The ID is always in the range [INT_MAX+1, UINT_MAX].
81  This makes it easy on windows to reserve the positive integer range
82  for the index of a child and not clash with the unique ids.
83 */
84 QAccessible::Id QAccessibleCache::acquireId() const
85 {
86  static const QAccessible::Id FirstId = QAccessible::Id(INT_MAX) + 1;
87  static QAccessible::Id lastUsedId = FirstId;
88 
89  while (idToInterface.contains(lastUsedId)) {
90  // (wrap back when when we reach UINT_MAX - 1)
91  // -1 because on Android -1 is taken for the "View" so just avoid it completely for consistency
92  if (lastUsedId == UINT_MAX - 1)
93  lastUsedId = FirstId;
94  else
95  ++lastUsedId;
96  }
97 
98  return lastUsedId;
99 }
100 
102 {
103  return idToInterface.value(id);
104 }
105 
107 {
108  return interfaceToId.value(iface);
109 }
110 
112 {
113  if (obj) {
114  const QMetaObject *mo = obj->metaObject();
115  for (auto pair : objectToId.values(obj)) {
116  if (pair.second == mo) {
117  return pair.first;
118  }
119  }
120  }
121  return 0;
122 }
123 
130 {
131  if (obj) {
132  const QMetaObject *mo = obj->metaObject();
133  for (auto pair : objectToId.values(obj)) {
134  if (pair.second == mo) {
135  return true;
136  }
137  }
138  }
139  return false;
140 }
141 
143 {
144  Q_ASSERT(iface);
145  Q_UNUSED(object);
146 
147  // object might be 0
148  Q_ASSERT(!containsObject(object));
149  Q_ASSERT_X(!interfaceToId.contains(iface), "", "Accessible interface inserted into cache twice!");
150 
151  QAccessible::Id id = acquireId();
152  QObject *obj = iface->object();
153  Q_ASSERT(object == obj);
154  if (obj) {
155  objectToId.insert(obj, qMakePair(id, obj->metaObject()));
156  connect(obj, &QObject::destroyed, this, &QAccessibleCache::objectDestroyed);
157  }
158  idToInterface.insert(id, iface);
159  interfaceToId.insert(iface, id);
160  qCDebug(lcAccessibilityCache) << "insert - id:" << id << " iface:" << iface;
161  return id;
162 }
163 
164 void QAccessibleCache::objectDestroyed(QObject* obj)
165 {
166  /*
167  In some cases we might add a not fully-constructed object to the cache. This might happen with
168  for instance QWidget subclasses that are in the construction phase. If updateAccessibility() is
169  called in the constructor of QWidget (directly or indirectly), it it will end up asking for the
170  classname of that widget in order to know which accessibility interface subclass the
171  accessibility factory should instantiate and return. However, since that requires a virtual
172  call to metaObject(), it will return the metaObject() of QWidget (not for the subclass), and so
173  the factory will ultimately return a rather generic QAccessibleWidget instead of a more
174  specialized interface. Even though it is a "incomplete" interface it will be put in the cache
175  and it will be usable as if the object is a widget. In order for the cache to not just return
176  the same generic QAccessibleWidget for that object, we have to check if the cache matches
177  the objects QMetaObject. We therefore use a QMultiHash and also store the QMetaObject * in
178  the value. We therefore might potentially store several values for the corresponding object
179  (in theory one for each level in the class inheritance chain)
180 
181  This means that after the object have been fully constructed, we will at some point again query
182  for the interface for the same object, but now its metaObject() returns the correct
183  QMetaObject, so it won't return the QAccessibleWidget that is associated with the object in the
184  cache. Instead it will go to the factory and create the _correct_ specialized interface for the
185  object. If that succeeded, it will also put that entry in the cache. We will therefore in those
186  cases insert *two* cache entries for the same object (using QMultiHash). They both must live
187  until the object is destroyed.
188 
189  So when the object is destroyed we might have to delete two entries from the cache.
190  */
191  for (auto pair : objectToId.values(obj)) {
192  QAccessible::Id id = pair.first;
193  Q_ASSERT_X(idToInterface.contains(id), "", "QObject with accessible interface deleted, where interface not in cache!");
194  deleteInterface(id, obj);
195  }
196 }
197 
199 {
200  QAccessibleInterface *iface = idToInterface.take(id);
201  qCDebug(lcAccessibilityCache) << "delete - id:" << id << " iface:" << iface;
202  if (!iface) // the interface may be deleted already
203  return;
204  interfaceToId.take(iface);
205  if (!obj)
206  obj = iface->object();
207  if (obj)
208  objectToId.remove(obj);
209  delete iface;
210 
211 #ifdef Q_OS_MAC
212  removeCocoaElement(id);
213 #endif
214 }
215 
217 
218 #include "moc_qaccessiblecache_p.cpp"
219 
220 #endif
bool containsObject(QObject *obj) const
~QAccessibleCache() override
QAccessible::Id insert(QObject *object, QAccessibleInterface *iface) const
QAccessibleInterface * interfaceForId(QAccessible::Id id) const
void deleteInterface(QAccessible::Id id, QObject *obj=nullptr)
QAccessible::Id idForInterface(QAccessibleInterface *iface) const
static QAccessibleCache * instance()
QAccessible::Id idForObject(QObject *obj) const
unsigned Id
Definition: qaccessible.h:404
The QAccessibleInterface class defines an interface that exposes information about accessible objects...
Definition: qaccessible.h:459
virtual QObject * object() const =0
T take(const Key &key)
Definition: qhash.h:929
QList< Key > keys() const
Definition: qhash.h:1029
bool contains(const Key &key) const noexcept
Definition: qhash.h:944
T value(const Key &key) const noexcept
Definition: qhash.h:997
iterator insert(const Key &key, const T &value)
Definition: qhash.h:1228
QList< T > values() const
Definition: qhash.h:1617
qsizetype remove(const Key &key)
Definition: qhash.h:1464
iterator insert(const Key &key, const T &value)
Definition: qhash.h:1861
The QObject class is the base class of all Qt objects.
Definition: qobject.h:125
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
Definition: qobject.cpp:2772
void destroyed(QObject *=nullptr)
auto mo
[7]
QT_BEGIN_NAMESPACE Q_LOGGING_CATEGORY(lcAccessibilityCache, "qt.accessibility.cache")
void qAddPostRoutine(QtCleanUpFunction p)
#define qCDebug(category,...)
GLenum GLuint id
[6]
Definition: qopengl.h:270
GLhandleARB obj
[2]
Definition: qopenglext.h:4164
constexpr decltype(auto) qMakePair(T1 &&value1, T2 &&value2) noexcept(noexcept(std::make_pair(std::forward< T1 >(value1), std::forward< T2 >(value2))))
Definition: qpair.h:55
#define Q_ASSERT(cond)
Definition: qrandom.cpp:84
#define Q_ASSERT_X(cond, x, msg)
Definition: qrandom.cpp:85
Q_UNUSED(salary)
[21]
The QMetaObject class contains meta-information about Qt objects.
Definition: qobjectdefs.h:165