a11y: Do not refer to destructed QObject in Windows bridge
authorJan Arve Saether <jan-arve.saether@digia.com>
Fri, 7 Dec 2012 14:29:06 +0000 (15:29 +0100)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Tue, 11 Dec 2012 17:34:19 +0000 (18:34 +0100)
The crash could happen if a QWidget in the UI got deleted, and
the AT client later tried to access the widget through the cache
(qAccessibleRecentSentEvents()).

Solution: Use a QPointer as a guard.

Task-number: QTBUG-26187

Change-Id: I1aa716766626cf171757e76ba255a6d5ae4fd854
Reviewed-by: Frederik Gladhorn <frederik.gladhorn@digia.com>
src/plugins/platforms/windows/accessible/qwindowsaccessibility.cpp
src/plugins/platforms/windows/accessible/qwindowsmsaaaccessible.cpp

index db2d5f9..b94fef5 100644 (file)
@@ -48,6 +48,7 @@
 #include <QtCore/qlocale.h>
 #include <QtCore/qmap.h>
 #include <QtCore/qpair.h>
+#include <QtCore/qpointer.h>
 #include <QtCore/qsettings.h>
 #include <QtGui/qaccessible.h>
 #include <QtGui/qaccessible2.h>
 
 
 // This stuff is used for widgets/items with no window handle:
-typedef QMap<int, QPair<QObject*,int> > NotifyMap;
+typedef QMap<int, QPair<QPointer<QObject>,int> > NotifyMap;
 Q_GLOBAL_STATIC(NotifyMap, qAccessibleRecentSentEvents)
 
-
 QT_BEGIN_NAMESPACE
 
 
@@ -193,14 +193,25 @@ void QWindowsAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event)
     HWND hWnd = (HWND)platform->nativeResourceForWindow("handle", window);
 
     static int eventNum = 0;
-    if (event->type() != QAccessible::MenuCommand) { // MenuCommand is faked
-        // See comment "SENDING EVENTS TO OBJECTS WITH NO WINDOW HANDLE"
+    if (event->type() != QAccessible::MenuCommand && // MenuCommand is faked
+        event->type() != QAccessible::ObjectDestroyed) {
+        /* In some rare occasions, the server (Qt) might get a ::get_accChild call with a
+           childId that references an entry in the cache where there was a dangling
+           QObject-pointer. Previously we crashed on this.
+
+           There is no point in actually notifying the AT client that the object got destroyed,
+           because the AT client won't query for get_accChild if the event is ObjectDestroyed
+           anyway, and we have no other way of mapping the eventId argument to the actual
+           child/descendant object. (Firefox seems to simply completely ignore
+           EVENT_OBJECT_DESTROY).
+
+           We therefore guard each QObject in the cache with a QPointer, and only notify the AT
+           client if the type is not ObjectDestroyed.
+        */
         eventNum %= 50;              //[0..49]
         int eventId = - (eventNum - 1);
-
-        qAccessibleRecentSentEvents()->insert(eventId, qMakePair(event->object(), event->child()));
-        ptrNotifyWinEvent(event->type(), hWnd, OBJID_CLIENT, eventId );
-
+        qAccessibleRecentSentEvents()->insert(eventId, qMakePair(QPointer<QObject>(event->object()), event->child()));
+        ptrNotifyWinEvent(event->type(), hWnd, OBJID_CLIENT, eventId);
         ++eventNum;
     }
 #endif // Q_OS_WINCE
@@ -244,7 +255,8 @@ IAccessible *QWindowsAccessibility::wrap(QAccessibleInterface *acc)
 */
 QPair<QObject*, int> QWindowsAccessibility::getCachedObject(int entryId)
 {
-    return qAccessibleRecentSentEvents()->value(entryId);
+    QPair<QPointer<QObject>, int> pair = qAccessibleRecentSentEvents()->value(entryId);
+    return qMakePair(pair.first.data(), pair.second);
 }
 
 /*
index 3280536..f1bdc77 100644 (file)
@@ -757,7 +757,7 @@ HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::get_accChild(VARIANT varChildI
                 acc = res;
             }
         } else {
-            qWarning("get_accChild got a negative varChildID, but did not find it in cache");
+            qWarning("get_accChild got a negative varChildID (%d), but did not find it in cache", childIndex);
         }
     } else {
         if (childIndex) {