Changeset 225613 in webkit


Ignore:
Timestamp:
Dec 6, 2017 4:55:01 PM (6 years ago)
Author:
Alan Bujtas
Message:

Remove nodes from AXObjectCache when the associated subframe document is getting destroyed.
https://bugs.webkit.org/show_bug.cgi?id=180503
<rdar://problem/35891328

Reviewed by Chris Fleizach.

While AXObjectCache lives on the mainframe's document, it caches nodes from every subframe document.
When a node is being destroyed, we deregister it from the AX cache through the Node's destructor.
Soon after the document is detached from the frame/frame is detached from the frame tree, this codepath
is no longer available (no access to the AXObjectCache object) and from this point we are unable to deregister
nodes associated with the current document.
In AXObjectCache::prepareForDocumentDestruction(), we preemptively remove all the cached nodes associated
with the about-to-be-destroyed document.

Covered by existing tests.

  • accessibility/AXObjectCache.cpp:

(WebCore::AXObjectCache::remove):
(WebCore::filterForRemoval):
(WebCore::AXObjectCache::prepareForDocumentDestruction): Collecting the nodes and removing them later is
not the most performant way but in order to have a single code path for the de-registration (AXObjectCache::remove)
I think it's worth going down the slower path -which should not really be that slower anyway since those
lists tend to stay small.
(WebCore::AXObjectCache::clearTextMarkerNodesInUse): Deleted.

  • accessibility/AXObjectCache.h:

(WebCore::AXObjectCache::removeNodeForUse):
(WebCore::AXObjectCache::remove):

  • dom/Document.cpp:

(WebCore::Document::prepareForDestruction):

  • dom/Node.cpp:

(WebCore::Node::willBeDeletedFrom):
(WebCore::Node::moveNodeToNewDocument):

Location:
trunk/Source/WebCore
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r225610 r225613  
     12017-12-06  Zalan Bujtas  <zalan@apple.com>
     2
     3        Remove nodes from AXObjectCache when the associated subframe document is getting destroyed.
     4        https://bugs.webkit.org/show_bug.cgi?id=180503
     5        <rdar://problem/35891328
     6
     7        Reviewed by Chris Fleizach.
     8
     9        While AXObjectCache lives on the mainframe's document, it caches nodes from every subframe document.
     10        When a node is being destroyed, we deregister it from the AX cache through the Node's destructor.
     11        Soon after the document is detached from the frame/frame is detached from the frame tree, this codepath
     12        is no longer available (no access to the AXObjectCache object) and from this point we are unable to deregister
     13        nodes associated with the current document.
     14        In AXObjectCache::prepareForDocumentDestruction(), we preemptively remove all the cached nodes associated
     15        with the about-to-be-destroyed document.
     16
     17        Covered by existing tests.
     18
     19        * accessibility/AXObjectCache.cpp:
     20        (WebCore::AXObjectCache::remove):
     21        (WebCore::filterForRemoval):
     22        (WebCore::AXObjectCache::prepareForDocumentDestruction): Collecting the nodes and removing them later is
     23        not the most performant way but in order to have a single code path for the de-registration (AXObjectCache::remove)
     24        I think it's worth going down the slower path -which should not really be that slower anyway since those
     25        lists tend to stay small.
     26        (WebCore::AXObjectCache::clearTextMarkerNodesInUse): Deleted.
     27        * accessibility/AXObjectCache.h:
     28        (WebCore::AXObjectCache::removeNodeForUse):
     29        (WebCore::AXObjectCache::remove):
     30        * dom/Document.cpp:
     31        (WebCore::Document::prepareForDestruction):
     32        * dom/Node.cpp:
     33        (WebCore::Node::willBeDeletedFrom):
     34        (WebCore::Node::moveNodeToNewDocument):
     35
    1362017-12-06  Brady Eidson  <beidson@apple.com>
    237
  • trunk/Source/WebCore/accessibility/AXObjectCache.cpp

    r225142 r225613  
    717717}
    718718
    719 void AXObjectCache::remove(Node* node)
    720 {
    721     if (!node)
    722         return;
    723 
    724     if (is<Element>(*node)) {
    725         m_deferredRecomputeIsIgnoredList.remove(downcast<Element>(node));
    726         m_deferredSelectedChildredChangedList.remove(downcast<Element>(node));
    727     }
    728     m_deferredTextChangedList.remove(node);
     719void AXObjectCache::remove(Node& node)
     720{
     721    if (is<Element>(node)) {
     722        m_deferredRecomputeIsIgnoredList.remove(downcast<Element>(&node));
     723        m_deferredSelectedChildredChangedList.remove(downcast<Element>(&node));
     724    }
     725    m_deferredTextChangedList.remove(&node);
    729726    removeNodeForUse(node);
    730727
    731     remove(m_nodeObjectMapping.take(node));
    732 
    733     if (m_currentModalNode == node)
     728    remove(m_nodeObjectMapping.take(&node));
     729
     730    if (m_currentModalNode == &node)
    734731        m_currentModalNode = nullptr;
    735     m_modalNodesSet.remove(node);
    736 
    737     remove(node->renderer());
     732    m_modalNodesSet.remove(&node);
     733
     734    remove(node.renderer());
    738735}
    739736
     
    27292726}
    27302727
    2731 void AXObjectCache::clearTextMarkerNodesInUse(Document* document)
    2732 {
    2733     if (!document)
    2734         return;
    2735    
    2736     // Check each node to see if it's inside the document being deleted, of if it no longer belongs to a document.
    2737     HashSet<Node*> nodesToDelete;
    2738     for (const auto& node : m_textMarkerNodes) {
    2739         if (!node->isConnected() || &(node)->document() == document)
    2740             nodesToDelete.add(node);
    2741     }
    2742    
    2743     for (const auto& node : nodesToDelete)
    2744         m_textMarkerNodes.remove(node);
     2728template<typename T>
     2729static void filterForRemoval(const ListHashSet<T>& list, const Document& document, HashSet<Node*>& nodesToRemove)
     2730{
     2731    for (auto* node : list) {
     2732        if (node->isConnected() && &node->document() != &document)
     2733            continue;
     2734        nodesToRemove.add(node);
     2735    }
     2736}
     2737
     2738void AXObjectCache::prepareForDocumentDestruction(const Document& document)
     2739{
     2740    HashSet<Node*> nodesToRemove;
     2741    filterForRemoval(m_textMarkerNodes, document, nodesToRemove);
     2742    filterForRemoval(m_modalNodesSet, document, nodesToRemove);
     2743    filterForRemoval(m_deferredRecomputeIsIgnoredList, document, nodesToRemove);
     2744    filterForRemoval(m_deferredTextChangedList, document, nodesToRemove);
     2745    filterForRemoval(m_deferredSelectedChildredChangedList, document, nodesToRemove);
     2746
     2747    for (auto* node : nodesToRemove)
     2748        remove(*node);
    27452749}
    27462750   
  • trunk/Source/WebCore/accessibility/AXObjectCache.h

    r225037 r225613  
    159159   
    160160    void remove(RenderObject*);
    161     void remove(Node*);
     161    void remove(Node&);
    162162    void remove(Widget*);
    163163    void remove(AXID);
     
    321321    void frameLoadingEventNotification(Frame*, AXLoadingEvent);
    322322
    323     void clearTextMarkerNodesInUse(Document*);
     323    void prepareForDocumentDestruction(const Document&);
    324324
    325325    void startCachingComputedObjectAttributesUntilTreeMutates();
     
    362362    // This is a weak reference cache for knowing if Nodes used by TextMarkers are valid.
    363363    void setNodeInUse(Node* n) { m_textMarkerNodes.add(n); }
    364     void removeNodeForUse(Node* n) { m_textMarkerNodes.remove(n); }
     364    void removeNodeForUse(Node& n) { m_textMarkerNodes.remove(&n); }
    365365    bool isNodeInUse(Node* n) { return m_textMarkerNodes.contains(n); }
    366366   
     
    420420    HashMap<Widget*, AXID> m_widgetObjectMapping;
    421421    HashMap<Node*, AXID> m_nodeObjectMapping;
    422     HashSet<Node*> m_textMarkerNodes;
     422    ListHashSet<Node*> m_textMarkerNodes;
    423423    std::unique_ptr<AXComputedObjectAttributeCache> m_computedObjectAttributeCache;
    424424    WEBCORE_EXPORT static bool gAccessibilityEnabled;
     
    529529inline void AXObjectCache::remove(AXID) { }
    530530inline void AXObjectCache::remove(RenderObject*) { }
    531 inline void AXObjectCache::remove(Node*) { }
     531inline void AXObjectCache::remove(Node&) { }
    532532inline void AXObjectCache::remove(Widget*) { }
    533533inline void AXObjectCache::selectedChildrenChanged(RenderObject*) { }
  • trunk/Source/WebCore/dom/Document.cpp

    r225583 r225613  
    23402340
    23412341#if HAVE(ACCESSIBILITY)
    2342     // Sub-frames need to cleanup Nodes in the text marker cache when the Document disappears.
    23432342    if (this != &topDocument()) {
    2344         if (AXObjectCache* cache = existingAXObjectCache())
    2345             cache->clearTextMarkerNodesInUse(this);
     2343        // Let the ax cache know that this subframe goes out of scope.
     2344        if (auto* cache = existingAXObjectCache())
     2345            cache->prepareForDocumentDestruction(*this);
    23462346    }
    23472347#endif
  • trunk/Source/WebCore/dom/Node.cpp

    r225524 r225613  
    327327#endif
    328328
    329     if (AXObjectCache* cache = document.existingAXObjectCache())
    330         cache->remove(this);
     329    if (auto* cache = document.existingAXObjectCache())
     330        cache->remove(*this);
    331331}
    332332
     
    20102010    if (AXObjectCache::accessibilityEnabled()) {
    20112011        if (auto* cache = oldDocument.existingAXObjectCache())
    2012             cache->remove(this);
     2012            cache->remove(*this);
    20132013    }
    20142014
Note: See TracChangeset for help on using the changeset viewer.