Change traversing tree to render tree in screen reader.
authorSangYong Park <sy302.park@samsung.com>
Mon, 18 Feb 2013 23:33:01 +0000 (08:33 +0900)
committerGerrit Code Review <gerrit2@kim11>
Tue, 26 Mar 2013 06:05:46 +0000 (15:05 +0900)
[Title] Change traversing tree to render tree in screen reader.
[Issue#] N/A
[Problem]
[Cause]
[Solution]

Change-Id: Ic4c426d8fc0044ca78a13fc0a7e9018a1d5868e2

Source/WebCore/dom/Document.cpp
Source/WebCore/page/ChromeClient.h
Source/WebCore/rendering/RenderObject.cpp
Source/WebKit2/WebProcess/WebCoreSupport/WebChromeClient.cpp
Source/WebKit2/WebProcess/WebCoreSupport/WebChromeClient.h
Source/WebKit2/WebProcess/WebPage/WebPage.h
Source/WebKit2/WebProcess/WebPage/efl/WebPageEfl.cpp
Source/WebKit2/WebProcess/WebPage/efl/tizen/ScreenReader.cpp
Source/WebKit2/WebProcess/WebPage/efl/tizen/ScreenReader.h

index 965a879..4b9e5f5 100755 (executable)
@@ -3995,9 +3995,6 @@ void Document::nodeWillBeRemoved(Node* n)
         frame->eventHandler()->nodeWillBeRemoved(n);
         frame->selection()->nodeWillBeRemoved(n);
         frame->page()->dragCaretController()->nodeWillBeRemoved(n);
-#if ENABLE(TIZEN_SCREEN_READER)
-        frame->page()->chrome()->client()->nodeWillBeRemoved(n);
-#endif
     }
 }
 
index cb327b5..39759da 100755 (executable)
@@ -395,7 +395,7 @@ namespace WebCore {
 #endif
 
 #if ENABLE(TIZEN_SCREEN_READER)
-        virtual void nodeWillBeRemoved(Node*) { }
+        virtual void rendererWillBeDestroyed(RenderObject*) { }
 #endif
 
     protected:
index 45f58ef..eb2a012 100755 (executable)
@@ -2307,6 +2307,11 @@ void RenderObject::willBeDestroyed()
     if (frame() && frame()->eventHandler()->autoscrollRenderer() == this)
         frame()->eventHandler()->stopAutoscrollTimer(true);
 
+#if ENABLE(TIZEN_SCREEN_READER)
+    if (frame())
+        frame()->page()->chrome()->client()->rendererWillBeDestroyed(this);
+#endif
+
     if (AXObjectCache::accessibilityEnabled()) {
         document()->axObjectCache()->childrenChanged(this->parent());
         document()->axObjectCache()->remove(this);
index edd8dd4..ed999b1 100755 (executable)
@@ -965,9 +965,9 @@ float WebChromeClient::contentsScaleFactor() const
 #endif
 
 #if ENABLE(TIZEN_SCREEN_READER)
-void WebChromeClient::nodeWillBeRemoved(Node* node)
+void WebChromeClient::rendererWillBeDestroyed(RenderObject* object)
 {
-    m_page->updateScreenReaderFocus(node);
+    m_page->updateScreenReaderFocus(object);
 }
 #endif
 
index 86e9a75..b990992 100755 (executable)
@@ -258,7 +258,7 @@ private:
 #endif
 
 #if ENABLE(TIZEN_SCREEN_READER)
-    virtual void nodeWillBeRemoved(WebCore::Node*);
+    virtual void rendererWillBeDestroyed(WebCore::RenderObject*);
 #endif
 
     String m_cachedToolTip;
index f39ea2a..44eaa42 100755 (executable)
@@ -756,7 +756,7 @@ public:
     void moveScreenReaderFocus(bool, bool&);
     void moveScreenReaderFocusByPoint(const WebCore::IntPoint&);
     void recalcScreenReaderFocusRect();
-    void updateScreenReaderFocus(WebCore::Node*);
+    void updateScreenReaderFocus(WebCore::RenderObject*);
     void clearScreenReader();
 #endif
 
index 93530d1..8e1a7ca 100755 (executable)
@@ -1537,14 +1537,14 @@ void WebPage::recalcScreenReaderFocusRect()
     sendScreenReaderFocusRect(this, m_screenReader->getFocusedNode());
 }
 
-void WebPage::updateScreenReaderFocus(Node* willRemoveNode)
+void WebPage::updateScreenReaderFocus(RenderObject* object)
 {
     if (!m_screenReader)
         return;
 
-    if (!willRemoveNode)
+    if (!object)
         m_screenReader->clearFocus();
-    else if (!m_screenReader->nodeWillBeRemoved(willRemoveNode))
+    else if (!m_screenReader->rendererWillBeDestroyed(object))
         return;
 
     send(Messages::WebPageProxy::DidScreenReaderFocusRectChanged(IntRect()));
index d2584ed..67b8ec3 100755 (executable)
@@ -58,7 +58,9 @@ IntSize ScreenReader::s_hitTestPadding = IntSize();
 
 ScreenReader::ScreenReader(WebPage* page)
     : m_page(page)
+    , m_focusedObject(0)
     , m_hasFocus(false)
+    , m_isForward(true)
 {
     if (++s_readerCount) {
         AXObjectCache::enableAccessibility();
@@ -73,26 +75,42 @@ ScreenReader::~ScreenReader()
     --s_readerCount;
 }
 
-static Node* traverseNode(Node* node, bool forward)
+RenderObject* ScreenReader::traverse(RenderObject* object)
 {
-    if (forward)
-        return node->traverseNextNode();
-    else
-        return node->traversePreviousNode();
+    if (m_isForward) {
+        if (object->firstChild())
+            return object->firstChild();
+    } else {
+        if (object->lastChild())
+            return object->lastChild();
+    }
+
+    return traverseSibling(object);
 }
 
-static Node* traverseSibling(Node* node, bool forward)
+RenderObject* ScreenReader::traverseSibling(RenderObject* object)
 {
-    if (forward)
-        return node->traverseNextSibling();
-    else
-        return node->traversePreviousSibling();
+    if (m_isForward) {
+        do {
+            if (object->nextSibling())
+                return object->nextSibling();
+        } while ((object = object->parent()));
+    } else {
+        do {
+            if (object->previousSibling())
+                return object->previousSibling();
+        } while ((object = object->parent()));
+    }
+
+    return 0;
 }
 
-static Node* traverseOwnerElementSibling(Node* node, bool forward)
+RenderObject* ScreenReader::ownerElementSibling(RenderObject* object)
 {
-    while ((node = node->document()->ownerElement())) {
-        Node* next = traverseSibling(node, forward);
+    Node* node;
+    while ((node = object->document()->ownerElement())) {
+        object = node->renderer();
+        RenderObject* next = traverseSibling(object);
         if (next)
             return next;
     }
@@ -100,57 +118,31 @@ static Node* traverseOwnerElementSibling(Node* node, bool forward)
     return 0;
 }
 
-static Node* contentDocumentBody(HTMLFrameOwnerElement* ownerElement)
+static RenderObject* contentDocumentBody(RenderObject* object)
 {
-    if (ownerElement->contentDocument())
-        return ownerElement->contentDocument()->body();
-    else
+    HTMLFrameOwnerElement* ownerElement = toFrameOwnerElement(object->node());
+    if (!ownerElement || !ownerElement->contentDocument())
         return 0;
-}
 
-static Node* lastDescendant(Node* node)
-{
-    while (node->hasChildNodes())
-        node = node->lastChild();
-    return node;
+    return ownerElement->contentDocument()->body()->renderer();
 }
 
-static bool isVisible(AccessibilityObject* object)
+static RenderObject* lastLeafChild(RenderObject* object)
 {
-    if (!object || !object->renderer() || object->renderer()->style()->visibility() != VISIBLE)
-        return false;
-    IntRect boundingRect = object->pixelSnappedBoundingBoxRect();
-    return (boundingRect.maxX() >= 0 && boundingRect.maxY() >= 0);
+    while (object->lastChild())
+        object = object->lastChild();
+    return object;
 }
 
-static bool isAriaFocusable(Node* node)
+static AccessibilityObject* visibleAXObject(RenderObject* object)
 {
-    AccessibilityObject* object = node->document()->axObjectCache()->getOrCreate(node->renderer());
-    if (!object)
-        return false;
-
-    switch (object->roleValue()) {
-    case ButtonRole:
-    case CheckBoxRole:
-    case ComboBoxRole:
-    case MenuRole:
-    case MenuBarRole:
-    case MenuItemRole:
-    case LinkRole:
-    case ListBoxOptionRole:
-    case ProgressIndicatorRole:
-    case RadioButtonRole:
-    case ScrollBarRole:
-    case SliderRole:
-    case TreeRole:
-    case TreeGridRole:
-    case TreeItemRole:
-    case WebCoreLinkRole:
-        return true;
-    default:
-        break;
-    }
-    return false;
+    if (!object || !object->node() || object->style()->visibility() != VISIBLE)
+        return 0;
+    AccessibilityObject* axObject = object->document()->axObjectCache()->getOrCreate(object);
+    if (!axObject)
+        return 0;
+    IntRect boundingRect = axObject->pixelSnappedBoundingBoxRect();
+    return (boundingRect.maxX() > 0 && boundingRect.maxY() > 0) ? axObject : 0;
 }
 
 static bool isSpace(UChar character)
@@ -182,6 +174,41 @@ static bool containsOnlyWhitespace(const String& string)
     return true;
 }
 
+static bool isAriaFocusable(Node* node)
+{
+    AccessibilityObject* axObject = node->document()->axObjectCache()->getOrCreate(node->renderer());
+    if (!axObject)
+        return false;
+
+    switch (axObject->roleValue()) {
+    case ButtonRole:
+    case CheckBoxRole:
+    case ComboBoxRole:
+    case MenuRole:
+    case MenuBarRole:
+    case MenuItemRole:
+    case LinkRole:
+    case ListBoxOptionRole:
+    case ProgressIndicatorRole:
+    case RadioButtonRole:
+    case ScrollBarRole:
+    case SliderRole:
+    case TreeRole:
+    case TreeGridRole:
+    case TreeItemRole:
+    case WebCoreLinkRole:
+        return true;
+    default:
+        break;
+    }
+
+    if (!containsOnlyWhitespace(axObject->ariaLabeledByAttribute())
+        || !containsOnlyWhitespace(axObject->getAttribute(aria_labelAttr)))
+        return true;
+
+    return false;
+}
+
 static bool hasText(Node* node)
 {
     return !containsOnlyWhitespace(node->nodeValue());
@@ -196,11 +223,11 @@ static bool isFocusable(Node* node)
 
 static bool isAriaReadable(Node* node)
 {
-    AccessibilityObject* object = node->document()->axObjectCache()->getOrCreate(node->renderer());
-    if (!object)
+    AccessibilityObject* axObject = node->document()->axObjectCache()->getOrCreate(node->renderer());
+    if (!axObject)
         return false;
 
-    switch (object->roleValue()) {
+    switch (axObject->roleValue()) {
     case ImageRole:
         return true;
     default:
@@ -222,60 +249,75 @@ static bool isReadable(Node* node)
     return true;
 }
 
-static Node* findFocusableNode(Node* node, bool forward)
+static bool isAriaHidden(AccessibilityObject* axObject)
 {
-    node->document()->updateLayoutIgnorePendingStylesheets();
+    if (equalIgnoringCase(axObject->getAttribute(aria_hiddenAttr), "true"))
+        return true;
 
+    while ((axObject = axObject->parentObject())) {
+        if (equalIgnoringCase(axObject->getAttribute(aria_hiddenAttr), "true"))
+            return true;
+    }
+
+    return false;
+}
+
+RenderObject* ScreenReader::findFocusable(RenderObject* object)
+{
     do {
-        AccessibilityObject* object = node->document()->axObjectCache()->getOrCreate(node->renderer());
-        if (!isVisible(object))
+        AccessibilityObject* axObject = visibleAXObject(object);
+        if (!axObject)
             continue;
 
+        Node* node = object->node();
         if (node->isFrameOwnerElement()) {
-            Node* body = contentDocumentBody(toFrameOwnerElement(node));
+            RenderObject* body = contentDocumentBody(object);
             if (body) {
-                Node* result = findFocusableNode(forward ? body : lastDescendant(body), forward);
+                RenderObject* result = findFocusable(m_isForward ? body : lastLeafChild(body));
                 if (result)
                     return result;
             }
-        } else if (isFocusable(node) || isReadable(node))
-            return node;
-    } while ((node = traverseNode(node, forward)));
+        } else if ((isFocusable(node) || isReadable(node)) && !isAriaHidden(axObject))
+            return object;
+    } while ((object = traverse(object)));
 
     return 0;
 }
 
 bool ScreenReader::moveFocus(bool forward)
 {
-    Node* node;
-    if (!m_focusedNode) {
-        node = m_page->mainFrame()->document()->body();
+    m_isForward = forward;
+    m_page->mainFrame()->document()->updateLayoutIgnorePendingStylesheets();
+
+    RenderObject* object;
+    if (!m_focusedObject) {
+        object = m_page->mainFrame()->document()->body()->renderer();
         if (!forward)
-            node = lastDescendant(node);
+            object = lastLeafChild(object);
     } else {
-        node = traverseNode(m_focusedNode.get(), forward);
-        if (!node)
-            node = traverseOwnerElementSibling(m_focusedNode.get(), forward);
+        object = traverse(m_focusedObject);
+        if (!object)
+            object = ownerElementSibling(m_focusedObject);
     }
 
-    if (!node) {
+    if (!object) {
         clearFocus();
         return false;
     }
 
-    Node* candidate;
+    RenderObject* candidate;
     do {
-        candidate = findFocusableNode(node, forward);
+        candidate = findFocusable(object);
         if (candidate)
             break;
-    } while ((node = traverseOwnerElementSibling(node, forward)));
+    } while ((object = ownerElementSibling(object)));
 
     if (!candidate) {
         clearFocus();
         return false;
     }
 
-    node = candidate;
+    Node* node = candidate->node();
     while (node) {
         node->renderer()->scrollRectToVisible(node->getRect(), ScrollAlignment::alignCenterIfNeeded, ScrollAlignment::alignCenterIfNeeded);
         node = node->document()->ownerElement();
@@ -295,8 +337,11 @@ static Node* findShortestDistanceNode(const IntPoint& point, const HitTestResult
 
     for (; it != end; ++it) {
         for (Node* node = it->get(); node; node = node->parentOrHostNode()) {
-            if (isFocusable(node) || isReadable(node))
-                candidates.add(node);
+            if (isFocusable(node) || isReadable(node)) {
+                AccessibilityObject* object = node->document()->axObjectCache()->getOrCreate(node->renderer());
+                if (object && !isAriaHidden(object))
+                    candidates.add(node);
+            }
         }
     }
 
@@ -332,17 +377,16 @@ static Node* findShortestDistanceNode(const IntPoint& point, const HitTestResult
 bool ScreenReader::moveFocus(const IntPoint& point)
 {
     Frame* mainFrame = m_page->mainFrame();
-    FrameView* view = mainFrame->view();
-    view->updateLayoutAndStyleIfNeededRecursive();
+    mainFrame->document()->updateLayoutIgnorePendingStylesheets();
 
-    IntPoint hitTestPoint = view->windowToContents(point);
+    IntPoint hitTestPoint = mainFrame->view()->windowToContents(point);
     HitTestResult result = mainFrame->eventHandler()->hitTestResultAtPoint(hitTestPoint, false, false, DontHitTestScrollbars, HitTestRequest::ReadOnly | HitTestRequest::Active, s_hitTestPadding);
 
     Node* candidate = findShortestDistanceNode(hitTestPoint, result.rectBasedTestResult());
-    if (!candidate || candidate == m_focusedNode)
+    if (!candidate || candidate->renderer() == m_focusedObject)
         return false;
 
-    setFocus(candidate);
+    setFocus(candidate->renderer());
 
     return true;
 }
@@ -351,6 +395,7 @@ static String ariaRoleText(AccessibilityRole roleValue)
 {
     switch (roleValue) {
     case ButtonRole:
+    case PopUpButtonRole:
         return "button";
     case CheckBoxRole:
         return "check box";
@@ -359,17 +404,18 @@ static String ariaRoleText(AccessibilityRole roleValue)
     case ImageRole:
         return "image";
     case LinkRole:
+    case WebCoreLinkRole:
         return "link";
     case ListBoxOptionRole:
         return "list box option";
     case ProgressIndicatorRole:
-        return "progress bar";
+        return "";
     case RadioButtonRole:
         return "radio button";
     case SliderRole:
         return "slider";
-    case WebCoreLinkRole:
-        return "link";
+    case TabRole:
+        return "tab";
     default:
         break;
     }
@@ -378,75 +424,83 @@ static String ariaRoleText(AccessibilityRole roleValue)
 
 static bool addNodeText(Node* node, bool hasSubtreeText, Vector<String>& textList)
 {
-    AccessibilityObject* object = node->document()->axObjectCache()->getOrCreate(node->renderer());
-    if (!isVisible(object))
+    AccessibilityObject* axObject = visibleAXObject(node->renderer());
+    if (!axObject || isAriaHidden(axObject))
         return hasSubtreeText;
 
     if (node->isElementNode()) {
         Element* element = toElement(node);
-        String label, text, more;
+        String type, text, state;
 
         if (element->isLink())
-            label = "link";
+            type = "link";
         else if (element->isFormControlElement()) {
             if (element->hasTagName(HTMLNames::buttonTag)) {
-                label = static_cast<HTMLFormControlElement*>(element)->type();
+                type = static_cast<HTMLFormControlElement*>(element)->type();
                 text = static_cast<HTMLButtonElement*>(element)->value();
             } else if (element->hasTagName(HTMLNames::fieldsetTag))
-                label = static_cast<HTMLFormControlElement*>(element)->type();
+                type = static_cast<HTMLFormControlElement*>(element)->type();
             else if (element->hasTagName(HTMLNames::inputTag)) {
-                label = "edit field";
+                type = "edit field";
                 text = static_cast<HTMLInputElement*>(element)->alt();
                 if (text.isEmpty())
                     text = static_cast<HTMLInputElement*>(element)->value();
             } else if (element->hasTagName(HTMLNames::keygenTag))
-                label = static_cast<HTMLFormControlElement*>(element)->type();
+                type = static_cast<HTMLFormControlElement*>(element)->type();
             else if (element->hasTagName(HTMLNames::outputTag)) {
-                label = static_cast<HTMLFormControlElement*>(element)->type();
+                type = static_cast<HTMLFormControlElement*>(element)->type();
                 text = static_cast<HTMLOutputElement*>(element)->value();
             } else if (element->hasTagName(HTMLNames::selectTag)) {
-                label = static_cast<HTMLFormControlElement*>(element)->type();
+                type = static_cast<HTMLFormControlElement*>(element)->type();
                 text = static_cast<HTMLSelectElement*>(element)->value();
                 if (static_cast<HTMLSelectElement*>(element)->size() == 1)
-                    more = "1 item";
+                    state = "1 item";
                 else
-                    more = String::format("%d items", static_cast<HTMLSelectElement*>(element)->size());
+                    state = String::format("%d items", static_cast<HTMLSelectElement*>(element)->size());
             } else if (element->hasTagName(HTMLNames::textareaTag)) {
-                label = "edit field";
+                type = "edit field";
                 text = static_cast<HTMLTextAreaElement*>(element)->value();
             }
         } else if (element->hasTagName(HTMLNames::imgTag)) {
-            label = "image";
+            type = "image";
             text = static_cast<HTMLImageElement*>(element)->altText();
         } else if (element->hasTagName(HTMLNames::optionTag)) {
             text = static_cast<HTMLOptionElement*>(element)->label();
             if (static_cast<HTMLOptionElement*>(element)->selected())
-                more = "selected";
+                state = "selected";
         }
 
-        String ariaText = object->ariaLabeledByAttribute();
-        if (!containsOnlyWhitespace(ariaText))
-            label = ariaText;
-        else if (!containsOnlyWhitespace((ariaText = element->fastGetAttribute(aria_labelAttr))))
-            label = ariaText;
-        else if (!containsOnlyWhitespace((ariaText = ariaRoleText(object->roleValue()))))
-            label = ariaText;
-
-        if (!containsOnlyWhitespace((ariaText = object->ariaDescribedByAttribute())))
-            text = ariaText;
-        else if (text.isEmpty() && node->isHTMLElement()) {
+        if (text.isEmpty() && node->isHTMLElement()) {
             const AtomicString& title = element->fastGetAttribute(HTMLNames::titleAttr);
             if (!containsOnlyWhitespace(title))
                 text = title;
         }
 
-        if (text.isEmpty() && label.isEmpty())
+        if (element->fastHasAttribute(HTMLNames::roleAttr))
+            type = element->fastGetAttribute(HTMLNames::roleAttr).isEmpty()? emptyString() : ariaRoleText(axObject->roleValue());
+
+        String more, ariaText;
+        if (!containsOnlyWhitespace((ariaText = axObject->ariaDescribedByAttribute())))
+            more = ariaText;
+        else if (!containsOnlyWhitespace((ariaText = axObject->ariaLabeledByAttribute())))
+            more = ariaText;
+        else if (!containsOnlyWhitespace((ariaText = element->fastGetAttribute(aria_labelAttr))))
+            more = ariaText;
+
+        if (text.isEmpty() && type.isEmpty())
             return hasSubtreeText;
 
+        if (axObject->isSelected())
+            state = "selected";
+        else if (axObject->isChecked())
+            state = "checked";
+
         if (!text.isEmpty())
             textList.append(text);
-        if (!label.isEmpty())
-            textList.append(label);
+        if (!type.isEmpty())
+            textList.append(type);
+        if (!state.isEmpty())
+            textList.append(state);
         if (!more.isEmpty())
             textList.append(more);
     } else if (node->isTextNode() && hasText(node))
@@ -454,7 +508,7 @@ static bool addNodeText(Node* node, bool hasSubtreeText, Vector<String>& textLis
     else
         return hasSubtreeText;
 
-    if (!object->isEnabled())
+    if (!axObject->isEnabled())
         textList.append("disabled");
 
     return true;
@@ -488,12 +542,12 @@ static String subtreeText(Node* top)
     return text.toString().simplifyWhiteSpace();
 }
 
-bool ScreenReader::setFocus(Node* node)
+bool ScreenReader::setFocus(RenderObject* object)
 {
-    m_focusedNode = node;
+    m_focusedObject = object;
     m_hasFocus = true;
 
-    String text = subtreeText(node);
+    String text = subtreeText(object->node());
     if (text.isEmpty())
         return false;
 
@@ -506,23 +560,25 @@ Node* ScreenReader::getFocusedNode()
 {
     if (!m_hasFocus)
         return 0;
-    return m_focusedNode.get();
+
+    return m_focusedObject->node();
 }
 
-bool ScreenReader::nodeWillBeRemoved(Node* node)
+bool ScreenReader::rendererWillBeDestroyed(RenderObject* object)
 {
-    if (!node->contains(m_focusedNode.get()))
+    if (m_focusedObject != object)
         return false;
 
     clearFocus();
-    m_focusedNode = traverseNode(node, false);
+    m_isForward = false;
+    m_focusedObject = traverse(object);
 
     return true;
 }
 
 void ScreenReader::clearFocus()
 {
-    m_focusedNode = 0;
+    m_focusedObject = 0;
     m_hasFocus = false;
     m_page->send(Messages::WebPageProxy::DidScreenReaderTextChanged(emptyString()));
 }
index b9ea56e..b1f30cc 100755 (executable)
@@ -34,6 +34,7 @@
 namespace WebCore {
     class IntPoint;
     class Node;
+    class RenderObject;
 }
 
 namespace WebKit {
@@ -53,17 +54,24 @@ public:
 
     WebCore::Node* getFocusedNode();
 
-    bool nodeWillBeRemoved(WebCore::Node*);
+    bool rendererWillBeDestroyed(WebCore::RenderObject*);
     void clearFocus();
 
 private:
     ScreenReader(WebPage*);
 
-    bool setFocus(WebCore::Node*);
+    WebCore::RenderObject* traverse(WebCore::RenderObject*);
+    WebCore::RenderObject* traverseSibling(WebCore::RenderObject*);
+    WebCore::RenderObject* ownerElementSibling(WebCore::RenderObject*);
+
+    WebCore::RenderObject* findFocusable(WebCore::RenderObject*);
+
+    bool setFocus(WebCore::RenderObject*);
 
     WebPage* m_page;
-    RefPtr<WebCore::Node> m_focusedNode;
+    WebCore::RenderObject* m_focusedObject;
     bool m_hasFocus;
+    bool m_isForward;
 
     static int s_readerCount;
     static WebCore::IntSize s_hitTestPadding;