Title: [131871] trunk
Revision
131871
Author
[email protected]
Date
2012-10-19 01:01:06 -0700 (Fri, 19 Oct 2012)

Log Message

AX: labelForElement is slow when there are a lot of DOM elements
https://bugs.webkit.org/show_bug.cgi?id=97825

Reviewed by Ryosuke Niwa.

Source/WebCore:

Adds a DocumentOrderedMap to TreeScope that allows accessibility to
quickly map from an id to the label for that id. This speeds up
AccessibilityNode::labelForElement, which was a bottleneck in Chromium
when accessibility was on.

Tests: accessibility/title-ui-element-correctness.html
       perf/accessibility-title-ui-element.html

* accessibility/AccessibilityNodeObject.cpp:
(WebCore::AccessibilityNodeObject::labelForElement):
* dom/DocumentOrderedMap.cpp:
(WebCore::keyMatchesLabelForAttribute):
(WebCore):
(WebCore::DocumentOrderedMap::get):
(WebCore::DocumentOrderedMap::getElementByLabelForAttribute):
* dom/DocumentOrderedMap.h:
(DocumentOrderedMap):
* dom/Element.cpp:
(WebCore::Element::insertedInto):
(WebCore::Element::removedFrom):
(WebCore::Element::updateLabel):
(WebCore):
(WebCore::Element::willModifyAttribute):
* dom/Element.h:
(Element):
* dom/TreeScope.cpp:
(WebCore::TreeScope::TreeScope):
(WebCore::TreeScope::destroyTreeScopeData):
(WebCore::TreeScope::addLabel):
(WebCore):
(WebCore::TreeScope::removeLabel):
(WebCore::TreeScope::labelElementForId):
* dom/TreeScope.h:
(WebCore):
(TreeScope):
(WebCore::TreeScope::shouldCacheLabelsByForAttribute):

Tools:

Implement titleUIElement in the chromium port of DRT, and
fix getAccessibleElementById so that it ensures the backing store
is up-to-date.

* DumpRenderTree/chromium/TestRunner/AccessibilityControllerChromium.cpp:
(AccessibilityController::getAccessibleElementById):
* DumpRenderTree/chromium/TestRunner/AccessibilityUIElementChromium.cpp:
(AccessibilityUIElement::titleUIElementCallback):

LayoutTests:

Adds two new tests for titleUIElement that run on both Mac and
Chromium. One tests correctness, the other tests speed.

Fixes one test so that it passes on Chromium.
Enables other tests that now pass on Chromium.

* accessibility/secure-textfield-title-ui.html:
* accessibility/title-ui-element-correctness-expected.txt: Added.
* accessibility/title-ui-element-correctness.html: Added.
* perf/accessibility-title-ui-element-expected.txt: Added.
* perf/accessibility-title-ui-element.html: Added.
* platform/chromium/TestExpectations:

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (131870 => 131871)


--- trunk/LayoutTests/ChangeLog	2012-10-19 07:51:24 UTC (rev 131870)
+++ trunk/LayoutTests/ChangeLog	2012-10-19 08:01:06 UTC (rev 131871)
@@ -1,3 +1,23 @@
+2012-10-18  Dominic Mazzoni  <[email protected]>
+
+        AX: labelForElement is slow when there are a lot of DOM elements
+        https://bugs.webkit.org/show_bug.cgi?id=97825
+
+        Reviewed by Ryosuke Niwa.
+
+        Adds two new tests for titleUIElement that run on both Mac and
+        Chromium. One tests correctness, the other tests speed.
+
+        Fixes one test so that it passes on Chromium.
+        Enables other tests that now pass on Chromium.
+
+        * accessibility/secure-textfield-title-ui.html:
+        * accessibility/title-ui-element-correctness-expected.txt: Added.
+        * accessibility/title-ui-element-correctness.html: Added.
+        * perf/accessibility-title-ui-element-expected.txt: Added.
+        * perf/accessibility-title-ui-element.html: Added.
+        * platform/chromium/TestExpectations:
+
 2012-10-18  Alexander Pavlov  <[email protected]>
 
         Web Inspector: [Styles] Property considered overridden if it is non-inherited important property in inherited style

Modified: trunk/LayoutTests/accessibility/secure-textfield-title-ui.html (131870 => 131871)


--- trunk/LayoutTests/accessibility/secure-textfield-title-ui.html	2012-10-19 07:51:24 UTC (rev 131870)
+++ trunk/LayoutTests/accessibility/secure-textfield-title-ui.html	2012-10-19 08:01:06 UTC (rev 131871)
@@ -21,8 +21,7 @@
             pass.focus();
             var titleUIElement = accessibilityController.focusedElement.titleUIElement();
             var titleText = titleUIElement.childAtIndex(0);
-            var pattern = "AXValue: Password";
-            if (titleText.allAttributes().indexOf(pattern) != -1) {
+            if (titleText.stringValue == "AXValue: Password") {
                 result.innerText += "Test passed\n";
             }
             else {

Added: trunk/LayoutTests/accessibility/title-ui-element-correctness-expected.txt (0 => 131871)


--- trunk/LayoutTests/accessibility/title-ui-element-correctness-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/accessibility/title-ui-element-correctness-expected.txt	2012-10-19 08:01:06 UTC (rev 131871)
@@ -0,0 +1,23 @@
+This tests that titleUIElement works correctly even when things change dynamically.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS axElement('control1').titleUIElement().isEqual(axElement('label1')) is true
+PASS axElement('control2').titleUIElement().isEqual(axElement('label2')) is true
+PASS hasTitleUIElement(axElement('control3')) is false
+PASS document.getElementById('label3').setAttribute('for', 'control3'); axElement('control3').titleUIElement().isEqual(axElement('label3')) is true
+PASS var label4Element = createLabelWithIdAndForAttribute('label4', 'control4'); hasTitleUIElement(axElement('control4')) is false
+PASS document.getElementById('container').appendChild(label4Element); hasTitleUIElement(axElement('control4')) is true
+PASS axElement('control4').titleUIElement().isEqual(axElement('label4')) is true
+PASS label4Element.parentElement.removeChild(label4Element); hasTitleUIElement(axElement('control4')) is false
+PASS hasTitleUIElement(axElement('control5')) is false
+PASS reparentNodeIntoContainer(document.getElementById('control5'), document.getElementById('label5')); axElement('control5').titleUIElement() != null is true
+PASS axElement('control5').titleUIElement().isEqual(axElement('label5')) is true
+PASS axElement('control6').titleUIElement().isEqual(axElement('label6b')) is true
+PASS newLabel6Element = createLabelWithIdAndForAttribute('label6a', 'control6'); document.body.insertBefore(newLabel6Element, document.body.firstChild); axElement('control6').titleUIElement().isEqual(axElement('label6a')) is true
+PASS document.body.removeChild(newLabel6Element); axElement('control6').titleUIElement().isEqual(axElement('label6b')) is true
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Property changes on: trunk/LayoutTests/accessibility/title-ui-element-correctness-expected.txt
___________________________________________________________________

Added: svn:eol-style

Added: trunk/LayoutTests/accessibility/title-ui-element-correctness.html (0 => 131871)


--- trunk/LayoutTests/accessibility/title-ui-element-correctness.html	                        (rev 0)
+++ trunk/LayoutTests/accessibility/title-ui-element-correctness.html	2012-10-19 08:01:06 UTC (rev 131871)
@@ -0,0 +1,113 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+<script src=""
+
+<div id="container">
+
+  <div>
+    <label id="label1" for="" 1</label>
+    <input id="control1" type="text">
+  </div>
+
+  <div>
+    <label id="label2">
+      Label 2
+      <input id="control2" type="text">
+    </label>
+  </div>
+
+  <div>
+    <label id="label3">Label 3</label>
+    <input id="control3" type="text">
+  </div>
+
+  <div>
+    <input id="control4" type="text">
+  </div>
+
+  <div>
+    <label id="label5">
+      Label 5
+    </label>
+    <input id="control5" type="text">
+  </div>
+
+  <div>
+    <label id="label6b" for="" 6b</label>
+    <label id="label6c" for="" 6c</label>
+    <input id="control6" type="text">
+  </div>
+</div>
+
+<div id="console"></div>
+<script>
+description("This tests that titleUIElement works correctly even when things change dynamically.");
+
+if (window.testRunner && window.accessibilityController) {
+    testRunner.dumpAsText();
+
+    function hasTitleUIElement(axElement) {
+        var label1 = accessibilityController.accessibleElementById("label1");
+        var titleUIElement = axElement.titleUIElement();
+        if (titleUIElement == null)
+            return false;
+        return titleUIElement.role == label1.role;
+    }
+
+    function createLabelWithIdAndForAttribute(id, forAttributeValue) {
+        var labelElement = document.createElement("label");
+        labelElement.id = id;
+        labelElement.setAttribute("for", forAttributeValue);
+        labelElement.innerText = "Label for " + forAttributeValue;
+        return labelElement;
+    }
+
+    function reparentNodeIntoContainer(node, container) {
+        node.parentElement.removeChild(node);
+        container.appendChild(node);
+    }
+
+    function axElement(id) {
+        return accessibilityController.accessibleElementById(id);
+    }
+
+    shouldBe("axElement('control1').titleUIElement().isEqual(axElement('label1'))", "true");
+
+    shouldBe("axElement('control2').titleUIElement().isEqual(axElement('label2'))", "true");
+
+    // Test changing the "for" attribute dynamically.
+    shouldBe("hasTitleUIElement(axElement('control3'))", "false");
+    shouldBe("document.getElementById('label3').setAttribute('for', 'control3'); axElement('control3').titleUIElement().isEqual(axElement('label3'))", "true");
+
+    // Test unattached label element that's subsequently attached.
+    var label4Element = document.createElement("label");
+    label4Element.id = "label4";
+    label4Element.setAttribute("for", "control4");
+    label4Element.innerText = "Label 4";
+    shouldBe("var label4Element = createLabelWithIdAndForAttribute('label4', 'control4'); hasTitleUIElement(axElement('control4'))", "false");
+    shouldBe("document.getElementById('container').appendChild(label4Element); hasTitleUIElement(axElement('control4'))", "true");
+    shouldBe("axElement('control4').titleUIElement().isEqual(axElement('label4'))", "true");
+
+    // Test what happens when the label is detached.
+    shouldBe("label4Element.parentElement.removeChild(label4Element); hasTitleUIElement(axElement('control4'))", "false");
+
+    // Test label that gets a control reparented into it.
+    shouldBe("hasTitleUIElement(axElement('control5'))", "false");
+
+    shouldBe("reparentNodeIntoContainer(document.getElementById('control5'), document.getElementById('label5')); axElement('control5').titleUIElement() != null", "true");
+    shouldBe("axElement('control5').titleUIElement().isEqual(axElement('label5'))", "true");
+
+    // Make sure the first label is returned, even as labels are added and removed.
+    shouldBe("axElement('control6').titleUIElement().isEqual(axElement('label6b'))", "true");
+    shouldBe("newLabel6Element = createLabelWithIdAndForAttribute('label6a', 'control6'); document.body.insertBefore(newLabel6Element, document.body.firstChild); axElement('control6').titleUIElement().isEqual(axElement('label6a'))", "true");
+    shouldBe("document.body.removeChild(newLabel6Element); axElement('control6').titleUIElement().isEqual(axElement('label6b'))", "true");
+
+    document.getElementById('container').style.display = 'none';
+}
+
+</script>
+
+<script src=""
+</body>
+</html>
Property changes on: trunk/LayoutTests/accessibility/title-ui-element-correctness.html
___________________________________________________________________

Added: svn:eol-style

Added: trunk/LayoutTests/perf/accessibility-title-ui-element-expected.txt (0 => 131871)


--- trunk/LayoutTests/perf/accessibility-title-ui-element-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/perf/accessibility-title-ui-element-expected.txt	2012-10-19 08:01:06 UTC (rev 131871)
@@ -0,0 +1,3 @@
+Tests that titleUIElement on an accessibility element runs in amortized constant time.
+PASS
+
Property changes on: trunk/LayoutTests/perf/accessibility-title-ui-element-expected.txt
___________________________________________________________________

Added: svn:eol-style

Added: trunk/LayoutTests/perf/accessibility-title-ui-element.html (0 => 131871)


--- trunk/LayoutTests/perf/accessibility-title-ui-element.html	                        (rev 0)
+++ trunk/LayoutTests/perf/accessibility-title-ui-element.html	2012-10-19 08:01:06 UTC (rev 131871)
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src=""
+<script>
+
+var parentContainer;
+var axControl;
+
+function endsWith(str, suffix) {
+    return str.substr(str.length - suffix.length) == suffix;
+}
+
+function setup(magnitude) {
+    if (parentContainer)
+        document.body.removeChild(parentContainer);
+    parentContainer = document.createElement('div');
+    document.body.appendChild(parentContainer);
+
+    var junkContainer = document.createElement('div');
+    parentContainer.appendChild(junkContainer);
+
+    var label = document.createElement('label');
+    label.setAttribute('for', 'control');
+    label.innerText = 'Label';
+    parentContainer.appendChild(label);
+
+    var control = document.createElement('input');
+    control.type = 'text';
+    control.id = 'control';
+    parentContainer.appendChild(control);
+
+    parentContainer.offsetLeft;
+    axControl = accessibilityController.accessibleElementById('control');
+
+    for (var i = 0; i < 10 * magnitude; ++i) {
+        var div = document.createElement('div');
+        div.innerHTML = "<p></p><p></p><p></p><p></p><p></p>";
+        junkContainer.appendChild(div);
+    }
+    parentContainer.offsetLeft;
+}
+
+function test(magnitude) {
+    for (var i = 0; i < 100; i++) {
+        axControl.titleUIElement();
+    }
+}
+
+Magnitude.description("Tests that titleUIElement on an accessibility element runs in amortized constant time.");
+Magnitude.run(setup, test, Magnitude.CONSTANT);
+</script>
+</body>
+</html>
Property changes on: trunk/LayoutTests/perf/accessibility-title-ui-element.html
___________________________________________________________________

Added: svn:eol-style

Modified: trunk/LayoutTests/platform/chromium/TestExpectations (131870 => 131871)


--- trunk/LayoutTests/platform/chromium/TestExpectations	2012-10-19 07:51:24 UTC (rev 131870)
+++ trunk/LayoutTests/platform/chromium/TestExpectations	2012-10-19 08:01:06 UTC (rev 131871)
@@ -1402,14 +1402,11 @@
 crbug.com/10322 accessibility/legend.html [ Skip ]
 crbug.com/10322 accessibility/lists.html [ Skip ]
 crbug.com/10322 accessibility/media-element.html [ Skip ]
-crbug.com/10322 accessibility/non-data-table-cell-title-ui-element.html [ Skip ]
 crbug.com/10322 accessibility/onclick-handlers.html [ Skip ]
 crbug.com/10322 accessibility/placeholder.html [ Skip ]
 crbug.com/10322 accessibility/plugin.html [ Skip ]
 crbug.com/10322 accessibility/radio-button-checkbox-size.html [ Skip ]
 crbug.com/10322 accessibility/radio-button-group-members.html [ Skip ]
-crbug.com/10322 accessibility/radio-button-title-label.html [ Skip ]
-crbug.com/10322 accessibility/secure-textfield-title-ui.html [ Skip ]
 crbug.com/10322 accessibility/table-attributes.html [ Skip ]
 crbug.com/10322 accessibility/table-cell-spans.html [ Skip ]
 crbug.com/10322 accessibility/table-cells.html [ Skip ]
@@ -1418,7 +1415,6 @@
 crbug.com/10322 accessibility/table-sections.html [ Skip ]
 crbug.com/10322 accessibility/table-with-aria-role.html [ Skip ]
 crbug.com/10322 accessibility/table-with-rules.html [ Skip ]
-crbug.com/10322 accessibility/th-as-title-ui.html [ Skip ]
 crbug.com/10322 accessibility/transformed-element.html [ Skip ]
 crbug.com/10322 accessibility/visible-elements.html [ Skip ]
 

Modified: trunk/Source/WebCore/ChangeLog (131870 => 131871)


--- trunk/Source/WebCore/ChangeLog	2012-10-19 07:51:24 UTC (rev 131870)
+++ trunk/Source/WebCore/ChangeLog	2012-10-19 08:01:06 UTC (rev 131871)
@@ -1,3 +1,47 @@
+2012-10-18  Dominic Mazzoni  <[email protected]>
+
+        AX: labelForElement is slow when there are a lot of DOM elements
+        https://bugs.webkit.org/show_bug.cgi?id=97825
+
+        Reviewed by Ryosuke Niwa.
+
+        Adds a DocumentOrderedMap to TreeScope that allows accessibility to
+        quickly map from an id to the label for that id. This speeds up
+        AccessibilityNode::labelForElement, which was a bottleneck in Chromium
+        when accessibility was on.
+
+        Tests: accessibility/title-ui-element-correctness.html
+               perf/accessibility-title-ui-element.html
+
+        * accessibility/AccessibilityNodeObject.cpp:
+        (WebCore::AccessibilityNodeObject::labelForElement):
+        * dom/DocumentOrderedMap.cpp:
+        (WebCore::keyMatchesLabelForAttribute):
+        (WebCore):
+        (WebCore::DocumentOrderedMap::get):
+        (WebCore::DocumentOrderedMap::getElementByLabelForAttribute):
+        * dom/DocumentOrderedMap.h:
+        (DocumentOrderedMap):
+        * dom/Element.cpp:
+        (WebCore::Element::insertedInto):
+        (WebCore::Element::removedFrom):
+        (WebCore::Element::updateLabel):
+        (WebCore):
+        (WebCore::Element::willModifyAttribute):
+        * dom/Element.h:
+        (Element):
+        * dom/TreeScope.cpp:
+        (WebCore::TreeScope::TreeScope):
+        (WebCore::TreeScope::destroyTreeScopeData):
+        (WebCore::TreeScope::addLabel):
+        (WebCore):
+        (WebCore::TreeScope::removeLabel):
+        (WebCore::TreeScope::labelElementForId):
+        * dom/TreeScope.h:
+        (WebCore):
+        (TreeScope):
+        (WebCore::TreeScope::shouldCacheLabelsByForAttribute):
+
 2012-10-19  Eugene Klyuchnikov  <[email protected]>
 
         Web Inspector: Update localizedStrings.js

Modified: trunk/Source/WebCore/accessibility/AccessibilityNodeObject.cpp (131870 => 131871)


--- trunk/Source/WebCore/accessibility/AccessibilityNodeObject.cpp	2012-10-19 07:51:24 UTC (rev 131870)
+++ trunk/Source/WebCore/accessibility/AccessibilityNodeObject.cpp	2012-10-19 08:01:06 UTC (rev 131871)
@@ -59,6 +59,7 @@
 #include "HTMLTextFormControlElement.h"
 #include "HitTestRequest.h"
 #include "HitTestResult.h"
+#include "LabelableElement.h"
 #include "LocalizedStrings.h"
 #include "MathMLNames.h"
 #include "NodeList.h"
@@ -1001,16 +1002,20 @@
 
 HTMLLabelElement* AccessibilityNodeObject::labelForElement(Element* element) const
 {
-    RefPtr<NodeList> list = element->document()->getElementsByTagName("label");
-    unsigned len = list->length();
-    for (unsigned i = 0; i < len; i++) {
-        if (list->item(i)->hasTagName(labelTag)) {
-            HTMLLabelElement* label = static_cast<HTMLLabelElement*>(list->item(i));
-            if (label->control() == element)
-                return label;
-        }
+    if (!element->isHTMLElement() || !toHTMLElement(element)->isLabelable())
+        return 0;
+
+    const AtomicString& id = element->getIdAttribute();
+    if (!id.isEmpty()) {
+        if (HTMLLabelElement* label = element->treeScope()->labelElementForId(id))
+            return label;
     }
 
+    for (Element* parent = element->parentElement(); parent; parent = parent->parentElement()) {
+        if (parent->hasTagName(labelTag))
+            return static_cast<HTMLLabelElement*>(parent);
+    }
+
     return 0;
 }
 

Modified: trunk/Source/WebCore/dom/DocumentOrderedMap.cpp (131870 => 131871)


--- trunk/Source/WebCore/dom/DocumentOrderedMap.cpp	2012-10-19 07:51:24 UTC (rev 131870)
+++ trunk/Source/WebCore/dom/DocumentOrderedMap.cpp	2012-10-19 08:01:06 UTC (rev 131871)
@@ -55,6 +55,11 @@
     return element->hasTagName(mapTag) && static_cast<HTMLMapElement*>(element)->getName().lower().impl() == key;
 }
 
+inline bool keyMatchesLabelForAttribute(AtomicStringImpl* key, Element* element)
+{
+    return element->hasTagName(labelTag) && element->getAttribute(forAttr).impl() == key;
+}
+
 void DocumentOrderedMap::clear()
 {
     m_map.clear();
@@ -149,5 +154,9 @@
     return get<keyMatchesLowercasedMapName>(key, scope);
 }
 
+Element* DocumentOrderedMap::getElementByLabelForAttribute(AtomicStringImpl* key, const TreeScope* scope) const
+{
+    return get<keyMatchesLabelForAttribute>(key, scope);
+}
+
 } // namespace WebCore
-

Modified: trunk/Source/WebCore/dom/DocumentOrderedMap.h (131870 => 131871)


--- trunk/Source/WebCore/dom/DocumentOrderedMap.h	2012-10-19 07:51:24 UTC (rev 131870)
+++ trunk/Source/WebCore/dom/DocumentOrderedMap.h	2012-10-19 08:01:06 UTC (rev 131871)
@@ -52,6 +52,7 @@
     Element* getElementById(AtomicStringImpl*, const TreeScope*) const;
     Element* getElementByMapName(AtomicStringImpl*, const TreeScope*) const;
     Element* getElementByLowercasedMapName(AtomicStringImpl*, const TreeScope*) const;
+    Element* getElementByLabelForAttribute(AtomicStringImpl*, const TreeScope*) const;
 
     void checkConsistency() const;
 
@@ -80,4 +81,3 @@
 } // namespace WebCore
 
 #endif // DocumentOrderedMap_h
-

Modified: trunk/Source/WebCore/dom/Element.cpp (131870 => 131871)


--- trunk/Source/WebCore/dom/Element.cpp	2012-10-19 07:51:24 UTC (rev 131870)
+++ trunk/Source/WebCore/dom/Element.cpp	2012-10-19 08:01:06 UTC (rev 131871)
@@ -48,6 +48,7 @@
 #include "HTMLElement.h"
 #include "HTMLFormCollection.h"
 #include "HTMLFrameOwnerElement.h"
+#include "HTMLLabelElement.h"
 #include "HTMLNames.h"
 #include "HTMLOptionsCollection.h"
 #include "HTMLParserIdioms.h"
@@ -959,6 +960,12 @@
     if (!nameValue.isNull())
         updateName(nullAtom, nameValue);
 
+    if (hasTagName(labelTag)) {
+        TreeScope* scope = treeScope();
+        if (scope->shouldCacheLabelsByForAttribute())
+            updateLabel(scope, nullAtom, fastGetAttribute(forAttr));
+    }
+
     return InsertionDone;
 }
 
@@ -983,6 +990,12 @@
         const AtomicString& nameValue = getNameAttribute();
         if (!nameValue.isNull())
             updateName(nameValue, nullAtom);
+
+        if (hasTagName(labelTag)) {
+            TreeScope* treeScope = insertionPoint->treeScope();
+            if (treeScope->shouldCacheLabelsByForAttribute())
+                updateLabel(treeScope, fastGetAttribute(forAttr), nullAtom);
+        }
     }
 
     ContainerNode::removedFrom(insertionPoint);
@@ -2125,12 +2138,33 @@
 }
 #endif
 
+void Element::updateLabel(TreeScope* scope, const AtomicString& oldForAttributeValue, const AtomicString& newForAttributeValue)
+{
+    ASSERT(hasTagName(labelTag));
+
+    if (!inDocument())
+        return;
+
+    if (oldForAttributeValue == newForAttributeValue)
+        return;
+
+    if (!oldForAttributeValue.isEmpty())
+        scope->removeLabel(oldForAttributeValue, static_cast<HTMLLabelElement*>(this));
+    if (!newForAttributeValue.isEmpty())
+        scope->addLabel(newForAttributeValue, static_cast<HTMLLabelElement*>(this));
+}
+
 void Element::willModifyAttribute(const QualifiedName& name, const AtomicString& oldValue, const AtomicString& newValue)
 {
     if (isIdAttributeName(name))
         updateId(oldValue, newValue);
     else if (name == HTMLNames::nameAttr)
         updateName(oldValue, newValue);
+    else if (name == HTMLNames::forAttr && hasTagName(labelTag)) {
+        TreeScope* scope = treeScope();
+        if (scope->shouldCacheLabelsByForAttribute())
+            updateLabel(scope, oldValue, newValue);
+    }
 
 #if ENABLE(MUTATION_OBSERVERS)
     if (OwnPtr<MutationObserverInterestGroup> recipients = MutationObserverInterestGroup::createForAttributesMutation(this, name))

Modified: trunk/Source/WebCore/dom/Element.h (131870 => 131871)


--- trunk/Source/WebCore/dom/Element.h	2012-10-19 07:51:24 UTC (rev 131870)
+++ trunk/Source/WebCore/dom/Element.h	2012-10-19 08:01:06 UTC (rev 131871)
@@ -315,6 +315,7 @@
     void updateId(const AtomicString& oldId, const AtomicString& newId);
     void updateId(TreeScope*, const AtomicString& oldId, const AtomicString& newId);
     void updateName(const AtomicString& oldName, const AtomicString& newName);
+    void updateLabel(TreeScope*, const AtomicString& oldForAttributeValue, const AtomicString& newForAttributeValue);
 
     void removeCachedHTMLCollection(HTMLCollection*, CollectionType);
 

Modified: trunk/Source/WebCore/dom/TreeScope.cpp (131870 => 131871)


--- trunk/Source/WebCore/dom/TreeScope.cpp	2012-10-19 07:51:24 UTC (rev 131870)
+++ trunk/Source/WebCore/dom/TreeScope.cpp	2012-10-19 08:01:06 UTC (rev 131871)
@@ -36,6 +36,7 @@
 #include "Frame.h"
 #include "HTMLAnchorElement.h"
 #include "HTMLFrameOwnerElement.h"
+#include "HTMLLabelElement.h"
 #include "HTMLMapElement.h"
 #include "HTMLNames.h"
 #include "IdTargetObserverRegistry.h"
@@ -55,6 +56,7 @@
 TreeScope::TreeScope(ContainerNode* rootNode)
     : m_rootNode(rootNode)
     , m_parentTreeScope(0)
+    , m_shouldCacheLabelsByForAttribute(false)
     , m_idTargetObserverRegistry(IdTargetObserverRegistry::create())
 {
     ASSERT(rootNode);
@@ -72,6 +74,7 @@
 {
     m_elementsById.clear();
     m_imageMapsByName.clear();
+    m_labelsByForAttribute.clear();
 }
 
 void TreeScope::setParentTreeScope(TreeScope* newParentScope)
@@ -144,6 +147,36 @@
     return static_cast<HTMLMapElement*>(m_imageMapsByName.getElementByMapName(AtomicString(name).impl(), this));
 }
 
+void TreeScope::addLabel(const AtomicString& forAttributeValue, HTMLLabelElement* element)
+{
+    m_labelsByForAttribute.add(forAttributeValue.impl(), element);
+}
+
+void TreeScope::removeLabel(const AtomicString& forAttributeValue, HTMLLabelElement* element)
+{
+    m_labelsByForAttribute.remove(forAttributeValue.impl(), element);
+}
+
+HTMLLabelElement* TreeScope::labelElementForId(const AtomicString& forAttributeValue)
+{
+    if (!m_shouldCacheLabelsByForAttribute) {
+        m_shouldCacheLabelsByForAttribute = true;
+        for (Node* node = rootNode(); node; node = node->traverseNextNode()) {
+            if (node->hasTagName(labelTag)) {
+                HTMLLabelElement* label = static_cast<HTMLLabelElement*>(node);
+                const AtomicString& forValue = label->fastGetAttribute(forAttr);
+                if (!forValue.isEmpty())
+                    addLabel(forValue, label);
+            }
+        }
+    }
+
+    if (forAttributeValue.isEmpty())
+        return 0;
+
+    return static_cast<HTMLLabelElement*>(m_labelsByForAttribute.getElementByLabelForAttribute(forAttributeValue.impl(), this));
+}
+
 DOMSelection* TreeScope::getSelection() const
 {
     if (!rootNode()->document()->frame())

Modified: trunk/Source/WebCore/dom/TreeScope.h (131870 => 131871)


--- trunk/Source/WebCore/dom/TreeScope.h	2012-10-19 07:51:24 UTC (rev 131870)
+++ trunk/Source/WebCore/dom/TreeScope.h	2012-10-19 08:01:06 UTC (rev 131871)
@@ -35,6 +35,7 @@
 class ContainerNode;
 class DOMSelection;
 class Element;
+class HTMLLabelElement;
 class HTMLMapElement;
 class IdTargetObserverRegistry;
 class Node;
@@ -62,6 +63,12 @@
     void removeImageMap(HTMLMapElement*);
     HTMLMapElement* getImageMap(const String& url) const;
 
+    // For accessibility.
+    bool shouldCacheLabelsByForAttribute() const { return m_shouldCacheLabelsByForAttribute; }
+    void addLabel(const AtomicString& forAttributeValue, HTMLLabelElement*);
+    void removeLabel(const AtomicString& forAttributeValue, HTMLLabelElement*);
+    HTMLLabelElement* labelElementForId(const AtomicString& forAttributeValue);
+
     DOMSelection* getSelection() const;
 
     // Find first anchor with the given name.
@@ -94,6 +101,9 @@
     DocumentOrderedMap m_elementsById;
     DocumentOrderedMap m_imageMapsByName;
 
+    DocumentOrderedMap m_labelsByForAttribute;
+    bool m_shouldCacheLabelsByForAttribute;
+
     OwnPtr<IdTargetObserverRegistry> m_idTargetObserverRegistry;
 
     mutable RefPtr<DOMSelection> m_selection;

Modified: trunk/Tools/ChangeLog (131870 => 131871)


--- trunk/Tools/ChangeLog	2012-10-19 07:51:24 UTC (rev 131870)
+++ trunk/Tools/ChangeLog	2012-10-19 08:01:06 UTC (rev 131871)
@@ -1,3 +1,19 @@
+2012-10-18  Dominic Mazzoni  <[email protected]>
+
+        AX: labelForElement is slow when there are a lot of DOM elements
+        https://bugs.webkit.org/show_bug.cgi?id=97825
+
+        Reviewed by Ryosuke Niwa.
+
+        Implement titleUIElement in the chromium port of DRT, and
+        fix getAccessibleElementById so that it ensures the backing store
+        is up-to-date.
+
+        * DumpRenderTree/chromium/TestRunner/AccessibilityControllerChromium.cpp:
+        (AccessibilityController::getAccessibleElementById):
+        * DumpRenderTree/chromium/TestRunner/AccessibilityUIElementChromium.cpp:
+        (AccessibilityUIElement::titleUIElementCallback):
+
 2012-10-17  Ilya Tikhonovsky  <[email protected]>
 
         Web Inspector: NMI provide data for mixing with tcmalloc heap dumps.

Modified: trunk/Tools/DumpRenderTree/chromium/TestRunner/src/AccessibilityControllerChromium.cpp (131870 => 131871)


--- trunk/Tools/DumpRenderTree/chromium/TestRunner/src/AccessibilityControllerChromium.cpp	2012-10-19 07:51:24 UTC (rev 131870)
+++ trunk/Tools/DumpRenderTree/chromium/TestRunner/src/AccessibilityControllerChromium.cpp	2012-10-19 08:01:06 UTC (rev 131871)
@@ -117,6 +117,9 @@
     if (m_rootElement.isNull())
         m_rootElement = m_webView->accessibilityObject();
 
+    if (!m_rootElement.updateBackingStoreAndCheckValidity())
+        return 0;
+
     return findAccessibleElementByIdRecursive(m_rootElement, WebString::fromUTF8(id.c_str()));
 }
 

Modified: trunk/Tools/DumpRenderTree/chromium/TestRunner/src/AccessibilityUIElementChromium.cpp (131870 => 131871)


--- trunk/Tools/DumpRenderTree/chromium/TestRunner/src/AccessibilityUIElementChromium.cpp	2012-10-19 07:51:24 UTC (rev 131870)
+++ trunk/Tools/DumpRenderTree/chromium/TestRunner/src/AccessibilityUIElementChromium.cpp	2012-10-19 08:01:06 UTC (rev 131871)
@@ -774,7 +774,13 @@
 
 void AccessibilityUIElement::titleUIElementCallback(const CppArgumentList&, CppVariant* result)
 {
-    result->setNull();
+    WebAccessibilityObject obj = accessibilityObject().titleUIElement();
+    if (obj.isNull()) {
+        result->setNull();
+        return;
+    }
+
+    result->set(*(m_factory->getOrCreate(obj)->getAsCppVariant()));
 }
 
 void AccessibilityUIElement::setSelectedTextRangeCallback(const CppArgumentList&arguments, CppVariant* result)
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to