Support event notifications in IsolatedTree mode.
authorandresg_22@apple.com <andresg_22@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 12 Feb 2020 17:31:03 +0000 (17:31 +0000)
committerandresg_22@apple.com <andresg_22@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 12 Feb 2020 17:31:03 +0000 (17:31 +0000)
https://bugs.webkit.org/show_bug.cgi?id=207581

Reviewed by Chris Fleizach.

- DOM/Render trees notifications are listened to by AXObjectCache and
the corresponding IsolatedTree is updated.
- The update is now happening for state, value and children change
notifications.
- AXObjectCache::updateIsolatedTree re-generates the subtree rooted at
the node receiving the notification.
- Consolidated creation of AXIsolatedObjects by passing treeID and
parentID to the create method and constructor.
- Changes to the IsolatedTree are set on the main thread using the
NodeChange structure, and applied to the tree on the AX thread.
- The updated IsolatedObjects are attached to their corresponding
platform wrappers on the AX thread, so that after creation they are only
accessed on the secondary thread.

* accessibility/AXObjectCache.cpp:
(WebCore::AXObjectCache::postNotification): Invokes updateIsolatedTree.
(WebCore::createIsolatedTreeHierarchy):
(WebCore::AXObjectCache::generateIsolatedTree):
(WebCore::AXObjectCache::updateIsolatedTree):
(WebCore::AXObjectCache::createIsolatedTreeHierarchy): Became a static helper function.
* accessibility/AXObjectCache.h:
* accessibility/AccessibilityObjectInterface.h:
* accessibility/isolatedtree/AXIsolatedObject.cpp:
(WebCore::AXIsolatedObject::AXIsolatedObject):
(WebCore::AXIsolatedObject::create):
(WebCore::AXIsolatedObject::setProperty):
(WebCore::AXIsolatedObject::setParent):
(WebCore::AXIsolatedObject::detachFromParent):
(WebCore::AXIsolatedObject::setTreeIdentifier): Deleted, the tree identifier is set in the constructor.
* accessibility/isolatedtree/AXIsolatedObject.h:
* accessibility/isolatedtree/AXIsolatedTree.cpp:
(WebCore::AXIsolatedTree::NodeChange::NodeChange):
(WebCore::AXIsolatedTree::appendNodeChanges):
(WebCore::AXIsolatedTree::applyPendingChanges):
* accessibility/isolatedtree/AXIsolatedTree.h:
* accessibility/isolatedtree/mac/AXIsolatedObjectMac.mm:
(WebCore::AXIsolatedObject::attachPlatformWrapper):
* accessibility/mac/AXObjectCacheMac.mm:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@256442 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebCore/ChangeLog
Source/WebCore/accessibility/AXObjectCache.cpp
Source/WebCore/accessibility/AXObjectCache.h
Source/WebCore/accessibility/AccessibilityObjectInterface.h
Source/WebCore/accessibility/isolatedtree/AXIsolatedObject.cpp
Source/WebCore/accessibility/isolatedtree/AXIsolatedObject.h
Source/WebCore/accessibility/isolatedtree/AXIsolatedTree.cpp
Source/WebCore/accessibility/isolatedtree/AXIsolatedTree.h
Source/WebCore/accessibility/isolatedtree/mac/AXIsolatedObjectMac.mm
Source/WebCore/accessibility/mac/AXObjectCacheMac.mm

index 3e85311..52a62ca 100644 (file)
@@ -1,3 +1,49 @@
+2020-02-12  Andres Gonzalez  <andresg_22@apple.com>
+
+        Support event notifications in IsolatedTree mode.
+        https://bugs.webkit.org/show_bug.cgi?id=207581
+
+        Reviewed by Chris Fleizach.
+
+        - DOM/Render trees notifications are listened to by AXObjectCache and
+        the corresponding IsolatedTree is updated.
+        - The update is now happening for state, value and children change
+        notifications.
+        - AXObjectCache::updateIsolatedTree re-generates the subtree rooted at
+        the node receiving the notification.
+        - Consolidated creation of AXIsolatedObjects by passing treeID and
+        parentID to the create method and constructor.
+        - Changes to the IsolatedTree are set on the main thread using the
+        NodeChange structure, and applied to the tree on the AX thread.
+        - The updated IsolatedObjects are attached to their corresponding
+        platform wrappers on the AX thread, so that after creation they are only
+        accessed on the secondary thread.
+
+        * accessibility/AXObjectCache.cpp:
+        (WebCore::AXObjectCache::postNotification): Invokes updateIsolatedTree.
+        (WebCore::createIsolatedTreeHierarchy):
+        (WebCore::AXObjectCache::generateIsolatedTree):
+        (WebCore::AXObjectCache::updateIsolatedTree):
+        (WebCore::AXObjectCache::createIsolatedTreeHierarchy): Became a static helper function.
+        * accessibility/AXObjectCache.h:
+        * accessibility/AccessibilityObjectInterface.h:
+        * accessibility/isolatedtree/AXIsolatedObject.cpp:
+        (WebCore::AXIsolatedObject::AXIsolatedObject):
+        (WebCore::AXIsolatedObject::create):
+        (WebCore::AXIsolatedObject::setProperty):
+        (WebCore::AXIsolatedObject::setParent):
+        (WebCore::AXIsolatedObject::detachFromParent):
+        (WebCore::AXIsolatedObject::setTreeIdentifier): Deleted, the tree identifier is set in the constructor.
+        * accessibility/isolatedtree/AXIsolatedObject.h:
+        * accessibility/isolatedtree/AXIsolatedTree.cpp:
+        (WebCore::AXIsolatedTree::NodeChange::NodeChange):
+        (WebCore::AXIsolatedTree::appendNodeChanges):
+        (WebCore::AXIsolatedTree::applyPendingChanges):
+        * accessibility/isolatedtree/AXIsolatedTree.h:
+        * accessibility/isolatedtree/mac/AXIsolatedObjectMac.mm:
+        (WebCore::AXIsolatedObject::attachPlatformWrapper):
+        * accessibility/mac/AXObjectCacheMac.mm:
+
 2020-02-12  Zalan Bujtas  <zalan@apple.com>
 
         [LFC][IFC] Rename Display::Run::TextContext to TextContent
index 328a5eb..086ef44 100644 (file)
@@ -1118,6 +1118,10 @@ void AXObjectCache::postNotification(AXCoreObject* object, Document* document, A
     if (!object)
         return;
 
+#if ENABLE(ACCESSIBILITY_ISOLATED_TREE)
+    updateIsolatedTree(object, notification);
+#endif
+
     if (postType == PostAsynchronously) {
         m_notificationsToPost.append(std::make_pair(object, notification));
         if (!m_notificationPostTimer.isActive())
@@ -3068,21 +3072,19 @@ void AXObjectCache::performDeferredCacheUpdate()
 }
     
 #if ENABLE(ACCESSIBILITY_ISOLATED_TREE)
-Ref<AXIsolatedObject> AXObjectCache::createIsolatedTreeHierarchy(AXCoreObject& object, AXID parentID, AXObjectCache* axObjectCache, AXIsolatedTree& tree, Vector<Ref<AXIsolatedObject>>& nodeChanges, bool isRoot)
+static Ref<AXIsolatedObject> createIsolatedTreeHierarchy(AXCoreObject& object, AXIsolatedTreeID treeID, AXID parentID, bool attachWrapper, Vector<AXIsolatedTree::NodeChange>& nodeChanges)
 {
-    auto isolatedTreeNode = AXIsolatedObject::create(object, isRoot);
-    nodeChanges.append(isolatedTreeNode.copyRef());
-
-    isolatedTreeNode->setTreeIdentifier(tree.treeIdentifier());
-    isolatedTreeNode->setParent(parentID);
-    axObjectCache->attachWrapper(&isolatedTreeNode.get(), object.wrapper());
+    auto isolatedObject = AXIsolatedObject::create(object, treeID, parentID);
+    nodeChanges.append(AXIsolatedTree::NodeChange(isolatedObject, object.wrapper()));
+    if (attachWrapper)
+        isolatedObject->attachPlatformWrapper(object.wrapper());
 
     for (const auto& child : object.children()) {
-        auto staticChild = createIsolatedTreeHierarchy(*child, isolatedTreeNode->objectID(), axObjectCache, tree, nodeChanges, false);
-        isolatedTreeNode->appendChild(staticChild->objectID());
+        auto staticChild = createIsolatedTreeHierarchy(*child, treeID, isolatedObject->objectID(), attachWrapper, nodeChanges);
+        isolatedObject->appendChild(staticChild->objectID());
     }
 
-    return isolatedTreeNode;
+    return isolatedObject;
 }
 
 Ref<AXIsolatedTree> AXObjectCache::generateIsolatedTree(PageIdentifier pageID, Document& document)
@@ -3100,8 +3102,8 @@ Ref<AXIsolatedTree> AXObjectCache::generateIsolatedTree(PageIdentifier pageID, D
 
     auto* axRoot = axObjectCache->getOrCreate(document.view());
     if (axRoot) {
-        Vector<Ref<AXIsolatedObject>> nodeChanges;
-        auto isolatedRoot = createIsolatedTreeHierarchy(*axRoot, InvalidAXID, axObjectCache, *tree, nodeChanges, true);
+        Vector<AXIsolatedTree::NodeChange> nodeChanges;
+        auto isolatedRoot = createIsolatedTreeHierarchy(*axRoot, tree->treeIdentifier(), InvalidAXID, true, nodeChanges);
         tree->setRootNode(isolatedRoot);
         tree->appendNodeChanges(nodeChanges);
     }
@@ -3112,6 +3114,32 @@ Ref<AXIsolatedTree> AXObjectCache::generateIsolatedTree(PageIdentifier pageID, D
 
     return makeRef(*tree);
 }
+
+void AXObjectCache::updateIsolatedTree(AXCoreObject* object, AXNotification notification)
+{
+    if (!m_pageID)
+        return;
+
+    auto tree = AXIsolatedTree::treeForPageID(*m_pageID);
+    if (!tree)
+        return;
+
+    switch (notification) {
+    case AXCheckedStateChanged:
+    case AXChildrenChanged:
+    case AXValueChanged: {
+        tree->removeNode(object->objectID());
+        auto* parent = object->parentObject();
+        AXID parentID = parent ? parent->objectID() : InvalidAXID;
+        Vector<AXIsolatedTree::NodeChange> nodeChanges;
+        auto isolatedObject = createIsolatedTreeHierarchy(*object, tree->treeIdentifier(), parentID, false, nodeChanges);
+        tree->appendNodeChanges(nodeChanges);
+        break;
+    }
+    default:
+        break;
+    }
+}
 #endif
     
 void AXObjectCache::deferRecomputeIsIgnoredIfNeeded(Element* element)
index 590de52..bee9ba1 100644 (file)
@@ -25,6 +25,7 @@
 
 #pragma once
 
+#include "AXIsolatedTree.h"
 #include "AXTextStateChangeIntent.h"
 #include "AccessibilityObject.h"
 #include "Range.h"
 
 namespace WebCore {
 
-#if ENABLE(ACCESSIBILITY_ISOLATED_TREE)
-class AXIsolatedObject;
-class AXIsolatedTree;
-#endif
 class Document;
 class HTMLAreaElement;
 class HTMLTextFormControlElement;
@@ -178,9 +175,6 @@ private:
     using DOMObjectVariant = Variant<std::nullptr_t, RenderObject*, Node*, Widget*>;
     void cacheAndInitializeWrapper(AccessibilityObject*, DOMObjectVariant = nullptr);
     void attachWrapper(AXCoreObject*);
-#if ENABLE(ACCESSIBILITY_ISOLATED_TREE)
-    void attachWrapper(AXIsolatedObject*, WebAccessibilityObjectWrapper*);
-#endif
 
 public:
     void childrenChanged(Node*, Node* newChild = nullptr);
@@ -199,17 +193,6 @@ public:
     void deferAttributeChangeIfNeeded(const QualifiedName&, Element*);
     void recomputeIsIgnored(RenderObject* renderer);
 
-#if ENABLE(ACCESSIBILITY_ISOLATED_TREE)
-    WEBCORE_EXPORT static bool clientSupportsIsolatedTree();
-private:
-    AXCoreObject* isolatedTreeRootObject();
-    static AXCoreObject* isolatedTreeFocusedObject(Document&);
-    void setIsolatedTreeFocusedObject(Node*);
-    static Ref<AXIsolatedTree> generateIsolatedTree(PageIdentifier, Document&);
-    static Ref<AXIsolatedObject> createIsolatedTreeHierarchy(AXCoreObject&, AXID, AXObjectCache*, AXIsolatedTree&, Vector<Ref<AXIsolatedObject>>&, bool isRoot);
-#endif
-
-public:
     WEBCORE_EXPORT bool canUseSecondaryAXThread();
 
 #if ENABLE(ACCESSIBILITY)
@@ -367,7 +350,16 @@ public:
     void deferTextReplacementNotificationForTextControl(HTMLTextFormControlElement&, const String& previousValue);
 
     RefPtr<Range> rangeMatchesTextNearRange(RefPtr<Range>, const String&);
-    
+
+#if ENABLE(ACCESSIBILITY_ISOLATED_TREE)
+    WEBCORE_EXPORT static bool clientSupportsIsolatedTree();
+private:
+    AXCoreObject* isolatedTreeRootObject();
+    static AXCoreObject* isolatedTreeFocusedObject(Document&);
+    void setIsolatedTreeFocusedObject(Node*);
+    static Ref<AXIsolatedTree> generateIsolatedTree(PageIdentifier, Document&);
+    void updateIsolatedTree(AXCoreObject*, AXNotification);
+#endif
 
 protected:
     void postPlatformNotification(AXCoreObject*, AXNotification);
index 2d81176..81517ca 100644 (file)
@@ -70,7 +70,6 @@ struct ScrollRectToVisibleOptions;
 
 typedef unsigned AXID;
 extern const AXID InvalidAXID;
-typedef unsigned AXIsolatedTreeID;    
 
 enum class AccessibilityRole {
     Annotation = 1,
index 9fb700a..197df45 100644 (file)
 
 namespace WebCore {
 
-AXIsolatedObject::AXIsolatedObject(AXCoreObject& object, bool isRoot)
-    : m_id(object.objectID())
+AXIsolatedObject::AXIsolatedObject(AXCoreObject& object, AXIsolatedTreeID treeID, AXID parentID)
+    : m_treeID(treeID)
+    , m_parentID(parentID)
+    , m_id(object.objectID())
 {
     ASSERT(isMainThread());
-    initializeAttributeData(object, isRoot);
-    m_initialized = true;
+    if (auto tree = AXIsolatedTree::treeForID(m_treeID))
+        m_cachedTree = tree;
+    initializeAttributeData(object, parentID == InvalidAXID);
 }
 
-Ref<AXIsolatedObject> AXIsolatedObject::create(AXCoreObject& object, bool isRoot)
+Ref<AXIsolatedObject> AXIsolatedObject::create(AXCoreObject& object, AXIsolatedTreeID treeID, AXID parentID)
 {
-    return adoptRef(*new AXIsolatedObject(object, isRoot));
+    return adoptRef(*new AXIsolatedObject(object, treeID, parentID));
 }
 
 AXIsolatedObject::~AXIsolatedObject() = default;
@@ -386,7 +389,6 @@ void AXIsolatedObject::setObjectVectorProperty(AXPropertyName propertyName, Acce
 
 void AXIsolatedObject::setProperty(AXPropertyName propertyName, AttributeValueVariant&& value, bool shouldRemove)
 {
-    ASSERT(!m_initialized);
     ASSERT(isMainThread());
 
     if (shouldRemove)
@@ -404,7 +406,7 @@ void AXIsolatedObject::appendChild(AXID axID)
 void AXIsolatedObject::setParent(AXID parent)
 {
     ASSERT(isMainThread());
-    m_parent = parent;
+    m_parentID = parent;
 }
 
 void AXIsolatedObject::detachRemoteParts(AccessibilityDetachmentType detachmentType)
@@ -425,14 +427,7 @@ bool AXIsolatedObject::isDetached() const
 
 void AXIsolatedObject::detachFromParent()
 {
-    m_parent = InvalidAXID;
-}
-
-void AXIsolatedObject::setTreeIdentifier(AXIsolatedTreeID treeIdentifier)
-{
-    m_treeIdentifier = treeIdentifier;
-    if (auto tree = AXIsolatedTree::treeForID(m_treeIdentifier))
-        m_cachedTree = tree;
+    m_parentID = InvalidAXID;
 }
 
 const AXCoreObject::AccessibilityChildrenVector& AXIsolatedObject::children(bool)
index 3aa9699..71c1c2c 100644 (file)
@@ -47,16 +47,16 @@ class AXIsolatedTree;
 
 class AXIsolatedObject final : public AXCoreObject {
 public:
-    static Ref<AXIsolatedObject> create(AXCoreObject&, bool isRoot);
+    static Ref<AXIsolatedObject> create(AXCoreObject&, AXIsolatedTreeID, AXID parentID);
     ~AXIsolatedObject();
 
     void setObjectID(AXID id) override { m_id = id; }
     AXID objectID() const override { return m_id; }
     void init() override { }
 
+    void attachPlatformWrapper(AccessibilityObjectWrapper*);
     bool isDetached() const override;
 
-    void setTreeIdentifier(AXIsolatedTreeID);
     void setParent(AXID);
     void appendChild(AXID);
 
@@ -64,13 +64,13 @@ private:
     void detachRemoteParts(AccessibilityDetachmentType) override;
     void detachPlatformWrapper(AccessibilityDetachmentType) override;
 
-    AXID parent() const { return m_parent; }
+    AXID parent() const { return m_parentID; }
 
-    AXIsolatedTreeID treeIdentifier() const { return m_treeIdentifier; }
+    AXIsolatedTreeID treeIdentifier() const { return m_treeID; }
     AXIsolatedTree* tree() const { return m_cachedTree.get(); }
 
     AXIsolatedObject() = default;
-    AXIsolatedObject(AXCoreObject&, bool isRoot);
+    AXIsolatedObject(AXCoreObject&, AXIsolatedTreeID, AXID parentID);
     void initializeAttributeData(AXCoreObject&, bool isRoot);
     AXCoreObject* associatedAXObject() const
     {
@@ -821,11 +821,10 @@ private:
 
     void updateBackingStore() override;
 
-    AXID m_parent { InvalidAXID };
-    AXID m_id { InvalidAXID };
-    bool m_initialized { false };
-    AXIsolatedTreeID m_treeIdentifier;
+    AXIsolatedTreeID m_treeID;
     RefPtr<AXIsolatedTree> m_cachedTree;
+    AXID m_parentID { InvalidAXID };
+    AXID m_id { InvalidAXID };
     Vector<AXID> m_childrenIDs;
     Vector<RefPtr<AXCoreObject>> m_children;
 
index b6fe7d2..a3090d2 100644 (file)
@@ -42,6 +42,18 @@ static unsigned newTreeID()
     return ++s_currentTreeID;
 }
 
+AXIsolatedTree::NodeChange::NodeChange(const Ref<AXIsolatedObject>& isolatedObject, AccessibilityObjectWrapper* wrapper)
+    : m_isolatedObject(isolatedObject.copyRef())
+    , m_wrapper(wrapper)
+{
+}
+
+AXIsolatedTree::NodeChange::NodeChange(const NodeChange& other)
+    : m_isolatedObject(other.m_isolatedObject.copyRef())
+    , m_wrapper(other.m_wrapper)
+{
+}
+
 HashMap<PageIdentifier, Ref<AXIsolatedTree>>& AXIsolatedTree::treePageCache()
 {
     static NeverDestroyed<HashMap<PageIdentifier, Ref<AXIsolatedTree>>> map;
@@ -155,11 +167,11 @@ void AXIsolatedTree::removeNode(AXID axID)
     m_pendingRemovals.append(axID);
 }
 
-void AXIsolatedTree::appendNodeChanges(Vector<Ref<AXIsolatedObject>>& log)
+void AXIsolatedTree::appendNodeChanges(const Vector<NodeChange>& log)
 {
     LockHolder locker { m_changeLogLock };
-    for (auto& node : log)
-        m_pendingAppends.append(node.copyRef());
+    for (const auto& node : log)
+        m_pendingAppends.append(node);
 }
 
 void AXIsolatedTree::applyPendingChanges()
@@ -167,22 +179,23 @@ void AXIsolatedTree::applyPendingChanges()
     RELEASE_ASSERT(!isMainThread());
     LockHolder locker { m_changeLogLock };
 
-    // We don't clear the pending IDs beacause if the next round of updates does not modify them, then they stay the same
-    // value without extra bookkeeping.
     m_focusedNodeID = m_pendingFocusedNodeID;
 
-    for (auto& item : m_pendingAppends)
-        m_readerThreadNodeMap.add(item->objectID(), WTFMove(item));
-    m_pendingAppends.clear();
-
-    for (auto& item : m_pendingRemovals) {
+    for (const auto& item : m_pendingRemovals) {
         if (auto object = nodeForID(item))
             object->detach(AccessibilityDetachmentType::ElementDestroyed);
         m_readerThreadNodeMap.remove(item);
     }
     m_pendingRemovals.clear();
+
+    for (auto& item : m_pendingAppends) {
+        ASSERT(item.m_wrapper);
+        item.m_isolatedObject->attachPlatformWrapper(item.m_wrapper);
+        m_readerThreadNodeMap.add(item.m_isolatedObject->objectID(), item.m_isolatedObject.copyRef());
+    }
+    m_pendingAppends.clear();
 }
-    
+
 } // namespace WebCore
 
 #endif // ENABLE(ACCESSIBILITY_ISOLATED_TREE)
index 69f778b..00187d0 100644 (file)
@@ -27,6 +27,7 @@
 
 #if ENABLE(ACCESSIBILITY_ISOLATED_TREE)
 
+#include "AccessibilityObjectInterface.h"
 #include "PageIdentifier.h"
 #include <wtf/HashMap.h>
 #include <wtf/RefPtr.h>
 
 namespace WebCore {
 
+class AXIsolatedObject;
+class AXObjectCache;
 class Page;
 
+typedef unsigned AXIsolatedTreeID;
+
 class AXIsolatedTree : public ThreadSafeRefCounted<AXIsolatedTree> {
     WTF_MAKE_NONCOPYABLE(AXIsolatedTree); WTF_MAKE_FAST_ALLOCATED;
 
@@ -56,8 +61,15 @@ public:
     RefPtr<AXIsolatedObject> nodeForID(AXID) const;
     static RefPtr<AXIsolatedObject> nodeInTreeForID(AXIsolatedTreeID, AXID);
 
+    struct NodeChange {
+        Ref<AXIsolatedObject> m_isolatedObject;
+        AccessibilityObjectWrapper* m_wrapper;
+        NodeChange(const Ref<AXIsolatedObject>&, AccessibilityObjectWrapper*);
+        NodeChange(const NodeChange&);
+    };
+
     // Call on main thread
-    void appendNodeChanges(Vector<Ref<AXIsolatedObject>>&);
+    void appendNodeChanges(const Vector<NodeChange>&);
     void removeNode(AXID);
 
     void setRootNode(Ref<AXIsolatedObject>&);
@@ -80,7 +92,7 @@ private:
     HashMap<AXID, Ref<AXIsolatedObject>> m_readerThreadNodeMap;
 
     // Written to by main thread under lock, accessed and applied by AX thread.
-    Vector<Ref<AXIsolatedObject>> m_pendingAppends;
+    Vector<NodeChange> m_pendingAppends;
     Vector<AXID> m_pendingRemovals;
     AXID m_pendingFocusedNodeID;
     Lock m_changeLogLock;
index 61ec288..56b2803 100644 (file)
 
 namespace WebCore {
 
+void AXIsolatedObject::attachPlatformWrapper(AccessibilityObjectWrapper* wrapper)
+{
+    [wrapper attachIsolatedObject:this];
+    setWrapper(wrapper);
+}
+
 void AXIsolatedObject::detachPlatformWrapper(AccessibilityDetachmentType)
 {
     [wrapper() detach];
index aa1f8f4..9d494f1 100644 (file)
@@ -238,14 +238,6 @@ void AXObjectCache::attachWrapper(AXCoreObject* obj)
     obj->setWrapper(wrapper.get());
 }
 
-#if ENABLE(ACCESSIBILITY_ISOLATED_TREE)
-void AXObjectCache::attachWrapper(AXIsolatedObject* isolatedObject, WebAccessibilityObjectWrapper* wrapper)
-{
-    [wrapper attachIsolatedObject:(AXCoreObject*)isolatedObject];
-    isolatedObject->setWrapper(wrapper);
-}
-#endif
-
 static BOOL axShouldRepostNotificationsForTests = false;
 
 void AXObjectCache::setShouldRepostNotificationsForTests(bool value)