Skip to content

[css-ui-4] computed value of user-select #336

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
yoichio opened this issue Jul 21, 2016 · 34 comments
Closed

[css-ui-4] computed value of user-select #336

yoichio opened this issue Jul 21, 2016 · 34 comments

Comments

@yoichio
Copy link

yoichio commented Jul 21, 2016

https://drafts.csswg.org/css-ui-4/#propdef-user-select

In this section, the spec says computed value is determined in a way rather than inheritance.
However, computed value is used for inheritance and user-select is not ineherited:
https://drafts.csswg.org/css-cascade/#value-stages

Is this really computed value, not used value?

@frivoal frivoal added the css-ui-4 Current Work label Jul 21, 2016
@frivoal frivoal self-assigned this Jul 21, 2016
@frivoal
Copy link
Collaborator

frivoal commented Jul 21, 2016

Since there is no layout involved, it seems more natural to me to do it on the computed value, but I suppose we could do as a used value as well. Let's see what the group thinks

@kojiishi
Copy link
Contributor

Using computed value has additional cost to implement at least for Blink, even if it doesn't involve layout, so we prefer used value unless there is strong reasons to use computed values. @dbaron ?

@upsuper
Copy link
Member

upsuper commented Jul 25, 2016

Not completely sure what is the question here. Is this about the special computing rules under

The computed value is the specified value, except:

If it is, I think that making it computed value adds complexity to impl for Gecko as well, especially the rule for editable elements. I hope it could be done either in the UA sheet (so as specified value, no special computation rule), or as used value.

@frivoal
Copy link
Collaborator

frivoal commented Jul 25, 2016

I hope it could be done either in the UA sheet (so as specified value, no special computation rule), or as used value.

I don't think it cannot be done in the UA stylesheet, but I don't think there's a big problem in doing it as a used value instead of computed value. The need to lookup the value on parent elements, and to check whether the element it applies to is editable or not remains, so there is some inherent complexity to solve, but if that's more easily dealt with at used value time in both Gecko and Blink, we should probably do it there.

@upsuper
Copy link
Member

upsuper commented Jul 25, 2016

Dealing things as non-queryable value is always easier than queryable value I suppose :)

@astearns astearns removed the Agenda+ label Aug 1, 2016
@gregwhitworth
Copy link
Contributor

We implemented -ms-user-select to resolve auto at computed value time and would prefer for it to remain that way.

@kojiishi
Copy link
Contributor

kojiishi commented Aug 4, 2016

I spoke from general principle, but then read the spec a bit more and wrote a test to understand the situation around this property. This looks like complicated enough that it's hard to make a call just from a general principle.

  • Blink/WebKit implements this property as inherited.
  • The spec defines this as non-inherited, but tries to be as if auto and none inherits.
  • Edge either inherits, or non-inherited but do its own inheritance following the spec; can't determine from the test.
  • Gecko doesn't inherit, and doesn't implement the computed value behavior of auto except for editable elements.

Were there discussions and resolutions to make this property non-inherited, but do its own inheritance only for auto and none?

@frivoal
Copy link
Collaborator

frivoal commented Aug 5, 2016

I don't remember where we discussed this, so finding the minutes will take a while, but yes, I did present this design and explain the justification behind it to the WG when adding it to the spec.

Long story short: Yes, I do know that this description is not a perfect match with existing reality, but nothing is, since existing reality is not fully interoperable. This is meant to be a compromise trying to preserve as much as possible interoperability where there is some already, and at the same time to be as sane as possible when we have some room for maneuver.

@frivoal
Copy link
Collaborator

frivoal commented Aug 5, 2016

Links to previous discussions:

@kojiishi
Copy link
Contributor

kojiishi commented Aug 8, 2016

Thank you for the pointers. @yoichio, @yosinch, and I discussed on this, it looks like our concern was not explicitly discussed yet in the past, so please allow us to raise.

Background

From the spec, it looks like the intention is to make contain not to inherit but other values to inherit. We do not have a general mechanism for some values of a property inherits but some do not.

We compute all properties for all elements, regardless whether a property is used or not. Inheritance is normally computed by sharing a data structure that contains all inherited properties from parents.

Since the current spec defines user-select as not-inherited, but computes auto depends on parent's computed style, this logic needs to run on every element, in a way that it is not shareable, even when the property is not used. We'd like to avoid this performance impact.

Proposal

We propose to split values to two separate properties depends on whether we want to inherit or not.

user-select: text | all | none
initial: text
inherited: yes
user-select-contain: none | contain
initial: none
inherited: no

This change will add a new combination, all and contain, which looks like a positive side effect for authors.

Thoughts?

@frivoal
Copy link
Collaborator

frivoal commented Aug 8, 2016

Can you clarify if my understanding is correct?

  • for both properties, the computed value is the specified value
  • contain+text does the same as what contain does today
  • contain+none does the same as none
  • contain+all means:
    • if you are starting the selection from outside of the element, the same as contain today
    • if the selection would start from inside the element, select the entire element, but do not allow extending the selection outside of it

Is that right? I'd need to think deeper about this, but based on this first read (and bikeshedding aside), I think this might work.

A couple things we would probably need to tweak, though:

  • user-select-contain would need an auto value that computes to (or behaves as, if you prefer used value time) contain on editable elements, and as none on the rest.
  • user-select may also need to take an auto value. It wouldn't do the magic inheritance thing of today, but:
    • all implementations have it
    • we need auto to behave as none on ::before, ::after, ::marker, unless we want to break compat and make these things selectable by default, or deal with that in the UA stylesheet.

@frivoal
Copy link
Collaborator

frivoal commented Aug 8, 2016

@gregwhitworth Since Microsoft has the only implementation of user-select: contain (called -ms-user-select: element in your implementation), I'm curious what you think of the proposal above.

@frivoal
Copy link
Collaborator

frivoal commented Aug 8, 2016

Also, note that one other reason for the magic auto-based pseudo inheretiance was to make it possible to preserve a Mozilla behavior: in their implementation, user-select doesn't inherit into absolute positioned children. Mozilla has said that this was a historical accident, and that they're OK with changing that, so I've changed the computed value rules of auto accordingly, but the auto-based pseudo-inheritance does allow us to do this kind of thing, while the proposed design does not. Personally, I'm OK with losing that ability, but I think it's worth mentioning it while we're discussing the design.

@kojiishi
Copy link
Contributor

kojiishi commented Aug 8, 2016

for both properties, the computed value is the specified value

Yes.

contain+text does the same as what contain does today

Yes.

contain+none does the same as none

Yes.

contain+all means:...

Yes. The latter case is an additional behavior from the current spec.

user-select-contain would need an auto value that computes to (or behaves as, if you prefer used value time) contain on editable elements, and as none on the rest.

I'm ok with it, but a question is if author wants to set user-select-contain: none to editable elements or textarea. If it's not useful, we can just set to contain for such elements; i.e.,

input[type=text], textarea, [contenteditable] { user-select-contain: contain; }

correct?

we need auto to behave as none on ::before, ::after, ::marker

I haven't tested this yet, is it interoperable today, including Blink and WebKit? Is there a test?

Assuming yes, again, if you just want to set to a specific value in specific cases, auto isn't really needed, you can just set it either by UA stylesheet or by code. auto computing to different values has additional complexity than that, so we'd like to limit it to only when it cannot be achieved by other methods.

@kojiishi
Copy link
Contributor

kojiishi commented Aug 8, 2016

BTW, Gecko's implementation of -webkit-user-select doesn't inherit at all, as far as I can tell from my test.

And Gecko doesn't implement -moz-user-select nor unprefixed, unless I'm mistaken in my test, correct?

@frivoal
Copy link
Collaborator

frivoal commented Aug 8, 2016

I'm ok with it, but a question is if author wants to set user-select-contain: none to editable elements or textarea. If it's not useful, we can just set to contain for such elements; i.e.,
input[type=text], textarea, [contenteditable] { user-select-contain: contain; }
correct?

It is probably not useful to allow editable elements to have user-select-contain: none, but that means we cannot do it via the UA stylesheet, since people would then be able to override it. But thinking again, that does not mean we need an auto value after all. We just need to say that both user-select-contain: none and user-select-contain: contain compute to (or behave as, if we don't want to do it as computed value time) user-select-contain: contain on editable elements.

@frivoal
Copy link
Collaborator

frivoal commented Aug 8, 2016

And Gecko doesn't implement -moz-user-select nor unprefixed, unless I'm mistaken in my test, correct?

Gecko does implement -moz-user-select, with the values auto, text, none, and all (also -moz-none, but since FF21, that does the same as none). It used to implement partial inheritance via auto with one exception: don't inherit into abspos. But as far as I can tell, the latest version no longer has that exception, making "inheritance via auto" and actual inheritance effectively the same. The behavior of -webkit-user-select is, as far as I can tell after a few minutes of poking at it, the same as the behavior of -moz-user-select.

By the way, I am basing my conclusions not on doing various queries via the OM, as I don't fully trust the OM to accurately reflect what the browser's internals are. Instead, I am checking the actual behavior. Your test uses getComputedStyle(), and in this case it seems indeed not to match reality.

On top of inconsistencies across browsers, this sort of wackyness is also why my spec isn't a 100% match of existing implementations. Instead I tried to preserve the useful observable behavior.

@kojiishi
Copy link
Contributor

kojiishi commented Aug 8, 2016

since people would then be able to override it

I think it's ok to allow override when needed in edge cases, such as nested editable host. We should provide the correct default, while allowing authors to override, looks good design to me.

BTW, the link to "editable host" is broken in the spec, you probably mean http://w3c.github.io/editing/contentEditable.html#dfn-editing-host correct?

Thanks for the info for Gecko, I didn't know it can support properties without exposing to OM.

@frivoal
Copy link
Collaborator

frivoal commented Aug 9, 2016

BTW, the link to "editable host" is broken in the spec, you probably mean http://w3c.github.io/editing/contentEditable.html#dfn-editing-host correct?

The specs have been moved around, and urls and links not preserved 😢. The HTML5 REC has the same issue btw: if you try to follow the links from https://www.w3.org/TR/html5/editing.html#editing-host, you'll end up in the same place.

The link you suggest seems to be the correct one in W3C land, but the WHATWG definition is more exhaustive: https://w3c.github.io/editing/execCommand.html#editing-host

@johanneswilm, Is the difference between your definition and the WHATWG's intentional? Could you work with the WHATWG to resolve the differences (you don't have the mention about design-mode, they lack handling values of contenteditable other than true and false

@frivoal
Copy link
Collaborator

frivoal commented Aug 9, 2016

Thanks for the info for Gecko, I didn't know it can support properties without exposing to OM.

Also, I believe the opposite is also true, there are a few properties in gecko which are parsed (and I think exposed to the OM, but I might be wrong about that, or it might only be sometimes), yet do absolutely nothing. A long time ago, it seems that people in charge of the parser thought it would be a good idea to support anything they found in a spec, regardless of whether the teams in charge of the rest of the engine were prepared to do the rest. Thankfully, this hasn't happened in a looong time, but I believe there are a few remains of this.

@frivoal
Copy link
Collaborator

frivoal commented Aug 9, 2016

I think it's ok to allow override when needed in edge cases, such as nested editable host. We should provide the correct default, while allowing authors to override, looks good design to me.

Maybe. Does any browser support that behavior though? Browsers that don't have the contain value explicitly still have that behavior on editable elements, and I don't think anyone makes it possible for selections to escape the editable element. Also, I don't think I've heard anyone calling for it (but that might just be me not listening enough). In the absence of both implementations or demand, I though it would be simpler to just not allow it. If there's evidence to the contrary, I'm open to changing my mind on this though, as my opposition based on practical concerns, not on a matter of principle.

@kojiishi
Copy link
Contributor

We experimented how feasible the suggestion to create two internal properties, one inherit and one not-inherit, and make user-select as a shorthand for them.

It worked ok, but a discussion with style-dev concluded that we prefer not making them internal.

So we'd like to back to the discussion if WG is ok to make 3 properties; one inherit, one not inherit, and user-select as a shorthand to them.

@frivoal can you update your side? IIUC you're talking to someone for the 3 properties proposal?

@kojiishi kojiishi changed the title [css-ui-4][user-select] value is used value? [css-ui-4][user-select] per-value inheritance Sep 12, 2016
@frivoal
Copy link
Collaborator

frivoal commented Sep 13, 2016

I've looked into splitting the current 'user-select' property into longhands, to see if it helps with the issue of a non inherited property that has one value whose effect depends on the value on the parent element (this has been described as quasi-inheritance or selective inheritance). So here's the attempt:

'user-select' becomes a shorthand for 'user-select-inside' and 'user-select-outside' (to be bikesheded).

Name: user-select-inside
Inherited: yes
Values: text | none | all
Initial: text

The values mean the same as what they currently mean on 'user-select'.

Name: user-select-outside
Inherited: no
Values: auto | contain
Initial: auto

''contain'' on an element prevents a selection started within that element to be extended outside of it.

''user-select-outside:auto'' computes to (or alternatively, behaves as) ''user-select-outside: contain'' on editable elements.

''contain'' + ''none'' is the same as ''auto'' + ''none''.r ''contain'' + ''all'' could either be the same as ''auto'' + ''all'', or have subttely different behavior when trying to select from outside. These two combination are not particularly problematic to handle, but it is doubtfull whether they are of any use.

user-select user-select-inside user-select-outside
auto inherit (Gecko / Edge / IE / Spec behavior)
text (Chrome / Safari behavior)
auto
text text auto
none none auto
all all auto
contain text contain

This design can work, but it has two oddities. The first one, which is rare but not unique, being that one of the longhands is inherited and the other not. The second one, for which I believe there is no precendent, is that one value of the shorthand (''auto'') expands to the reserved keyword ''inherit'' in one of the longhands.

These longhands could be exposed to authors or possibly kept as internal implemenation details.


''user-select: auto'' in the Spec / Mozilla / Edge / IE behavior is used what is effectively partial inheritance in a non inherited property. In the Chrome / Safari behavior, 'user-select' is inherited, and ''user-select:auto'' behaves identically to ''user-select: text'', and serves no purpose.

If we keep ''contain'' in the same (non inherited) property, ''auto'' is necessary to make things work.

If we split ''contain'' into a separate property, ''auto'' serves no purpose other than backwards compatibility, with a different behavior in Chrome + Safari vs Gecko + Edge + IE. We could consider not including ''auto'' in the standard shorthand, an only keeping it in the prefixed versions.

If we keep the visible part as a single property, and implement it internally as the two longhands described above, we can keep the same behavior as if we only had a single property. I do not know whether this would be simpler to implement for Blink, due to the peculiar use of ''inherit'' as an expantion of ''auto'' on the shorthand. I suspect it is, since this would only come into play when ''auto'' is manually specified and the rest of the time you can do normal inheritance on one property and normal non-inheritance on the other, but that's for Google to see. When taking that route, a bit of fudging would be needed on getComputedStyle() since it normally returns nothing for shorhands, but that shouldn't be hard (if ($user-select-outside == "contain") return "contain" else return $user-select-inside)


I believe that the approach taken in the spec offers a simpler/better better author experience than trying to use the longhands (no useless combinations such as contain+none, no need to remeber to reset 'user-select-inside' to ''text'' if you want to set 'user-select-outside' to ''contain'' and have it do something useful), and has more separate implemenations (Gecko + IE/Edge vs Webkit/Blink pre-fork).

Moreover, looking at the value on a parent element to determine the computed value on a child element is explicitely mentioned in the cascading spec as something that can be expected of computed values:

Note: In general, the computed value resolves the specified value as far as possible without laying out the document or performing other expensive or hard-to-parallelize operations, such as resolving network requests or retrieving values other than from the element and its parent.

All in all, I could live with the two-properties-with-shorthand solution if all implementers prefered it, but I don't think it is a better solution than the one currently specified, which aligns with what IE/Edge and Gecko do.

The hidden longhand approach does seem to work around the difficulties you may run into if you were to do a straightforward implementation, and I am sure other implementation strategies exist.

I hope we can keep the spec as it is.

@frivoal
Copy link
Collaborator

frivoal commented Sep 13, 2016

@kojiishi By the way, a quite note about a comment you made in the google group post you linked to:

Historically, user-select was an inherited property, and Blink/WebKit implements so today. MS then find "contain" is useful, which we agree, but that value should not inherit; i.e., the child of "contain" should be reset. So for this snippet:

The

has "user-select: contain", while the has "user-select: text", and inherits from .

CSS WG then discussed on this, probably quite ago, and decided to describe what the spec says today. In short, IIUC:

Add "auto" to this property, that computes as if it looks like a "mixed" inheritance.

This isn't quite right. Historically, user-select was not inherited, nor was it non-inherited. It was unspecified. the Webkit implementation (which Blink shares) is inherited. The Gecko and the IE/Edge implementations are not. This isn't new, and the auto value has existed all along (even though it isn't useful in the webkit/blink side of the world). The "contain" value took advantage of the existence of this auto value, but "auto" wasn't created for it.

@kojiishi
Copy link
Contributor

Thanks for the history, and for the correction. Blink and WebKit do not support auto up to today.

So may I understand you think it's possible but prefer to keep the current way to express per-value inheritance?

Since my comment is from Blink community, it'd probably good to discuss at TPAC where some of them are available. cc @esprehn @shans

@esprehn
Copy link

esprehn commented Sep 14, 2016

Yeah lets discuss this at TPAC, I really don't want to introduce a new concept to CSS where specific values can control inheritance. user-select doesn't seem like it justifies increasing the complexity of the entire platform.

@frivoal
Copy link
Collaborator

frivoal commented Sep 15, 2016

Actually, my shorthand/longhand proposal above has different (and worse) behavior in some cases:
if an editable element is a child of a non editable element, then the descendants of the editable element would be unselectable, which isn't what we want. So scrap that, I don't think the shorthand/longhand approach works.

@frivoal
Copy link
Collaborator

frivoal commented Sep 15, 2016

I really don't want to introduce a new concept to CSS where specific values can control inheritance.

But that's not what the proposal is. There is no inheritance, and there's just a property whose computed value depends on a property of the parent, and that's not a new concept in the platform.

@frivoal
Copy link
Collaborator

frivoal commented Sep 15, 2016

Blink and WebKit do not support auto up to today.

They do, just with the not terribly useful behavior of always making it compute to text. Proof: http://jsbin.com/ciwema/edit?html,css,output

So may I understand you think it's possible but prefer to keep the current way to express per-value inheritance?

As I said above, there is no actual per value inheritance. Only computed value that depends on the parent. Yes, the result is that in some situations, it kind of does the same as inheritance, and in others, it does not, so explaining the effects by saying "it behaves sort of like inheritance, except that...." sounds fine for teaching purposes, but there is no new concept being introduced, and I'd prefer that we used the precise terminology when discussing the mechanics or the proposal.

As for whether it is possible to use the two properties (possibly with the shorthand), I did think so, but I've changed by mind (see #336 (comment)).

If you have a contenteditable element (or an element that's editable for other reasons) that's a child of a user-select:none element, in all browsers:

  • the selection is contained in the editable element, even in browsers that don't have an excplict "contain" value on user-select
  • the editable element and its descendants are selectable

In the dual property approach, the second point would not be true. The spec is written to be compatible with everybody's existing behavior in that respect, and that change would break this. I'd argue this is for the worse, and not web compatible.

@kojiishi
Copy link
Contributor

Just to clarify again, I'm ok either way since the impl concern on the performance is gone, thanks to Rossen and your advice on the conf call, but I didn't understand what you wrote and appreciate clarification:

If you have a contenteditable element (or an element that's editable for other reasons) that's a child of a user-select:none element, in all browsers:

  • the editable element and its descendants are selectable

This can be achieved by a UA stylesheet:

[contenteditable] {
  user-select-inside: text;
  user-select-outside: contain;
}

no? Is it only our assumption of the above UA stylesheet was different, or did I miss anything?

@frivoal
Copy link
Collaborator

frivoal commented Sep 15, 2016

Right, I was not assuming anything in the UA stylesheet. You could indeed mostly solve the problem this way, although you'd probably need to add !important, as this is currently not something that can be overridden. And you would also need to whitelist various form controls. And also need to deal with non-standard ways of making something editable (-webkit-user-modify), but non standard stuff is your problem :)

I still find the original proposal cleaner, but since we can make something roughly equivalent with two properties, a shorthand, and some UA stylesheet additions, I suppose I wouldn't object if you can get other browser vendors on board.

@frivoal frivoal changed the title [css-ui-4][user-select] per-value inheritance [css-ui-4] computed value of user-select Sep 15, 2016
@kojiishi
Copy link
Contributor

Chrome team discussed on this again and we concluded that the current spec is fine for us, so closing this. Thanks for the investigations and discussions.

@frivoal
Copy link
Collaborator

frivoal commented Sep 20, 2016

Thank you for the constructive feedback. In the end, even though we decided to keep the current spec as is, I think exploring the various other solutions was worth it.

@frivoal
Copy link
Collaborator

frivoal commented Dec 30, 2019

Marking as "Closed as Accepted" rather than "Rejected", because #3344, which is basically reopening this issue, was eventually Accepted.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants