[CSSWG] Minutes New York F2F 2022-08-01 Part III: CSS Highlight API [css-highlight-api]

=========================================
  These are the official CSSWG minutes.
  Unless you're correcting the minutes,
 Please respond by starting a new thread
   with an appropriate subject line.
=========================================


CSS Highlight API
-----------------

  - dandclark explained the current 3 proposals for issue #7513
      (Approaches for dispatching highlight pointer events) and
      indicated a preference for option C which is having two events,
      one for highlight and one for DOM.
      - emilio introduced a fourth option to add an API to answer if a
          certain point intersects a highlight. There was significant
          discussion around the feasibility of the approach and
          interest in investigating more to see if some concerns could
          be mitigated. emilio added more details to github after the
          meeting:
https://github.com/w3c/csswg-drafts/issues/7513#issuecomment-1201631397
      - TabAtkins raised the possibility of changing option A where
          each highlight gets its own event to require registration
          before dispatch.
      - Discussion will continue on github to determine the feasibility
          of these new options.
  - Having an API to return what highlights intersected broadly made
      sense as a solution to issue #7512 (Is a HighlightPointerEvent
      type needed?) however the final discussion will need to wait on a
      decision for issue #7513.

===== FULL MEETING MINUTES ======

Agenda: https://github.com/w3c/csswg-drafts/projects/30

Scribe: TabAtkins

CSS Highlight API
=================

Approaches for dispatching highlight pointer events
---------------------------------------------------
  github: https://github.com/w3c/csswg-drafts/issues/7513

  dandclark: So pointer events with highlights, this is about making
             highlight pseudos interactive like for a spellchecker

  dandclark: First question is how do we dispatch these events, what's
             the event order
  dandclark: I assume the highlight object is the right thing to
             receive the event, rather than the individual ranges
  dandclark: If I have a bunch of ranges on a single highlight, I
             probably want to do the same thing for each
  dandclark: seems silly to have to set an event listener on each
             range, especially as the highlight moves
  dandclark: and when considering dispatches, need to consider there
             can be multiple types of highlights, and possibly
             overlapping
  dandclark: like a find-on-page that emphasizes and scrolls, and also
             a spellcheck that underlines misspelled words and shows
             spelling suggestions
  dandclark: might want one of those to eat the click, so spelling
             error can win over find-on-page

  dandclark: so I can talk about a few approaches
  dandclark: In the issue, version A is each highlight gets its own
             event, in descending priority order
  dandclark: They can be cancelable, so the default is to advance to
             the next highlight, and you can preventDefault()
  dandclark: Suspicious to fire an arbitrary number of events for a
             single action though
  dandclark: Approach B is a single event for the top priority
             highlight, define event path so it goes through other
             highlights, then bubbles to dom tree
  dandclark: this seems like pretty normal approach. But there's a web
             compat problem
  dandclark: Currently pointer events can only ever be an element
  dandclark: but now suddenly they can have non-element targets
  dandclark: so might break assumptions
  dandclark: The Edge explainer suggests lying about the event target
             to get around this
  dandclark: Thinking about it more, think it's not the right approach
  dandclark: Approach C is a combo. We dispatch two events, one for
             highlights, one for dom tree. Highlight one propagates
             through overlapping highlights. If it's not
             preventDefault'd by the end, we fire a new one on the dom
             tree
  dandclark: That dual-event approach is where I lean now

  emilio: Rather than messing with the dom events and re-targeting, can
          we add an API to highlight that tells "is point X
          intersecting the highlight?"? seems like that would let you
          do what you want with regular dom events
  emilio: you handle the pointer event and ask the highlights if the
          point overlaps, and just deal with normal dom event
  emilio: Seems simpler but still gives all the use-cases
  emilio: If you have two objects, handling pointer event on one or the
          other...
  emilio: the normal element pointer event should still work even if
          you click on the highlight
  emilio: Seems simpler and less risky, and doesn't introduce new event
          model
  emilio: and will work with touch events, etc
  emilio: Curious if it's been considered?
  TabAtkins: So assumption is you'd iterate through all your highlights
             and check?
  emilio: Yes. and they can check if it's been defaulted, you can deal
          with the order yourself, etc.

  dandclark: This seems useful for- given I have a click with an
             offset, show me all the highlights under this location? or
             go through each highlight in the registry and check?
  dandclark: Can def see if there's a pointer event handler on the
             body, and I get an event, could ask platform for all the
             highlights at this point
  <emilio> boolean intersects(long clientX, long clientY)
  <bkardell> emilio: that is on highlight?
  emilio: Was thinking about method on the highlight object
  emilio: so you iterate over the highlights in the order you want
  emilio: but that should provide all the flexibility you'll ever need

  emilio: If we want something more convenient, could put one on the
          highlight registry that gives back all of them
  emilio: would just be sugar over a for loop
  emilio: so could use if you want a central place to handle all the
          highlights
  emilio: but might also want to deal with each highlight separately

  flackr: My point was partially overlapped with emilio, concerned
          about a special event before the dom event
  flackr: Could consider the highlight to be the normal action, so if
          the normal event wasn't defaulted you dispatch to highlight
  flackr: Longer term, we want to support css pseudo targets, wondering
          if that helps so we can just target those
  dandclark: The highlight pseudo is a pseudo object, it's a little
             special...
  flackr: I think my main concern is whether those should be given the
          event first
  flackr: I think visually the highlight is behind the text, so from
          dev pov the element should get first crack, and pseudo gets
          opportunity after

  dbaron: I think it's worth trying to get event.stopPropagation vs
          preventDefault to behave as closely as possible to normal
  dbaron: I think what Dan was originally defining was to make
          preventDefault act like stopPropagation
  dbaron: Hope we can do something better even if the dom event spec
          needs to expose some extra concepts

  dbaron: Thoughts on emilio's proposal
  dbaron: first is I'm a little concerned about an approach that forces
          you to add event listeners to the doc, for cases where impls
          want to make optimizations for passive listeners
  dbaron: If it's an event type where registering a non-passive
          listener, and you need to register non-passive for the
          highlight, you have to take the perf penalty document-wide
  dbaron: That seems undesirable
  dbaron: so I think that's a reason you might want to have
          eventListener registration be on more specific targets
  dandclark: I like the simplicity of emilio's idea
  dandclark: if we can find a way around the perf hit

  emilio: The initial proposal was about clicks
  emilio: Don't think we can get much opt about passive vs non-passive
          event listeners
  emilio: don't see how having highlights doesn't prevent you from
          losing optimizations
  dbaron: What you said about the way to use the proposal is put an
          Event Listener on the document, and look up the point
  dbaron: And that way of using it can be problematic
  emilio: Right, if you care about scroll and things that benefit from
          passive
  emilio: But API doesn't let you use it for other event types
  dbaron: I think when it's like "this is only for clicks *right now*",
          unsure we want to design around that
  emilio: So API right now takes an x/y
  emilio: Have clear text for mouse events to resolve that
  emilio: Could let the method take an event object instead, so we can
          limit it to certain event types
  dbaron: Ah yeah, other point I'd forgot. I think taking x/y doesn't
          handle [missed]
  dbaron: you might click a piece of text with highlights that obscures
          another piece with highlights
  dbaron: you're probably interested in the top element's highlights,
          not the obscured one
  emilio: Could define the api similar to getelementfrompoint where you
          do account for hit-testing
  emilio: Could define to only care about rects, like union the ranges
  emilio: or like elementFromPoint which cares about hit-testing and
          p-e:none/etc
  emilio: Seems you could get that with just an x/y if you define it to
          do hit-testing
  dbaron: yeah that sounds like it works
  emilio: So define it in terms of elementsFromPoint
  emilio: If any of the elements intersect the highlight... something
          like that
  emilio: specific semantics for intersecting highlights could be left
          to...
  emilio: if both intersect the consumer can choose

  flackr: I think x/y works if you can get highlights on the targeted
          element
  flackr: that'll account for hit-testing
  flackr: but this is def going to be a performance issue, even if
          limited to mouse events
  flackr: people might want to drag highlights, so they'll have
          pointerstart, and that means we have to delay scrolling
  flackr: but if we fire on the highlight we can recognize it won't
          stop scrolling
  emilio: But that would penalize other scenarios
  emilio: not sure what the best way to do it is
  emilio: needs real thought especially for pointerdown
  emilio: [missed]
  emilio: There are use-cases for pointerdown
  emilio: If we extend proposal, we'd dispatch to highlights, then to
          web content... we'd do it for every event type anyone would
          care about
  flackr: Highlight needs to be in the event target tree, but yeah
          currently tree is strictly ancestor tree so that would be a
          disconnect

  astearns: Question about emilio's proposal
  astearns: You get coords, ask document for highlights.
  astearns: Are we still able to have top priority highlight prevent
            other highlights from responding?
  emilio: Yes, just check if the event was preventDefault'd and return
          rather than moving on to the next highlight
  <dbaron> (do you mean stopPropagation or preventDefault for that
           case?)
  <TabAtkins> (don't think it matters for the discussion)
  astearns: So it's manual?
  TabAtkins: Yes, all fully manually handled, they're not fired on the
             highlights by the browser stuff

  fremy: So if you have multiple types of code doing highlights they
         don't know about each other
  fremy: How can they work together?
  emilio: Initial proposal was a bool on the highlight
  emilio: If you need to be aware of one vs the other you need a
          central place to do it
  TabAtkins: Right, given dan's initial use-cases (scroll and
             spellcheck) you *need* them to coordinate with each other
             anyway
  emilio: With my proposal there's no change to event prop. You fire
          the same events as with no highlights
  emilio: Up to the website to either centralize their highlight
          handling, or have different listeners that handle them
          separately
  fremy: But you can't decide which acts first
  emilio: With a bool and two coords approach, that's right because you
          have to handle the highlights
  emilio: But if we have the method on the highlight registry that
          returns highlights in order, you can check yourself if the
          one you're in charge of is first
  fremy: So if you're not, do you try again?
  emilio: If you don't have event listeners in the right order you can
          have weirdness
  fremy: But order isn't one order
  fremy: Not sure there's one correct order
  dbaron: I think emilio's proposal is that page author has to write
          their own event registration system if they want to deal with
          multiple highlights
  dbaron: We provide an api in the platform that says "here's the
          ordered list of highlights affected by this" and the author
          can process the list with their own reg system however they
          want
  fremy: Yeah, just weird we don't provide that
  fremy: You assume libraries will cooperate, not clear to me they will
         if they're different sources
  fremy: strange to assume that
  <fantasai> +1 fremy
  <GameMaker> +1 fremy as well
  emilio: my proposal is minimum that lets you build this
  emilio: but solving general case firing event listeners in an order
          that solves every use case, yeah it's a much more complicated
          problem
  emilio: have to define which events do this, we start with click but
          have a million events

  fremy: Another option is we provide it. For each highlight, define
         which events you want to receive, and we have an api that
         dispatches to them in a pseudo-event manner
  fremy: So does preventDefault for you, etc
  emilio: I'm not even sure there's a canonical order you want to define
          events on
  emilio: spellcheck error with a link inside
  emilio: Might seem reasonable to fire spellcheck first and link if
          not handled, but might be reasonable other way around
  emilio: I think easiest is to make website choose
  dandclark: Advantage of having platform handle this, where we just
             propagate the event, is we already had an order of
             highlights
  dandclark: so think there is an advantage to use the existing order
             since things are built in already
  emilio: There is reasons to do before, but also after
  emilio: Not clear to me why we should do them before or after in
          specific
  fremy: I think this makes sense, let authors to do this
  emilio: I think having an api that gives you ranges in order would be
          low level
  emilio: if libraries say we can't use this because there's no good
          way to coordinate, then we can decide whether to fire events
          before or after
  emilio: seems like an elegant way to handle the use-cases without
          making a call

  scribe: fantasai

  TabAtkins: I think one detail of fremy's proposal is being missed,
             which is getting the list ordered is reasonable and
             authors handling on his own
  TabAtkins: The problem is that you have to re-invent event dispatch,
             and hope other libraries can coordinate with you
  TabAtkins: Given a list of highlights to dispatch on, it fires events
             using normal event listener mechanics
  TabAtkins: handles preventDefault etc, as if part of the normal event
             dispatch order
  TabAtkins: so don't have to re-invent
  TabAtkins: doesn't require cross-library coordination of the events
             themselves
  TabAtkins: e.g. fire highlight event, take a list of highlights,
             cranks through them in order as if part of event chain
  emilio: So would be equivalent to making highlight event listener
  emilio: so basically it would be a matter of writing that loop with
          the current event and dealing the pD/etc in that order
  emilio: with api from before, give me a list of highlights, would be
          a matter of writing that loop with current event and dealing
          with event propagation and stuff manually
  emilio: that seems reasonable
  emilio: page would be responsible at some point, to propagate the
          highlight events
  TabAtkins: on the highlight registry, yeah
  emilio: Seems like either extension or replacement
  emilio: Still requires page's coordination

  astearns: Tab described your first option from the issue?
  dandclark: Sounds similar, except instead of dispatching, need to
             register to dispatch
  TabAtkins: Yes, page author still has to handle ordering and do what
             they need to do
  TabAtkins: but otherwise platform handles events for you
  dandclark: So if I have highlights on my page
  TabAtkins: Ask the highlight registry are at this x,y, then call this
             with all of those
  TabAtkins: in priority order
  TabAtkins: if a highlight preventsDefault, then later things don't
             get the event
  TabAtkins: which is what you want
  TabAtkins: [missed]

  dandclark: How do we know it's not happening twice?
  dandclark: If my final page library says, okay, I need to trigger
             this ? and propagate the highlights
  dandclark: [too fast]
  dandclark: I have two different libraries
  TabAtkins: If you have 2 libraries both installing event handler on
             the element
  TabAtkins: If not preventDefault, each one would run through list,
             and you'd get repetitious behavior
  florian: This approach both allows but also requires you to coordinate
  florian: simple scenarios can coordinate by accident, but overall, if
           doing with multiple independent libraries
  florian: need some kind of plan for interaction
  florian: you need to think through it

  florian: Another thing I wanted to ask, unsure if decides,
  florian: all this discussion here, we're going to have a very similar
           discussion for every other type of pseudo
  florian: e.g. ellipsis
  florian: We shouldn't have 3-4 different solutions for each type of
           pseudo
  florian: I think this approach works better, you're in charge of
           coordinating, coordinate to your heart's delight
  florian: but it is a low-level approach, you *have to* coordinate
  florian: For those who have thought about this, how do you generalize
           to other pseudos?

  TabAtkins: Only concern I have is the actual invocation of your
             behavior, already have API for that called event listeners
  TabAtkins: not using that sucks
  TabAtkins: but if we recommend to authors to call dispatchEvent on
             your things, that's probably sufficient
  TabAtkins: we have communication API ... but at least not inventing a
             new callback system
  florian: can we do both? By default have a dispatcher that that walks
           up the highlight priority list
  florian: and if you don't like that, do your own?
  florian: If you don't say anything, we dispatch like this. If you
           don't like it, roll your own
  fremy: If you want [missed]
  fremy: we already have mechanism for that
  emilio: But initial proposal fires at DOM
  fremy: If you decide that you want to dispatch something, if it's
         important, you can say it's capture phase. We already have
         mechanism for it, this coordination happens every day
  fremy: We have bubble phase, capturing, etc.
  fremy: we already have these
  fremy: it works

  emilio: If the page hasn't called dispatchEvent on the registry
  emilio: if we do ourselves, prevents page from doing it later
  emilio: so only default we could choose is after DOM dispatch
  emilio: but might want to dispatch earlier than that
  emilio: so I think making highlight inherit from EventTarget makes
          sense
  emilio: and propagating to highlight whenever page wants is reasonable
  emilio: but how would that look like?
  emilio: undefined ?? x,?
  emilio: do for mouse events normally?
  emilio: do the list thing?
  TabAtkins: Do the list thing manually
  TabAtkins: You can check preventDefault by defaultPrevented
  TabAtkins: We can put example code in the spec
  emilio: So you don't want an API that does it for you?
  TabAtkins: Probably not
  *fantasai would like a summary

  dandclark: If we have 2 libraries that are not coordinating, which
             one writes the for loop?
  TabAtkins: You should check to see if default has been prevented
  TabAtkins: if you're first, you can preventDefault() if you've
             handled things
  TabAtkins: or you can just stop propagation immediately
  TabAtkins: Event system can handle that
  fremy: You may say that you don't want to prevent, but still click on
         the button
  TabAtkins: Events should check themselves to see if prevented
  fremy: if you preventDefault, the button won't click

  emilio: That's the key of the issue, there's no good answer on
          whatever is the good answer
  emilio: if you have a spelling error on a button, you might want a
          popup to suggest a fix
  emilio: or you might want to click the button
  emilio: It's not known which you want
  fremy: I agree, but if every library makes its own for loop because
         it's necessary, then ??
  emilio: We're not suggesting that
  emilio: The library doesn't need to write the for loop. They add e.g.
          click event listener on its object
  emilio: something on the page writes the for loop once, and
          dispatches to all highlights
  emilio: So no library needs to write the for loop
  emilio: That's done once whenever page wants to handle that

  fremy: If I use grammarly, grammarly needs to write the for loop or
         else it doesn't work
  fremy: So they will write it because they need it
  emilio: Then they would only handle ranges that they register
  fremy: But it still does not solve issue of order
  fremy: run the grammarly thing and no consideration that some other
         things on top of them
  emilio: If preventDefaulted then ???
  fremy: Still the same issue, it's not preventDefaulted if there's
         another highlight before it
  fremy: You have multiple highlights. Grammarly wants to work. So they
         will need to add an event to handle fix. If they want to make
         for loop, they can only include grammarly stuff
  fremy: but they might want to run grammarly code only if no highlight
         on top of them
  emilio: If the page has dispatched the same event to the highlight
          already
  emilio: like you would dispatch the mouse event from the platform
  emilio: If another highlight provider did something, registered dom
          event in capturephase, and have preventDefaulted it, then
          grammarly can check that and bail out
  emilio: This event has been handled before, we may not even need our
          for loop
  fremy: I don't quite understand, it's not clear what are we doing

  TabAtkins: There are various ways for event handler to check if I've
             been called with this before
  TabAtkins: In most cases not required, all first-party content, and
             don't care much
  TabAtkins: should be generally OK, and complex cases need complexity
             anyway
  TabAtkins: You can say I only want to be dispatched once, and
             guarantee that on your own, by checking and immediately
             returning if you get called a second time
  fremy: ...
  TabAtkins: Normal event dispatch would not call the same event on the
             same element more than once
  TabAtkins: but we're not using normal event dispatch
  <TabAtkins> (store the last event you saw dispatched to yourself, and
              check if the new one is the same as the last; if so
              return immediately)
  <TabAtkins> so you can avoid the "double-dispatch" entirely on your
              own without any 3rd party coordination

  astearns: Any other lingering questions, or take back to the issue to
            discuss the current state of this fourth way
  * fantasai doesn't quite understand what the proposal is
  dandclark: I agree with taking back to the issue, I'm also a bit lost
  astearns: Emilio, can I ask you to write up the proposal with all the
            details so that Dan has something to digest more easily
            than the minutes?

  dandclark: Does this still have the perf issue dbaron raised?
  dandclark: [missed]
  emilio: For clicks sure there's no perf issue
  emilio: For things like pointerDown, if you want notPassive handling
          of pointerDown, it feels like a global event listener is
          maybe not ideal
  emilio: This may be fixable another way
  emilio: like, maybe we need another member of listenerOptions
  emilio: I don't have an answer for that right now

  flackr: Maybe the highlight could have a different touchAction?
  flackr: Normally we encourage people to use passive events and use
          touch Action to specify whether to scroll or not
  flackr: if we had a way to specify the touch-action on the highlights
          to not depend
  emilio: Yes, you might want to allow touch-action on highlight
          pseudos, make that behave properly
  emilio: it's a very good idea

  astearns: Anything more on this issue?
  dandclark: Yes, I think we're good
  astearns: Do we discuss next issue? or wait on the details of this
            one?
  dandclark: I think we wait
  dandclark: Next one is about answering question of given dispatch,
             how to we determine which range was targetted. I suggested
             adding a new event type
  astearns: so closing off current issue and going to next issue

Is a HighlightPointerEvent type needed?
---------------------------------------
  github: https://github.com/w3c/csswg-drafts/issues/7512

  dandclark: If the solution we go with here involves highlight being
             an event target
  dandclark: which either way I think will be the case
  dandclark: how do I know which range was targeted?
  dandclark: e.g. I'm a spell checker, but got a click, but which range
             was clicked?
  dandclark: Multiple ways to fix
  dandclark: Original solution maybe doesn't make sense, but if we do
             have some kind of intersects API then maybe that answers
             the question
  dandclark: If we're re-using the existing pointer events, then
             [missed]
  dandclark: Call some kind of API, what were my intersection ranges?
  dandclark: I need to know which ranges and which highlight
  dandclark: perhaps API could be just onHighlight? similar to what ??
             was proposing
  dandclark: I add intersects() function to Highlight, it takes x,y and
             it returns a list of ranges in the highlight
  dandclark: maybe that's the simplest
  dandclark: highlight could potentially have several overlapping on
             the same text
  dandclark: Does that sound reasonable?

  emilio: yes...
  emilio: given we already have a hit testing function that gives you
          list of highlights, maybe also takes ranges
  emilio: I think this is related to previous issue
  emilio: Either way, if we go through event listener path or dom event
          thing, or the thing we were discussing
  emilio: I'd rather resolve that first, even if it's not the final
          solution
  emilio: maybe instead of sequence of highlights we need to return
          sequence of highlights and ranges
  dandclark: Sounds good, just want to make sure the problem is on the
             radar

  flackr: I don't think we should have a specific event type, because I
          think we will want to target multiple types of events at
          highlights
  flackr: prefer to have event that bubbles up. You dispatch to
          specific range, and then on the highlight you can check for
          which range
  emilio: but target would be the DOM element
  flackr: right, but ...
  emilio: But unsure about event target
  emilio: [missed]
  emilio: We may need to figure out a way... obviously, if we provide
          the API so that if we dispatch both highlights and ranges,
          the page could put the ranges property and expand all of the
          object
  emilio: It's worth thinking about, but depends on other issues

  GameMaker: I just want to make sure that when we're talking about
             ranges that highlights are done in abstract ranges, so
             keep that in mind
  GameMaker: make sure we remember they're AbstractRanges
  emilio: You can build highlights with StaticRange and Range
  GameMaker: Yeah, AbstractRange can be either
  emilio: Right

  astearns: Anything else on this issue?
  astearns: OK, we'll come back to both of these relatively soon

Received on Tuesday, 30 August 2022 00:00:23 UTC