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: /* DOM object for element.style */ michael@0: michael@0: #include "nsDOMCSSAttrDeclaration.h" michael@0: michael@0: #include "mozilla/css/Declaration.h" michael@0: #include "mozilla/css/StyleRule.h" michael@0: #include "mozilla/dom/Element.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsIDOMMutationEvent.h" michael@0: #include "nsIURI.h" michael@0: #include "nsNodeUtils.h" michael@0: #include "nsWrapperCacheInlines.h" michael@0: #include "nsIFrame.h" michael@0: #include "ActiveLayerTracker.h" michael@0: michael@0: using namespace mozilla; michael@0: michael@0: nsDOMCSSAttributeDeclaration::nsDOMCSSAttributeDeclaration(dom::Element* aElement, michael@0: bool aIsSMILOverride) michael@0: : mElement(aElement) michael@0: , mIsSMILOverride(aIsSMILOverride) michael@0: { michael@0: MOZ_COUNT_CTOR(nsDOMCSSAttributeDeclaration); michael@0: michael@0: NS_ASSERTION(aElement, "Inline style for a NULL element?"); michael@0: } michael@0: michael@0: nsDOMCSSAttributeDeclaration::~nsDOMCSSAttributeDeclaration() michael@0: { michael@0: MOZ_COUNT_DTOR(nsDOMCSSAttributeDeclaration); michael@0: } michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(nsDOMCSSAttributeDeclaration, mElement) michael@0: michael@0: // mElement holds a strong ref to us, so if it's going to be michael@0: // skipped, the attribute declaration can't be part of a garbage michael@0: // cycle. michael@0: NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsDOMCSSAttributeDeclaration) michael@0: if (tmp->mElement && Element::CanSkip(tmp->mElement, true)) { michael@0: if (tmp->PreservingWrapper()) { michael@0: // This marks the wrapper black. michael@0: tmp->GetWrapper(); michael@0: } michael@0: return true; michael@0: } michael@0: return tmp->IsBlack(); michael@0: NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsDOMCSSAttributeDeclaration) michael@0: return tmp->IsBlack() || michael@0: (tmp->mElement && Element::CanSkipInCC(tmp->mElement)); michael@0: NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsDOMCSSAttributeDeclaration) michael@0: return tmp->IsBlack() || michael@0: (tmp->mElement && Element::CanSkipThis(tmp->mElement)); michael@0: NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMCSSAttributeDeclaration) michael@0: NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY michael@0: NS_IMPL_QUERY_TAIL_INHERITING(nsDOMCSSDeclaration) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMCSSAttributeDeclaration) michael@0: NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMCSSAttributeDeclaration) michael@0: michael@0: nsresult michael@0: nsDOMCSSAttributeDeclaration::SetCSSDeclaration(css::Declaration* aDecl) michael@0: { michael@0: NS_ASSERTION(mElement, "Must have Element to set the declaration!"); michael@0: css::StyleRule* oldRule = michael@0: mIsSMILOverride ? mElement->GetSMILOverrideStyleRule() : michael@0: mElement->GetInlineStyleRule(); michael@0: NS_ASSERTION(oldRule, "Element must have rule"); michael@0: michael@0: nsRefPtr newRule = michael@0: oldRule->DeclarationChanged(aDecl, false); michael@0: if (!newRule) { michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: return michael@0: mIsSMILOverride ? mElement->SetSMILOverrideStyleRule(newRule, true) : michael@0: mElement->SetInlineStyleRule(newRule, nullptr, true); michael@0: } michael@0: michael@0: nsIDocument* michael@0: nsDOMCSSAttributeDeclaration::DocToUpdate() michael@0: { michael@0: // XXXbz this is a bit of a hack, especially doing it before the michael@0: // BeginUpdate(), but this is a good chokepoint where we know we michael@0: // plan to modify the CSSDeclaration, so need to notify michael@0: // AttributeWillChange if this is inline style. michael@0: if (!mIsSMILOverride) { michael@0: nsNodeUtils::AttributeWillChange(mElement, kNameSpaceID_None, michael@0: nsGkAtoms::style, michael@0: nsIDOMMutationEvent::MODIFICATION); michael@0: } michael@0: michael@0: // We need OwnerDoc() rather than GetCurrentDoc() because it might michael@0: // be the BeginUpdate call that inserts mElement into the document. michael@0: return mElement->OwnerDoc(); michael@0: } michael@0: michael@0: css::Declaration* michael@0: nsDOMCSSAttributeDeclaration::GetCSSDeclaration(bool aAllocate) michael@0: { michael@0: if (!mElement) michael@0: return nullptr; michael@0: michael@0: css::StyleRule* cssRule; michael@0: if (mIsSMILOverride) michael@0: cssRule = mElement->GetSMILOverrideStyleRule(); michael@0: else michael@0: cssRule = mElement->GetInlineStyleRule(); michael@0: michael@0: if (cssRule) { michael@0: return cssRule->GetDeclaration(); michael@0: } michael@0: if (!aAllocate) { michael@0: return nullptr; michael@0: } michael@0: michael@0: // cannot fail michael@0: css::Declaration *decl = new css::Declaration(); michael@0: decl->InitializeEmpty(); michael@0: nsRefPtr newRule = new css::StyleRule(nullptr, decl); michael@0: michael@0: // this *can* fail (inside SetAttrAndNotify, at least). michael@0: nsresult rv; michael@0: if (mIsSMILOverride) michael@0: rv = mElement->SetSMILOverrideStyleRule(newRule, false); michael@0: else michael@0: rv = mElement->SetInlineStyleRule(newRule, nullptr, false); michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: return nullptr; // the decl will be destroyed along with the style rule michael@0: } michael@0: michael@0: return decl; michael@0: } michael@0: michael@0: void michael@0: nsDOMCSSAttributeDeclaration::GetCSSParsingEnvironment(CSSParsingEnvironment& aCSSParseEnv) michael@0: { michael@0: NS_ASSERTION(mElement, "Something is severely broken -- there should be an Element here!"); michael@0: michael@0: nsIDocument* doc = mElement->OwnerDoc(); michael@0: aCSSParseEnv.mSheetURI = doc->GetDocumentURI(); michael@0: aCSSParseEnv.mBaseURI = mElement->GetBaseURI(); michael@0: aCSSParseEnv.mPrincipal = mElement->NodePrincipal(); michael@0: aCSSParseEnv.mCSSLoader = doc->CSSLoader(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDOMCSSAttributeDeclaration::GetParentRule(nsIDOMCSSRule **aParent) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aParent); michael@0: michael@0: *aParent = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* virtual */ nsINode* michael@0: nsDOMCSSAttributeDeclaration::GetParentObject() michael@0: { michael@0: return mElement; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDOMCSSAttributeDeclaration::SetPropertyValue(const nsCSSProperty aPropID, michael@0: const nsAString& aValue) michael@0: { michael@0: // Scripted modifications to style.opacity or style.transform michael@0: // could immediately force us into the animated state if heuristics suggest michael@0: // this is scripted animation. michael@0: if (aPropID == eCSSProperty_opacity || aPropID == eCSSProperty_transform || michael@0: aPropID == eCSSProperty_left || aPropID == eCSSProperty_top || michael@0: aPropID == eCSSProperty_right || aPropID == eCSSProperty_bottom || michael@0: aPropID == eCSSProperty_margin_left || aPropID == eCSSProperty_margin_top || michael@0: aPropID == eCSSProperty_margin_right || aPropID == eCSSProperty_margin_bottom) { michael@0: nsIFrame* frame = mElement->GetPrimaryFrame(); michael@0: if (frame) { michael@0: ActiveLayerTracker::NotifyInlineStyleRuleModified(frame, aPropID); michael@0: } michael@0: } michael@0: return nsDOMCSSDeclaration::SetPropertyValue(aPropID, aValue); michael@0: }