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 /* representation of a SMIL-animatable CSS property on an element */
8 
9 #include "nsSMILCSSProperty.h"
10 
11 #include "mozilla/dom/Element.h"
12 #include "mozilla/Move.h"
13 #include "nsSMILCSSValueType.h"
14 #include "nsSMILValue.h"
15 #include "nsComputedDOMStyle.h"
16 #include "nsCSSProps.h"
17 #include "nsIDOMElement.h"
18 #include "nsIDocument.h"
19 
20 using namespace mozilla::dom;
21 
22 // Helper function
23 static bool
GetCSSComputedValue(Element * aElem,nsCSSPropertyID aPropID,nsAString & aResult)24 GetCSSComputedValue(Element* aElem,
25                     nsCSSPropertyID aPropID,
26                     nsAString& aResult)
27 {
28   MOZ_ASSERT(!nsCSSProps::IsShorthand(aPropID),
29              "Can't look up computed value of shorthand property");
30   MOZ_ASSERT(nsSMILCSSProperty::IsPropertyAnimatable(aPropID),
31              "Shouldn't get here for non-animatable properties");
32 
33   nsIDocument* doc = aElem->GetUncomposedDoc();
34   if (!doc) {
35     // This can happen if we process certain types of restyles mid-sample
36     // and remove anonymous animated content from the document as a result.
37     // See bug 534975.
38     return false;
39   }
40 
41   nsIPresShell* shell = doc->GetShell();
42   if (!shell) {
43     NS_WARNING("Unable to look up computed style -- no pres shell");
44     return false;
45   }
46 
47   RefPtr<nsComputedDOMStyle> computedStyle =
48     NS_NewComputedDOMStyle(aElem, EmptyString(), shell);
49 
50   computedStyle->GetPropertyValue(aPropID, aResult);
51   return true;
52 }
53 
54 // Class Methods
nsSMILCSSProperty(nsCSSPropertyID aPropID,Element * aElement)55 nsSMILCSSProperty::nsSMILCSSProperty(nsCSSPropertyID aPropID,
56                                      Element* aElement)
57   : mPropID(aPropID), mElement(aElement)
58 {
59   MOZ_ASSERT(IsPropertyAnimatable(mPropID),
60              "Creating a nsSMILCSSProperty for a property "
61              "that's not supported for animation");
62 }
63 
64 nsSMILValue
GetBaseValue() const65 nsSMILCSSProperty::GetBaseValue() const
66 {
67   // To benefit from Return Value Optimization and avoid copy constructor calls
68   // due to our use of return-by-value, we must return the exact same object
69   // from ALL return points. This function must only return THIS variable:
70   nsSMILValue baseValue;
71 
72   // SPECIAL CASE: (a) Shorthands
73   //               (b) 'display'
74   if (nsCSSProps::IsShorthand(mPropID) || mPropID == eCSSProperty_display) {
75     // We can't look up the base (computed-style) value of shorthand
76     // properties because they aren't guaranteed to have a consistent computed
77     // value.
78     //
79     // Also, although we can look up the base value of the display property,
80     // doing so involves clearing and resetting the property which can cause
81     // frames to be recreated which we'd like to avoid.
82     //
83     // In either case, just return a dummy value (initialized with the right
84     // type, so as not to indicate failure).
85     nsSMILValue tmpVal(&nsSMILCSSValueType::sSingleton);
86     Swap(baseValue, tmpVal);
87     return baseValue;
88   }
89 
90   // GENERAL CASE: Non-Shorthands
91   // (1) Put empty string in override style for property mPropID
92   // (saving old override style value, so we can set it again when we're done)
93   nsICSSDeclaration* overrideDecl = mElement->GetSMILOverrideStyle();
94   nsAutoString cachedOverrideStyleVal;
95   if (overrideDecl) {
96     overrideDecl->GetPropertyValue(mPropID, cachedOverrideStyleVal);
97     // (Don't bother clearing override style if it's already empty)
98     if (!cachedOverrideStyleVal.IsEmpty()) {
99       overrideDecl->SetPropertyValue(mPropID, EmptyString());
100     }
101   }
102 
103   // (2) Get Computed Style
104   nsAutoString computedStyleVal;
105   bool didGetComputedVal = GetCSSComputedValue(mElement, mPropID,
106                                                  computedStyleVal);
107 
108   // (3) Put cached override style back (if it's non-empty)
109   if (overrideDecl && !cachedOverrideStyleVal.IsEmpty()) {
110     overrideDecl->SetPropertyValue(mPropID, cachedOverrideStyleVal);
111   }
112 
113   // (4) Populate our nsSMILValue from the computed style
114   if (didGetComputedVal) {
115     // When we parse animation values we check if they are context-sensitive or
116     // not so that we don't cache animation values whose meaning may change.
117     // For base values however this is unnecessary since on each sample the
118     // compositor will fetch the (computed) base value and compare it against
119     // the cached (computed) value and detect changes for us.
120     nsSMILCSSValueType::ValueFromString(mPropID, mElement,
121                                         computedStyleVal, baseValue,
122                                         nullptr);
123   }
124   return baseValue;
125 }
126 
127 nsresult
ValueFromString(const nsAString & aStr,const SVGAnimationElement * aSrcElement,nsSMILValue & aValue,bool & aPreventCachingOfSandwich) const128 nsSMILCSSProperty::ValueFromString(const nsAString& aStr,
129                                    const SVGAnimationElement* aSrcElement,
130                                    nsSMILValue& aValue,
131                                    bool& aPreventCachingOfSandwich) const
132 {
133   NS_ENSURE_TRUE(IsPropertyAnimatable(mPropID), NS_ERROR_FAILURE);
134 
135   nsSMILCSSValueType::ValueFromString(mPropID, mElement, aStr, aValue,
136       &aPreventCachingOfSandwich);
137 
138   if (aValue.IsNull()) {
139     return NS_ERROR_FAILURE;
140   }
141 
142   // XXX Due to bug 536660 (or at least that seems to be the most likely
143   // culprit), when we have animation setting display:none on a <use> element,
144   // if we DON'T set the property every sample, chaos ensues.
145   if (!aPreventCachingOfSandwich && mPropID == eCSSProperty_display) {
146     aPreventCachingOfSandwich = true;
147   }
148   return NS_OK;
149 }
150 
151 nsresult
SetAnimValue(const nsSMILValue & aValue)152 nsSMILCSSProperty::SetAnimValue(const nsSMILValue& aValue)
153 {
154   NS_ENSURE_TRUE(IsPropertyAnimatable(mPropID), NS_ERROR_FAILURE);
155 
156   // Convert nsSMILValue to string
157   nsAutoString valStr;
158   if (!nsSMILCSSValueType::ValueToString(aValue, valStr)) {
159     NS_WARNING("Failed to convert nsSMILValue for CSS property into a string");
160     return NS_ERROR_FAILURE;
161   }
162 
163   // Use string value to style the target element
164   nsICSSDeclaration* overrideDecl = mElement->GetSMILOverrideStyle();
165   if (overrideDecl) {
166     nsAutoString oldValStr;
167     overrideDecl->GetPropertyValue(mPropID, oldValStr);
168     if (valStr.Equals(oldValStr)) {
169       return NS_OK;
170     }
171     overrideDecl->SetPropertyValue(mPropID, valStr);
172   }
173   return NS_OK;
174 }
175 
176 void
ClearAnimValue()177 nsSMILCSSProperty::ClearAnimValue()
178 {
179   // Put empty string in override style for our property
180   nsICSSDeclaration* overrideDecl = mElement->GetSMILOverrideStyle();
181   if (overrideDecl) {
182     overrideDecl->SetPropertyValue(mPropID, EmptyString());
183   }
184 }
185 
186 // Based on http://www.w3.org/TR/SVG/propidx.html
187 // static
188 bool
IsPropertyAnimatable(nsCSSPropertyID aPropID)189 nsSMILCSSProperty::IsPropertyAnimatable(nsCSSPropertyID aPropID)
190 {
191   // NOTE: Right now, Gecko doesn't recognize the following properties from
192   // the SVG Property Index:
193   //   alignment-baseline
194   //   baseline-shift
195   //   color-profile
196   //   color-rendering
197   //   glyph-orientation-horizontal
198   //   glyph-orientation-vertical
199   //   kerning
200   //   writing-mode
201 
202   switch (aPropID) {
203     case eCSSProperty_clip:
204     case eCSSProperty_clip_rule:
205     case eCSSProperty_clip_path:
206     case eCSSProperty_color:
207     case eCSSProperty_color_interpolation:
208     case eCSSProperty_color_interpolation_filters:
209     case eCSSProperty_cursor:
210     case eCSSProperty_display:
211     case eCSSProperty_dominant_baseline:
212     case eCSSProperty_fill:
213     case eCSSProperty_fill_opacity:
214     case eCSSProperty_fill_rule:
215     case eCSSProperty_filter:
216     case eCSSProperty_flood_color:
217     case eCSSProperty_flood_opacity:
218     case eCSSProperty_font:
219     case eCSSProperty_font_family:
220     case eCSSProperty_font_size:
221     case eCSSProperty_font_size_adjust:
222     case eCSSProperty_font_stretch:
223     case eCSSProperty_font_style:
224     case eCSSProperty_font_variant:
225     case eCSSProperty_font_weight:
226     case eCSSProperty_height:
227     case eCSSProperty_image_rendering:
228     case eCSSProperty_letter_spacing:
229     case eCSSProperty_lighting_color:
230     case eCSSProperty_marker:
231     case eCSSProperty_marker_end:
232     case eCSSProperty_marker_mid:
233     case eCSSProperty_marker_start:
234     case eCSSProperty_mask:
235     case eCSSProperty_mask_type:
236     case eCSSProperty_opacity:
237     case eCSSProperty_overflow:
238     case eCSSProperty_pointer_events:
239     case eCSSProperty_shape_rendering:
240     case eCSSProperty_stop_color:
241     case eCSSProperty_stop_opacity:
242     case eCSSProperty_stroke:
243     case eCSSProperty_stroke_dasharray:
244     case eCSSProperty_stroke_dashoffset:
245     case eCSSProperty_stroke_linecap:
246     case eCSSProperty_stroke_linejoin:
247     case eCSSProperty_stroke_miterlimit:
248     case eCSSProperty_stroke_opacity:
249     case eCSSProperty_stroke_width:
250     case eCSSProperty_text_anchor:
251     case eCSSProperty_text_decoration:
252     case eCSSProperty_text_decoration_line:
253     case eCSSProperty_text_rendering:
254     case eCSSProperty_vector_effect:
255     case eCSSProperty_width:
256     case eCSSProperty_visibility:
257     case eCSSProperty_word_spacing:
258       return true;
259 
260     // EXPLICITLY NON-ANIMATABLE PROPERTIES:
261     // (Some of these aren't supported at all in Gecko -- I've commented those
262     // ones out. If/when we add support for them, uncomment their line here)
263     // ----------------------------------------------------------------------
264     // case eCSSProperty_enable_background:
265     // case eCSSProperty_glyph_orientation_horizontal:
266     // case eCSSProperty_glyph_orientation_vertical:
267     // case eCSSProperty_writing_mode:
268     case eCSSProperty_direction:
269     case eCSSProperty_unicode_bidi:
270       return false;
271 
272     default:
273       return false;
274   }
275 }
276