LCOV - code coverage report
Current view: directory - layout/style - nsTransitionManager.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 288 0 0.0 %
Date: 2012-06-02 Functions: 25 0 0.0 %

       1                 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2                 : /* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
       3                 : /* ***** BEGIN LICENSE BLOCK *****
       4                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       5                 :  *
       6                 :  * The contents of this file are subject to the Mozilla Public License Version
       7                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       8                 :  * the License. You may obtain a copy of the License at
       9                 :  * http://www.mozilla.org/MPL/
      10                 :  *
      11                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      12                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      13                 :  * for the specific language governing rights and limitations under the
      14                 :  * License.
      15                 :  *
      16                 :  * The Original Code is nsTransitionManager.
      17                 :  *
      18                 :  * The Initial Developer of the Original Code is the Mozilla Foundation.
      19                 :  * Portions created by the Initial Developer are Copyright (C) 2009
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *   L. David Baron <dbaron@dbaron.org>, Mozilla Corporation (original author)
      24                 :  *
      25                 :  * Alternatively, the contents of this file may be used under the terms of
      26                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      27                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      28                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      29                 :  * of those above. If you wish to allow use of your version of this file only
      30                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      31                 :  * use your version of this file under the terms of the MPL, indicate your
      32                 :  * decision by deleting the provisions above and replace them with the notice
      33                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      34                 :  * the provisions above, a recipient may use your version of this file under
      35                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      36                 :  *
      37                 :  * ***** END LICENSE BLOCK ***** */
      38                 : 
      39                 : /* Code to start and animate CSS transitions. */
      40                 : 
      41                 : #include "nsTransitionManager.h"
      42                 : #include "nsIContent.h"
      43                 : #include "nsStyleContext.h"
      44                 : #include "nsCSSProps.h"
      45                 : #include "mozilla/TimeStamp.h"
      46                 : #include "nsRefreshDriver.h"
      47                 : #include "nsRuleProcessorData.h"
      48                 : #include "nsIStyleRule.h"
      49                 : #include "nsRuleWalker.h"
      50                 : #include "nsRuleData.h"
      51                 : #include "gfxColor.h"
      52                 : #include "nsCSSPropertySet.h"
      53                 : #include "nsStyleAnimation.h"
      54                 : #include "nsEventDispatcher.h"
      55                 : #include "nsGUIEvent.h"
      56                 : #include "mozilla/dom/Element.h"
      57                 : 
      58                 : using mozilla::TimeStamp;
      59                 : using mozilla::TimeDuration;
      60                 : 
      61                 : namespace dom = mozilla::dom;
      62                 : namespace css = mozilla::css;
      63                 : 
      64                 : /*****************************************************************************
      65                 :  * Per-Element data                                                          *
      66                 :  *****************************************************************************/
      67                 : 
      68                 : struct ElementPropertyTransition
      69               0 : {
      70                 :   nsCSSProperty mProperty;
      71                 :   nsStyleAnimation::Value mStartValue, mEndValue;
      72                 :   TimeStamp mStartTime; // actual start plus transition delay
      73                 : 
      74                 :   // data from the relevant nsTransition
      75                 :   TimeDuration mDuration;
      76                 :   css::ComputedTimingFunction mTimingFunction;
      77                 : 
      78                 :   // This is the start value to be used for a check for whether a
      79                 :   // transition is being reversed.  Normally the same as mStartValue,
      80                 :   // except when this transition started as the reversal of another
      81                 :   // in-progress transition.  Needed so we can handle two reverses in a
      82                 :   // row.
      83                 :   nsStyleAnimation::Value mStartForReversingTest;
      84                 :   // Likewise, the portion (in value space) of the "full" reversed
      85                 :   // transition that we're actually covering.  For example, if a :hover
      86                 :   // effect has a transition that moves the element 10px to the right
      87                 :   // (by changing 'left' from 0px to 10px), and the mouse moves in to
      88                 :   // the element (starting the transition) but then moves out after the
      89                 :   // transition has advanced 4px, the second transition (from 10px/4px
      90                 :   // to 0px) will have mReversePortion of 0.4.  (If the mouse then moves
      91                 :   // in again when the transition is back to 2px, the mReversePortion
      92                 :   // for the third transition (from 0px/2px to 10px) will be 0.8.
      93                 :   double mReversePortion;
      94                 : 
      95                 :   // Compute the portion of the *value* space that we should be through
      96                 :   // at the given time.  (The input to the transition timing function
      97                 :   // has time units, the output has value units.)
      98                 :   double ValuePortionFor(TimeStamp aRefreshTime) const;
      99                 : 
     100               0 :   bool IsRemovedSentinel() const
     101                 :   {
     102               0 :     return mStartTime.IsNull();
     103                 :   }
     104                 : 
     105               0 :   void SetRemovedSentinel()
     106                 :   {
     107                 :     // assign the null time stamp
     108               0 :     mStartTime = TimeStamp();
     109               0 :   }
     110                 : };
     111                 : 
     112                 : double
     113               0 : ElementPropertyTransition::ValuePortionFor(TimeStamp aRefreshTime) const
     114                 : {
     115                 :   // Set |timePortion| to the portion of the way we are through the time
     116                 :   // input to the transition's timing function (always within the range
     117                 :   // 0-1).
     118               0 :   double duration = mDuration.ToSeconds();
     119               0 :   NS_ABORT_IF_FALSE(duration >= 0.0, "negative duration forbidden");
     120                 :   double timePortion;
     121               0 :   if (duration == 0.0) {
     122                 :     // When duration is zero, we can still have a transition when delay
     123                 :     // is nonzero.  mStartTime already incorporates delay.
     124               0 :     if (aRefreshTime >= mStartTime) {
     125               0 :       timePortion = 1.0;
     126                 :     } else {
     127               0 :       timePortion = 0.0;
     128                 :     }
     129                 :   } else {
     130               0 :     timePortion = (aRefreshTime - mStartTime).ToSeconds() / duration;
     131               0 :     if (timePortion < 0.0)
     132               0 :       timePortion = 0.0; // use start value during transition-delay
     133               0 :     if (timePortion > 1.0)
     134               0 :       timePortion = 1.0; // we might be behind on flushing
     135                 :   }
     136                 : 
     137               0 :   return mTimingFunction.GetValue(timePortion);
     138                 : }
     139                 : 
     140                 : struct ElementTransitions : public mozilla::css::CommonElementAnimationData
     141               0 : {
     142               0 :   ElementTransitions(dom::Element *aElement, nsIAtom *aElementProperty,
     143                 :                      nsTransitionManager *aTransitionManager)
     144                 :     : CommonElementAnimationData(aElement, aElementProperty,
     145               0 :                                  aTransitionManager)
     146                 :   {
     147               0 :   }
     148                 : 
     149                 :   void EnsureStyleRuleFor(TimeStamp aRefreshTime);
     150                 : 
     151                 : 
     152                 :   // Either zero or one for each CSS property:
     153                 :   nsTArray<ElementPropertyTransition> mPropertyTransitions;
     154                 : 
     155                 :   // This style rule overrides style data with the currently
     156                 :   // transitioning value for an element that is executing a transition.
     157                 :   // It only matches when styling with animation.  When we style without
     158                 :   // animation, we need to not use it so that we can detect any new
     159                 :   // changes; if necessary we restyle immediately afterwards with
     160                 :   // animation.
     161                 :   nsRefPtr<css::AnimValuesStyleRule> mStyleRule;
     162                 :   // The refresh time associated with mStyleRule.
     163                 :   TimeStamp mStyleRuleRefreshTime;
     164                 : };
     165                 : 
     166                 : static void
     167               0 : ElementTransitionsPropertyDtor(void           *aObject,
     168                 :                                nsIAtom        *aPropertyName,
     169                 :                                void           *aPropertyValue,
     170                 :                                void           *aData)
     171                 : {
     172               0 :   ElementTransitions *et = static_cast<ElementTransitions*>(aPropertyValue);
     173                 : #ifdef DEBUG
     174               0 :   NS_ABORT_IF_FALSE(!et->mCalledPropertyDtor, "can't call dtor twice");
     175               0 :   et->mCalledPropertyDtor = true;
     176                 : #endif
     177               0 :   delete et;
     178               0 : }
     179                 : 
     180                 : void
     181               0 : ElementTransitions::EnsureStyleRuleFor(TimeStamp aRefreshTime)
     182                 : {
     183               0 :   if (!mStyleRule || mStyleRuleRefreshTime != aRefreshTime) {
     184               0 :     mStyleRule = new css::AnimValuesStyleRule();
     185               0 :     mStyleRuleRefreshTime = aRefreshTime;
     186                 : 
     187               0 :     for (PRUint32 i = 0, i_end = mPropertyTransitions.Length(); i < i_end; ++i)
     188                 :     {
     189               0 :       ElementPropertyTransition &pt = mPropertyTransitions[i];
     190               0 :       if (pt.IsRemovedSentinel()) {
     191               0 :         continue;
     192                 :       }
     193                 : 
     194               0 :       nsStyleAnimation::Value *val = mStyleRule->AddEmptyValue(pt.mProperty);
     195                 : 
     196               0 :       double valuePortion = pt.ValuePortionFor(aRefreshTime);
     197                 : #ifdef DEBUG
     198                 :       bool ok =
     199                 : #endif
     200                 :         nsStyleAnimation::Interpolate(pt.mProperty,
     201                 :                                       pt.mStartValue, pt.mEndValue,
     202               0 :                                       valuePortion, *val);
     203               0 :       NS_ABORT_IF_FALSE(ok, "could not interpolate values");
     204                 :     }
     205                 :   }
     206               0 : }
     207                 : 
     208                 : /*****************************************************************************
     209                 :  * nsTransitionManager                                                       *
     210                 :  *****************************************************************************/
     211                 : 
     212                 : already_AddRefed<nsIStyleRule>
     213               0 : nsTransitionManager::StyleContextChanged(dom::Element *aElement,
     214                 :                                          nsStyleContext *aOldStyleContext,
     215                 :                                          nsStyleContext *aNewStyleContext)
     216                 : {
     217               0 :   NS_PRECONDITION(aOldStyleContext->GetPseudo() ==
     218                 :                       aNewStyleContext->GetPseudo(),
     219                 :                   "pseudo type mismatch");
     220                 :   // If we were called from ReparentStyleContext, this assertion would
     221                 :   // actually fire.  If we need to be called from there, we can probably
     222                 :   // just remove it; the condition probably isn't critical, although
     223                 :   // it's worth thinking about some more.
     224               0 :   NS_PRECONDITION(aOldStyleContext->HasPseudoElementData() ==
     225                 :                       aNewStyleContext->HasPseudoElementData(),
     226                 :                   "pseudo type mismatch");
     227                 : 
     228                 :   // NOTE: Things in this function (and ConsiderStartingTransition)
     229                 :   // should never call PeekStyleData because we don't preserve gotten
     230                 :   // structs across reframes.
     231                 : 
     232                 :   // Return sooner (before the startedAny check below) for the most
     233                 :   // common case: no transitions specified or running.
     234               0 :   const nsStyleDisplay *disp = aNewStyleContext->GetStyleDisplay();
     235               0 :   nsCSSPseudoElements::Type pseudoType = aNewStyleContext->GetPseudoType();
     236               0 :   if (pseudoType != nsCSSPseudoElements::ePseudo_NotPseudoElement) {
     237               0 :     if (pseudoType != nsCSSPseudoElements::ePseudo_before &&
     238                 :         pseudoType != nsCSSPseudoElements::ePseudo_after) {
     239               0 :       return nsnull;
     240                 :     }
     241                 : 
     242               0 :     NS_ASSERTION((pseudoType == nsCSSPseudoElements::ePseudo_before &&
     243                 :                   aElement->Tag() == nsGkAtoms::mozgeneratedcontentbefore) ||
     244                 :                  (pseudoType == nsCSSPseudoElements::ePseudo_after &&
     245                 :                   aElement->Tag() == nsGkAtoms::mozgeneratedcontentafter),
     246                 :                  "Unexpected aElement coming through");
     247                 : 
     248                 :     // Else the element we want to use from now on is the element the
     249                 :     // :before or :after is attached to.
     250               0 :     aElement = aElement->GetParent()->AsElement();
     251                 :   }
     252                 : 
     253                 :   ElementTransitions *et =
     254               0 :       GetElementTransitions(aElement, pseudoType, false);
     255               0 :   if (!et &&
     256                 :       disp->mTransitionPropertyCount == 1 &&
     257               0 :       disp->mTransitions[0].GetDelay() == 0.0f &&
     258               0 :       disp->mTransitions[0].GetDuration() == 0.0f) {
     259               0 :     return nsnull;
     260                 :   }      
     261                 : 
     262                 : 
     263               0 :   if (aNewStyleContext->PresContext()->IsProcessingAnimationStyleChange()) {
     264               0 :     return nsnull;
     265                 :   }
     266                 :   
     267               0 :   if (aNewStyleContext->GetParent() &&
     268               0 :       aNewStyleContext->GetParent()->HasPseudoElementData()) {
     269                 :     // Ignore transitions on things that inherit properties from
     270                 :     // pseudo-elements.
     271                 :     // FIXME (Bug 522599): Add tests for this.
     272               0 :     return nsnull;
     273                 :   }
     274                 : 
     275                 :   // Per http://lists.w3.org/Archives/Public/www-style/2009Aug/0109.html
     276                 :   // I'll consider only the transitions from the number of items in
     277                 :   // 'transition-property' on down, and later ones will override earlier
     278                 :   // ones (tracked using |whichStarted|).
     279               0 :   bool startedAny = false;
     280               0 :   nsCSSPropertySet whichStarted;
     281               0 :   for (PRUint32 i = disp->mTransitionPropertyCount; i-- != 0; ) {
     282               0 :     const nsTransition& t = disp->mTransitions[i];
     283                 :     // Check delay and duration first, since they default to zero, and
     284                 :     // when they're both zero, we can ignore the transition.
     285               0 :     if (t.GetDelay() != 0.0f || t.GetDuration() != 0.0f) {
     286                 :       // We might have something to transition.  See if any of the
     287                 :       // properties in question changed and are animatable.
     288                 :       // FIXME: Would be good to find a way to share code between this
     289                 :       // interpretation of transition-property and the one below.
     290               0 :       nsCSSProperty property = t.GetProperty();
     291               0 :       if (property == eCSSPropertyExtra_no_properties ||
     292                 :           property == eCSSProperty_UNKNOWN) {
     293                 :         // Nothing to do, but need to exclude this from cases below.
     294               0 :       } else if (property == eCSSPropertyExtra_all_properties) {
     295               0 :         for (nsCSSProperty p = nsCSSProperty(0); 
     296                 :              p < eCSSProperty_COUNT_no_shorthands;
     297                 :              p = nsCSSProperty(p + 1)) {
     298                 :           ConsiderStartingTransition(p, t, aElement, et,
     299                 :                                      aOldStyleContext, aNewStyleContext,
     300               0 :                                      &startedAny, &whichStarted);
     301                 :         }
     302               0 :       } else if (nsCSSProps::IsShorthand(property)) {
     303               0 :         CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(subprop, property) {
     304                 :           ConsiderStartingTransition(*subprop, t, aElement, et,
     305                 :                                      aOldStyleContext, aNewStyleContext,
     306               0 :                                      &startedAny, &whichStarted);
     307                 :         }
     308                 :       } else {
     309                 :         ConsiderStartingTransition(property, t, aElement, et,
     310                 :                                    aOldStyleContext, aNewStyleContext,
     311               0 :                                    &startedAny, &whichStarted);
     312                 :       }
     313                 :     }
     314                 :   }
     315                 : 
     316                 :   // Stop any transitions for properties that are no longer in
     317                 :   // 'transition-property'.
     318                 :   // Also stop any transitions for properties that just changed (and are
     319                 :   // still in the set of properties to transition), but we didn't just
     320                 :   // start the transition because delay and duration are both zero.
     321               0 :   if (et) {
     322                 :     bool checkProperties =
     323               0 :       disp->mTransitions[0].GetProperty() != eCSSPropertyExtra_all_properties;
     324               0 :     nsCSSPropertySet allTransitionProperties;
     325               0 :     if (checkProperties) {
     326               0 :       for (PRUint32 i = disp->mTransitionPropertyCount; i-- != 0; ) {
     327               0 :         const nsTransition& t = disp->mTransitions[i];
     328                 :         // FIXME: Would be good to find a way to share code between this
     329                 :         // interpretation of transition-property and the one above.
     330               0 :         nsCSSProperty property = t.GetProperty();
     331               0 :         if (property == eCSSPropertyExtra_no_properties ||
     332                 :             property == eCSSProperty_UNKNOWN) {
     333                 :           // Nothing to do, but need to exclude this from cases below.
     334               0 :         } else if (property == eCSSPropertyExtra_all_properties) {
     335               0 :           for (nsCSSProperty p = nsCSSProperty(0); 
     336                 :                p < eCSSProperty_COUNT_no_shorthands;
     337                 :                p = nsCSSProperty(p + 1)) {
     338               0 :             allTransitionProperties.AddProperty(p);
     339                 :           }
     340               0 :         } else if (nsCSSProps::IsShorthand(property)) {
     341               0 :           CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(subprop, property) {
     342               0 :             allTransitionProperties.AddProperty(*subprop);
     343                 :           }
     344                 :         } else {
     345               0 :           allTransitionProperties.AddProperty(property);
     346                 :         }
     347                 :       }
     348                 :     }
     349                 : 
     350               0 :     nsTArray<ElementPropertyTransition> &pts = et->mPropertyTransitions;
     351               0 :     PRUint32 i = pts.Length();
     352               0 :     NS_ABORT_IF_FALSE(i != 0, "empty transitions list?");
     353               0 :     nsStyleAnimation::Value currentValue;
     354               0 :     do {
     355               0 :       --i;
     356               0 :       ElementPropertyTransition &pt = pts[i];
     357                 :           // properties no longer in 'transition-property'
     358               0 :       if ((checkProperties &&
     359               0 :            !allTransitionProperties.HasProperty(pt.mProperty)) ||
     360                 :           // properties whose computed values changed but delay and
     361                 :           // duration are both zero
     362                 :           !ExtractComputedValueForTransition(pt.mProperty, aNewStyleContext,
     363               0 :                                              currentValue) ||
     364               0 :           currentValue != pt.mEndValue) {
     365                 :         // stop the transition
     366               0 :         pts.RemoveElementAt(i);
     367                 :       }
     368                 :     } while (i != 0);
     369                 : 
     370               0 :     if (pts.IsEmpty()) {
     371               0 :       et->Destroy();
     372               0 :       et = nsnull;
     373                 :     }
     374                 :   }
     375                 : 
     376               0 :   if (!startedAny) {
     377               0 :     return nsnull;
     378                 :   }
     379                 : 
     380               0 :   NS_ABORT_IF_FALSE(et, "must have element transitions if we started "
     381                 :                         "any transitions");
     382                 : 
     383                 :   // In the CSS working group discussion (2009 Jul 15 telecon,
     384                 :   // http://www.w3.org/mid/4A5E1470.4030904@inkedblade.net ) of
     385                 :   // http://lists.w3.org/Archives/Public/www-style/2009Jun/0121.html ,
     386                 :   // the working group decided that a transition property on an
     387                 :   // element should not cause any transitions if the property change
     388                 :   // is itself inheriting a value that is transitioning on an
     389                 :   // ancestor.  So, to get the correct behavior, we continue the
     390                 :   // restyle that caused this transition using a "covering" rule that
     391                 :   // covers up any changes on which we started transitions, so that
     392                 :   // descendants don't start their own transitions.  (In the case of
     393                 :   // negative transition delay, this covering rule produces different
     394                 :   // results than applying the transition rule immediately would).
     395                 :   // Our caller is responsible for restyling again using this covering
     396                 :   // rule.
     397                 : 
     398               0 :   nsRefPtr<css::AnimValuesStyleRule> coverRule = new css::AnimValuesStyleRule;
     399               0 :   if (!coverRule) {
     400               0 :     NS_WARNING("out of memory");
     401               0 :     return nsnull;
     402                 :   }
     403                 :   
     404               0 :   nsTArray<ElementPropertyTransition> &pts = et->mPropertyTransitions;
     405               0 :   for (PRUint32 i = 0, i_end = pts.Length(); i < i_end; ++i) {
     406               0 :     ElementPropertyTransition &pt = pts[i];
     407               0 :     if (whichStarted.HasProperty(pt.mProperty)) {
     408               0 :       coverRule->AddValue(pt.mProperty, pt.mStartValue);
     409                 :     }
     410                 :   }
     411                 : 
     412               0 :   return coverRule.forget();
     413                 : }
     414                 : 
     415                 : void
     416               0 : nsTransitionManager::ConsiderStartingTransition(nsCSSProperty aProperty,
     417                 :                        const nsTransition& aTransition,
     418                 :                        dom::Element *aElement,
     419                 :                        ElementTransitions *&aElementTransitions,
     420                 :                        nsStyleContext *aOldStyleContext,
     421                 :                        nsStyleContext *aNewStyleContext,
     422                 :                        bool *aStartedAny,
     423                 :                        nsCSSPropertySet *aWhichStarted)
     424                 : {
     425                 :   // IsShorthand itself will assert if aProperty is not a property.
     426               0 :   NS_ABORT_IF_FALSE(!nsCSSProps::IsShorthand(aProperty),
     427                 :                     "property out of range");
     428                 : 
     429               0 :   if (aWhichStarted->HasProperty(aProperty)) {
     430                 :     // A later item in transition-property already started a
     431                 :     // transition for this property, so we ignore this one.
     432                 :     // See comment above and
     433                 :     // http://lists.w3.org/Archives/Public/www-style/2009Aug/0109.html .
     434               0 :     return;
     435                 :   }
     436                 : 
     437               0 :   if (nsCSSProps::kAnimTypeTable[aProperty] == eStyleAnimType_None) {
     438               0 :     return;
     439                 :   }
     440                 : 
     441               0 :   ElementPropertyTransition pt;
     442               0 :   nsStyleAnimation::Value dummyValue;
     443                 :   bool haveValues =
     444                 :     ExtractComputedValueForTransition(aProperty, aOldStyleContext,
     445               0 :                                       pt.mStartValue) &&
     446                 :     ExtractComputedValueForTransition(aProperty, aNewStyleContext,
     447               0 :                                       pt.mEndValue);
     448                 :   bool shouldAnimate =
     449                 :     haveValues &&
     450               0 :     pt.mStartValue != pt.mEndValue &&
     451                 :     // Check that we can interpolate between these values
     452                 :     // (If this is ever a performance problem, we could add a
     453                 :     // CanInterpolate method, but it seems fine for now.)
     454                 :     nsStyleAnimation::Interpolate(aProperty, pt.mStartValue, pt.mEndValue,
     455               0 :                                   0.5, dummyValue);
     456                 : 
     457               0 :   PRUint32 currentIndex = nsTArray<ElementPropertyTransition>::NoIndex;
     458               0 :   if (aElementTransitions) {
     459                 :     nsTArray<ElementPropertyTransition> &pts =
     460               0 :       aElementTransitions->mPropertyTransitions;
     461               0 :     for (PRUint32 i = 0, i_end = pts.Length(); i < i_end; ++i) {
     462               0 :       if (pts[i].mProperty == aProperty) {
     463               0 :         currentIndex = i;
     464               0 :         break;
     465                 :       }
     466                 :     }
     467                 :   }
     468                 : 
     469               0 :   nsPresContext *presContext = aNewStyleContext->PresContext();
     470                 : 
     471               0 :   if (!shouldAnimate) {
     472                 :     nsTArray<ElementPropertyTransition> &pts =
     473               0 :       aElementTransitions->mPropertyTransitions;
     474               0 :     if (currentIndex != nsTArray<ElementPropertyTransition>::NoIndex &&
     475               0 :         (!haveValues || pts[currentIndex].mEndValue != pt.mEndValue)) {
     476                 :       // We're in the middle of a transition, but just got a
     477                 :       // non-transition style change changing to exactly the
     478                 :       // current in-progress value.   (This is quite easy to cause
     479                 :       // using 'transition-delay'.)
     480                 :       //
     481                 :       // We also check that this current in-progress value is different
     482                 :       // from the end value; we don't want to cancel a transition that
     483                 :       // is almost done (and whose current value rounds to its end
     484                 :       // value) just because we got an unrelated style change.
     485               0 :       pts.RemoveElementAt(currentIndex);
     486               0 :       if (pts.IsEmpty()) {
     487               0 :         aElementTransitions->Destroy();
     488                 :         // |aElementTransitions| is now a dangling pointer!
     489               0 :         aElementTransitions = nsnull;
     490                 :       }
     491                 :       // WalkTransitionRule already called RestyleForAnimation.
     492                 :     }
     493                 :     return;
     494                 :   }
     495                 : 
     496                 :   TimeStamp mostRecentRefresh =
     497               0 :     presContext->RefreshDriver()->MostRecentRefresh();
     498                 : 
     499               0 :   const nsTimingFunction &tf = aTransition.GetTimingFunction();
     500               0 :   float delay = aTransition.GetDelay();
     501               0 :   float duration = aTransition.GetDuration();
     502               0 :   if (duration < 0.0) {
     503                 :     // The spec says a negative duration is treated as zero.
     504               0 :     duration = 0.0;
     505                 :   }
     506               0 :   pt.mStartForReversingTest = pt.mStartValue;
     507               0 :   pt.mReversePortion = 1.0;
     508                 : 
     509                 :   // We need to check two things if we have a currently running
     510                 :   // transition for this property.
     511               0 :   if (currentIndex != nsTArray<ElementPropertyTransition>::NoIndex) {
     512                 :     const ElementPropertyTransition &oldPT =
     513               0 :       aElementTransitions->mPropertyTransitions[currentIndex];
     514                 : 
     515               0 :     if (oldPT.mEndValue == pt.mEndValue) {
     516                 :       // If we got a style change that changed the value to the endpoint
     517                 :       // of the currently running transition, we don't want to interrupt
     518                 :       // its timing function.
     519                 :       // WalkTransitionRule already called RestyleForAnimation.
     520                 :       return;
     521                 :     }
     522                 : 
     523                 :     // If the new transition reverses the old one, we'll need to handle
     524                 :     // the timing differently.
     525               0 :     if (!oldPT.IsRemovedSentinel() &&
     526               0 :         oldPT.mStartForReversingTest == pt.mEndValue) {
     527                 :       // Compute the appropriate negative transition-delay such that right
     528                 :       // now we'd end up at the current position.
     529                 :       double valuePortion =
     530               0 :         oldPT.ValuePortionFor(mostRecentRefresh) * oldPT.mReversePortion +
     531               0 :         (1.0 - oldPT.mReversePortion); 
     532                 :       // A timing function with negative y1 (or y2!) might make
     533                 :       // valuePortion negative.  In this case, we still want to apply our
     534                 :       // reversing logic based on relative distances, not make duration
     535                 :       // negative.
     536               0 :       if (valuePortion < 0.0)
     537               0 :         valuePortion = -valuePortion;
     538                 :       // A timing function with y2 (or y1!) greater than one might
     539                 :       // advance past its terminal value.  It's probably a good idea to
     540                 :       // clamp valuePortion to be at most one to preserve the invariant
     541                 :       // that a transition will complete within at most its specified
     542                 :       // time.
     543               0 :       if (valuePortion > 1.0)
     544               0 :         valuePortion = 1.0;
     545                 : 
     546                 :       // Negative delays are essentially part of the transition
     547                 :       // function, so reduce them along with the duration, but don't
     548                 :       // reduce positive delays.
     549               0 :       if (delay < 0.0f)
     550               0 :         delay *= valuePortion;
     551               0 :       duration *= valuePortion;
     552                 : 
     553               0 :       pt.mStartForReversingTest = oldPT.mEndValue;
     554               0 :       pt.mReversePortion = valuePortion;
     555                 :     }
     556                 :   }
     557                 : 
     558               0 :   pt.mProperty = aProperty;
     559               0 :   pt.mStartTime = mostRecentRefresh + TimeDuration::FromMilliseconds(delay);
     560               0 :   pt.mDuration = TimeDuration::FromMilliseconds(duration);
     561               0 :   pt.mTimingFunction.Init(tf);
     562                 : 
     563               0 :   if (!aElementTransitions) {
     564                 :     aElementTransitions =
     565                 :       GetElementTransitions(aElement, aNewStyleContext->GetPseudoType(),
     566               0 :                             true);
     567               0 :     if (!aElementTransitions) {
     568               0 :       NS_WARNING("allocating ElementTransitions failed");
     569                 :       return;
     570                 :     }
     571                 :   }
     572                 :   
     573                 :   nsTArray<ElementPropertyTransition> &pts =
     574               0 :     aElementTransitions->mPropertyTransitions;
     575                 : #ifdef DEBUG
     576               0 :   for (PRUint32 i = 0, i_end = pts.Length(); i < i_end; ++i) {
     577               0 :     NS_ABORT_IF_FALSE(i == currentIndex ||
     578                 :                       pts[i].mProperty != aProperty,
     579                 :                       "duplicate transitions for property");
     580                 :   }
     581                 : #endif
     582               0 :   if (currentIndex != nsTArray<ElementPropertyTransition>::NoIndex) {
     583               0 :     pts[currentIndex] = pt;
     584                 :   } else {
     585               0 :     if (!pts.AppendElement(pt)) {
     586               0 :       NS_WARNING("out of memory");
     587                 :       return;
     588                 :     }
     589                 :   }
     590                 : 
     591                 :   nsRestyleHint hint =
     592               0 :     aNewStyleContext->GetPseudoType() ==
     593                 :       nsCSSPseudoElements::ePseudo_NotPseudoElement ?
     594               0 :     eRestyle_Self : eRestyle_Subtree;
     595               0 :   presContext->PresShell()->RestyleForAnimation(aElement, hint);
     596                 : 
     597               0 :   *aStartedAny = true;
     598               0 :   aWhichStarted->AddProperty(aProperty);
     599                 : }
     600                 : 
     601                 : ElementTransitions*
     602               0 : nsTransitionManager::GetElementTransitions(dom::Element *aElement,
     603                 :                                            nsCSSPseudoElements::Type aPseudoType,
     604                 :                                            bool aCreateIfNeeded)
     605                 : {
     606               0 :   if (!aCreateIfNeeded && PR_CLIST_IS_EMPTY(&mElementData)) {
     607                 :     // Early return for the most common case.
     608               0 :     return nsnull;
     609                 :   }
     610                 : 
     611                 :   nsIAtom *propName;
     612               0 :   if (aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement) {
     613               0 :     propName = nsGkAtoms::transitionsProperty;
     614               0 :   } else if (aPseudoType == nsCSSPseudoElements::ePseudo_before) {
     615               0 :     propName = nsGkAtoms::transitionsOfBeforeProperty;
     616               0 :   } else if (aPseudoType == nsCSSPseudoElements::ePseudo_after) {
     617               0 :     propName = nsGkAtoms::transitionsOfAfterProperty;
     618                 :   } else {
     619               0 :     NS_ASSERTION(!aCreateIfNeeded,
     620                 :                  "should never try to create transitions for pseudo "
     621                 :                  "other than :before or :after");
     622               0 :     return nsnull;
     623                 :   }
     624                 :   ElementTransitions *et = static_cast<ElementTransitions*>(
     625               0 :                              aElement->GetProperty(propName));
     626               0 :   if (!et && aCreateIfNeeded) {
     627                 :     // FIXME: Consider arena-allocating?
     628               0 :     et = new ElementTransitions(aElement, propName, this);
     629               0 :     if (!et) {
     630               0 :       NS_WARNING("out of memory");
     631               0 :       return nsnull;
     632                 :     }
     633                 :     nsresult rv = aElement->SetProperty(propName, et,
     634               0 :                                         ElementTransitionsPropertyDtor, nsnull);
     635               0 :     if (NS_FAILED(rv)) {
     636               0 :       NS_WARNING("SetProperty failed");
     637               0 :       delete et;
     638               0 :       return nsnull;
     639                 :     }
     640                 : 
     641               0 :     AddElementData(et);
     642                 :   }
     643                 : 
     644               0 :   return et;
     645                 : }
     646                 : 
     647                 : /*
     648                 :  * nsIStyleRuleProcessor implementation
     649                 :  */
     650                 : 
     651                 : void
     652               0 : nsTransitionManager::WalkTransitionRule(RuleProcessorData* aData,
     653                 :                                         nsCSSPseudoElements::Type aPseudoType)
     654                 : {
     655                 :   ElementTransitions *et =
     656               0 :     GetElementTransitions(aData->mElement, aPseudoType, false);
     657               0 :   if (!et) {
     658               0 :     return;
     659                 :   }
     660                 : 
     661               0 :   if (aData->mPresContext->IsProcessingRestyles() &&
     662               0 :       !aData->mPresContext->IsProcessingAnimationStyleChange()) {
     663                 :     // If we're processing a normal style change rather than one from
     664                 :     // animation, don't add the transition rule.  This allows us to
     665                 :     // compute the new style value rather than having the transition
     666                 :     // override it, so that we can start transitioning differently.
     667                 : 
     668                 :     // We need to immediately restyle with animation
     669                 :     // after doing this.
     670               0 :     if (et) {
     671                 :       nsRestyleHint hint =
     672                 :         aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement ?
     673               0 :         eRestyle_Self : eRestyle_Subtree;
     674               0 :       mPresContext->PresShell()->RestyleForAnimation(aData->mElement, hint);
     675                 :     }
     676               0 :     return;
     677                 :   }
     678                 : 
     679                 :   et->EnsureStyleRuleFor(
     680               0 :     aData->mPresContext->RefreshDriver()->MostRecentRefresh());
     681                 : 
     682               0 :   aData->mRuleWalker->Forward(et->mStyleRule);
     683                 : }
     684                 : 
     685                 : /* virtual */ void
     686               0 : nsTransitionManager::RulesMatching(ElementRuleProcessorData* aData)
     687                 : {
     688               0 :   NS_ABORT_IF_FALSE(aData->mPresContext == mPresContext,
     689                 :                     "pres context mismatch");
     690                 :   WalkTransitionRule(aData,
     691               0 :                      nsCSSPseudoElements::ePseudo_NotPseudoElement);
     692               0 : }
     693                 : 
     694                 : /* virtual */ void
     695               0 : nsTransitionManager::RulesMatching(PseudoElementRuleProcessorData* aData)
     696                 : {
     697               0 :   NS_ABORT_IF_FALSE(aData->mPresContext == mPresContext,
     698                 :                     "pres context mismatch");
     699                 : 
     700                 :   // Note:  If we're the only thing keeping a pseudo-element frame alive
     701                 :   // (per ProbePseudoStyleContext), we still want to keep it alive, so
     702                 :   // this is ok.
     703               0 :   WalkTransitionRule(aData, aData->mPseudoType);
     704               0 : }
     705                 : 
     706                 : /* virtual */ void
     707               0 : nsTransitionManager::RulesMatching(AnonBoxRuleProcessorData* aData)
     708                 : {
     709               0 : }
     710                 : 
     711                 : #ifdef MOZ_XUL
     712                 : /* virtual */ void
     713               0 : nsTransitionManager::RulesMatching(XULTreeRuleProcessorData* aData)
     714                 : {
     715               0 : }
     716                 : #endif
     717                 : 
     718                 : /* virtual */ size_t
     719               0 : nsTransitionManager::SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const
     720                 : {
     721               0 :   return CommonAnimationManager::SizeOfExcludingThis(aMallocSizeOf);
     722                 : }
     723                 : 
     724                 : /* virtual */ size_t
     725               0 : nsTransitionManager::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const
     726                 : {
     727               0 :   return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
     728                 : }
     729                 : 
     730               0 : struct TransitionEventInfo {
     731                 :   nsCOMPtr<nsIContent> mElement;
     732                 :   nsTransitionEvent mEvent;
     733                 : 
     734               0 :   TransitionEventInfo(nsIContent *aElement, nsCSSProperty aProperty,
     735                 :                       TimeDuration aDuration)
     736                 :     : mElement(aElement),
     737                 :       mEvent(true, NS_TRANSITION_END,
     738               0 :              NS_ConvertUTF8toUTF16(nsCSSProps::GetStringValue(aProperty)),
     739               0 :              aDuration.ToSeconds())
     740                 :   {
     741               0 :   }
     742                 : 
     743                 :   // nsTransitionEvent doesn't support copy-construction, so we need
     744                 :   // to ourselves in order to work with nsTArray
     745               0 :   TransitionEventInfo(const TransitionEventInfo &aOther)
     746                 :     : mElement(aOther.mElement),
     747                 :       mEvent(true, NS_TRANSITION_END,
     748               0 :              aOther.mEvent.propertyName, aOther.mEvent.elapsedTime)
     749                 :   {
     750               0 :   }
     751                 : };
     752                 : 
     753                 : /* virtual */ void
     754               0 : nsTransitionManager::WillRefresh(mozilla::TimeStamp aTime)
     755                 : {
     756               0 :   NS_ABORT_IF_FALSE(mPresContext,
     757                 :                     "refresh driver should not notify additional observers "
     758                 :                     "after pres context has been destroyed");
     759               0 :   if (!mPresContext->GetPresShell()) {
     760                 :     // Someone might be keeping mPresContext alive past the point
     761                 :     // where it has been torn down; don't bother doing anything in
     762                 :     // this case.  But do get rid of all our transitions so we stop
     763                 :     // triggering refreshes.
     764               0 :     RemoveAllElementData();
     765               0 :     return;
     766                 :   }
     767                 : 
     768               0 :   nsTArray<TransitionEventInfo> events;
     769                 : 
     770                 :   // Trim transitions that have completed, and post restyle events for
     771                 :   // frames that are still transitioning.
     772                 :   {
     773               0 :     PRCList *next = PR_LIST_HEAD(&mElementData);
     774               0 :     while (next != &mElementData) {
     775               0 :       ElementTransitions *et = static_cast<ElementTransitions*>(next);
     776               0 :       next = PR_NEXT_LINK(next);
     777                 : 
     778               0 :       NS_ABORT_IF_FALSE(et->mElement->GetCurrentDoc() ==
     779                 :                           mPresContext->Document(),
     780                 :                         "nsGenericElement::UnbindFromTree should have "
     781                 :                         "destroyed the element transitions object");
     782                 : 
     783               0 :       PRUint32 i = et->mPropertyTransitions.Length();
     784               0 :       NS_ABORT_IF_FALSE(i != 0, "empty transitions list?");
     785               0 :       do {
     786               0 :         --i;
     787               0 :         ElementPropertyTransition &pt = et->mPropertyTransitions[i];
     788               0 :         if (pt.IsRemovedSentinel()) {
     789                 :           // Actually remove transitions one cycle after their
     790                 :           // completion.  See comment below.
     791               0 :           et->mPropertyTransitions.RemoveElementAt(i);
     792               0 :         } else if (pt.mStartTime + pt.mDuration <= aTime) {
     793                 :           // This transition has completed.
     794                 : 
     795                 :           // Fire transitionend events only for transitions on elements
     796                 :           // and not those on pseudo-elements, since we can't target an
     797                 :           // event at pseudo-elements.
     798               0 :           if (et->mElementProperty == nsGkAtoms::transitionsProperty) {
     799               0 :             nsCSSProperty prop = pt.mProperty;
     800               0 :             if (nsCSSProps::PropHasFlags(prop, CSS_PROPERTY_REPORT_OTHER_NAME))
     801                 :             {
     802               0 :               prop = nsCSSProps::OtherNameFor(prop);
     803                 :             }
     804                 :             events.AppendElement(
     805               0 :               TransitionEventInfo(et->mElement, prop, pt.mDuration));
     806                 :           }
     807                 : 
     808                 :           // Leave this transition in the list for one more refresh
     809                 :           // cycle, since we haven't yet processed its style change, and
     810                 :           // if we also have (already, or will have from processing
     811                 :           // transitionend events or other refresh driver notifications)
     812                 :           // a non-animation style change that would affect it, we need
     813                 :           // to know not to start a new transition for the transition
     814                 :           // from the almost-completed value to the final value.
     815               0 :           pt.SetRemovedSentinel();
     816                 :         }
     817                 :       } while (i != 0);
     818                 : 
     819                 :       // We need to restyle even if the transition rule no longer
     820                 :       // applies (in which case we just made it not apply).
     821               0 :       NS_ASSERTION(et->mElementProperty == nsGkAtoms::transitionsProperty ||
     822                 :                    et->mElementProperty == nsGkAtoms::transitionsOfBeforeProperty ||
     823                 :                    et->mElementProperty == nsGkAtoms::transitionsOfAfterProperty,
     824                 :                    "Unexpected element property; might restyle too much");
     825                 :       nsRestyleHint hint = et->mElementProperty == nsGkAtoms::transitionsProperty ?
     826               0 :         eRestyle_Self : eRestyle_Subtree;
     827               0 :       mPresContext->PresShell()->RestyleForAnimation(et->mElement, hint);
     828                 : 
     829               0 :       if (et->mPropertyTransitions.IsEmpty()) {
     830               0 :         et->Destroy();
     831                 :         // |et| is now a dangling pointer!
     832               0 :         et = nsnull;
     833                 :       }
     834                 :     }
     835                 :   }
     836                 : 
     837                 :   // We might have removed transitions above.
     838               0 :   ElementDataRemoved();
     839                 : 
     840               0 :   for (PRUint32 i = 0, i_end = events.Length(); i < i_end; ++i) {
     841               0 :     TransitionEventInfo &info = events[i];
     842               0 :     nsEventDispatcher::Dispatch(info.mElement, mPresContext, &info.mEvent);
     843                 : 
     844               0 :     if (!mPresContext) {
     845               0 :       break;
     846                 :     }
     847                 :   }
     848                 : }

Generated by: LCOV version 1.7