Bug 138189

Summary: Web Inspector: Show Selector's Specificity
Product: WebKit Reporter: Joseph Pecoraro <joepeck>
Component: Web InspectorAssignee: Joseph Pecoraro <joepeck>
Status: RESOLVED FIXED    
Severity: Normal CC: benjamin, commit-queue, graouts, joepeck, timothy, webkit-bug-importer
Priority: P2 Keywords: InRadar
Version: 528+ (Nightly build)   
Hardware: All   
OS: All   
Attachments:
Description Flags
[PATCH] Proposed Fix
benjamin: review+, joepeck: commit-queue-
[PATCH] Proposed Fix
none
[IMAGE] Example none

Description Joseph Pecoraro 2014-10-29 15:04:21 PDT
* SUMMARY:
The inspector should show selector's specificity
Comment 1 Joseph Pecoraro 2014-10-29 15:04:41 PDT
<rdar://problem/18717361>
Comment 2 Radar WebKit Bug Importer 2014-10-29 15:04:45 PDT
<rdar://problem/18817190>
Comment 3 Joseph Pecoraro 2014-10-29 15:29:08 PDT
Created attachment 240632 [details]
[PATCH] Proposed Fix
Comment 4 Benjamin Poulain 2014-10-30 17:30:46 PDT
Comment on attachment 240632 [details]
[PATCH] Proposed Fix

View in context: https://bugs.webkit.org/attachment.cgi?id=240632&action=review

The C++ side looks reasonable, no idea about the JS part. You may want to get a second review :)

> LayoutTests/inspector/css/selector-specificity.html:15
> +h1:before,
> +body h1:before,
> +body.a h1.b:before {

:before -> ::before.

":before" is the legacy syntax from CSS 2.

> Source/JavaScriptCore/inspector/protocol/CSS.json:66
> +                { "name": "specificity", "optional": true, "type": "array", "items": { "type": "integer" }, "description": "Specificity (a, b, c) tuple. If missing the specificity is not known statically and may be dynamic." }

What would be the value for dynamic specificity?

> Source/WebCore/css/CSSSelector.h:53
> +        static const unsigned maxValueMask = 0xffffff;
> +        static const unsigned idMask = 0xff0000;
> +        static const unsigned classMask = 0xff00;
> +        static const unsigned elementMask = 0xff;

I would prefer a typed enum to namespace this:
enum class SpecificityMask {
    Id,
    Class,
    Element,
    All
}
Comment 5 Joseph Pecoraro 2014-10-31 11:24:34 PDT
(In reply to comment #4)
> Comment on attachment 240632 [details]
> [PATCH] Proposed Fix
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=240632&action=review
> 
> The C++ side looks reasonable, no idea about the JS part. You may want to
> get a second review :)
> 
> > LayoutTests/inspector/css/selector-specificity.html:15
> > +h1:before,
> > +body h1:before,
> > +body.a h1.b:before {
> 
> :before -> ::before.
> 
> ":before" is the legacy syntax from CSS 2.

Oops!


> > Source/JavaScriptCore/inspector/protocol/CSS.json:66
> > +                { "name": "specificity", "optional": true, "type": "array", "items": { "type": "integer" }, "description": "Specificity (a, b, c) tuple. If missing the specificity is not known statically and may be dynamic." }
> 
> What would be the value for dynamic specificity?

It could just be no specificity = dynamic.

> > Source/WebCore/css/CSSSelector.h:53
> > +        static const unsigned maxValueMask = 0xffffff;
> > +        static const unsigned idMask = 0xff0000;
> > +        static const unsigned classMask = 0xff00;
> > +        static const unsigned elementMask = 0xff;
> 
> I would prefer a typed enum to namespace this:
> enum class SpecificityMask {
>     Id,
>     Class,
>     Element,
>     All
> }

I don't understand this comment.

The masks are bitmasks for the different pieces of the specificity tuple stored in the "unsigned" returned from CSSSelector::specificity. I'm not sure a typed enum is useful here.
Comment 6 Timothy Hatcher 2014-10-31 11:33:41 PDT
Comment on attachment 240632 [details]
[PATCH] Proposed Fix

View in context: https://bugs.webkit.org/attachment.cgi?id=240632&action=review

> Source/WebInspectorUI/UserInterface/Views/CSSStyleDeclarationSection.js:183
> +                selectorElement.title = WebInspector.UIString("Specificity: (%d, %d, %d)").format(specificity[0], specificity[1], specificity[2]);

I expected a single number in the UI, not a tuple. It is hard to understand what the three numbers represent. The CSS spec totals the three into a final, easier to compare, number.

http://www.w3.org/TR/selectors/#specificity
Comment 7 Joseph Pecoraro 2014-10-31 12:00:04 PDT
(In reply to comment #6)
> Comment on attachment 240632 [details]
> [PATCH] Proposed Fix
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=240632&action=review
> 
> > Source/WebInspectorUI/UserInterface/Views/CSSStyleDeclarationSection.js:183
> > +                selectorElement.title = WebInspector.UIString("Specificity: (%d, %d, %d)").format(specificity[0], specificity[1], specificity[2]);
> 
> I expected a single number in the UI, not a tuple. It is hard to understand
> what the three numbers represent. The CSS spec totals the three into a
> final, easier to compare, number.

Yes, I thought about this too.


The spec says:
<http://www.w3.org/TR/selectors/#specificity>
> Concatenating the three numbers a-b-c (in a number system with a large base) gives the specificity.

However, assuming a specific number base (x) can produce an incorrect number if individually either (a), (b), or (c) is larger then the base (x).

This explains it clearly:
<http://juicystudio.com/article/selector-specificity.php>
> One solution suggested is to count the numbers in each column, and multiply by its denary position.
> In simple terms, multiply a by 100, b by 10, and c by 1 (remains the same), and then add them
> together. The problem with this approach is that it falls down when one of the columns has a count
> of ten or higher. For example, 11 elements (c) would outweigh a single class (b) using this method,
> but that cannot be true. A single class would outweigh 100 or more elements. The simplest way to
> cater for the large base is to just accept that b outweighs any number of c, and that a outweighs
> any number of b (which is true of any number system).

However, it is not uncommon to do simple math for selectors with (1000, 100, 10, 1). As mentioned above and elsewhere:
<http://www.smashingmagazine.com/2007/07/27/css-specificity-things-you-should-know/>
> Start at 0, add 1000 for style attribute, add 100 for each ID, add 10 for each attribute,
> class or pseudo-class, add 1 for each element name or pseudo-element.

I think that is reasonable. How do you feel about displaying something like:

    Specificity (5, 4, 2) ≈ 542.

I think in practice we won't find many selectors that have more than 10 tags. We may see selectors with more than 10 class/pseudo components but it is probably rare. We can always see if this happens by just checking if any value is > 10 and decide to do something else. Having the "approx" sign instead of "equal" sign gives us a get out of jail card for these edge cases.
Comment 8 Joseph Pecoraro 2014-10-31 16:57:35 PDT
Comment on attachment 240632 [details]
[PATCH] Proposed Fix

View in context: https://bugs.webkit.org/attachment.cgi?id=240632&action=review

>>> Source/WebCore/css/CSSSelector.h:53
>>> +        static const unsigned elementMask = 0xff;
>> 
>> I would prefer a typed enum to namespace this:
>> enum class SpecificityMask {
>>     Id,
>>     Class,
>>     Element,
>>     All
>> }
> 
> I don't understand this comment.
> 
> The masks are bitmasks for the different pieces of the specificity tuple stored in the "unsigned" returned from CSSSelector::specificity. I'm not sure a typed enum is useful here.

Oh, I see now. I forgot to include the CSSSelector.cpp changes here. I'm just moving these masks from the .cpp to the .h so the inspector code can use it. New patch soon.
Comment 9 Benjamin Poulain 2014-10-31 17:22:22 PDT
Personally I find the single number unreadable.

Cases with >= 10 in a column are fairly common too in practice.

In WebKit, the max value is type 255. You could have funny cases:
-(1, 0, 0) = 100
-(0, 0, 255) = 255

You could adds zeros I guess:
-(1, 0, 0) = 001000000
-(0, 0, 255) = 000000255
Comment 10 Joseph Pecoraro 2014-10-31 17:54:06 PDT
Created attachment 240772 [details]
[PATCH] Proposed Fix

Take 2.
Comment 11 Joseph Pecoraro 2014-10-31 17:55:28 PDT
Created attachment 240773 [details]
[IMAGE] Example

I think we hit the sweet spot with:

    Specificity: (1, 0, 1) ≈ 101

The tuple is the hard truth. The number we show after is a mere approximation I would say most web developers are thinking of in their head.
Comment 12 Timothy Hatcher 2014-11-03 11:39:13 PST
Comment on attachment 240772 [details]
[PATCH] Proposed Fix

View in context: https://bugs.webkit.org/attachment.cgi?id=240772&action=review

> Source/WebInspectorUI/UserInterface/Models/DOMNodeStyles.js:763
> +            return selectors.map(function(selectorText) {
> +                return new WebInspector.CSSSelector(selectorText);
> +            });

Wild return ... return here. JS, stay crazy.
Comment 13 WebKit Commit Bot 2014-11-03 12:03:53 PST
Comment on attachment 240772 [details]
[PATCH] Proposed Fix

Clearing flags on attachment: 240772

Committed r175483: <http://trac.webkit.org/changeset/175483>
Comment 14 WebKit Commit Bot 2014-11-03 12:03:57 PST
All reviewed patches have been landed.  Closing bug.