Bug 61532

Summary: A focusedNode should be kept in each TreeScope, not in each Document.
Product: WebKit Reporter: Hayato Ito <hayato>
Component: DOMAssignee: Nobody <webkit-unassigned>
Status: RESOLVED WONTFIX    
Severity: Normal CC: dglazkov, dominicc, morrita, rolandsteiner
Priority: P2    
Version: 528+ (Nightly build)   
Hardware: Unspecified   
OS: Unspecified   
Bug Depends on:    
Bug Blocks: 59832, 61409    

Description Hayato Ito 2011-05-26 07:54:34 PDT
Currently, a focusedNode is tracked in each Document using Document::focusedNode().
To make elements in shadow DOM focusable and make it simple, it is nice to keep focusedNode in each TreeScope, not in each Document.
Comment 1 Hayato Ito 2011-05-29 22:21:20 PDT
Let me share my idea briefly here.

The main motivation is to make a 'shadow-host' act similar to a 'iframe', which is a boundary of focus scope. For example, if a iframe element is not focusable, all descendant elements in the iframe are skipped when we press 'Tab' key. I'd like to apply the same behavior to shadow host.

I am trying to implement 'tab traversal into shadow' in bug 61410. But it turned out that we should make things changed: I'd like to have focusedNode in each TreeScope, not in each Document. Let me explain by using examples:

See the following tree:

  Document (outer)
    - <p>
    - <iframe>  <--- A
       - Document (inner)
         - <input>  <---- B
         - <input>


 When a node B is focused, the following things will happen:
  - focusController::focusedFrame -> A
  - outerDocument::focusedNode() -> 0 (Hmm.. I'd like to change this behavior. If that were A, some things would get simpler..?)
  - innerDocument::focusedNode() -> B
  - outerDocument.activeElement -> A
  - innerDocument.activeElement -> B


See the next tree, which contains a shadow-host element

  Document
    - <p>
    - <shadow-host>   <--- A
        .. shadow-root
             - <input>  <---- B
             - <input>


 When a node B is focused, what is expected? In working patch in https://bugs.webkit.org/attachment.cgi?id=95126, the following will happen:
  - focusController::focusedFrame -> a document (There is only one document, which is shared between an outer tree and a shadow root.)
  - document::focusedNode() -> B
  - document.activeElement ->  should be A (Not implemented yet, there is a working patch in bug 61413 ).

I'd like to vanish this difference and treat these uniformly:

- Have focusedNode in each TreeScope, not in each Document.
- To make FocusControler have focusedTreeSocpe() in addition to focusedFrame().

By these changes, we cat get current deepest focusedNode by focusController->focusedTreeScope()->focusedNode().
And ideally, document.activeElement is always same to the return value of 'document::focusedNode'.

So the latter case should be:

  focusController::focusedTreeScope() -> shadow-root 
  shadow-root::focusedNode() -> B
  shadow-root::activeElement -> ??? (Should TreeScope has activeElement?)
  document::focusedNode() -> A (shadow host)
  document.activeElement ->  A (shadow host)

This is my rough idea.
Comment 2 Dominic Cooney 2011-05-29 22:42:15 PDT
That sounds good to me. Putting activeElement on TreeScope and having it point to the shadow host when an element in the shadow has focus sounds good.

I don’t understand why focusedNode belongs on TreeScope. Is focusedNode() the same for all TreeScopes in a document? Why does having focusedNode on TreeScope make things easier?

focusedNode is completely internal to WebKit, right? Who uses it? Why would making it non-0 when a frame is focused make things easier? Seems like it might be good to know when you’re crossing an iframe boundary.

Another question: What happens when you have this?

Document
  - <shadow-host> <--- A
    … shadow-root
      - <content>
    - <input> <--- B

Say node B has focus. Is the activeElement of the document A or B?
Comment 3 Hayato Ito 2011-05-29 23:44:10 PDT
Thank you for the comment. I appreciate it.

(In reply to comment #2)
> That sounds good to me. Putting activeElement on TreeScope and having it point to the shadow host when an element in the shadow has focus sounds good.
> 
> I don’t understand why focusedNode belongs on TreeScope. Is focusedNode() the same for all TreeScopes in a document? Why does having focusedNode on TreeScope make things easier?
> 
> focusedNode is completely internal to WebKit, right? Who uses it? Why would making it non-0 when a frame is focused make things easier? Seems like it might be good to know when you’re crossing an iframe boundary.

That's good point. Actually we don't need maintain each focusedNode in each scope. We can calculate an upper scope's direct focused child on the fly once we know a deepest focused Node.
e.g. If we know E is focused, we can easily calculate that A's direct focused child is B.

  - shadow-host - A
     - shadow-host - B
        - iframe - C
           - shadow-host - D
                <input> E ---> focused

Maybe the similar discussion can apply to the Document::m_focusedNode. A focusedNode is the same for all Documents (and iframes) within one 'page', isn't it? We can calculate each iframe's activeElement on the fly only if we know a deepest focused Node. Document::activeElement() is actually doing that. It looks that Document::m_focusedNode is only used internally to make implementations simpler.
I'd would like to treat iframe and shadow-root uniformly.
  - Both maintain a focusedNode.
  - Or neither maintain focusedNode. Only focusedController maintains focusedNode within one page.

I've not started to try implementation of this idea, so having focusedNode in each TreeScope might not be a good idea.  Anyway, it should be just a implementation detail, so I might change my mind if things won't go well as I expected.


> 
> Another question: What happens when you have this?
> 
> Document
>   - <shadow-host> <--- A
>     … shadow-root
>       - <content>
>     - <input> <--- B
> 
> Say node B has focus. Is the activeElement of the document A or B?

Interesting. In this case, document.activeElement should be 'A' for me at the first glance.
But we should discuss which is better and natural.
Comment 4 Roland Steiner 2011-06-07 23:15:26 PDT
(In reply to comment #1)
> When a node B is focused, the following things will happen:
>  - focusController::focusedFrame -> A
>  - outerDocument::focusedNode() -> 0 (Hmm.. I'd like to change this behavior. If that were A, some things would get simpler..?)
>  - innerDocument::focusedNode() -> B
>  - outerDocument.activeElement -> A
>  - innerDocument.activeElement -> B

This seems to be correct to me. The focused node is not in outerDocument, so outerDocument::focusedNode() can only be 0 (the <iframe> isn't what is focused, after all).

(In reply to comment #3)
>   - shadow-host - A
>      - shadow-host - B
>         - iframe - C
>            - shadow-host - D
>                 <input> E ---> focused

All in all, from the discussion it seems to me, that the question rather is how should 'activeElement' behave, rather than focusedNode (?).

One possibility could be to have a separate TreeScope.activeElement: a TreeScope's activeElement could point to the deepest ancestor node of the focused node that is still in the same TreeScope, while a Document's activeElement is the deepest ancestor node of the focused node that is still in the same document. (where ancestors cross document boundaries). However, this would require a distinction between 'Document-as-TreeScope' and 'Document-as-Document'.

> > Another question: What happens when you have this?
> > 
> > Document
> >   - <shadow-host> <--- A
> >     … shadow-root
> >       - <content>
> >     - <input> <--- B
> > 
> > Say node B has focus. Is the activeElement of the document A or B?
> 
> Interesting. In this case, document.activeElement should be 'A' for me at the first glance.
> But we should discuss which is better and natural.

(Assuming B is a child of the shadow host) IMHO this can only be 'B' as otherwise activeElement would require rendering knowledge.
Comment 5 Hayato Ito 2011-06-08 22:51:23 PDT
Thank you for the comments. Let me update the status.

I tried to implement this idea, keeping a focusedNode in each TreeScope.
But I have not used this approach in implementing <Tab> traversal in shadow, bug 61410.
I just used the current approach, keeping a focusedNode in each Document.

Although I am not sure which is better or not, let me close this bug as WONTFIX until we have clear thought that this is worth doing or not. I might open this issue again in the future.


(In reply to comment #4)
> (In reply to comment #1)
> > When a node B is focused, the following things will happen:
> >  - focusController::focusedFrame -> A
> >  - outerDocument::focusedNode() -> 0 (Hmm.. I'd like to change this behavior. If that were A, some things would get simpler..?)
> >  - innerDocument::focusedNode() -> B
> >  - outerDocument.activeElement -> A
> >  - innerDocument.activeElement -> B
> 
> This seems to be correct to me. The focused node is not in outerDocument, so outerDocument::focusedNode() can only be 0 (the <iframe> isn't what is focused, after all).
> 
> (In reply to comment #3)
> >   - shadow-host - A
> >      - shadow-host - B
> >         - iframe - C
> >            - shadow-host - D
> >                 <input> E ---> focused
> 
> All in all, from the discussion it seems to me, that the question rather is how should 'activeElement' behave, rather than focusedNode (?).
> 
> One possibility could be to have a separate TreeScope.activeElement: a TreeScope's activeElement could point to the deepest ancestor node of the focused node that is still in the same TreeScope, while a Document's activeElement is the deepest ancestor node of the focused node that is still in the same document. (where ancestors cross document boundaries). However, this would require a distinction between 'Document-as-TreeScope' and 'Document-as-Document'.
> 
> > > Another question: What happens when you have this?
> > > 
> > > Document
> > >   - <shadow-host> <--- A
> > >     … shadow-root
> > >       - <content>
> > >     - <input> <--- B
> > > 
> > > Say node B has focus. Is the activeElement of the document A or B?
> > 
> > Interesting. In this case, document.activeElement should be 'A' for me at the first glance.
> > But we should discuss which is better and natural.
> 
> (Assuming B is a child of the shadow host) IMHO this can only be 'B' as otherwise activeElement would require rendering knowledge.