Open Bug 1327801 Opened 7 years ago Updated 2 years ago

:Visited links incorrectly change CSS "color" property during CSS transition

Categories

(Core :: CSS Parsing and Computation, defect, P3)

defect

Tracking

()

People

(Reporter: arni2033, Unassigned)

Details

>>>   My Info:   Win7_64, Nightly 49, 32bit, ID 20160526082509
STR_1:
1. Visit http://example.org/
2. Open url [1] in a new tab
3. Uncheck the checkbox on the page

AR:  1st link's color changes:   RED -> BLUE -> GREEN -> RED.   Transition BLUE -> GREEN is smooth
ER:  1st link should stay RED

[1]
data:text/html,
<input class="i" type="checkbox" checked="checked">
<div>
<a class="h" target="_blank" href="http://example.org/">Example.org</a><br>
<a class="h" href="http://example.org/%232016.11.22 21-32-20">Example.org Unique</a><br>
</div>
<style>
    body {
        background:black;
    }
    .i:checked + div > .h{
        color:%2300ff7f;
    }
    .h {
        transition:all 1s ease;
    }
    .h:visited{
        color:red!important;
    }
</style>
No longer blocks: 1277113
Component: Untriaged → CSS Parsing and Computation
Product: Firefox → Core
A slightly different testcase with the following style:
> body {
>     background:black;
> }
> .i:checked + div > .h {
>     color:#00ff7f !important;
> }
> .h {
>     transition:all 1s ease;
> }
> .i + div > .h:visited{
>     color:red;
> }

Chrome correctly does transition between green and red for the visited link here, while Firefox doesn't.

Side note that we need to ensure that the script should not be able to distinguish between the two links. I added the following script to the page
> <script>
>   for (let $a of document.querySelectorAll('.h')) {
>     $a.addEventListener('transitionend', evt => {
>       console.log($a, evt);
>     });
>   }
>   let s1 = getComputedStyle(document.querySelector('.h'));
>   let s2 = getComputedStyle(document.querySelector('.h~.h'));
>   setInterval(function() {
>     console.log(`1: ${s1.color}, 2: ${s2.color}`);
>   }, 100);
> </script>
which confirms that Chrome doesn't leak any information via these things.
birtles, you may have some thought here?

I guess it means we would need to set mStyleIfVisited properly for style context of the transition.
Flags: needinfo?(bbirtles)
So let me check I understand: the issue is not that transitions override !important properties (they should) but that they should not fire for :visited declarations in a way that is detectable.

I might slightly prefer to simply not fire transitions on :visited because the ways in which they might be detected will probably grow. Currently we'd need to be careful about getComputedStyle and transition events, but in future we will also have to hide them from Element.getAnimations() when we ship that. And perhaps that surface area will grow even further as we add new APIs through Houdini etc. I could be persuaded otherwise, though.

Of course, if we decide they should not fire, we should probably coordinate with Chrome and update the spec. I wonder how useful it is to actually transition :visited links?
Flags: needinfo?(bbirtles)
The event should be fired, because otherwise script can detect whether the link is visited via detecting whether there is transition event. Chrome's behavior is correct as far as I can see.

The rendering on screen should respect :visited rules, but anything else (including events, Animation API, etc.) should behave as if :visited is never evluated to true at all.
(In reply to Xidorn Quan [:xidorn] (UTC+10) from comment #4)
> The event should be fired, because otherwise script can detect whether the
> link is visited via detecting whether there is transition event. Chrome's
> behavior is correct as far as I can see.

If we modify the original test case from comment 0 to:

    body {
        background:black;
    }
    .h {
        color: red;
        transition: all 1s ease;
    }
    .i:checked + div > .h:visited {
        color: blue;
    }

Should we fire transition events or not? My thinking was no, and I think you were suggesting the same thing, i.e. that we "should behave as if :visited is never evluated to true at all".

I notice that we currently *don't* fire a transition in this case. On the other hand Chrome does fire a transition, but dispatches *two* transitionend events, i.e. it acts as if :visited always evaluates true here.
Interesting... I guess they fire event on both cases that either visited style is changed or non-visited style is changed. That doesn't leak information as well.

I guess it depends on which is easier to implement then. If firing event that way isn't harder than handling as if :visited is not evaluated to true, then we can follow their behavior and have the spec match this behavior.

In addition, they don't fire any event (and no transition either) when the color is changed because of history update. It seems we don't either, which is good.
I'm curious if this is a regression.


(In reply to Xidorn Quan [:xidorn] (UTC+10) from comment #4)
> The rendering on screen should respect :visited rules, but anything else
> (including events, Animation API, etc.) should behave as if :visited is
> never evaluated to true at all.

There's also another option, which is to transition both the non-:visited and :visited styles (regardless of whether the link is visited or not), and fire events for both.  That might be confusing, though.  Or, alternatively, to transition both but only fire events for the non-:visited ones.
Priority: -- → P3
Severity: normal → S3
You need to log in before you can comment on or make changes to this bug.