summaryrefslogtreecommitdiffstats
path: root/dom/smil/nsSMILCSSProperty.cpp
blob: def89ef9f6f9b6120354dbd8b9c49272bcff8c4f (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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 CSS property on an element */

#include "nsSMILCSSProperty.h"

#include "mozilla/dom/Element.h"
#include "mozilla/Move.h"
#include "nsSMILCSSValueType.h"
#include "nsSMILValue.h"
#include "nsComputedDOMStyle.h"
#include "nsCSSProps.h"
#include "nsIDOMElement.h"
#include "nsIDocument.h"

using namespace mozilla;
using namespace mozilla::dom;

// Helper function
static bool
GetCSSComputedValue(Element* aElem,
                    nsCSSPropertyID aPropID,
                    nsAString& aResult)
{
  MOZ_ASSERT(!nsCSSProps::IsShorthand(aPropID),
             "Can't look up computed value of shorthand property");
  MOZ_ASSERT(nsSMILCSSProperty::IsPropertyAnimatable(aPropID),
             "Shouldn't get here for non-animatable properties");

  nsIDocument* doc = aElem->GetUncomposedDoc();
  if (!doc) {
    // This can happen if we process certain types of restyles mid-sample
    // and remove anonymous animated content from the document as a result.
    // See bug 534975.
    return false;
  }

  RefPtr<nsComputedDOMStyle> computedStyle =
    NS_NewComputedDOMStyle(aElem, EmptyString(), doc);

  computedStyle->GetPropertyValue(aPropID, aResult);
  return true;
}

// Class Methods
nsSMILCSSProperty::nsSMILCSSProperty(nsCSSPropertyID aPropID,
                                     Element* aElement)
  : mPropID(aPropID), mElement(aElement)
{
  MOZ_ASSERT(IsPropertyAnimatable(mPropID),
             "Creating a nsSMILCSSProperty for a property "
             "that's not supported for animation");
}

nsSMILValue
nsSMILCSSProperty::GetBaseValue() const
{
  // To benefit from Return Value Optimization and avoid copy constructor calls
  // due to our use of return-by-value, we must return the exact same object
  // from ALL return points. This function must only return THIS variable:
  nsSMILValue baseValue;

  // SPECIAL CASE: (a) Shorthands
  //               (b) 'display'
  if (nsCSSProps::IsShorthand(mPropID) || mPropID == eCSSProperty_display) {
    // We can't look up the base (computed-style) value of shorthand
    // properties because they aren't guaranteed to have a consistent computed
    // value.
    //
    // Also, although we can look up the base value of the display property,
    // doing so involves clearing and resetting the property which can cause
    // frames to be recreated which we'd like to avoid.
    //
    // In either case, just return a dummy value (initialized with the right
    // type, so as not to indicate failure).
    nsSMILValue tmpVal(&nsSMILCSSValueType::sSingleton);
    Swap(baseValue, tmpVal);
    return baseValue;
  }

  // GENERAL CASE: Non-Shorthands
  // (1) Put empty string in override style for property mPropID
  // (saving old override style value, so we can set it again when we're done)
  nsICSSDeclaration* overrideDecl = mElement->GetSMILOverrideStyle();
  nsAutoString cachedOverrideStyleVal;
  if (overrideDecl) {
    overrideDecl->GetPropertyValue(mPropID, cachedOverrideStyleVal);
    // (Don't bother clearing override style if it's already empty)
    if (!cachedOverrideStyleVal.IsEmpty()) {
      overrideDecl->SetPropertyValue(mPropID, EmptyString());
    }
  }

  // (2) Get Computed Style
  nsAutoString computedStyleVal;
  bool didGetComputedVal = GetCSSComputedValue(mElement, mPropID,
                                                 computedStyleVal);

  // (3) Put cached override style back (if it's non-empty)
  if (overrideDecl && !cachedOverrideStyleVal.IsEmpty()) {
    overrideDecl->SetPropertyValue(mPropID, cachedOverrideStyleVal);
  }

  // (4) Populate our nsSMILValue from the computed style
  if (didGetComputedVal) {
    // When we parse animation values we check if they are context-sensitive or
    // not so that we don't cache animation values whose meaning may change.
    // For base values however this is unnecessary since on each sample the
    // compositor will fetch the (computed) base value and compare it against
    // the cached (computed) value and detect changes for us.
    nsSMILCSSValueType::ValueFromString(mPropID, mElement,
                                        computedStyleVal, baseValue,
                                        nullptr);
  }
  return baseValue;
}

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

  nsSMILCSSValueType::ValueFromString(mPropID, mElement, aStr, aValue,
      &aPreventCachingOfSandwich);

  if (aValue.IsNull()) {
    return NS_ERROR_FAILURE;
  }

  // XXX Due to bug 536660 (or at least that seems to be the most likely
  // culprit), when we have animation setting display:none on a <use> element,
  // if we DON'T set the property every sample, chaos ensues.
  if (!aPreventCachingOfSandwich && mPropID == eCSSProperty_display) {
    aPreventCachingOfSandwich = true;
  }
  return NS_OK;
}

nsresult
nsSMILCSSProperty::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 CSS property into a string");
    return NS_ERROR_FAILURE;
  }

  // Use string value to style the target element
  nsICSSDeclaration* overrideDecl = mElement->GetSMILOverrideStyle();
  if (overrideDecl) {
    nsAutoString oldValStr;
    overrideDecl->GetPropertyValue(mPropID, oldValStr);
    if (valStr.Equals(oldValStr)) {
      return NS_OK;
    }
    overrideDecl->SetPropertyValue(mPropID, valStr);
  }
  return NS_OK;
}

void
nsSMILCSSProperty::ClearAnimValue()
{
  // Put empty string in override style for our property
  nsICSSDeclaration* overrideDecl = mElement->GetSMILOverrideStyle();
  if (overrideDecl) {
    overrideDecl->SetPropertyValue(mPropID, EmptyString());
  }
}

// Based on http://www.w3.org/TR/SVG/propidx.html
// static
bool
nsSMILCSSProperty::IsPropertyAnimatable(nsCSSPropertyID aPropID)
{
  // NOTE: Right now, Gecko doesn't recognize the following properties from
  // the SVG Property Index:
  //   alignment-baseline
  //   baseline-shift
  //   color-profile
  //   color-rendering
  //   glyph-orientation-horizontal
  //   glyph-orientation-vertical
  //   kerning
  //   writing-mode

  switch (aPropID) {
    case eCSSProperty_clip:
    case eCSSProperty_clip_rule:
    case eCSSProperty_clip_path:
    case eCSSProperty_color:
    case eCSSProperty_color_interpolation:
    case eCSSProperty_color_interpolation_filters:
    case eCSSProperty_cursor:
    case eCSSProperty_display:
    case eCSSProperty_dominant_baseline:
    case eCSSProperty_fill:
    case eCSSProperty_fill_opacity:
    case eCSSProperty_fill_rule:
    case eCSSProperty_filter:
    case eCSSProperty_flood_color:
    case eCSSProperty_flood_opacity:
    case eCSSProperty_font:
    case eCSSProperty_font_family:
    case eCSSProperty_font_size:
    case eCSSProperty_font_size_adjust:
    case eCSSProperty_font_stretch:
    case eCSSProperty_font_style:
    case eCSSProperty_font_variant:
    case eCSSProperty_font_weight:
    case eCSSProperty_height:
    case eCSSProperty_image_rendering:
    case eCSSProperty_letter_spacing:
    case eCSSProperty_lighting_color:
    case eCSSProperty_marker:
    case eCSSProperty_marker_end:
    case eCSSProperty_marker_mid:
    case eCSSProperty_marker_start:
    case eCSSProperty_mask:
    case eCSSProperty_mask_type:
    case eCSSProperty_opacity:
    case eCSSProperty_overflow:
    case eCSSProperty_pointer_events:
    case eCSSProperty_shape_rendering:
    case eCSSProperty_stop_color:
    case eCSSProperty_stop_opacity:
    case eCSSProperty_stroke:
    case eCSSProperty_stroke_dasharray:
    case eCSSProperty_stroke_dashoffset:
    case eCSSProperty_stroke_linecap:
    case eCSSProperty_stroke_linejoin:
    case eCSSProperty_stroke_miterlimit:
    case eCSSProperty_stroke_opacity:
    case eCSSProperty_stroke_width:
    case eCSSProperty_text_anchor:
    case eCSSProperty_text_decoration:
    case eCSSProperty_text_decoration_line:
    case eCSSProperty_text_rendering:
    case eCSSProperty_vector_effect:
    case eCSSProperty_width:
    case eCSSProperty_visibility:
    case eCSSProperty_word_spacing:
      return true;

    // EXPLICITLY NON-ANIMATABLE PROPERTIES:
    // (Some of these aren't supported at all in Gecko -- I've commented those
    // ones out. If/when we add support for them, uncomment their line here)
    // ----------------------------------------------------------------------
    // case eCSSProperty_enable_background:
    // case eCSSProperty_glyph_orientation_horizontal:
    // case eCSSProperty_glyph_orientation_vertical:
    // case eCSSProperty_writing_mode:
    case eCSSProperty_direction:
    case eCSSProperty_unicode_bidi:
      return false;

    default:
      return false;
  }
}