summaryrefslogtreecommitdiffstats
path: root/dom/smil/nsSMILMappedAttribute.cpp
blob: b43469facb23f2e02f967f2bede105f044ffb9e1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/* representation of a SMIL-animatable mapped attribute on an element */
#include "nsSMILMappedAttribute.h"
#include "nsContentUtils.h"
#include "nsError.h" // For NS_PROPTABLE_PROP_OVERWRITTEN
#include "nsSMILValue.h"
#include "nsSMILCSSValueType.h"
#include "nsIDocument.h"
#include "nsIPresShell.h"
#include "nsCSSProps.h"
#include "mozilla/dom/Element.h"

// Callback function, for freeing string buffers stored in property table
static void
ReleaseStringBufferPropertyValue(void*    aObject,       /* unused */
                                 nsIAtom* aPropertyName, /* unused */
                                 void*    aPropertyValue,
                                 void*    aData          /* unused */)
{
  nsStringBuffer* buf = static_cast<nsStringBuffer*>(aPropertyValue);
  buf->Release();
}


nsresult
nsSMILMappedAttribute::ValueFromString(const nsAString& aStr,
                                       const mozilla::dom::SVGAnimationElement* aSrcElement,
                                       nsSMILValue& aValue,
                                       bool& aPreventCachingOfSandwich) const
{
  NS_ENSURE_TRUE(IsPropertyAnimatable(mPropID), NS_ERROR_FAILURE);

  nsSMILCSSValueType::ValueFromString(mPropID, mElement, aStr, aValue,
                                      &aPreventCachingOfSandwich);
  return aValue.IsNull() ? NS_ERROR_FAILURE : NS_OK;
}

nsSMILValue
nsSMILMappedAttribute::GetBaseValue() const
{
  nsAutoString baseStringValue;
  RefPtr<nsIAtom> attrName = GetAttrNameAtom();
  bool success = mElement->GetAttr(kNameSpaceID_None, attrName,
                                     baseStringValue);
  nsSMILValue baseValue;
  if (success) {
    // For base values, we don't need to worry whether the value returned is
    // context-sensitive or not since the compositor will take care of comparing
    // the returned (computed) base value and its cached value and determining
    // if an update is required or not.
    nsSMILCSSValueType::ValueFromString(mPropID, mElement,
                                        baseStringValue, baseValue, nullptr);
  } else {
    // Attribute is unset -- use computed value.
    // FIRST: Temporarily clear animated value, to make sure it doesn't pollute
    // the computed value. (We want base value, _without_ animations applied.)
    void* buf = mElement->UnsetProperty(SMIL_MAPPED_ATTR_ANIMVAL,
                                        attrName, nullptr);
    FlushChangesToTargetAttr();

    // SECOND: we use nsSMILCSSProperty::GetBaseValue to look up the property's
    // computed value.  NOTE: This call will temporarily clear the SMIL
    // override-style for the corresponding CSS property on our target element.
    // This prevents any animations that target the CSS property from affecting
    // animations that target the mapped attribute.
    baseValue = nsSMILCSSProperty::GetBaseValue();

    // FINALLY: If we originally had an animated value set, then set it again.
    if (buf) {
      mElement->SetProperty(SMIL_MAPPED_ATTR_ANIMVAL, attrName, buf,
                            ReleaseStringBufferPropertyValue);
      FlushChangesToTargetAttr();
    }
  }
  return baseValue;
}

nsresult
nsSMILMappedAttribute::SetAnimValue(const nsSMILValue& aValue)
{
  NS_ENSURE_TRUE(IsPropertyAnimatable(mPropID), NS_ERROR_FAILURE);

  // Convert nsSMILValue to string
  nsAutoString valStr;
  if (!nsSMILCSSValueType::ValueToString(aValue, valStr)) {
    NS_WARNING("Failed to convert nsSMILValue for mapped attr into a string");
    return NS_ERROR_FAILURE;
  }

  RefPtr<nsIAtom> attrName = GetAttrNameAtom();
  nsStringBuffer* oldValStrBuf = static_cast<nsStringBuffer*>
    (mElement->GetProperty(SMIL_MAPPED_ATTR_ANIMVAL, attrName));
  if (oldValStrBuf) {
    nsString oldValStr;
    nsContentUtils::PopulateStringFromStringBuffer(oldValStrBuf, oldValStr);
    if (valStr.Equals(oldValStr)) {
      // New animated value is the same as the old; nothing to do.
      return NS_OK;
    }
  }

  // Set the string as this mapped attribute's animated value.
  nsStringBuffer* valStrBuf =
    nsCSSValue::BufferFromString(nsString(valStr)).take();
  nsresult rv = mElement->SetProperty(SMIL_MAPPED_ATTR_ANIMVAL,
                                      attrName, valStrBuf,
                                      ReleaseStringBufferPropertyValue);
  if (rv == NS_PROPTABLE_PROP_OVERWRITTEN) {
    rv = NS_OK;
  }
  FlushChangesToTargetAttr();

  return rv;
}

void
nsSMILMappedAttribute::ClearAnimValue()
{
  RefPtr<nsIAtom> attrName = GetAttrNameAtom();
  mElement->DeleteProperty(SMIL_MAPPED_ATTR_ANIMVAL, attrName);
  FlushChangesToTargetAttr();
}

void
nsSMILMappedAttribute::FlushChangesToTargetAttr() const
{
  // Clear animated content-style-rule
  mElement->DeleteProperty(SMIL_MAPPED_ATTR_ANIMVAL,
                           SMIL_MAPPED_ATTR_STYLERULE_ATOM);
  nsIDocument* doc = mElement->GetUncomposedDoc();

  // Request animation restyle
  if (doc) {
    nsIPresShell* shell = doc->GetShell();
    if (shell) {
      shell->RestyleForAnimation(mElement, eRestyle_Self);
    }
  }
}

already_AddRefed<nsIAtom>
nsSMILMappedAttribute::GetAttrNameAtom() const
{
  return NS_Atomize(nsCSSProps::GetStringValue(mPropID));
}