This is an archived snapshot of W3C's public bugzilla bug tracker, decommissioned in April 2019. Please see the home page for more details.

Bug 17090 - [Shadow]: Listening to specific nodes, distributed to insertion points is hard
Summary: [Shadow]: Listening to specific nodes, distributed to insertion points is hard
Status: RESOLVED FIXED
Alias: None
Product: WebAppsWG
Classification: Unclassified
Component: HISTORICAL - Component Model (show other bugs)
Version: unspecified
Hardware: PC All
: P2 normal
Target Milestone: ---
Assignee: Dimitri Glazkov
QA Contact: public-webapps-bugzilla
URL:
Whiteboard:
Keywords:
Depends on:
Blocks: 14978
  Show dependency treegraph
 
Reported: 2012-05-17 19:33 UTC by Dimitri Glazkov
Modified: 2012-06-21 18:14 UTC (History)
1 user (show)

See Also:


Attachments

Description Dimitri Glazkov 2012-05-17 19:33:58 UTC
Consider this use case:

* There is an insertion point, with zero or more nodes distributed into it
* User clicks on one of the items
* The event handler in shadow DOM subtree wants to know which item was
clicked on.

Our newly-implemented lower-boundary encapsulation logic will
dutifully prevent this from happening.

So currently, the only solution here is to register an event listener
on each node that's distributed to the insertion point.

This seems suboptimal, since it effectively leaks the functional
encapsulation (now you have to worry about cleaning up the event
listeners when the node is moved, etc.)
Comment 1 Hayato Ito 2012-05-18 10:14:11 UTC
I agree that this use case is worth discussing.

In current spec, an event listener which is registered to an insertion point can not know which distributed node does trigger the event.
It sounds reasonable that developers want to know the clicked node through event.target as long as it does not break upper/lower boundary.
In current implementation, event.target returns InsertionPoint itself, not the distributed node which triggers the event.

(In reply to comment #0)
> Consider this use case:
> 
> * There is an insertion point, with zero or more nodes distributed into it
> * User clicks on one of the items
> * The event handler in shadow DOM subtree wants to know which item was
> clicked on.
> 
> Our newly-implemented lower-boundary encapsulation logic will
> dutifully prevent this from happening.
> 
> So currently, the only solution here is to register an event listener
> on each node that's distributed to the insertion point.
> 
> This seems suboptimal, since it effectively leaks the functional
> encapsulation (now you have to worry about cleaning up the event
> listeners when the node is moved, etc.)
Comment 2 Dimitri Glazkov 2012-06-12 16:55:36 UTC
I think we should break the encapsulation boundary symmetry here.

Since the insertion point is specifically designed to be replaced by contents of a shadow host when rendering, we can justify saying that the insertion points do not retarget.

Of course, this means we have to rethink the whole thing again :) Yikes!
Comment 3 Hayato Ito 2012-06-13 00:54:35 UTC
Okay. I am for it. Event dispatching returns to us. :)
I think that it must be useful for developers to know most *accurate* node as event.target as long as it does not break encapsulation.

Let me formulate which node event.target should be.

(In reply to comment #2)
> I think we should break the encapsulation boundary symmetry here.
> 
> Since the insertion point is specifically designed to be replaced by contents
> of a shadow host when rendering, we can justify saying that the insertion
> points do not retarget.
> 
> Of course, this means we have to rethink the whole thing again :) Yikes!
Comment 4 Hayato Ito 2012-06-13 12:04:39 UTC
I think we can change the current spec with a little modification for re-targeting event.target to support the new behavior.
I've just done a tiny experiment using WebKit and that worked well.

As for re-targeting event.relatedTarget, let me think further. Let me report the result later.

(In reply to comment #3)
> Okay. I am for it. Event dispatching returns to us. :)
> I think that it must be useful for developers to know most *accurate* node as
> event.target as long as it does not break encapsulation.
> 
> Let me formulate which node event.target should be.
> 
> (In reply to comment #2)
> > I think we should break the encapsulation boundary symmetry here.
> > 
> > Since the insertion point is specifically designed to be replaced by contents
> > of a shadow host when rendering, we can justify saying that the insertion
> > points do not retarget.
> > 
> > Of course, this means we have to rethink the whole thing again :) Yikes!
Comment 5 Hayato Ito 2012-06-14 07:46:24 UTC
I propose the following algorithms for 6.1 and 6.2 of Shadow DOM spec. That can achieve desired results.
I've just implemented a new algorithm which should produces an equivalent result, using WebKit. That seemed to work well.


6.1. Event Retargeting

Input
  NODE, a DOM node
Output
  TARGETS, a list of tuples, each containing NODE's ancestor and its relative target

1. Let STACK be a stack of DOM nodes
2. Let ANCESTOR be NODE
3. Repeat while ANCESTOR exists:
  1. If STACK is empty, push ANCESTOR into STACK
  2. Otherwise, if ANCESTOR is an insertion point
     1. Let TARGET be the DOM node at the top of STACK
     2. push TARGET into STACK
  3. Let TARGET be the DOM node at the top of STACK     
  4. Add (TARGET, ANCESTOR) tuple to TARGETS
  5. If ANCESTOR is a shadow root
    1. Pop STACK
  6. Set ANCESTOR to be the result of parent calculation algorithm, given ANCESTOR as input


6.2. Retargeting relatedTarget

Input
  NODE, the DOM node on which event listeners would be invoked
  RELATED, the related target for the event
Output
  ADJUSTED, the adjusted related target for NODE


1. Let TARGET be NODE
2. Let ADJUSTED be undefined
3. Repeat while TARGET exists
  1. Let ANCESTOR be RELATED
  2. Let STACK be a stack of DOM nodes
  3. Repeat while ANCESTOR exists:
    1. If STACK is empty, push ANCESTOR into STACK
    2. Otherwise, if ANCESTOR is an insertion point
       1. Let HEAD be the DOM node at the top of STACK
       2. push HEAD into STACK
    3. If a ANCESTOR and TARGET are in the same subtree:
       1. let ADJUSTED be the DOM node at the top of STACK
       2. stops
    4. If ANCESTOR is a shadow root
       1. Pop STACK
    5. Set ANCESTOR to be the result of parent calculation algorithm, given ANCESTOR as input
  4. If TARGET is a shadow root, let TARGET be the shadow host of TARGET
  5. Otherwise, let TARGET be TARGET's parent node
Comment 6 Hayato Ito 2012-06-14 09:03:43 UTC
Just for reference, the WIP implementation in WebKit is here:
https://bugs.webkit.org/show_bug.cgi?id=89073
Comment 7 Dimitri Glazkov 2012-06-14 16:26:18 UTC
(In reply to comment #6)

Good work! I'll put this into spec this tomorrow.
Comment 8 Hayato Ito 2012-06-15 11:33:35 UTC
I've updated the algorithm. Because we should treat the case for fallback elements in an insertion point, I've added a check logic which makes sure that LAST is actually distributed to a insertion point.

6.1. Event Retargeting

Input
  NODE, a DOM node
Output
  TARGETS, a list of tuples, each containing NODE's ancestor and its relative
target

1. Let STACK be a stack of DOM nodes
2. Let ANCESTOR be NODE
3. Let LAST be undefined
4. Repeat while ANCESTOR exists:
  1. If STACK is empty, push ANCESTOR into STACK
  2. Otherwise, if ANCESTOR is an insertion point or a shadow insertion point:
     1. If LAST is distributed or assigned into ANCESTOR:
       1. Let TARGET be the DOM node at the top of STACK
       2. push TARGET into STACK
  3. Let TARGET be the DOM node at the top of STACK     
  4. Add (TARGET, ANCESTOR) tuple to TARGETS
  5. If ANCESTOR is a shadow root, pop STACK
  6. Let LAST be ANCESTOR
  7. Set ANCESTOR to be the result of parent calculation algorithm, given ANCESTOR as input


6.2. Retargeting relatedTarget

Input
  NODE, the DOM node on which event listeners would be invoked
  RELATED, the related target for the event
Output
  ADJUSTED, the adjusted related target for NODE


1. Let TARGET be NODE
2. Let ADJUSTED be undefined
3. Repeat while TARGET exists
  1. Let STACK be a stack of DOM nodes
  2. Let ANCESTOR be RELATED
  3. Let LAST be undefined
  4. Repeat while ANCESTOR exists:
    1. If STACK is empty, push ANCESTOR into STACK
    2. Otherwise, if ANCESTOR is an insertion point or a shadow insertion point:
      1. If LAST is distributed or assigned into ANCESTOR:
        1. Let HEAD be the DOM node at the top of STACK
        2. push HEAD into STACK
    3. If a ANCESTOR and TARGET are in the same subtree:
       1. let ADJUSTED be the DOM node at the top of STACK
       2. stops
    4. If ANCESTOR is a shadow root, pop STACK
    5. Let LAST be ANCESTOR
    6. Set ANCESTOR to be the result of parent calculation algorithm, given ANCESTOR as input
  5. If TARGET is a shadow root, let TARGET be the shadow host of TARGET
  6. Otherwise, let TARGET be TARGET's parent node
Comment 9 Dimitri Glazkov 2012-06-15 23:35:24 UTC
http://dvcs.w3.org/hg/webcomponents/rev/4db7e315afb5

Did I get this right?

Please reopen if I made a mistake.
Comment 10 Hayato Ito 2012-06-21 18:14:19 UTC
Looks good and I've just landed the implementation in WebKit as http://trac.webkit.org/changeset/120945.

(In reply to comment #9)
> http://dvcs.w3.org/hg/webcomponents/rev/4db7e315afb5
> 
> Did I get this right?
> 
> Please reopen if I made a mistake.