Bug 37079 - Links around blocks (e.g. divs) results in too many VoiceOver call outs
Summary: Links around blocks (e.g. divs) results in too many VoiceOver call outs
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: New Bugs (show other bugs)
Version: 528+ (Nightly build)
Hardware: Other OS X 10.5
: P2 Normal
Assignee: Nobody
URL:
Keywords: InRadar
Depends on:
Blocks:
 
Reported: 2010-04-05 02:40 PDT by Maciej Stachowiak
Modified: 2010-04-22 22:47 PDT (History)
5 users (show)

See Also:


Attachments
Patch (18.13 KB, patch)
2010-04-05 02:52 PDT, Maciej Stachowiak
no flags Details | Formatted Diff | Diff
Patch (23.62 KB, patch)
2010-04-22 03:22 PDT, Maciej Stachowiak
mitz: review+
Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Maciej Stachowiak 2010-04-05 02:40:26 PDT
Links around blocks (e.g. divs) results in too many VoiceOver call outs
Comment 1 Maciej Stachowiak 2010-04-05 02:52:56 PDT
Created attachment 52520 [details]
Patch
Comment 2 Maciej Stachowiak 2010-04-05 02:53:43 PDT
<rdar://problem/7234118>
Comment 3 mitz 2010-04-05 15:36:27 PDT
Comment on attachment 52520 [details]
Patch

Beth and I looked at the patch, and we suspect the logic for Case 3 below may be wrong:

> @@ -158,8 +272,31 @@ AccessibilityObject* AccessibilityRender
>  {
>      if (!m_renderer)
>          return 0;
> +
> +    RenderObject* nextSibling = 0;
> +
> +    // Case 1: node is a block and has an inline continuation. Next sibling is the inline continuation's
> +    // first child.
> +    RenderInline* inlineContinuation;
> +    if (m_renderer->isRenderBlock() && (inlineContinuation = toRenderBlock(m_renderer)->inlineContinuation()))
> +        nextSibling = firstChildConsideringContinuation(inlineContinuation);
> +
> +    // Case 2: node has an actual next sibling
> +    else if (RenderObject* ns = m_renderer->nextSibling())
> +        nextSibling = ns;
> +
> +    // Case 3: node has no next sibling, and its parent is an inline with a continuation.
> +    else if (isInlineWithContinuation(m_renderer->parent())) {
> +        RenderObject* continuation = toRenderInline(m_renderer->parent())->continuation();
> +        
> +        // Case 3a: continuation is a block - in this case the block itself is the next sibling.
> +        if (continuation->isRenderBlock())
> +            nextSibling = continuation;
> +        // Case 3b: continuation is an inline - in this case the inline's first child is the next sibling
> +        else
> +            nextSibling = firstChildConsideringContinuation(continuation);
> +    }

Considering a case like <span id="span1">a<div>b</div></span>c<span id="span2">d</span>, if asked for the next sibling of the first RenderInline for span1, the above code will hit case 3 (because that inline doesn’t have a next sibling in its parent anonymous block), but then it will fail the condition for that case, because its parent is not an inline. Instead, shouldn’t we return the RenderInline for span2? Perhaps the way to do this is to follow the inlineContinuation chain and return the next sibling of the last renderer on the chain?
Comment 4 Maciej Stachowiak 2010-04-05 22:22:10 PDT
Comment on attachment 52520 [details]
Patch

Marking this r- because upon discussion, Mitz and I concluded that there was indeed a bug.
Comment 5 Maciej Stachowiak 2010-04-22 03:22:12 PDT
Created attachment 54047 [details]
Patch
Comment 6 Maciej Stachowiak 2010-04-22 03:23:25 PDT
(In reply to comment #5)
> Created an attachment (id=54047) [details]
> Patch

I believe the new patch addresses Mitz's comments above.
Comment 7 mitz 2010-04-22 11:28:52 PDT
Comment on attachment 54047 [details]
Patch

> +static inline RenderObject* lastChildConsideringContinuation(RenderObject* renderer)
> +{
> +    RenderObject* lastChild = renderer->lastChild();
> +    RenderObject* prev = renderer;

prev is unused (you just assign to it).

> +    RenderObject* cur = renderer;
> +
> +    while (cur) {
> +        prev = cur;
> +
> +        if (RenderObject* lc = cur->lastChild())
> +            lastChild = lc;
> +
> +        if (cur->isRenderInline()) {
> +            cur = toRenderInline(cur)->continuation();
> +            if (cur && cur->isRenderBlock()) 
> +                cur = toRenderBlock(cur)->inlineContinuation();
> +        } else
> +            cur = toRenderBlock(cur)->inlineContinuation();

I can’t think of a case where you couldn’t just do cur = cur->inlineContinuation() (assuming cur is a block or an inline). In other words, the first branch seems needlessly complicated. What am I missing?

>  AccessibilityObject* AccessibilityRenderObject::firstChild() const
>  {
>      if (!m_renderer)
>          return 0;
>      
>      RenderObject* firstChild = m_renderer->firstChild();
> +
> +    if (!firstChild && isInlineWithContinuation(m_renderer))
> +        firstChild = firstChildConsideringContinuation(m_renderer);

You can just use firstChildConsideringContinuation(), because it includes the above (getting firstChild() and checking if it’s 0 etc.).

> +static inline RenderObject* endOfContinuations(RenderObject* renderer)
> +{
> +    RenderObject* prev = renderer;
> +    RenderObject* cur = renderer;
> +
> +    while (cur) {
> +        prev = cur;
> +        if (cur->isRenderInline()) {
> +            cur = toRenderInline(cur)->continuation();
> +            if (cur && cur->isRenderBlock()) 
> +                cur = toRenderBlock(cur)->inlineContinuation();
> +        } else
> +            cur = toRenderBlock(cur)->inlineContinuation();

Like in lastChildConsideringContinuations, I don’t understand why the inline case can’t just use inlineContinuation().

r=beth and me
Comment 8 Maciej Stachowiak 2010-04-22 22:47:29 PDT
Committed r58150: <http://trac.webkit.org/changeset/58150>