⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 287020 in webkit


Ignore:
Timestamp:
Dec 14, 2021, 2:43:09 AM (5 years ago)
Author:
Carlos Garcia Campos
Message:

[GTK][a11y] Register the wrappers tree when org.a11y.atspi.Cache.GetItems() is called
https://bugs.webkit.org/show_bug.cgi?id=234292

Reviewed by Joanmarie Diggs.

This ensures wrappers always have a reference before being added to the cache.

  • accessibility/AXObjectCache.h:
  • accessibility/atspi/AXObjectCacheAtspi.cpp:

(WebCore::AXObjectCache::attachWrapper): Defer the set parent call to next cache update.
(WebCore::AXObjectCache::platformPerformDeferredCacheUpdate): Do the set parent call here.

  • accessibility/atspi/AccessibilityAtspi.cpp:

(WebCore::AccessibilityAtspi::registerObject): Do not call addAccessible from here, since it needs the path,
it's now called from the caller of registerObject().
(WebCore::AccessibilityAtspi::parentChanged): Notify about parent property change.
(WebCore::AccessibilityAtspi::childrenChanged): Always emit ChildrenChanged when the tree is registered because
the atspi cache always consumes it.
(WebCore::AccessibilityAtspi::addAccessible): We no longer need to dispath addAccessible in the next run loop iteration.

  • accessibility/atspi/AccessibilityAtspi.h:
  • accessibility/atspi/AccessibilityObjectAtspi.cpp:

(WebCore::AccessibilityObjectAtspi::registerObject): Helper to register the object when path is not needed. Also
ensure the isolated tree is created before registering the object.
(WebCore::AccessibilityObjectAtspi::path): Call registerObject().
(WebCore::AccessibilityObjectAtspi::hyperlinkReference): Use registerObject() instead of path().
(WebCore::AccessibilityObjectAtspi::setParent): Call AccessibilityAtspi::parentChanged() if the wrapper is still
and attached and not ignored.
(WebCore::AccessibilityObjectAtspi::parentReference const): Use the parent member instead of asking the main
thread again.
(WebCore::AccessibilityObjectAtspi::isDefunct const): Return true if wrapper has been detached.
(WebCore::AccessibilityObjectAtspi::childAdded): We no longer need to dispatch childrenChanged in next run loop iteration.

  • accessibility/atspi/AccessibilityObjectAtspi.h:
  • accessibility/atspi/AccessibilityRootAtspi.cpp:

(WebCore::registerSubtree): Helper to register the wrappers tree recursively.
(WebCore::AccessibilityRootAtspi::registerTree): Register the wrappers tree.

  • accessibility/atspi/AccessibilityRootAtspi.h:
Location:
trunk/Source/WebCore
Files:
9 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r287019 r287020  
     12021-12-14  Carlos Garcia Campos  <cgarcia@igalia.com>
     2
     3        [GTK][a11y] Register the wrappers tree when org.a11y.atspi.Cache.GetItems() is called
     4        https://bugs.webkit.org/show_bug.cgi?id=234292
     5
     6        Reviewed by Joanmarie Diggs.
     7
     8        This ensures wrappers always have a reference before being added to the cache.
     9
     10        * accessibility/AXObjectCache.h:
     11        * accessibility/atspi/AXObjectCacheAtspi.cpp:
     12        (WebCore::AXObjectCache::attachWrapper): Defer the set parent call to next cache update.
     13        (WebCore::AXObjectCache::platformPerformDeferredCacheUpdate): Do the set parent call here.
     14        * accessibility/atspi/AccessibilityAtspi.cpp:
     15        (WebCore::AccessibilityAtspi::registerObject): Do not call addAccessible from here, since it needs the path,
     16        it's now called from the caller of registerObject().
     17        (WebCore::AccessibilityAtspi::parentChanged): Notify about parent property change.
     18        (WebCore::AccessibilityAtspi::childrenChanged): Always emit ChildrenChanged when the tree is registered because
     19        the atspi cache always consumes it.
     20        (WebCore::AccessibilityAtspi::addAccessible): We no longer need to dispath addAccessible in the next run loop iteration.
     21        * accessibility/atspi/AccessibilityAtspi.h:
     22        * accessibility/atspi/AccessibilityObjectAtspi.cpp:
     23        (WebCore::AccessibilityObjectAtspi::registerObject): Helper to register the object when path is not needed. Also
     24        ensure the isolated tree is created before registering the object.
     25        (WebCore::AccessibilityObjectAtspi::path): Call registerObject().
     26        (WebCore::AccessibilityObjectAtspi::hyperlinkReference): Use registerObject() instead of path().
     27        (WebCore::AccessibilityObjectAtspi::setParent): Call AccessibilityAtspi::parentChanged() if the wrapper is still
     28        and attached and not ignored.
     29        (WebCore::AccessibilityObjectAtspi::parentReference const): Use the parent member instead of asking the main
     30        thread again.
     31        (WebCore::AccessibilityObjectAtspi::isDefunct const): Return true if wrapper has been detached.
     32        (WebCore::AccessibilityObjectAtspi::childAdded): We no longer need to dispatch childrenChanged in next run loop iteration.
     33        * accessibility/atspi/AccessibilityObjectAtspi.h:
     34        * accessibility/atspi/AccessibilityRootAtspi.cpp:
     35        (WebCore::registerSubtree): Helper to register the wrappers tree recursively.
     36        (WebCore::AccessibilityRootAtspi::registerTree): Register the wrappers tree.
     37        * accessibility/atspi/AccessibilityRootAtspi.h:
     38
    1392021-12-14  Darin Adler  <darin@apple.com>
    240
  • trunk/Source/WebCore/accessibility/AXObjectCache.h

    r285991 r287020  
    528528    ListHashSet<RefPtr<AccessibilityObject>> m_deferredAttachedWrapperObjectList;
    529529    ListHashSet<GRefPtr<AccessibilityObjectWrapper>> m_deferredDetachedWrapperList;
     530#endif
     531#if USE(ATSPI)
     532    ListHashSet<RefPtr<AXCoreObject>> m_deferredParentChangedList;
    530533#endif
    531534};
  • trunk/Source/WebCore/accessibility/atspi/AXObjectCacheAtspi.cpp

    r286767 r287020  
    4343    axObject->setWrapper(wrapper.ptr());
    4444
    45     auto* axParent = axObject->parentObjectUnignored();
    46     if (!axParent) {
    47         if (axObject->isScrollView() && axObject->scrollView() == document().view())
    48             wrapper->setParent(nullptr); // nullptr parent means root.
    49         return;
    50     }
    51 
    52     auto* axParentWrapper = axParent->wrapper();
    53     if (!axParentWrapper)
    54         return;
    55 
    56     wrapper->setParent(axParentWrapper);
     45    m_deferredParentChangedList.add(axObject);
    5746}
    5847
    5948void AXObjectCache::platformPerformDeferredCacheUpdate()
    6049{
     50    auto handleParentChanged = [&](const AXCoreObject& axObject) {
     51        auto* wrapper = axObject.wrapper();
     52        if (!wrapper)
     53            return;
     54
     55        auto* axParent = axObject.parentObjectUnignored();
     56        if (!axParent) {
     57            if (axObject.isScrollView() && axObject.scrollView() == document().view())
     58                wrapper->setParent(nullptr); // nullptr parent means root.
     59            return;
     60        }
     61
     62        if (auto* axParentWrapper = axParent->wrapper())
     63            wrapper->setParent(axParentWrapper);
     64    };
     65
     66    for (const auto& axObject : m_deferredParentChangedList)
     67        handleParentChanged(*axObject);
     68    m_deferredParentChangedList.clear();
    6169}
    6270
  • trunk/Source/WebCore/accessibility/atspi/AccessibilityAtspi.cpp

    r287014 r287020  
    2727#include <glib/gi18n-lib.h>
    2828#include <wtf/MainThread.h>
     29#include <wtf/SetForScope.h>
    2930#include <wtf/SortedArrayMap.h>
    3031#include <wtf/UUID.h>
     
    124125    m_atspiObjects.add(&atspiObject, WTFMove(registeredObjects));
    125126
    126     addAccessible(atspiObject, path);
    127 
    128127    return path;
    129128}
     
    171170}
    172171
     172void AccessibilityAtspi::parentChanged(AccessibilityObjectAtspi& atspiObject)
     173{
     174    RELEASE_ASSERT(isMainThread());
     175    m_queue->dispatch([this, atspiObject = Ref { atspiObject }] {
     176        if (!m_connection)
     177            return;
     178
     179        // Always emit parentChanged when the tree is registered because the atspi cache always consumes it.
     180        if (!atspiObject->root().isTreeRegistered())
     181            return;
     182
     183        // We call path here to ensure it happens before parentReference() in case objects are not registered yet.
     184        auto path = atspiObject->path();
     185        g_dbus_connection_emit_signal(m_connection.get(), nullptr, path.utf8().data(), "org.a11y.atspi.Event.Object", "PropertyChange",
     186            g_variant_new("(siiva{sv})", "accessible-parent", 0, 0, atspiObject->parentReference(), nullptr), nullptr);
     187    });
     188}
     189
    173190void AccessibilityAtspi::childrenChanged(AccessibilityObjectAtspi& atspiObject, AccessibilityObjectAtspi& child, ChildrenChanged change)
    174191{
     
    176193    m_queue->dispatch([this, atspiObject = Ref { atspiObject }, child = Ref { child }, change] {
    177194        if (!m_connection)
     195            return;
     196
     197        // Always emit ChildrenChanged when the tree is registered because the atspi cache always consumes it.
     198        if (!atspiObject->root().isTreeRegistered())
    178199            return;
    179200
     
    416437        if (!g_strcmp0(methodName, "GetItems")) {
    417438            auto& atspi = *static_cast<AccessibilityAtspi*>(userData);
     439            SetForScope<bool> inGetItems(atspi.m_inGetItems, true);
    418440            GVariantBuilder builder = G_VARIANT_BUILDER_INIT(G_VARIANT_TYPE("(" GET_ITEMS_SIGNATURE ")"));
    419441            g_variant_builder_open(&builder, G_VARIANT_TYPE(GET_ITEMS_SIGNATURE));
    420             for (const auto* rootObject : atspi.m_rootObjects.keys()) {
     442            for (auto* rootObject : atspi.m_rootObjects.keys()) {
    421443                g_variant_builder_open(&builder, G_VARIANT_TYPE("(" ITEM_SIGNATURE ")"));
     444                rootObject->registerTree();
    422445                rootObject->serialize(&builder);
    423446                g_variant_builder_close(&builder);
     
    430453                auto wrapper = atspi.m_cache.get(path);
    431454                wrapper->updateBackingStore();
    432                 if (!atspi.m_cache.contains(path))
     455                if (!atspi.m_cache.contains(path) || wrapper->isDefunct())
    433456                    continue;
    434457                g_variant_builder_open(&builder, G_VARIANT_TYPE("(" ITEM_SIGNATURE ")"));
     
    460483}
    461484
    462 void AccessibilityAtspi::addAccessible(AccessibilityObjectAtspi& atspiObject, const String& path)
     485void AccessibilityAtspi::addAccessible(AccessibilityObjectAtspi& atspiObject)
    463486{
    464487    RELEASE_ASSERT(!isMainThread());
     
    466489        return;
    467490
    468     auto addResult = m_cache.add(path, &atspiObject);
     491    auto addResult = m_cache.add(atspiObject.path(), &atspiObject);
    469492    if (!addResult.isNewEntry)
    470493        return;
    471494
    472     // AddAccessible needs to be emitted after ChildrenChanged.
    473     RunLoop::current().dispatch([this, atspiObject = Ref { atspiObject }, path = path.isolatedCopy()] {
    474         atspiObject->updateBackingStore();
    475         if (!m_cache.contains(path))
    476             return;
    477 
    478         GVariantBuilder builder = G_VARIANT_BUILDER_INIT(G_VARIANT_TYPE("(" ITEM_SIGNATURE ")"));
    479         atspiObject->serialize(&builder);
    480         g_dbus_connection_emit_signal(m_connection.get(), nullptr, "/org/a11y/atspi/cache", "org.a11y.atspi.Cache", "AddAccessible",
    481             g_variant_new("(@(" ITEM_SIGNATURE "))", g_variant_builder_end(&builder)), nullptr);
    482     });
     495    if (m_inGetItems)
     496        return;
     497
     498    GVariantBuilder builder = G_VARIANT_BUILDER_INIT(G_VARIANT_TYPE("(" ITEM_SIGNATURE ")"));
     499    atspiObject.serialize(&builder);
     500    g_dbus_connection_emit_signal(m_connection.get(), nullptr, "/org/a11y/atspi/cache", "org.a11y.atspi.Cache", "AddAccessible",
     501        g_variant_new("(@(" ITEM_SIGNATURE "))", g_variant_builder_end(&builder)), nullptr);
    483502}
    484503
  • trunk/Source/WebCore/accessibility/atspi/AccessibilityAtspi.h

    r286173 r287020  
    5555    String registerHyperlink(AccessibilityObjectAtspi&, Vector<std::pair<GDBusInterfaceInfo*, GDBusInterfaceVTable*>>&&);
    5656
     57    void parentChanged(AccessibilityObjectAtspi&);
    5758    enum class ChildrenChanged { Added, Removed };
    5859    void childrenChanged(AccessibilityObjectAtspi&, AccessibilityObjectAtspi&, ChildrenChanged);
     
    7172    static const char* localizedRoleName(AccessibilityRole);
    7273
     74    void addAccessible(AccessibilityObjectAtspi&);
     75
    7376private:
    7477    void ensureCache();
    75     void addAccessible(AccessibilityObjectAtspi&, const String&);
    7678    void removeAccessible(AccessibilityObjectAtspi&);
    7779
     
    8587    unsigned m_cacheID { 0 };
    8688    HashMap<String, AccessibilityObjectAtspi*> m_cache;
     89    bool m_inGetItems { false };
    8790};
    8891
  • trunk/Source/WebCore/accessibility/atspi/AccessibilityObjectAtspi.cpp

    r286767 r287020  
    482482};
    483483
     484bool AccessibilityObjectAtspi::registerObject()
     485{
     486    RELEASE_ASSERT(!isMainThread());
     487    if (!m_path.isNull())
     488        return false;
     489
     490    m_isRegistered.store(true);
     491    Vector<std::pair<GDBusInterfaceInfo*, GDBusInterfaceVTable*>> interfaces;
     492    if (m_interfaces.contains(Interface::Accessible))
     493        interfaces.append({ const_cast<GDBusInterfaceInfo*>(&webkit_accessible_interface), &s_accessibleFunctions });
     494    if (m_interfaces.contains(Interface::Component))
     495        interfaces.append({ const_cast<GDBusInterfaceInfo*>(&webkit_component_interface), &s_componentFunctions });
     496    if (m_interfaces.contains(Interface::Text))
     497        interfaces.append({ const_cast<GDBusInterfaceInfo*>(&webkit_text_interface), &s_textFunctions });
     498    if (m_interfaces.contains(Interface::Value))
     499        interfaces.append({ const_cast<GDBusInterfaceInfo*>(&webkit_value_interface), &s_valueFunctions });
     500    if (m_interfaces.contains(Interface::Hyperlink))
     501        interfaces.append({ const_cast<GDBusInterfaceInfo*>(&webkit_hyperlink_interface), &s_hyperlinkFunctions });
     502    if (m_interfaces.contains(Interface::Hypertext))
     503        interfaces.append({ const_cast<GDBusInterfaceInfo*>(&webkit_hypertext_interface), &s_hypertextFunctions });
     504    if (m_interfaces.contains(Interface::Action))
     505        interfaces.append({ const_cast<GDBusInterfaceInfo*>(&webkit_action_interface), &s_actionFunctions });
     506    if (m_interfaces.contains(Interface::Document))
     507        interfaces.append({ const_cast<GDBusInterfaceInfo*>(&webkit_document_interface), &s_documentFunctions });
     508    if (m_interfaces.contains(Interface::Image))
     509        interfaces.append({ const_cast<GDBusInterfaceInfo*>(&webkit_image_interface), &s_imageFunctions });
     510    if (m_interfaces.contains(Interface::Selection))
     511        interfaces.append({ const_cast<GDBusInterfaceInfo*>(&webkit_selection_interface), &s_selectionFunctions });
     512    if (m_interfaces.contains(Interface::Table))
     513        interfaces.append({ const_cast<GDBusInterfaceInfo*>(&webkit_table_interface), &s_tableFunctions });
     514    if (m_interfaces.contains(Interface::TableCell))
     515        interfaces.append({ const_cast<GDBusInterfaceInfo*>(&webkit_table_cell_interface), &s_tableCellFunctions });
     516    if (!m_axObject) {
     517        // Isolated tree hasn't been created yet, call AccessibilityRootAtspi::child()
     518        // to create it before registering the object.
     519        Accessibility::performFunctionOnMainThread([this] {
     520            m_root.child();
     521        });
     522    }
     523    m_path = m_root.atspi().registerObject(*this, WTFMove(interfaces));
     524    m_root.atspi().addAccessible(*this);
     525
     526    return true;
     527}
     528
    484529const String& AccessibilityObjectAtspi::path()
    485530{
    486531    RELEASE_ASSERT(!isMainThread());
    487     if (m_path.isNull()) {
    488         m_isRegistered.store(true);
    489 
    490         Vector<std::pair<GDBusInterfaceInfo*, GDBusInterfaceVTable*>> interfaces;
    491         if (m_interfaces.contains(Interface::Accessible))
    492             interfaces.append({ const_cast<GDBusInterfaceInfo*>(&webkit_accessible_interface), &s_accessibleFunctions });
    493         if (m_interfaces.contains(Interface::Component))
    494             interfaces.append({ const_cast<GDBusInterfaceInfo*>(&webkit_component_interface), &s_componentFunctions });
    495         if (m_interfaces.contains(Interface::Text))
    496             interfaces.append({ const_cast<GDBusInterfaceInfo*>(&webkit_text_interface), &s_textFunctions });
    497         if (m_interfaces.contains(Interface::Value))
    498             interfaces.append({ const_cast<GDBusInterfaceInfo*>(&webkit_value_interface), &s_valueFunctions });
    499         if (m_interfaces.contains(Interface::Hyperlink))
    500             interfaces.append({ const_cast<GDBusInterfaceInfo*>(&webkit_hyperlink_interface), &s_hyperlinkFunctions });
    501         if (m_interfaces.contains(Interface::Hypertext))
    502             interfaces.append({ const_cast<GDBusInterfaceInfo*>(&webkit_hypertext_interface), &s_hypertextFunctions });
    503         if (m_interfaces.contains(Interface::Action))
    504             interfaces.append({ const_cast<GDBusInterfaceInfo*>(&webkit_action_interface), &s_actionFunctions });
    505         if (m_interfaces.contains(Interface::Document))
    506             interfaces.append({ const_cast<GDBusInterfaceInfo*>(&webkit_document_interface), &s_documentFunctions });
    507         if (m_interfaces.contains(Interface::Image))
    508             interfaces.append({ const_cast<GDBusInterfaceInfo*>(&webkit_image_interface), &s_imageFunctions });
    509         if (m_interfaces.contains(Interface::Selection))
    510             interfaces.append({ const_cast<GDBusInterfaceInfo*>(&webkit_selection_interface), &s_selectionFunctions });
    511         if (m_interfaces.contains(Interface::Table))
    512             interfaces.append({ const_cast<GDBusInterfaceInfo*>(&webkit_table_interface), &s_tableFunctions });
    513         if (m_interfaces.contains(Interface::TableCell))
    514             interfaces.append({ const_cast<GDBusInterfaceInfo*>(&webkit_table_cell_interface), &s_tableCellFunctions });
    515         m_path = m_root.atspi().registerObject(*this, WTFMove(interfaces));
    516     }
    517 
     532    registerObject();
    518533    return m_path;
    519534}
     
    529544    RELEASE_ASSERT(!isMainThread());
    530545    if (m_hyperlinkPath.isNull()) {
    531         path();
     546        registerObject();
    532547        m_hyperlinkPath = m_root.atspi().registerHyperlink(*this, { { const_cast<GDBusInterfaceInfo*>(&webkit_hyperlink_interface), &s_hyperlinkFunctions } });
    533548    }
     
    543558
    544559    m_parent = atspiParent;
     560    if (!m_coreObject || m_coreObject->accessibilityIsIgnored())
     561        return;
     562
     563    m_root.atspi().parentChanged(*this);
    545564    if (m_parent && *m_parent)
    546565        m_parent.value()->childAdded(*this);
     
    565584GVariant* AccessibilityObjectAtspi::parentReference() const
    566585{
    567     auto parentAtspi = parent();
    568     if (!parentAtspi)
     586    if (!m_parent)
    569587        return m_root.atspi().nullReference();
    570588
    571     if (!parentAtspi.value())
     589    if (!m_parent.value())
    572590        return m_root.reference();
    573591
    574     return parentAtspi.value()->reference();
     592    return m_parent.value()->reference();
    575593}
    576594
     
    847865
    848866        return states;
     867    });
     868}
     869
     870bool AccessibilityObjectAtspi::isDefunct() const
     871{
     872    return Accessibility::retrieveValueFromMainThread<bool>([this]() -> bool {
     873        return !m_coreObject;
    849874    });
    850875}
     
    11891214        return;
    11901215
    1191     RunLoop::main().dispatch([this, protectedThis = Ref { *this }, child = Ref { child }] {
    1192         if (!m_coreObject)
    1193             return;
    1194         m_root.atspi().childrenChanged(*this, child, AccessibilityAtspi::ChildrenChanged::Added);
    1195     });
     1216    m_root.atspi().childrenChanged(*this, child, AccessibilityAtspi::ChildrenChanged::Added);
    11961217}
    11971218
  • trunk/Source/WebCore/accessibility/atspi/AccessibilityObjectAtspi.h

    r286767 r287020  
    4242    ~AccessibilityObjectAtspi() = default;
    4343
     44    bool registerObject();
     45
    4446    enum class Interface : uint16_t {
    4547        Accessible = 1 << 0,
     
    6163    void setParent(std::optional<AccessibilityObjectAtspi*>);
    6264    WEBCORE_EXPORT std::optional<AccessibilityObjectAtspi*> parent() const;
     65    GVariant* parentReference() const;
    6366    WEBCORE_EXPORT void updateBackingStore();
    6467
     
    8487    WEBCORE_EXPORT AccessibilityObjectAtspi* childAt(unsigned) const;
    8588    WEBCORE_EXPORT uint64_t state() const;
     89    bool isDefunct() const;
    8690    void stateChanged(const char*, bool);
    8791    WEBCORE_EXPORT HashMap<String, String> attributes() const;
     
    158162    Vector<RefPtr<AccessibilityObjectAtspi>> wrapperVector(const Vector<RefPtr<AXCoreObject>>&) const;
    159163    int indexInParent() const;
    160     GVariant* parentReference() const;
    161164    void childAdded(AccessibilityObjectAtspi&);
    162165    void childRemoved(AccessibilityObjectAtspi&);
  • trunk/Source/WebCore/accessibility/atspi/AccessibilityRootAtspi.cpp

    r287014 r287020  
    186186    if (m_page)
    187187        m_page->setAccessibilityRootObject(nullptr);
     188}
     189
     190static void registerSubtree(AccessibilityObjectAtspi* atspiObject)
     191{
     192    if (!atspiObject)
     193        return;
     194
     195    if (!atspiObject->registerObject())
     196        return;
     197
     198    atspiObject->updateBackingStore();
     199    for (auto& child : atspiObject->children())
     200        registerSubtree(child.get());
     201}
     202
     203void AccessibilityRootAtspi::registerTree()
     204{
     205    RELEASE_ASSERT(!isMainThread());
     206    if (m_parentUniqueName.isNull())
     207        return;
     208
     209    registerSubtree(Accessibility::retrieveValueFromMainThread<AccessibilityObjectAtspi*>([this]() -> AccessibilityObjectAtspi* {
     210        return child();
     211    }));
     212    m_isTreeRegistered.store(true);
    188213}
    189214
  • trunk/Source/WebCore/accessibility/atspi/AccessibilityRootAtspi.h

    r287014 r287020  
    2323#include "AccessibilityAtspi.h"
    2424#include "IntRect.h"
     25#include <wtf/Atomics.h>
    2526#include <wtf/FastMalloc.h>
    2627#include <wtf/ThreadSafeRefCounted.h>
     
    4142    void registerObject(CompletionHandler<void(const String&)>&&);
    4243    void unregisterObject();
     44    void registerTree();
     45    bool isTreeRegistered() const { return m_isTreeRegistered.load(); }
    4346    void setPath(String&&);
    4447
     
    6770    String m_parentUniqueName;
    6871    String m_parentPath;
     72    Atomic<bool> m_isTreeRegistered { false };
    6973};
    7074
Note: See TracChangeset for help on using the changeset viewer.