diff options
Diffstat (limited to 'layout/style/nsDOMCSSAttrDeclaration.cpp')
-rw-r--r-- | layout/style/nsDOMCSSAttrDeclaration.cpp | 216 |
1 files changed, 216 insertions, 0 deletions
diff --git a/layout/style/nsDOMCSSAttrDeclaration.cpp b/layout/style/nsDOMCSSAttrDeclaration.cpp new file mode 100644 index 0000000000..acb4296eee --- /dev/null +++ b/layout/style/nsDOMCSSAttrDeclaration.cpp @@ -0,0 +1,216 @@ +/* -*- 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/. */ + +/* DOM object for element.style */ + +#include "nsDOMCSSAttrDeclaration.h" + +#include "mozilla/dom/Document.h" +#include "mozilla/dom/Element.h" +#include "mozilla/dom/SVGElement.h" +#include "mozilla/dom/MutationEventBinding.h" +#include "mozilla/DeclarationBlock.h" +#include "mozilla/InternalMutationEvent.h" +#include "mozilla/SMILCSSValueType.h" +#include "mozilla/SMILValue.h" +#include "mozAutoDocUpdate.h" +#include "nsWrapperCacheInlines.h" +#include "nsIFrame.h" +#include "ActiveLayerTracker.h" + +using namespace mozilla; +using namespace mozilla::dom; + +nsDOMCSSAttributeDeclaration::nsDOMCSSAttributeDeclaration(Element* aElement, + bool aIsSMILOverride) + : mElement(aElement), mIsSMILOverride(aIsSMILOverride) { + NS_ASSERTION(aElement, "Inline style for a NULL element?"); +} + +nsDOMCSSAttributeDeclaration::~nsDOMCSSAttributeDeclaration() = default; + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsDOMCSSAttributeDeclaration, mElement) + +// mElement holds a strong ref to us, so if it's going to be +// skipped, the attribute declaration can't be part of a garbage +// cycle. +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsDOMCSSAttributeDeclaration) + if (tmp->mElement && Element::CanSkip(tmp->mElement, true)) { + if (tmp->PreservingWrapper()) { + tmp->MarkWrapperLive(); + } + return true; + } + return tmp->HasKnownLiveWrapper(); +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END + +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsDOMCSSAttributeDeclaration) + return tmp->HasKnownLiveWrapper() || + (tmp->mElement && Element::CanSkipInCC(tmp->mElement)); +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END + +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsDOMCSSAttributeDeclaration) + return tmp->HasKnownLiveWrapper() || + (tmp->mElement && Element::CanSkipThis(tmp->mElement)); +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMCSSAttributeDeclaration) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY +NS_INTERFACE_MAP_END_INHERITING(nsDOMCSSDeclaration) + +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMCSSAttributeDeclaration) +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMCSSAttributeDeclaration) + +nsresult nsDOMCSSAttributeDeclaration::SetCSSDeclaration( + DeclarationBlock* aDecl, MutationClosureData* aClosureData) { + NS_ASSERTION(mElement, "Must have Element to set the declaration!"); + + // Whenever changing element.style values, aClosureData must be non-null. + // SMIL doesn't update Element's attribute values, so closure data isn't + // needed. + MOZ_ASSERT_IF(!mIsSMILOverride, aClosureData); + + // The closure needs to have been called by now, otherwise we shouldn't be + // getting here when the attribute hasn't changed. + MOZ_ASSERT_IF(aClosureData, !aClosureData->mClosure); + + aDecl->SetDirty(); + if (mIsSMILOverride) { + mElement->SetSMILOverrideStyleDeclaration(*aDecl); + return NS_OK; + } + return mElement->SetInlineStyleDeclaration(*aDecl, *aClosureData); +} + +Document* nsDOMCSSAttributeDeclaration::DocToUpdate() { + // We need OwnerDoc() rather than GetUncomposedDoc() because it might + // be the BeginUpdate call that inserts mElement into the document. + return mElement->OwnerDoc(); +} + +DeclarationBlock* nsDOMCSSAttributeDeclaration::GetOrCreateCSSDeclaration( + Operation aOperation, DeclarationBlock** aCreated) { + MOZ_ASSERT(aOperation != eOperation_Modify || aCreated); + + if (!mElement) return nullptr; + + DeclarationBlock* declaration; + if (mIsSMILOverride) { + declaration = mElement->GetSMILOverrideStyleDeclaration(); + } else { + declaration = mElement->GetInlineStyleDeclaration(); + } + + if (declaration) { + return declaration; + } + + if (aOperation != eOperation_Modify) { + return nullptr; + } + + // cannot fail + RefPtr<DeclarationBlock> decl = new DeclarationBlock(); + // Mark the declaration dirty so that it can be reused by the caller. + // Normally SetDirty is called later in SetCSSDeclaration. + decl->SetDirty(); +#ifdef DEBUG + RefPtr<DeclarationBlock> mutableDecl = decl->EnsureMutable(); + MOZ_ASSERT(mutableDecl == decl); +#endif + decl.swap(*aCreated); + return *aCreated; +} + +nsDOMCSSDeclaration::ParsingEnvironment +nsDOMCSSAttributeDeclaration::GetParsingEnvironment( + nsIPrincipal* aSubjectPrincipal) const { + return { + mElement->GetURLDataForStyleAttr(aSubjectPrincipal), + mElement->OwnerDoc()->GetCompatibilityMode(), + mElement->OwnerDoc()->CSSLoader(), + }; +} + +template <typename SetterFunc> +nsresult nsDOMCSSAttributeDeclaration::SetSMILValueHelper(SetterFunc aFunc) { + MOZ_ASSERT(mIsSMILOverride); + + // No need to do the ActiveLayerTracker / ScrollLinkedEffectDetector bits, + // since we're in a SMIL animation anyway, no need to try to detect we're a + // scripted animation. + RefPtr<DeclarationBlock> created; + DeclarationBlock* olddecl = + GetOrCreateCSSDeclaration(eOperation_Modify, getter_AddRefs(created)); + if (!olddecl) { + return NS_ERROR_NOT_AVAILABLE; + } + mozAutoDocUpdate autoUpdate(DocToUpdate(), true); + RefPtr<DeclarationBlock> decl = olddecl->EnsureMutable(); + + bool changed = aFunc(*decl); + + if (changed) { + // We can pass nullptr as the latter param, since this is + // mIsSMILOverride == true case. + SetCSSDeclaration(decl, nullptr); + } + return NS_OK; +} + +nsresult nsDOMCSSAttributeDeclaration::SetSMILValue( + const nsCSSPropertyID /*aPropID*/, const SMILValue& aValue) { + MOZ_ASSERT(aValue.mType == &SMILCSSValueType::sSingleton, + "We should only try setting a CSS value type"); + return SetSMILValueHelper([&aValue](DeclarationBlock& aDecl) { + return SMILCSSValueType::SetPropertyValues(aValue, aDecl); + }); +} + +nsresult nsDOMCSSAttributeDeclaration::SetSMILValue( + const nsCSSPropertyID aPropID, const SVGAnimatedLength& aLength) { + return SetSMILValueHelper([aPropID, &aLength](DeclarationBlock& aDecl) { + return SVGElement::UpdateDeclarationBlockFromLength( + aDecl, aPropID, aLength, SVGElement::ValToUse::Anim); + }); +} + +void nsDOMCSSAttributeDeclaration::SetPropertyValue( + const nsCSSPropertyID aPropID, const nsACString& aValue, + nsIPrincipal* aSubjectPrincipal, ErrorResult& aRv) { + // Scripted modifications to style.opacity or style.transform (or other + // transform-like properties, e.g. style.translate, style.rotate, style.scale) + // could immediately force us into the animated state if heuristics suggest + // this is scripted animation. + // FIXME: This is missing the margin shorthand and the logical versions of + // the margin properties, see bug 1266287. + if (aPropID == eCSSProperty_opacity || aPropID == eCSSProperty_transform || + aPropID == eCSSProperty_translate || aPropID == eCSSProperty_rotate || + aPropID == eCSSProperty_scale || aPropID == eCSSProperty_offset_path || + aPropID == eCSSProperty_offset_distance || + aPropID == eCSSProperty_offset_rotate || + aPropID == eCSSProperty_offset_anchor || aPropID == eCSSProperty_left || + aPropID == eCSSProperty_top || aPropID == eCSSProperty_right || + aPropID == eCSSProperty_bottom || + aPropID == eCSSProperty_background_position_x || + aPropID == eCSSProperty_background_position_y || + aPropID == eCSSProperty_background_position) { + nsIFrame* frame = mElement->GetPrimaryFrame(); + if (frame) { + ActiveLayerTracker::NotifyInlineStyleRuleModified(frame, aPropID, aValue, + this); + } + } + nsDOMCSSDeclaration::SetPropertyValue(aPropID, aValue, aSubjectPrincipal, + aRv); +} + +void nsDOMCSSAttributeDeclaration::MutationClosureFunction(void* aData) { + MutationClosureData* data = static_cast<MutationClosureData*>(aData); + // Clear mClosure pointer so that it doesn't get called again. + data->mClosure = nullptr; + data->mElement->InlineStyleDeclarationWillChange(*data); +} |