Bug 135293 - CSS JIT: Implement :visited pseudo class
Summary: CSS JIT: Implement :visited pseudo class
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: CSS (show other bugs)
Version: 528+ (Nightly build)
Hardware: Unspecified Unspecified
: P2 Normal
Assignee: Yusuke Suzuki
URL:
Keywords:
Depends on: 135255 135324
Blocks:
  Show dependency treegraph
 
Reported: 2014-07-25 09:42 PDT by Yusuke Suzuki
Modified: 2014-10-13 15:47 PDT (History)
4 users (show)

See Also:


Attachments
Patch (43.22 KB, patch)
2014-08-03 10:00 PDT, Yusuke Suzuki
no flags Details | Formatted Diff | Diff
Patch (44.54 KB, patch)
2014-08-03 10:45 PDT, Yusuke Suzuki
no flags Details | Formatted Diff | Diff
Patch (35.43 KB, patch)
2014-09-26 08:31 PDT, Yusuke Suzuki
no flags Details | Formatted Diff | Diff
Patch (35.53 KB, patch)
2014-09-26 08:40 PDT, Yusuke Suzuki
no flags Details | Formatted Diff | Diff
Patch (39.45 KB, patch)
2014-10-01 14:20 PDT, Yusuke Suzuki
no flags Details | Formatted Diff | Diff
Patch (35.56 KB, patch)
2014-10-12 19:11 PDT, Yusuke Suzuki
benjamin: review+
benjamin: commit-queue-
Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Yusuke Suzuki 2014-07-25 09:42:44 PDT
Implement :visited pseudo class for CSS JIT.
Comment 1 Yusuke Suzuki 2014-07-25 12:03:16 PDT
Implementation notes.

* preset VisitedMatchDisabled only when mode is QueryingRules (security issue)
* inside :not, :link and :visited are treated with tricky path
* handle :visited tricks

Seeing the dbaron's post about this[1] and WebKit code,

1. like :visited + span 
2. nested link elements

are affected.

[1]: http://dbaron.org/mozilla/visited-privacy
Comment 2 Yusuke Suzuki 2014-08-03 10:00:28 PDT
Created attachment 235945 [details]
Patch

WIP
Comment 3 Yusuke Suzuki 2014-08-03 10:45:28 PDT
Created attachment 235946 [details]
Patch

WIP
Comment 4 WebKit Commit Bot 2014-08-03 19:34:41 PDT
Attachment 235946 [details] did not pass style-queue:


ERROR: Source/WebCore/ChangeLog:8:  You should remove the 'No new tests' and either add and list tests, or explain why no new tests were possible.  [changelog/nonewtests] [5]
Total errors found: 1 in 3 files


If any of these errors are false positives, please file a bug against check-webkit-style.
Comment 5 Benjamin Poulain 2014-08-03 19:41:49 PDT
Wow, this is quite crazy.

I guess the state tracking is made necessary by the isLink() check of SelectorChecker? Do we know what kind of leak that guard is actually protecting? The blog post by David Baron is not exactly clear about that. Maybe it is something we should address during style resolution instead of the SelectorMatching.
Comment 6 Yusuke Suzuki 2014-08-04 12:47:44 PDT
(In reply to comment #5)
> I guess the state tracking is made necessary by the isLink() check of SelectorChecker?

Yes!!! That's correct.

> Do we know what kind of leak that guard is actually protecting? The blog post by David Baron is not exactly clear about that. Maybe it is something we should address during style resolution instead of the SelectorMatching.

Yeah, your question is right!!
Investigating the WebKit :visited implementation, I have some questions and I suspect that this dynamic visitedMatchType is really necessary.
Maybe I miss some WebKit internals, so I'd like to hear your opinions :)

1.
Seeting linkMatchType & adjacent's VisitedMatchTypeDisable protection combo, I think attacks by using getComputedStyle are already protected by them.
So I think that regardless of the result of SelectorChecker#match with isLink() check, getComputedStyle always returns the conservative (assumed as :link) style. Is it correct?

Currently, by using visitedMatchType in SelectorChecker, we disable the actual web pages' view (visual, not programmably readable) for special :visited links, nested and adjacent. But since they are already protected from getComputedStyle attacks by using linkMatchType, personally, I think it's not necessary. Showing :visited view to users is safe because these values are not readable.

I think we can remove visitedMatchType special handling from SelectorChecker by extending determineLinkMatchType to support adjacent case correctly. Do you think about this?
If so, I think we can clean up SelectorChecker and the SelectorCompiler becomes more simple :D

2.
And seeing the SelectorChecker's implementation, I found some edge cases, so I have one concern that current SelectorChecker's protection is already broken.
Currently, SelectorChecker only checks isLink() to the matched element for the fragment. But I think there's curious edge cases.

For example, a selector ":visited .mid .rightmost" is provided, the following ".rightmost"
<p>
  <a href="visited">
    <span class="mid">
      <a class="rightmost">TARGET</a>
    </span>
  </a>
</p>

won't match to this selector. However, the following ".rightmost"

<p>
  <a href="visited">
    <span class="mid">
      <a href="xxx">
        <span class="rightmost">TARGET</span>
      </a>
    </span>
  </a>
</p>

will match to this selector I think. Is it correct, and the expected behavior?
Comment 7 Yusuke Suzuki 2014-08-04 12:50:17 PDT
%s/that this dynamic visitedMatchType is really necessary/that this dynamic visitedMatchType is not necessary/
sorry, mis-typed...
Comment 8 Benjamin Poulain 2014-08-05 00:14:57 PDT
> Currently, by using visitedMatchType in SelectorChecker, we disable the actual web pages' view (visual, not programmably readable) for special :visited links, nested and adjacent. But since they are already protected from getComputedStyle attacks by using linkMatchType, personally, I think it's not necessary. Showing :visited view to users is safe because these values are not readable.

Note that visual aspect are programmatically observable. For example you could change the layout size and observe the size of the document.

--

But I tend to agree with you. Some checks just don't seem to make sense like in the example you posted.

Adding Antti for comment, I believe he rewrote :visited's style resolution a while ago.

Antti: do you know why SelectorChecker's visitedMatchType exists? What cases is it supposed to protect?
Comment 9 Antti Koivisto 2014-08-05 05:42:38 PDT
> Antti: do you know why SelectorChecker's visitedMatchType exists? What cases is it supposed to protect?

It isn't supposed to protect anything. In terms of security the trick is that selector checker (or style resolver in general) never looks into the  actual visited state so can't leak any information (decision to use visited style is done at paint time only).

What it does is to make :visited never match (as opposed to always matching) when doing querySelectorAll or other non-style queries. Any code that achieves the same result is fine.
Comment 10 Antti Koivisto 2014-08-05 05:58:06 PDT
It is also used for some semantics (no idea where these come from or whether they match other browsers):

        // Disable :visited matching when we see the first link or try to match anything else than an ancestors.
        if (context.element->isLink() || (relation != CSSSelector::Descendant && relation != CSSSelector::Child))
            nextContext.visitedMatchType = VisitedMatchDisabled;
Comment 11 Benjamin Poulain 2014-08-05 18:37:48 PDT
It seems simplifying SelectorChecker is a viable path forward. Refactoring this will make it easier to implement the JIT part.

We should make sure to test the hell out of it.
Comment 12 Yusuke Suzuki 2014-08-05 22:48:36 PDT
Antti, Benjamin: Thank you for your clarification.

> It seems simplifying SelectorChecker is a viable path forward. Refactoring this will make it easier to implement the JIT part.
> We should make sure to test the hell out of it.

Agreed. So I've opened the bug 135639 to refactor SelectorChecker and test to ensure there's no security issues :D
Comment 13 Yusuke Suzuki 2014-09-26 08:31:32 PDT
Created attachment 238718 [details]
Patch
Comment 14 Yusuke Suzuki 2014-09-26 08:40:36 PDT
Created attachment 238719 [details]
Patch
Comment 15 Benjamin Poulain 2014-09-30 14:31:53 PDT
Comment on attachment 238719 [details]
Patch

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

The part about :visited in top level selectors look good to me.

There is code related to :not() on which I would like more information:
    Wouldn't it be possible to handle :not(:link) with the generic path by adding discarding the :not() and adding :visited to the fragment's pseudo classes?

I r- to do a second round with your explanations for :not().

Your changes to StackAllocator looks correct. Feel free to land that separately as reviewed by me.

> Source/WebCore/ChangeLog:8
> +        This patch implements CSS JIT for :visited. And maake :not(:link) JIT-ed.

Typo: maake

> Source/WebCore/ChangeLog:23
> +        Edge cases are tested by the existing tests.
> +        :not(:link)
> +            fast/history/link-inside-not.html
> +        :not(:visited)
> +            fast/history/visited-inside-not.html
> +        :-webkit-any(:link)
> +            fast/history/link-inside-any.html
> +        :-webkit-any(:visited)
> +            fast/history/visited-inside-any.html

Mentioning this in the changelog is absolutely awesome, I wish more authors did that.

> Source/WebCore/cssjit/SelectorCompiler.cpp:616
> +            // And when :visited is inside :not (:not(:visited)), it returns CannotMatchAnything and it always passes this filter.

This comment is not necessary, the explanation in PseudoClassVisited is enough.

> Source/WebCore/cssjit/SelectorCompiler.cpp:630
> +                ASSERT_WITH_MESSAGE(selectorContext != SelectorContext::QuerySelector, "When visitedMatchEnabled is true, selector context is never QuerySelector.");

The message repeats the condition IMHO. Maybe: "QuerySelector should never match :visited link because it would be a privacy issue." or something like that?

> Source/WebCore/cssjit/SelectorCompiler.cpp:723
> +    bool visitedMatchEnabled = true;
> +    if (selectorContext == SelectorContext::QuerySelector)
> +        visitedMatchEnabled = false;

Could be:
bool visitedMatchEnabled = selectorContext != SelectorContext::QuerySelector;

> Source/WebCore/cssjit/SelectorCompiler.cpp:1431
> +    // Remember the stack base of the temporary variables.

This comment is superfluous, the code below is clear.

> Source/WebCore/cssjit/SelectorCompiler.cpp:1460
> +    // Invalidate temporaryStackBase if there's no temporary variables.
> +    if (temporaryStackBase == m_stackAllocator.stackTop())
> +        temporaryStackBase = StackAllocator::StackReference();

Instead of this, couldn't we check "temporaryStackBase == m_stackAllocator.stackTop()" instead of temporaryStackBase.isValid() when discarding the stack?

> Source/WebCore/cssjit/SelectorCompiler.cpp:1558
> +void SelectorCodeGenerator::generateRightmostTreeWalker(Assembler::JumpList& failureCases, const SelectorFragment& fragment)
> +{
> +    generateElementMatching(failureCases, failureCases, fragment);
> +}
> +

Why do you need this?
Comment 16 Yusuke Suzuki 2014-09-30 23:20:21 PDT
Committed r174142: <http://trac.webkit.org/changeset/174142>
Comment 17 Yusuke Suzuki 2014-10-01 12:19:02 PDT
Comment on attachment 238719 [details]
Patch

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

Thank you for your detailed review. And your idea is quite coooooooool! I've missed the point. By leveraging your idea, we can implement this much simpler!!

:not(:link) behaves like the following

+ When the visited matche is enabled, always pass
+ Otherwise, :not(:any-link)

In the current implementation, :visited check is deferred until the matching is finished. So even if the fragment inside :not has PseudoClassVisited, it doesn't cause :not(visited match enabled) behavior. This completely matches to the behavior of :not(:link).

I'll upload the revised patch!

>> Source/WebCore/ChangeLog:8
>> +        This patch implements CSS JIT for :visited. And maake :not(:link) JIT-ed.
> 
> Typo: maake

oops, thx!

>> Source/WebCore/cssjit/SelectorCompiler.cpp:616
>> +            // And when :visited is inside :not (:not(:visited)), it returns CannotMatchAnything and it always passes this filter.
> 
> This comment is not necessary, the explanation in PseudoClassVisited is enough.

ok I'll remove it.

>> Source/WebCore/cssjit/SelectorCompiler.cpp:630
>> +                ASSERT_WITH_MESSAGE(selectorContext != SelectorContext::QuerySelector, "When visitedMatchEnabled is true, selector context is never QuerySelector.");
> 
> The message repeats the condition IMHO. Maybe: "QuerySelector should never match :visited link because it would be a privacy issue." or something like that?

Sounds nice! I'll replace it with your suggestion.

>> Source/WebCore/cssjit/SelectorCompiler.cpp:723
>> +        visitedMatchEnabled = false;
> 
> Could be:
> bool visitedMatchEnabled = selectorContext != SelectorContext::QuerySelector;

done.

>> Source/WebCore/cssjit/SelectorCompiler.cpp:1431
>> +    // Remember the stack base of the temporary variables.
> 
> This comment is superfluous, the code below is clear.

Removed.

>> Source/WebCore/cssjit/SelectorCompiler.cpp:1460
>> +        temporaryStackBase = StackAllocator::StackReference();
> 
> Instead of this, couldn't we check "temporaryStackBase == m_stackAllocator.stackTop()" instead of temporaryStackBase.isValid() when discarding the stack?

Your suggestion is nice. Agreed, `temporaryStackBase == m_stackAllocator.stackTop()` clearly states the meaning of the code.

>> Source/WebCore/cssjit/SelectorCompiler.cpp:1558
>> +
> 
> Why do you need this?

I added it to maintain the one on one corresponding between the methods and combinators.

Rightmost => generateRightmostTreeWalker
Descendant => generateAncestorTreeWalker
Child => generateParentElementTreeWalker
DirectAdjacent => generateDirectAdjacentTreeWalker
IndirectAdjacent => generateIndirectAdjacentTreeWalker


Is it preferable to use generateElementMatching directly?
Comment 18 Yusuke Suzuki 2014-10-01 13:07:52 PDT
Comment on attachment 238719 [details]
Patch

Ah, oops.

In the following example,

*:not(link)

it matches the following <a>

<a href='xxx'>toplevel</a>

since visited match type is enabled.
To find it, when the :link is matched (normal :not failed), we need to pass the filter with storing the element as the last visited element.
It requires additional non-generic path in the :not.

And there's another edge case, `:-webkit-any(:not(:link), a) a` and the tree

<a>
  <a target></a>
</a>

If we choose :not(:link) as matched and storing it to the last visited element, the selector will fail since the last visited element is not the first encountered link.
However, if we choose a in the any's choices, the selector will succeed.
So to solve this, we should check whether it is the first encountered link when :not(:link) is used.
Comment 19 Yusuke Suzuki 2014-10-01 14:20:12 PDT
Created attachment 239050 [details]
Patch
Comment 20 Yusuke Suzuki 2014-10-01 14:29:08 PDT
Comment on attachment 239050 [details]
Patch

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

I've updated the patch.
Because of :-webkit-any(:not(:link)), the non-generic path is required in the :not filter check.

> Source/WebCore/cssjit/SelectorCompiler.cpp:3153
> +        }

To cover :-webkit-any(:not(:link)) case, we need to check whether the element is the first encountered link.
This check is needed only when the :not filter predicate passes (so, the whole filter failed).

And there's some room for optimization. The immediate check is only needed in the case of :-webkit-any(:not(:link)).
So in the normal cases (:not(:link)), we can leverage :visited check system; Inserting

if (:not(:link) and not inside :-webkit-any) {
    generateStoreLastVisitedElement(elementAddressRegister);
    localFailureCases.append(m_assembler.jump())
}

But it brings some more complexity. What do you think? I think it depends on whether :not(:link) is popular or not.

> LayoutTests/ChangeLog:9
> +        * fast/history/link-inside-not-inside-any.html: Added.

Added new tests for :-webkit-any(:not(:link)).
:not(:-webkit-any(:link)) is syntax error.
Comment 21 Benjamin Poulain 2014-10-02 20:38:54 PDT
(In reply to comment #17)
> (From update of attachment 238719 [details])
> View in context: https://bugs.webkit.org/attachment.cgi?id=238719&action=review
> 
> Thank you for your detailed review. And your idea is quite coooooooool! I've missed the point. By leveraging your idea, we can implement this much simpler!!
> 
> :not(:link) behaves like the following
> 
> + When the visited matche is enabled, always pass
> + Otherwise, :not(:any-link)
> 
> In the current implementation, :visited check is deferred until the matching is finished. So even if the fragment inside :not has PseudoClassVisited, it doesn't cause :not(visited match enabled) behavior. This completely matches to the behavior of :not(:link).

Rather:

:not(:link) would be:
-adding :visited to the parent selector if visited matching is enabled.
-always failing otherwise.

:not(:visited) would be:
-always passing in all cases.

The problem is the new :not() and :matches() but that is something we can deal with later.
Comment 22 Benjamin Poulain 2014-10-02 20:52:45 PDT
(In reply to comment #21)
> (In reply to comment #17)
> > (From update of attachment 238719 [details] [details])
> > View in context: https://bugs.webkit.org/attachment.cgi?id=238719&action=review
> > 
> > Thank you for your detailed review. And your idea is quite coooooooool! I've missed the point. By leveraging your idea, we can implement this much simpler!!
> > 
> > :not(:link) behaves like the following
> > 
> > + When the visited matche is enabled, always pass
> > + Otherwise, :not(:any-link)
> > 
> > In the current implementation, :visited check is deferred until the matching is finished. So even if the fragment inside :not has PseudoClassVisited, it doesn't cause :not(visited match enabled) behavior. This completely matches to the behavior of :not(:link).
> 
> Rather:
> 
> :not(:link) would be:
> -adding :visited to the parent selector if visited matching is enabled.
> -always failing otherwise.
> 
> :not(:visited) would be:
> -always passing in all cases.
> 
> The problem is the new :not() and :matches() but that is something we can deal with later.

Ok, reading your next comments explained everything...

Reading the edge cases, I am in favor of dropping :visited, and make :link a synonym of :any-link for any functional pseudo class.

The short term future is :matches() and :not() taking selector list. Those are critical use case to improve CSS. Since we do not have a clear path forward for :link and :visited in those, I am in favor of deferring the problem.

If you feel strongly about :not(:visited) and :not(:link), I'll review your latest patch. Otherwise let's cut the complexity.
Comment 23 Yusuke Suzuki 2014-10-06 04:11:06 PDT
(In reply to comment #22)
> 
> Ok, reading your next comments explained everything...
> 
> Reading the edge cases, I am in favor of dropping :visited, and make :link a synonym of :any-link for any functional pseudo class.
> 
> The short term future is :matches() and :not() taking selector list. Those are critical use case to improve CSS. Since we do not have a clear path forward for :link and :visited in those, I am in favor of deferring the problem.
> 
> If you feel strongly about :not(:visited) and :not(:link), I'll review your latest patch. Otherwise let's cut the complexity.

Thanks for your comment.
Does dropping :visited mean deferring implementing/considering the complex semantics of :visited/:link with functional pseudo classes and taking the simple way, correct?

+ :not(:visited) accepts all, and :-webkit-any(:visited) rejects all.
+ :link becomes :-any-link inside the functional pseudo classes
If so, agree with you :)
Comment 24 Benjamin Poulain 2014-10-06 20:18:02 PDT
(In reply to comment #23)
> (In reply to comment #22)
> > 
> > Ok, reading your next comments explained everything...
> > 
> > Reading the edge cases, I am in favor of dropping :visited, and make :link a synonym of :any-link for any functional pseudo class.
> > 
> > The short term future is :matches() and :not() taking selector list. Those are critical use case to improve CSS. Since we do not have a clear path forward for :link and :visited in those, I am in favor of deferring the problem.
> > 
> > If you feel strongly about :not(:visited) and :not(:link), I'll review your latest patch. Otherwise let's cut the complexity.
> 
> Thanks for your comment.
> Does dropping :visited mean deferring implementing/considering the complex semantics of :visited/:link with functional pseudo classes and taking the simple way, correct?
> 
> + :not(:visited) accepts all, and :-webkit-any(:visited) rejects all.

That sounds good. We assume ":visited" always fail in those cases.

> + :link becomes :-any-link inside the functional pseudo classes
> If so, agree with you :)

That sounds good too.
Comment 25 Yusuke Suzuki 2014-10-12 19:11:45 PDT
Created attachment 239709 [details]
Patch
Comment 26 Yusuke Suzuki 2014-10-12 19:17:37 PDT
Comment on attachment 239709 [details]
Patch

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

Sorry for the further late reply. I've updated the patch for the latest CSS JIT :)

> Source/WebCore/css/SelectorChecker.cpp:549
> +                    if (subcontext.selector->pseudoClassType() == CSSSelector::PseudoClassVisited)

Drop `:not(:link))` special path. Simplify it for further selectors extensions.

> Source/WebCore/cssjit/SelectorCompiler.cpp:629
> +                    ASSERT_WITH_MESSAGE(ignoreVisitedMode == VisitedMode::None, ":visited is disabled in the functional pseudo classes");

When the fragment level is `InFunctionalPseudoType`, :visited doesn't require any :visited checks.
As the result, the visited mode of the sub fragments always becomes VisitedMode::None.

> Source/WebCore/cssjit/SelectorCompiler.cpp:666
> +            ASSERT_WITH_MESSAGE(ignoreVisitedMode == VisitedMode::None, ":visited is disabled in the functional pseudo classes");

ditto

> Source/WebCore/cssjit/SelectorCompiler.cpp:-652
> -                return FunctionType::CannotCompile;

Drop the :not(:link) restrictions.

> Source/WebCore/cssjit/SelectorCompiler.cpp:1042
> +        rootBacktrackingMemoryRequirement.stackCount += 2;

When the visited mode is Visited, we allocate 2 more stack references in the root fragment.
In the current simplified semantics, visited is only effective on the root fragments.
Comment 27 Benjamin Poulain 2014-10-13 14:47:27 PDT
Comment on attachment 239709 [details]
Patch

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

Looks good to me. Sorry I changed my mind so many time on the processing of :visited. Thanks for keeping up with the updates.

It believe we should revisit our options after :matches() is implemented, including multiple matching.

>> Source/WebCore/cssjit/SelectorCompiler.cpp:1042
>> +    // Allocate the stack references for the last visited element and the start element.
>> +    if (visitedMode == VisitedMode::Visited)
>> +        rootBacktrackingMemoryRequirement.stackCount += 2;
> 
> When the visited mode is Visited, we allocate 2 more stack references in the root fragment.
> In the current simplified semantics, visited is only effective on the root fragments.

I would put this branch outside computeBacktrackingMemoryRequirements().

Those two registers are unaffected by backtracking. Having the check in generateSelectorChecker() would also put the branch in the same scope where the stack references are assigned.

> Source/WebCore/cssjit/SelectorCompiler.cpp:1561
> +    if (m_visitedMode == VisitedMode::Visited) {
> +        m_lastVisitedElement = m_backtrackingStack.takeLast();
> +        m_startElement = m_backtrackingStack.takeLast();
> +        unsigned offsetToStartElement = m_stackAllocator.offsetToStackReference(m_startElement);
> +        m_assembler.storePtr(elementAddressRegister, Assembler::Address(Assembler::stackPointerRegister, offsetToStartElement));
> +        unsigned offsetToLastVisitedElement = m_stackAllocator.offsetToStackReference(m_lastVisitedElement);
> +        m_assembler.storePtr(Assembler::TrustedImmPtr(nullptr), Assembler::Address(Assembler::stackPointerRegister, offsetToLastVisitedElement));
> +    }
> +

I would prefer this allocation to be more explicit.

First, allocate allocateUninitialized(). Then take two out of the vector for :visited. The assign the remaining vector to m_backtrackingStack.

> LayoutTests/TestExpectations:221
> -fast/history/link-inside-not.html [ Failure ]
> +# fast/history/link-inside-not.html [ Failure ]

Let's remove those lines entirely.
Comment 28 Yusuke Suzuki 2014-10-13 15:22:32 PDT
Comment on attachment 239709 [details]
Patch

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

No problem :) Your review is so helpful. Sorry for my late reply since I needed to implement my research project code...

> It believe we should revisit our options after :matches() is implemented, including multiple matching.

Yup

>>> Source/WebCore/cssjit/SelectorCompiler.cpp:1042
>>> +        rootBacktrackingMemoryRequirement.stackCount += 2;
>> 
>> When the visited mode is Visited, we allocate 2 more stack references in the root fragment.
>> In the current simplified semantics, visited is only effective on the root fragments.
> 
> I would put this branch outside computeBacktrackingMemoryRequirements().
> 
> Those two registers are unaffected by backtracking. Having the check in generateSelectorChecker() would also put the branch in the same scope where the stack references are assigned.

OK, that sounds nice.
I've put this in generateSelectorChecker, it's near to the use of :visited's stack reference allocation code.

>> Source/WebCore/cssjit/SelectorCompiler.cpp:1561
>> +
> 
> I would prefer this allocation to be more explicit.
> 
> First, allocate allocateUninitialized(). Then take two out of the vector for :visited. The assign the remaining vector to m_backtrackingStack.

It looks very nice to me.
So creating the variable, `temporaryStack`, allocating the stack references as this vector, taking 2 reference out of it and assigns the remaining vector to m_backtrackingStack. It makes the code clear.

>> LayoutTests/TestExpectations:221
>> +# fast/history/link-inside-not.html [ Failure ]
> 
> Let's remove those lines entirely.

done :)
Comment 29 Yusuke Suzuki 2014-10-13 15:47:11 PDT
Committed r174663: <http://trac.webkit.org/changeset/174663>