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 24115 - Introduce CSS.isValidSelector()
Summary: Introduce CSS.isValidSelector()
Alias: None
Product: CSS
Classification: Unclassified
Component: CSSOM (show other bugs)
Version: unspecified
Hardware: PC All
: P2 normal
Target Milestone: ---
Assignee: Simon Pieters
QA Contact: public-css-bugzilla
Depends on:
Reported: 2013-12-16 16:57 UTC by Anne
Modified: 2016-03-16 18:25 UTC (History)
3 users (show)

See Also:


Description Anne 2013-12-16 16:57:58 UTC
Throwing is slow:

So ideally for query() and queryAll() we do not throw and require people to use this new static method instead if they want to know if there was a syntax problem. (Browsers could still do some console logging I suppose if the console is open.)
Comment 1 Boris Zbarsky 2013-12-16 17:00:31 UTC
Note that has a slightly deeper stack, which shows that throwing is slower when the stack is deeper (say you're in a library).  At least in some UAs.
Comment 2 Simon Pieters 2013-12-17 14:23:15 UTC
Can we start with the use case? Why do people want to know if there was a syntax problem? (To polyfill selectors that aren't supported natively?)

Is the problem that people trigger and catch an exception from query() 100,000 times or 1,000,000 times and it becomes a performance problem?

Is the problem that query() is slow even if no exception is thrown, because it *can* throw? Is it only when it is invoked with the same argument on the same object in a tight loop?
Comment 3 Boris Zbarsky 2013-12-17 16:52:21 UTC
jQuery wants to know because it supports a superset of selector syntax.  So when someone does $("body *:header") jQuery will call querySelectorAll and then catch the exception and do its own processing.  But the cost of the exception causes significant overhead here (well over half the time, in my testing).

> Is the problem that people trigger and catch an exception from query() 100,000
> times or 1,000,000 times and it becomes a performance problem?

Comment 4 Simon Pieters 2013-12-18 09:53:06 UTC
I think a problem with CSS.isValidSelector is that a selector might be valid in query() but not in a style sheet.

Maybe instead of throwing query() could invoke an error callback or some such?

Is the performance problem with exceptions something that can't be fixed?
Comment 5 Anne 2013-12-18 11:58:00 UTC
What kind of selector would that be? That sounds bad. And if we are diverging selectors, surely we should then have two methods?

The perf thing is inherent to throwing exceptions it seems. Allocating an extra object, calculating stack trace. bz knows more.
Comment 6 Simon Pieters 2013-12-18 12:40:44 UTC

I think a slightly more realistic benchmark doesn't use the same string every time, and also the proposal here would mean that jQuery would invoke both CSS.isValidSelector and then query(), which seems equivalent to calling querySelector twice, so:

The throwing path is still a lot slower in Gecko and Blink, but in Presto the throwing code path isn't that slow. About the same as non-throwing in Blink and about twice as slow as non-throwing in Gecko.
Comment 7 Boris Zbarsky 2013-12-18 15:57:37 UTC
> I think a slightly more realistic benchmark doesn't use the same string every
> time

This has the opposite problem: it uses a different string pretty much every time.  That's not how real-life use cases of this work; in practice they use some fairly small number of strings, not 100000 different ones.

Furthermore, it's creating the strings via string concatenation, which is also not how this stuff is typically used and adds overhead that's the same in both the throwing and non-throwing case, reducing the difference between them.  I think is a closer approximation of how this would work in real life, but still ignoring the stack depth bit for throwing.

> The throwing path is still a lot slower in Gecko and Blink, but in Presto the
> throwing code path isn't that slow.

Throwing in today's SpiderMonkey (and I suspect V8) has to reconstruct stack data that the JIT normally doesn't keep track of during execution for perfomance reasons.  It's obviously possible to make throwing faster, by tracking that data all along, but mostly at the expense of making non-throwing script slower, which is a tough sell.

That said, there is definitely low-hanging fruit for throwing at least in Gecko that we're fixing.  On your testcase, I see numbers like so:

Presto (12.16, fwiw):  4670 3880
Recent Firefox nightly: 1470 9530
My local build with some changes to throwing: 1450 9160

I can probably get that throwing number down to about 6000 or so, I suspect, based on profiles.  But I doubt it'll get anywhere near as fast as the non-throwing case.
Comment 8 Simon Pieters 2013-12-18 21:06:04 UTC
Good points, and nice to see improvements in Gecko. :-)

Looking at some more data, it looks like in the invalid entries are in minority of the total usage, about 5-6%. This was in 2008, I don't know if there's a newer statistics. But it occurs to me that the performance problem could be solved by adding native support for :visible and :hidden.

Is there a Web page that has the performance problem that I can look at?
Comment 9 Boris Zbarsky 2013-12-18 21:14:51 UTC
I don't have a non-synthetic webpage, honestly.  Just people complaining to us that their "jquery is slow"...
Comment 10 Simon Pieters 2016-03-14 15:19:26 UTC
I think it makes more sense to solve this use case as part of
Comment 11 Anne 2016-03-14 16:02:20 UTC
What is the ETA on that? Subclassing might come quite soon, and we'd need this if we want to stop throwing for query/queryAll, which seems like a good idea.
Comment 12 Simon Pieters 2016-03-15 11:39:03 UTC
Tab, what is the status of some Houdini API covering this use case? I also see which seems somewhat similar.
Comment 13 Simon Pieters 2016-03-16 18:06:10 UTC
Is there implementor interest for this right now? If so I'm happy to spec it now.

From #css

<TabAtkins> zcorpan: I don't think anything in Houdini is trying to deal with selectors?
<TabAtkins> If you want this, we can just add a function somewhere.
<zcorpan> TabAtkins: not the parser API either?
<TabAtkins> We're not working on that *quite* yet, but even then, I'm not planning on adding an isSelectorValid() function. At most, allowing you to specify that something should be a selector, and then parse it as such.
<zcorpan> TabAtkins: yeah and "parse it as such" could tell you if it was valid or not, no?
<TabAtkins> Indirectly I guess, sure.
<zcorpan> so then it would address the use case, is my point. but if parser API is years off and people want this now, i suppose i should spec it
<TabAtkins> Yes.
Comment 14 Anne 2016-03-16 18:25:34 UTC
You know what, let me ping this issue once I think I can properly define Elements/query/queryAll. We can then do it all at once.