tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

SMILCompositor.cpp (8830B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "SMILCompositor.h"
      8 
      9 #include "SMILCSSProperty.h"
     10 #include "mozilla/dom/SVGSVGElement.h"
     11 #include "nsCSSProps.h"
     12 #include "nsComputedDOMStyle.h"
     13 #include "nsHashKeys.h"
     14 
     15 namespace mozilla {
     16 
     17 // PLDHashEntryHdr methods
     18 bool SMILCompositor::KeyEquals(KeyTypePointer aKey) const {
     19  return aKey && aKey->Equals(mKey);
     20 }
     21 
     22 /*static*/
     23 PLDHashNumber SMILCompositor::HashKey(KeyTypePointer aKey) {
     24  // Combine the 3 values into one numeric value, which will be hashed.
     25  // NOTE: We right-shift one of the pointers by 2 to get some randomness in
     26  // its 2 lowest-order bits. (Those shifted-off bits will always be 0 since
     27  // our pointers will be word-aligned.)
     28  return (NS_PTR_TO_UINT32(aKey->mElement.get()) >> 2) +
     29         NS_PTR_TO_UINT32(aKey->mAttributeName.get());
     30 }
     31 
     32 // Cycle-collection support
     33 void SMILCompositor::Traverse(nsCycleCollectionTraversalCallback* aCallback) {
     34  if (!mKey.mElement) return;
     35 
     36  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback, "Compositor mKey.mElement");
     37  aCallback->NoteXPCOMChild(mKey.mElement);
     38 }
     39 
     40 // Other methods
     41 void SMILCompositor::AddAnimationFunction(SMILAnimationFunction* aFunc) {
     42  if (aFunc) {
     43 #ifdef DEBUG
     44    // Check that we don't already have an animation function that references
     45    // the animation element used by aFunc. This will be an assert when we
     46    // later sort the list. Let's catch the assert now. We use the same
     47    // assert message from the sort.
     48    for (const SMILAnimationFunction* func : mAnimationFunctions) {
     49      MOZ_ASSERT(
     50          !aFunc->HasSameAnimationElement(func),
     51          "Two animations cannot have the same animation content element!");
     52    }
     53 #endif
     54    mAnimationFunctions.AppendElement(aFunc);
     55  }
     56 }
     57 
     58 void SMILCompositor::ComposeAttribute(bool& aMightHavePendingStyleUpdates) {
     59  if (!mKey.mElement) return;
     60 
     61  // If we might need to resolve base styles, grab a suitable ComputedStyle
     62  // for initializing our SMILAttr with.
     63  RefPtr<const ComputedStyle> baseComputedStyle;
     64  if (MightNeedBaseStyle()) {
     65    baseComputedStyle = nsComputedDOMStyle::GetUnanimatedComputedStyleNoFlush(
     66        mKey.mElement, {});
     67  }
     68 
     69  // FIRST: Get the SMILAttr (to grab base value from, and to eventually
     70  // give animated value to)
     71  UniquePtr<SMILAttr> smilAttr = CreateSMILAttr(baseComputedStyle);
     72  if (!smilAttr) {
     73    // Target attribute not found (or, out of memory)
     74    return;
     75  }
     76  if (mAnimationFunctions.IsEmpty()) {
     77    // No active animation functions. (We can still have a SMILCompositor in
     78    // that case if an animation function has *just* become inactive)
     79    smilAttr->ClearAnimValue();
     80    // Removing the animation effect may require a style update.
     81    aMightHavePendingStyleUpdates = true;
     82    return;
     83  }
     84 
     85  // SECOND: Sort the animationFunctions, to prepare for compositing.
     86  SMILAnimationFunction::Comparator comparator;
     87  mAnimationFunctions.Sort(comparator);
     88 
     89  // THIRD: Step backwards through animation functions to find out
     90  // which ones we actually care about.
     91  uint32_t firstFuncToCompose = GetFirstFuncToAffectSandwich();
     92 
     93  // FOURTH: Get & cache base value
     94  SMILValue sandwichResultValue;
     95  if (!mAnimationFunctions[firstFuncToCompose]->WillReplace()) {
     96    sandwichResultValue = smilAttr->GetBaseValue();
     97  }
     98  UpdateCachedBaseValue(sandwichResultValue);
     99 
    100  if (!mForceCompositing) {
    101    return;
    102  }
    103 
    104  // FIFTH: Compose animation functions
    105  aMightHavePendingStyleUpdates = true;
    106  uint32_t length = mAnimationFunctions.Length();
    107  for (uint32_t i = firstFuncToCompose; i < length; ++i) {
    108    mAnimationFunctions[i]->ComposeResult(*smilAttr, sandwichResultValue);
    109  }
    110  if (sandwichResultValue.IsNull()) {
    111    smilAttr->ClearAnimValue();
    112    return;
    113  }
    114 
    115  // SIXTH: Set the animated value to the final composited result.
    116  nsresult rv = smilAttr->SetAnimValue(sandwichResultValue);
    117  if (NS_FAILED(rv)) {
    118    NS_WARNING("SMILAttr::SetAnimValue failed");
    119  }
    120 }
    121 
    122 void SMILCompositor::ClearAnimationEffects() {
    123  if (!mKey.mElement || !mKey.mAttributeName) return;
    124 
    125  UniquePtr<SMILAttr> smilAttr = CreateSMILAttr(nullptr);
    126  if (!smilAttr) {
    127    // Target attribute not found (or, out of memory)
    128    return;
    129  }
    130  smilAttr->ClearAnimValue();
    131 }
    132 
    133 // Protected Helper Functions
    134 // --------------------------
    135 UniquePtr<SMILAttr> SMILCompositor::CreateSMILAttr(
    136    const ComputedStyle* aBaseComputedStyle) {
    137  NonCustomCSSPropertyId propId = GetCSSPropertyToAnimate();
    138 
    139  if (propId != eCSSProperty_UNKNOWN) {
    140    return MakeUnique<SMILCSSProperty>(propId, mKey.mElement.get(),
    141                                       aBaseComputedStyle);
    142  }
    143 
    144  return mKey.mElement->GetAnimatedAttr(mKey.mAttributeNamespaceID,
    145                                        mKey.mAttributeName);
    146 }
    147 
    148 NonCustomCSSPropertyId SMILCompositor::GetCSSPropertyToAnimate() const {
    149  if (mKey.mAttributeNamespaceID != kNameSpaceID_None) {
    150    return eCSSProperty_UNKNOWN;
    151  }
    152 
    153  NonCustomCSSPropertyId propId =
    154      nsCSSProps::LookupProperty(nsAtomCString(mKey.mAttributeName));
    155 
    156  if (!SMILCSSProperty::IsPropertyAnimatable(propId)) {
    157    return eCSSProperty_UNKNOWN;
    158  }
    159 
    160  // If we are animating the 'width' or 'height' of an outer SVG
    161  // element we should animate it as a CSS property, but for other elements
    162  // in SVG namespace (e.g. <rect>) we should animate it as a length attribute.
    163  if ((mKey.mAttributeName == nsGkAtoms::width ||
    164       mKey.mAttributeName == nsGkAtoms::height) &&
    165      mKey.mElement->GetNameSpaceID() == kNameSpaceID_SVG) {
    166    // Not an <svg> element.
    167    if (!mKey.mElement->IsSVGElement(nsGkAtoms::svg)) {
    168      return eCSSProperty_UNKNOWN;
    169    }
    170 
    171    // An inner <svg> element
    172    if (static_cast<dom::SVGSVGElement const&>(*mKey.mElement).IsInner()) {
    173      return eCSSProperty_UNKNOWN;
    174    }
    175 
    176    // Indeed an outer <svg> element, fall through.
    177  }
    178 
    179  return propId;
    180 }
    181 
    182 bool SMILCompositor::MightNeedBaseStyle() const {
    183  if (GetCSSPropertyToAnimate() == eCSSProperty_UNKNOWN) {
    184    return false;
    185  }
    186 
    187  // We should return true if at least one animation function might build on
    188  // the base value.
    189  for (const SMILAnimationFunction* func : mAnimationFunctions) {
    190    if (!func->WillReplace()) {
    191      return true;
    192    }
    193  }
    194 
    195  return false;
    196 }
    197 
    198 uint32_t SMILCompositor::GetFirstFuncToAffectSandwich() {
    199  // For performance reasons, we throttle most animations on elements in
    200  // display:none subtrees. (We can't throttle animations that target the
    201  // "display" property itself, though -- if we did, display:none elements
    202  // could never be dynamically displayed via animations.)
    203  // To determine whether we're in a display:none subtree, we will check the
    204  // element's primary frame since element in display:none subtree doesn't have
    205  // a primary frame. Before this process, we will construct frame when we
    206  // append an element to subtree. So we will not need to worry about pending
    207  // frame construction in this step.
    208  bool canThrottle = mKey.mAttributeName != nsGkAtoms::display &&
    209                     !mKey.mElement->GetPrimaryFrame();
    210 
    211  uint32_t i;
    212  for (i = mAnimationFunctions.Length(); i > 0; --i) {
    213    SMILAnimationFunction* curAnimFunc = mAnimationFunctions[i - 1];
    214    // In the following, the lack of short-circuit behavior of |= means that we
    215    // will ALWAYS run UpdateCachedTarget (even if mForceCompositing is true)
    216    // but only call HasChanged and WasSkippedInPrevSample if necessary.  This
    217    // is important since we need UpdateCachedTarget to run in order to detect
    218    // changes to the target in subsequent samples.
    219    mForceCompositing |= curAnimFunc->UpdateCachedTarget(mKey) ||
    220                         (curAnimFunc->HasChanged() && !canThrottle) ||
    221                         curAnimFunc->WasSkippedInPrevSample();
    222 
    223    if (curAnimFunc->WillReplace()) {
    224      --i;
    225      break;
    226    }
    227  }
    228 
    229  // Mark remaining animation functions as having been skipped so if we later
    230  // use them we'll know to force compositing.
    231  // Note that we only really need to do this if something has changed
    232  // (otherwise we would have set the flag on a previous sample) and if
    233  // something has changed mForceCompositing will be true.
    234  if (mForceCompositing) {
    235    for (uint32_t j = i; j > 0; --j) {
    236      mAnimationFunctions[j - 1]->SetWasSkipped();
    237    }
    238  }
    239  return i;
    240 }
    241 
    242 void SMILCompositor::UpdateCachedBaseValue(const SMILValue& aBaseValue) {
    243  if (mCachedBaseValue != aBaseValue) {
    244    // Base value has changed since last sample.
    245    mCachedBaseValue = aBaseValue;
    246    mForceCompositing = true;
    247  }
    248 }
    249 
    250 }  // namespace mozilla