AX: memoize expensive computation during blocks where tree doesn't change
authordmazzoni@google.com <dmazzoni@google.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 1 Feb 2013 23:50:41 +0000 (23:50 +0000)
committerdmazzoni@google.com <dmazzoni@google.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 1 Feb 2013 23:50:41 +0000 (23:50 +0000)
https://bugs.webkit.org/show_bug.cgi?id=106497

Reviewed by Ryosuke Niwa.

Source/WebCore:

Add a cache for computed attributes of AXObjects.
The cache can be enabled at the start of a batch of
read-only operations on the accessibility tree, and
it's cleared automatically when the tree mutates.
Currently accessibilityIsIgnored is cached, since it's
frequently called and relatively expensive to compute.

No new tests.

* accessibility/AXObjectCache.cpp:
(WebCore):
(WebCore::AXComputedObjectAttributeCache::getIgnored):
(WebCore::AXComputedObjectAttributeCache::setIgnored):
(WebCore::AXObjectCache::postNotification):
(WebCore::AXObjectCache::nodeTextChangeNotification):
(WebCore::AXObjectCache::handleScrollbarUpdate):
(WebCore::AXObjectCache::startCachingComputedObjectAttributesUntilTreeMutates):
(WebCore::AXObjectCache::stopCachingComputedObjectAttributes):
* accessibility/AXObjectCache.h:
(AXComputedObjectAttributeCache):
(WebCore::AXComputedObjectAttributeCache::create):
(WebCore::AXComputedObjectAttributeCache::AXComputedObjectAttributeCache):
(WebCore::AXComputedObjectAttributeCache::CachedAXObjectAttributes::CachedAXObjectAttributes):
(CachedAXObjectAttributes):
(WebCore):
(WebCore::AXObjectCache::computedObjectAttributeCache):
(AXObjectCache):
(WebCore::AXComputedObjectAttributeCache::getIgnored):
(WebCore::AXComputedObjectAttributeCache::setIgnored):
(WebCore::startCachingComputedObjectAttributesUntilTreeMutates):
(WebCore::stopCachingComputedObjectAttributes):
* accessibility/AccessibilityObject.cpp:
(WebCore::AccessibilityObject::findMatchingObjects):
* accessibility/AccessibilityRenderObject.cpp:
(WebCore):
(WebCore::AccessibilityRenderObject::accessibilityIsIgnored):
(WebCore::AccessibilityRenderObject::computeAccessibilityIsIgnored):
* accessibility/AccessibilityRenderObject.h:
(AccessibilityRenderObject):

Source/WebKit/chromium:

Expose two methods to enable and disable caching of
computed WebAccessibilityObject attributes, to speed up
batch read-only operations.

* public/WebAccessibilityObject.h:
(WebAccessibilityObject):
* src/WebAccessibilityObject.cpp:
(WebKit::WebAccessibilityObject::startCachingComputedObjectAttributesUntilTreeMutates):
(WebKit):
(WebKit::WebAccessibilityObject::stopCachingComputedObjectAttributes):

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

Source/WebCore/ChangeLog
Source/WebCore/accessibility/AXObjectCache.cpp
Source/WebCore/accessibility/AXObjectCache.h
Source/WebCore/accessibility/AccessibilityObject.cpp
Source/WebCore/accessibility/AccessibilityRenderObject.cpp
Source/WebCore/accessibility/AccessibilityRenderObject.h
Source/WebKit/chromium/ChangeLog
Source/WebKit/chromium/public/WebAccessibilityObject.h
Source/WebKit/chromium/src/WebAccessibilityObject.cpp

index e5c5a07..31b49a2 100644 (file)
@@ -1,3 +1,50 @@
+2013-02-01  Dominic Mazzoni  <dmazzoni@google.com>
+
+        AX: memoize expensive computation during blocks where tree doesn't change
+        https://bugs.webkit.org/show_bug.cgi?id=106497
+
+        Reviewed by Ryosuke Niwa.
+
+        Add a cache for computed attributes of AXObjects.
+        The cache can be enabled at the start of a batch of
+        read-only operations on the accessibility tree, and
+        it's cleared automatically when the tree mutates.
+        Currently accessibilityIsIgnored is cached, since it's
+        frequently called and relatively expensive to compute.
+
+        No new tests.
+
+        * accessibility/AXObjectCache.cpp:
+        (WebCore):
+        (WebCore::AXComputedObjectAttributeCache::getIgnored):
+        (WebCore::AXComputedObjectAttributeCache::setIgnored):
+        (WebCore::AXObjectCache::postNotification):
+        (WebCore::AXObjectCache::nodeTextChangeNotification):
+        (WebCore::AXObjectCache::handleScrollbarUpdate):
+        (WebCore::AXObjectCache::startCachingComputedObjectAttributesUntilTreeMutates):
+        (WebCore::AXObjectCache::stopCachingComputedObjectAttributes):
+        * accessibility/AXObjectCache.h:
+        (AXComputedObjectAttributeCache):
+        (WebCore::AXComputedObjectAttributeCache::create):
+        (WebCore::AXComputedObjectAttributeCache::AXComputedObjectAttributeCache):
+        (WebCore::AXComputedObjectAttributeCache::CachedAXObjectAttributes::CachedAXObjectAttributes):
+        (CachedAXObjectAttributes):
+        (WebCore):
+        (WebCore::AXObjectCache::computedObjectAttributeCache):
+        (AXObjectCache):
+        (WebCore::AXComputedObjectAttributeCache::getIgnored):
+        (WebCore::AXComputedObjectAttributeCache::setIgnored):
+        (WebCore::startCachingComputedObjectAttributesUntilTreeMutates):
+        (WebCore::stopCachingComputedObjectAttributes):
+        * accessibility/AccessibilityObject.cpp:
+        (WebCore::AccessibilityObject::findMatchingObjects):
+        * accessibility/AccessibilityRenderObject.cpp:
+        (WebCore):
+        (WebCore::AccessibilityRenderObject::accessibilityIsIgnored):
+        (WebCore::AccessibilityRenderObject::computeAccessibilityIsIgnored):
+        * accessibility/AccessibilityRenderObject.h:
+        (AccessibilityRenderObject):
+
 2013-02-01  Alexey Proskuryakov  <ap@apple.com>
 
         Build fix.
index 8eff326..e71e9d5 100644 (file)
 namespace WebCore {
 
 using namespace HTMLNames;
+
+AccessibilityObjectInclusion AXComputedObjectAttributeCache::getIgnored(AXID id) const
+{
+    HashMap<AXID, CachedAXObjectAttributes>::const_iterator it = m_idMapping.find(id);
+    return it != m_idMapping.end() ? it->value.ignored : DefaultBehavior;
+}
+
+void AXComputedObjectAttributeCache::setIgnored(AXID id, AccessibilityObjectInclusion inclusion)
+{
+    HashMap<AXID, CachedAXObjectAttributes>::iterator it = m_idMapping.find(id);
+    if (it != m_idMapping.end())
+        it->value.ignored = inclusion;
+    else {
+        CachedAXObjectAttributes attributes;
+        attributes.ignored = inclusion;
+        m_idMapping.set(id, attributes);
+    }
+}
     
 bool AXObjectCache::gAccessibilityEnabled = false;
 bool AXObjectCache::gAccessibilityEnhancedUserInterfaceEnabled = false;
@@ -634,6 +652,8 @@ void AXObjectCache::postNotification(RenderObject* renderer, AXNotification noti
     if (!renderer)
         return;
     
+    stopCachingComputedObjectAttributes();
+
     // Get an accessibility object that already exists. One should not be created here
     // because a render update may be in progress and creating an AX object can re-trigger a layout
     RefPtr<AccessibilityObject> object = get(renderer);
@@ -653,6 +673,8 @@ void AXObjectCache::postNotification(Node* node, AXNotification notification, bo
     if (!node)
         return;
     
+    stopCachingComputedObjectAttributes();
+
     // Get an accessibility object that already exists. One should not be created here
     // because a render update may be in progress and creating an AX object can re-trigger a layout
     RefPtr<AccessibilityObject> object = get(node);
@@ -669,6 +691,8 @@ void AXObjectCache::postNotification(Node* node, AXNotification notification, bo
 
 void AXObjectCache::postNotification(AccessibilityObject* object, Document* document, AXNotification notification, bool postToElement, PostType postType)
 {
+    stopCachingComputedObjectAttributes();
+
     if (object && !postToElement)
         object = object->observableObject();
 
@@ -710,6 +734,8 @@ void AXObjectCache::nodeTextChangeNotification(Node* node, AXTextChange textChan
     if (!node)
         return;
 
+    stopCachingComputedObjectAttributes();
+
     // Delegate on the right platform
     AccessibilityObject* obj = getOrCreate(node);
     nodeTextChangePlatformNotification(obj, textChange, offset, text);
@@ -735,8 +761,10 @@ void AXObjectCache::handleScrollbarUpdate(ScrollView* view)
         return;
     
     // We don't want to create a scroll view from this method, only update an existing one.
-    if (AccessibilityObject* scrollViewObject = get(view))
+    if (AccessibilityObject* scrollViewObject = get(view)) {
+        stopCachingComputedObjectAttributes();
         scrollViewObject->updateChildrenIfNecessary();
+    }
 }
     
 void AXObjectCache::handleAriaExpandedChange(Node* node)
@@ -753,6 +781,8 @@ void AXObjectCache::handleActiveDescendantChanged(Node* node)
 
 void AXObjectCache::handleAriaRoleChanged(Node* node)
 {
+    stopCachingComputedObjectAttributes();
+
     if (AccessibilityObject* obj = getOrCreate(node)) {
         obj->updateAccessibilityRole();
         obj->notifyIfIgnoredValueChanged();
@@ -804,6 +834,17 @@ void AXObjectCache::recomputeIsIgnored(RenderObject* renderer)
         obj->notifyIfIgnoredValueChanged();
 }
 
+void AXObjectCache::startCachingComputedObjectAttributesUntilTreeMutates()
+{
+    if (!m_computedObjectAttributeCache)
+        m_computedObjectAttributeCache = AXComputedObjectAttributeCache::create();
+}
+
+void AXObjectCache::stopCachingComputedObjectAttributes()
+{
+    m_computedObjectAttributeCache.clear();
+}
+
 VisiblePosition AXObjectCache::visiblePositionForTextMarkerData(TextMarkerData& textMarkerData)
 {
     if (!isNodeInUse(textMarkerData.node))
index 4edf815..e5df051 100644 (file)
@@ -52,6 +52,25 @@ struct TextMarkerData {
     EAffinity affinity;
 };
 
+class AXComputedObjectAttributeCache {
+public:
+    static PassOwnPtr<AXComputedObjectAttributeCache> create() { return adoptPtr(new AXComputedObjectAttributeCache()); }
+
+    AccessibilityObjectInclusion getIgnored(AXID) const;
+    void setIgnored(AXID, AccessibilityObjectInclusion);
+
+private:
+    AXComputedObjectAttributeCache() { }
+
+    struct CachedAXObjectAttributes {
+        CachedAXObjectAttributes() : ignored(DefaultBehavior) { }
+
+        AccessibilityObjectInclusion ignored;
+    };
+
+    HashMap<AXID, CachedAXObjectAttributes> m_idMapping;
+};
+
 enum PostType { PostSynchronously, PostAsynchronously };
 
 class AXObjectCache {
@@ -182,6 +201,11 @@ public:
 
     bool nodeHasRole(Node*, const AtomicString& role);
 
+    void startCachingComputedObjectAttributesUntilTreeMutates();
+    void stopCachingComputedObjectAttributes();
+
+    AXComputedObjectAttributeCache* computedObjectAttributeCache() { return m_computedObjectAttributeCache.get(); }
+
 protected:
     void postPlatformNotification(AccessibilityObject*, AXNotification);
     void nodeTextChangePlatformNotification(AccessibilityObject*, AXTextChange, unsigned offset, const String&);
@@ -201,6 +225,7 @@ private:
     HashMap<Widget*, AXID> m_widgetObjectMapping;
     HashMap<Node*, AXID> m_nodeObjectMapping;
     HashSet<Node*> m_textMarkerNodes;
+    OwnPtr<AXComputedObjectAttributeCache> m_computedObjectAttributeCache;
     static bool gAccessibilityEnabled;
     static bool gAccessibilityEnhancedUserInterfaceEnabled;
     
@@ -220,6 +245,8 @@ bool nodeHasRole(Node*, const String& role);
 bool isNodeAriaVisible(Node*);
     
 #if !HAVE(ACCESSIBILITY)
+inline AccessibilityObjectInclusion AXComputedObjectAttributeCache::getIgnored(AXID) const { return DefaultBehavior; }
+inline void AXComputedObjectAttributeCache::setIgnored(AXID, AccessibilityObjectInclusion) { }
 inline AXObjectCache::AXObjectCache(const Document* doc) : m_document(const_cast<Document*>(doc)), m_notificationPostTimer(this, 0) { }
 inline AXObjectCache::~AXObjectCache() { }
 inline AccessibilityObject* AXObjectCache::focusedUIElementForPage(const Page*) { return 0; }
@@ -234,6 +261,8 @@ inline AccessibilityObject* AXObjectCache::rootObject() { return 0; }
 inline AccessibilityObject* AXObjectCache::rootObjectForFrame(Frame*) { return 0; }
 inline Element* AXObjectCache::rootAXEditableElement(Node*) { return 0; }
 inline bool nodeHasRole(Node*, const String&) { return false; }
+inline void startCachingComputedObjectAttributesUntilTreeMutates() { }
+inline void stopCachingComputedObjectAttributes() { }
 inline bool isNodeAriaVisible(Node*) { return true; }
 inline const Element* AXObjectCache::rootAXEditableElement(const Node*) { return 0; }
 inline void AXObjectCache::attachWrapper(AccessibilityObject*) { }
index 8cdab47..f916e87 100644 (file)
@@ -460,6 +460,8 @@ void AccessibilityObject::findMatchingObjects(AccessibilitySearchCriteria* crite
     if (!criteria)
         return;
 
+    axObjectCache()->startCachingComputedObjectAttributesUntilTreeMutates();
+
     // This search mechanism only searches the elements before/after the starting object.
     // It does this by stepping up the parent chain and at each level doing a DFS.
     
index 8b364e0..4842753 100644 (file)
@@ -1121,13 +1121,36 @@ AccessibilityObjectInclusion AccessibilityRenderObject::accessibilityIsIgnoredBa
         
     return DefaultBehavior;
 }  
+
 bool AccessibilityRenderObject::accessibilityIsIgnored() const
 {
 #ifndef NDEBUG
     ASSERT(m_initialized);
 #endif
 
+    AXComputedObjectAttributeCache* attributeCache = axObjectCache()->computedObjectAttributeCache();
+    if (attributeCache) {
+        AccessibilityObjectInclusion ignored = attributeCache->getIgnored(axObjectID());
+        switch (ignored) {
+        case IgnoreObject:
+            return true;
+        case IncludeObject:
+            return false;
+        case DefaultBehavior:
+            break;
+        }
+    }
+
+    bool result = computeAccessibilityIsIgnored();
+
+    if (attributeCache)
+        attributeCache->setIgnored(axObjectID(), result ? IgnoreObject : IncludeObject);
+
+    return result;
+}
+
+bool AccessibilityRenderObject::computeAccessibilityIsIgnored() const
+{
     // Check first if any of the common reasons cause this element to be ignored.
     // Then process other use cases that need to be applied to all the various roles
     // that AccessibilityRenderObjects take on.
index 9baea67..0a5bdd2 100644 (file)
@@ -278,6 +278,8 @@ private:
     virtual bool ariaLiveRegionBusy() const;    
     
     bool inheritsPresentationalRole() const;
+
+    bool computeAccessibilityIsIgnored() const;
     
 #if ENABLE(MATHML)
     // All math elements return true for isMathElement().
index 62ca822..4f028bf 100644 (file)
@@ -1,3 +1,21 @@
+2013-02-01  Dominic Mazzoni  <dmazzoni@google.com>
+
+        AX: memoize expensive computation during blocks where tree doesn't change
+        https://bugs.webkit.org/show_bug.cgi?id=106497
+
+        Reviewed by Ryosuke Niwa.
+
+        Expose two methods to enable and disable caching of
+        computed WebAccessibilityObject attributes, to speed up
+        batch read-only operations.
+
+        * public/WebAccessibilityObject.h:
+        (WebAccessibilityObject):
+        * src/WebAccessibilityObject.cpp:
+        (WebKit::WebAccessibilityObject::startCachingComputedObjectAttributesUntilTreeMutates):
+        (WebKit):
+        (WebKit::WebAccessibilityObject::stopCachingComputedObjectAttributes):
+
 2013-02-01  Fady Samuel  <fsamuel@chromium.org>
 
         [Chromium] Expose WebNode::shadowHost()
index 1e662ae..06ab954 100644 (file)
@@ -76,6 +76,9 @@ public:
     WEBKIT_EXPORT static void enableAccessibility();
     WEBKIT_EXPORT static bool accessibilityEnabled();
 
+    WEBKIT_EXPORT void startCachingComputedObjectAttributesUntilTreeMutates();
+    WEBKIT_EXPORT void stopCachingComputedObjectAttributes();
+
     WEBKIT_EXPORT int axID() const;
 
     // Update the underlying tree, and return true if this object is
index 825b58b..abd4d56 100644 (file)
@@ -82,6 +82,16 @@ bool WebAccessibilityObject::accessibilityEnabled()
     return AXObjectCache::accessibilityEnabled();
 }
 
+void WebAccessibilityObject::startCachingComputedObjectAttributesUntilTreeMutates()
+{
+    m_private->axObjectCache()->startCachingComputedObjectAttributesUntilTreeMutates();
+}
+
+void WebAccessibilityObject::stopCachingComputedObjectAttributes()
+{
+    m_private->axObjectCache()->stopCachingComputedObjectAttributes();
+}
+
 bool WebAccessibilityObject::isDetached() const
 {
     if (m_private.isNull())