W3C home > Mailing lists > Public > www-style@w3.org > January 2012

Re: [css3-transitions] Transitions from display:none

From: James Robinson <jamesr@google.com>
Date: Tue, 17 Jan 2012 22:14:29 -0800
Message-ID: <CAD73mdJ9NOZMwuBFHrYTBk4T2zmpxBOSpXmwLTpucgTkWxPa2g@mail.gmail.com>
To: Boris Zbarsky <bzbarsky@mit.edu>
Cc: "Tab Atkins Jr." <jackalmage@gmail.com>, Sylvain Galineau <sylvaing@microsoft.com>, "www-style@w3.org" <www-style@w3.org>
On Fri, Jan 13, 2012 at 12:39 PM, Boris Zbarsky <bzbarsky@mit.edu> wrote:

> On 1/13/12 3:22 PM, Tab Atkins Jr. wrote:
>
>> No, it doesn't.  If the adblocker wants to know an image's size, it
>> will *request* the image's size.  *That* is an operation that requires
>> up-to-date layout information.  Setting the src of an image might
>> trigger an adblocker's operation, but it does not, itself, require any
>> layout info to complete.
>>
>
> My point is that you, as the spec writer, can't require that the act of
> setting an image src not trigger an ad blocker.
>
>
>  You seem to think that text input values are simpler than they really
>>> are...
>>> they're not.
>>>
>>
>> I do not understand at all how the style engine is involved in
>> changing the value of an input.  Enlighten me?
>>
>
> <shrug>.  I'm happy to paste a bunch of C++ here if you want if you don't
> want to take my word for it.  The short is that if values can be stored in
> multiple places and shuffled lazily (say between the element and the CSS
> box, which is what you see on screen), then the implementation can allow
> temporary inconsistent states which can only be resolved by making sure the
> set of CSS boxes is up to date, which involves computing style.
>
>
>  (The only thing I can think of is if it's considered similar to a DOM
>> mutation.)
>>
>
> Well, it certainly _is_ that in Gecko. Trusted code can even touch that
> DOM inside the text input.  I was leaving that out, because that's a whole
> separate kettle of worms.
>
>
>  Yes, I'd like to do so.  It makes sense to me to base this timing off
>> of the flush points (specifying when flush points occur)
>>
>
> I don't think this is feasible.
>
>
>  but there
>> may be a better place to do so, like "at the end of the current task"
>> (the hook used for the new mutation events).
>>
>
> Or perhaps off the same timing source that requestAnimationFrame runs from?


This is problematic if the page is in a background tab, since that turns
off the requestAnimationFrame mechanism.

To look at this problem another way, it seems like the proposal is to
define blocks of style changes that have to be treated atomically.  In
Sylvain's original scenario:
"""
E { display:none; color:red; transition: color 0.25s; }

...then script such as:

e.style.color = "blue";
e.style.display = "block";

The expectation is that no transition occurs because the element was
display:none when the color property was updated.

But if we now did:

e.style.display = "block";
e.style.color = "blue";
"""

it sounds like he's proposing each script assignment to Element.style be
treated atomically and changes resolved against each of those.  That would
be very difficult to implement in WebKit and I believe in gecko as well
because we treat larger blocks as atomic.  Roughly speaking WebKit treats
all changes between style flushes within a given document as atomic.
 There's not total consistency here either - WebKit flushes styles on
documents independently of each other, but it seems Gecko flushes across
documents in some cases - compare:
http://webstuff.nfshost.com/tests/outer.html
http://webstuff.nfshost.com/tests/outer2.html

for some odd results.

I think Boris is right that anything that exactly specifies where flush
points have to go is a non-starter.  In WebKit, we build and maintain a
number of ancillary data structures related to the CSS box tree at the same
time that we flush styles since the basic structure of the box tree depends
on computed style only.  As a result of this, we need to flush styles
whenever we need to update these data structures or anything that hangs off
of them.  As it turns out this happens in various places in the code.  As
Boris mentioned, editing is one of the hairier places where this occurs.

I also think that Sylvain's interpretation of the author's expectation is
not ideal.  First, it places significant restrictions on implementations -
we would have to either flush styles on every property assignment or attach
metadata to every property assignment in order to figure out whether an
animation really happened or not.  It's also not clear what is supposed to
happen in fairly common cases.  For example, what if display:none and
color:blue were controlled by classes display_none and color_blue and the
author did this:

e.className = "display_none color_blue";

do we have to define an order in which the class names change?  What if the
two styles were controlled by structural selectors and they were made to
match based on changes to the DOM not directly related to e? Would we have
to keep metadata around for all changes that could possibly change whether
a selector applied to an element?  It quickly gets out of hand, and I don't
think it is really that much easier to understand for authors.

Here's an alternate proposal:

Whenever the browser notices a change in the computed style of a property
that would result in an animation/transition, it queues a task. When the
task runs, it checks if a CSS box exists for that element and if the
computed value is something that triggers an animation/transition.  Then
and only then would the animation/transition actually start - all start
events fire, :animating/:transition pseudo-classes start matching, etc etc.
 If the element doesn't have a CSS box when the task runs, or if the
computed style has changed back such that the animation/transition would be
a no-op, then the animation/transition silently does not happen.

The advantage of doing this off a task is that the only work that needs to
happen if/when the browser flushes styles is queueing a task.  The behavior
is still somewhat racy in the sense that the author can race setTimeout()s
against the task and have the animation/transition start or not depending
on who wins the race, but I don't think that is any different from any
other setTimeout()-based wackiness.

Concretely with this proposal in Sylvain's example the transition would not
occur in either of the scenarios he mentions:

e.style.color = "blue";
e.style.display = "block";

e.style.display = "block";
e.style.color = "blue";

or in this slightly trickier one:

e.style.color = "blue";
document.body.offsetTop; // forces a style flush, at least in WebKit
e.style.display = "none";

This also means that the following would always return false:
<style>
#e:transitioning { background-color: green }
</style>
<script>
function checkForTransitionInSameScriptBlock() {
  e.style.color = "blue";
  return getComputedStyle(e).backgroundColor == "green"; // or rgb(0, 255,
0) or however it's supposed to serialize
}
</script>

If the style flush happens totally asynchronously with respect to script,
say at requestAnimationFrame time, the browser can go ahead and start the
animation/transition right then and there since there won't be any
script-observable difference in behavior between that and queuing a
separate task.

- James


>
>
> -Boris
>
>
>
Received on Wednesday, 18 January 2012 06:14:59 GMT

This archive was generated by hypermail 2.3.1 : Tuesday, 26 March 2013 17:20:48 GMT