michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* representation of a SMIL-animatable CSS property on an element */ michael@0: michael@0: #include "nsSMILCSSProperty.h" michael@0: #include "nsSMILCSSValueType.h" michael@0: #include "nsSMILValue.h" michael@0: #include "nsComputedDOMStyle.h" michael@0: #include "nsCSSProps.h" michael@0: #include "nsStyleAnimation.h" michael@0: #include "mozilla/dom/Element.h" michael@0: #include "nsIDOMElement.h" michael@0: #include "nsIDocument.h" michael@0: michael@0: using namespace mozilla::dom; michael@0: michael@0: // Helper function michael@0: static bool michael@0: GetCSSComputedValue(Element* aElem, michael@0: nsCSSProperty aPropID, michael@0: nsAString& aResult) michael@0: { michael@0: NS_ABORT_IF_FALSE(!nsCSSProps::IsShorthand(aPropID), michael@0: "Can't look up computed value of shorthand property"); michael@0: NS_ABORT_IF_FALSE(nsSMILCSSProperty::IsPropertyAnimatable(aPropID), michael@0: "Shouldn't get here for non-animatable properties"); michael@0: michael@0: nsIDocument* doc = aElem->GetCurrentDoc(); michael@0: if (!doc) { michael@0: // This can happen if we process certain types of restyles mid-sample michael@0: // and remove anonymous animated content from the document as a result. michael@0: // See bug 534975. michael@0: return false; michael@0: } michael@0: michael@0: nsIPresShell* shell = doc->GetShell(); michael@0: if (!shell) { michael@0: NS_WARNING("Unable to look up computed style -- no pres shell"); michael@0: return false; michael@0: } michael@0: michael@0: nsRefPtr computedStyle = michael@0: NS_NewComputedDOMStyle(aElem, EmptyString(), shell); michael@0: michael@0: computedStyle->GetPropertyValue(aPropID, aResult); michael@0: return true; michael@0: } michael@0: michael@0: // Class Methods michael@0: nsSMILCSSProperty::nsSMILCSSProperty(nsCSSProperty aPropID, michael@0: Element* aElement) michael@0: : mPropID(aPropID), mElement(aElement) michael@0: { michael@0: NS_ABORT_IF_FALSE(IsPropertyAnimatable(mPropID), michael@0: "Creating a nsSMILCSSProperty for a property " michael@0: "that's not supported for animation"); michael@0: } michael@0: michael@0: nsSMILValue michael@0: nsSMILCSSProperty::GetBaseValue() const michael@0: { michael@0: // To benefit from Return Value Optimization and avoid copy constructor calls michael@0: // due to our use of return-by-value, we must return the exact same object michael@0: // from ALL return points. This function must only return THIS variable: michael@0: nsSMILValue baseValue; michael@0: michael@0: // SPECIAL CASE: (a) Shorthands michael@0: // (b) 'display' michael@0: if (nsCSSProps::IsShorthand(mPropID) || mPropID == eCSSProperty_display) { michael@0: // We can't look up the base (computed-style) value of shorthand michael@0: // properties because they aren't guaranteed to have a consistent computed michael@0: // value. michael@0: // michael@0: // Also, although we can look up the base value of the display property, michael@0: // doing so involves clearing and resetting the property which can cause michael@0: // frames to be recreated which we'd like to avoid. michael@0: // michael@0: // In either case, just return a dummy value (initialized with the right michael@0: // type, so as not to indicate failure). michael@0: nsSMILValue tmpVal(&nsSMILCSSValueType::sSingleton); michael@0: baseValue.Swap(tmpVal); michael@0: return baseValue; michael@0: } michael@0: michael@0: // GENERAL CASE: Non-Shorthands michael@0: // (1) Put empty string in override style for property mPropID michael@0: // (saving old override style value, so we can set it again when we're done) michael@0: nsICSSDeclaration* overrideDecl = mElement->GetSMILOverrideStyle(); michael@0: nsAutoString cachedOverrideStyleVal; michael@0: if (overrideDecl) { michael@0: overrideDecl->GetPropertyValue(mPropID, cachedOverrideStyleVal); michael@0: // (Don't bother clearing override style if it's already empty) michael@0: if (!cachedOverrideStyleVal.IsEmpty()) { michael@0: overrideDecl->SetPropertyValue(mPropID, EmptyString()); michael@0: } michael@0: } michael@0: michael@0: // (2) Get Computed Style michael@0: nsAutoString computedStyleVal; michael@0: bool didGetComputedVal = GetCSSComputedValue(mElement, mPropID, michael@0: computedStyleVal); michael@0: michael@0: // (3) Put cached override style back (if it's non-empty) michael@0: if (overrideDecl && !cachedOverrideStyleVal.IsEmpty()) { michael@0: overrideDecl->SetPropertyValue(mPropID, cachedOverrideStyleVal); michael@0: } michael@0: michael@0: // (4) Populate our nsSMILValue from the computed style michael@0: if (didGetComputedVal) { michael@0: // When we parse animation values we check if they are context-sensitive or michael@0: // not so that we don't cache animation values whose meaning may change. michael@0: // For base values however this is unnecessary since on each sample the michael@0: // compositor will fetch the (computed) base value and compare it against michael@0: // the cached (computed) value and detect changes for us. michael@0: nsSMILCSSValueType::ValueFromString(mPropID, mElement, michael@0: computedStyleVal, baseValue, michael@0: nullptr); michael@0: } michael@0: return baseValue; michael@0: } michael@0: michael@0: nsresult michael@0: nsSMILCSSProperty::ValueFromString(const nsAString& aStr, michael@0: const SVGAnimationElement* aSrcElement, michael@0: nsSMILValue& aValue, michael@0: bool& aPreventCachingOfSandwich) const michael@0: { michael@0: NS_ENSURE_TRUE(IsPropertyAnimatable(mPropID), NS_ERROR_FAILURE); michael@0: michael@0: nsSMILCSSValueType::ValueFromString(mPropID, mElement, aStr, aValue, michael@0: &aPreventCachingOfSandwich); michael@0: michael@0: if (aValue.IsNull()) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: // XXX Due to bug 536660 (or at least that seems to be the most likely michael@0: // culprit), when we have animation setting display:none on a element, michael@0: // if we DON'T set the property every sample, chaos ensues. michael@0: if (!aPreventCachingOfSandwich && mPropID == eCSSProperty_display) { michael@0: aPreventCachingOfSandwich = true; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsSMILCSSProperty::SetAnimValue(const nsSMILValue& aValue) michael@0: { michael@0: NS_ENSURE_TRUE(IsPropertyAnimatable(mPropID), NS_ERROR_FAILURE); michael@0: michael@0: // Convert nsSMILValue to string michael@0: nsAutoString valStr; michael@0: if (!nsSMILCSSValueType::ValueToString(aValue, valStr)) { michael@0: NS_WARNING("Failed to convert nsSMILValue for CSS property into a string"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: // Use string value to style the target element michael@0: nsICSSDeclaration* overrideDecl = mElement->GetSMILOverrideStyle(); michael@0: if (overrideDecl) { michael@0: nsAutoString oldValStr; michael@0: overrideDecl->GetPropertyValue(mPropID, oldValStr); michael@0: if (valStr.Equals(oldValStr)) { michael@0: return NS_OK; michael@0: } michael@0: overrideDecl->SetPropertyValue(mPropID, valStr); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsSMILCSSProperty::ClearAnimValue() michael@0: { michael@0: // Put empty string in override style for our property michael@0: nsICSSDeclaration* overrideDecl = mElement->GetSMILOverrideStyle(); michael@0: if (overrideDecl) { michael@0: overrideDecl->SetPropertyValue(mPropID, EmptyString()); michael@0: } michael@0: } michael@0: michael@0: // Based on http://www.w3.org/TR/SVG/propidx.html michael@0: // static michael@0: bool michael@0: nsSMILCSSProperty::IsPropertyAnimatable(nsCSSProperty aPropID) michael@0: { michael@0: // NOTE: Right now, Gecko doesn't recognize the following properties from michael@0: // the SVG Property Index: michael@0: // alignment-baseline michael@0: // baseline-shift michael@0: // color-profile michael@0: // color-rendering michael@0: // glyph-orientation-horizontal michael@0: // glyph-orientation-vertical michael@0: // kerning michael@0: // writing-mode michael@0: michael@0: switch (aPropID) { michael@0: case eCSSProperty_clip: michael@0: case eCSSProperty_clip_rule: michael@0: case eCSSProperty_clip_path: michael@0: case eCSSProperty_color: michael@0: case eCSSProperty_color_interpolation: michael@0: case eCSSProperty_color_interpolation_filters: michael@0: case eCSSProperty_cursor: michael@0: case eCSSProperty_display: michael@0: case eCSSProperty_dominant_baseline: michael@0: case eCSSProperty_fill: michael@0: case eCSSProperty_fill_opacity: michael@0: case eCSSProperty_fill_rule: michael@0: case eCSSProperty_filter: michael@0: case eCSSProperty_flood_color: michael@0: case eCSSProperty_flood_opacity: michael@0: case eCSSProperty_font: michael@0: case eCSSProperty_font_family: michael@0: case eCSSProperty_font_size: michael@0: case eCSSProperty_font_size_adjust: michael@0: case eCSSProperty_font_stretch: michael@0: case eCSSProperty_font_style: michael@0: case eCSSProperty_font_variant: michael@0: case eCSSProperty_font_weight: michael@0: case eCSSProperty_height: michael@0: case eCSSProperty_image_rendering: michael@0: case eCSSProperty_letter_spacing: michael@0: case eCSSProperty_lighting_color: michael@0: case eCSSProperty_marker: michael@0: case eCSSProperty_marker_end: michael@0: case eCSSProperty_marker_mid: michael@0: case eCSSProperty_marker_start: michael@0: case eCSSProperty_mask: michael@0: case eCSSProperty_mask_type: michael@0: case eCSSProperty_opacity: michael@0: case eCSSProperty_overflow: michael@0: case eCSSProperty_pointer_events: michael@0: case eCSSProperty_shape_rendering: michael@0: case eCSSProperty_stop_color: michael@0: case eCSSProperty_stop_opacity: michael@0: case eCSSProperty_stroke: michael@0: case eCSSProperty_stroke_dasharray: michael@0: case eCSSProperty_stroke_dashoffset: michael@0: case eCSSProperty_stroke_linecap: michael@0: case eCSSProperty_stroke_linejoin: michael@0: case eCSSProperty_stroke_miterlimit: michael@0: case eCSSProperty_stroke_opacity: michael@0: case eCSSProperty_stroke_width: michael@0: case eCSSProperty_text_anchor: michael@0: case eCSSProperty_text_decoration: michael@0: case eCSSProperty_text_decoration_line: michael@0: case eCSSProperty_text_rendering: michael@0: case eCSSProperty_vector_effect: michael@0: case eCSSProperty_width: michael@0: case eCSSProperty_visibility: michael@0: case eCSSProperty_word_spacing: michael@0: return true; michael@0: michael@0: // EXPLICITLY NON-ANIMATABLE PROPERTIES: michael@0: // (Some of these aren't supported at all in Gecko -- I've commented those michael@0: // ones out. If/when we add support for them, uncomment their line here) michael@0: // ---------------------------------------------------------------------- michael@0: // case eCSSProperty_enable_background: michael@0: // case eCSSProperty_glyph_orientation_horizontal: michael@0: // case eCSSProperty_glyph_orientation_vertical: michael@0: // case eCSSProperty_writing_mode: michael@0: case eCSSProperty_direction: michael@0: case eCSSProperty_unicode_bidi: michael@0: return false; michael@0: michael@0: default: michael@0: return false; michael@0: } michael@0: }