diff options
Diffstat (limited to 'dom/base/nsStyledElement.cpp')
-rw-r--r-- | dom/base/nsStyledElement.cpp | 223 |
1 files changed, 223 insertions, 0 deletions
diff --git a/dom/base/nsStyledElement.cpp b/dom/base/nsStyledElement.cpp new file mode 100644 index 0000000000..01672eec84 --- /dev/null +++ b/dom/base/nsStyledElement.cpp @@ -0,0 +1,223 @@ +/* -*- 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/. */ + +#include "nsStyledElement.h" +#include "mozAutoDocUpdate.h" +#include "nsGkAtoms.h" +#include "nsAttrValue.h" +#include "nsAttrValueInlines.h" +#include "mozilla/dom/BindContext.h" +#include "mozilla/dom/CustomElementRegistry.h" +#include "mozilla/dom/ElementInlines.h" +#include "mozilla/dom/MutationEventBinding.h" +#include "mozilla/dom/MutationObservers.h" +#include "mozilla/InternalMutationEvent.h" +#include "mozilla/StaticPrefs_dom.h" +#include "nsDOMCSSDeclaration.h" +#include "nsDOMCSSAttrDeclaration.h" +#include "nsServiceManagerUtils.h" +#include "mozilla/dom/Document.h" +#include "mozilla/DeclarationBlock.h" +#include "mozilla/css/Loader.h" +#include "nsXULElement.h" +#include "nsContentUtils.h" +#include "nsStyleUtil.h" + +using namespace mozilla; +using namespace mozilla::dom; + +// Use the CC variant of this, even though this class does not define +// a new CC participant, to make QIing to the CC interfaces faster. +NS_IMPL_QUERY_INTERFACE_CYCLE_COLLECTION_INHERITED(nsStyledElement, + nsStyledElementBase, + nsStyledElement) + +//---------------------------------------------------------------------- +// nsIContent methods + +bool nsStyledElement::ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute, + const nsAString& aValue, + nsIPrincipal* aMaybeScriptedPrincipal, + nsAttrValue& aResult) { + if (aAttribute == nsGkAtoms::style && aNamespaceID == kNameSpaceID_None) { + ParseStyleAttribute(aValue, aMaybeScriptedPrincipal, aResult, false); + return true; + } + + return nsStyledElementBase::ParseAttribute(aNamespaceID, aAttribute, aValue, + aMaybeScriptedPrincipal, aResult); +} + +nsresult nsStyledElement::BeforeSetAttr(int32_t aNamespaceID, nsAtom* aName, + const nsAttrValueOrString* aValue, + bool aNotify) { + if (aNamespaceID == kNameSpaceID_None) { + if (aName == nsGkAtoms::style) { + if (aValue) { + SetMayHaveStyle(); + } + } + } + + return nsStyledElementBase::BeforeSetAttr(aNamespaceID, aName, aValue, + aNotify); +} + +void nsStyledElement::InlineStyleDeclarationWillChange( + MutationClosureData& aData) { + MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript()); + MOZ_ASSERT(OwnerDoc()->UpdateNestingLevel() > 0, + "Should be inside document update!"); + bool modification = false; + if (MayHaveStyle()) { + bool needsOldValue = !StaticPrefs::dom_mutation_events_cssom_disabled() && + nsContentUtils::HasMutationListeners( + this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED, this); + + if (!needsOldValue) { + CustomElementDefinition* definition = GetCustomElementDefinition(); + if (definition && + definition->IsInObservedAttributeList(nsGkAtoms::style)) { + needsOldValue = true; + } + } + + if (needsOldValue) { + nsAutoString oldValueStr; + modification = GetAttr(kNameSpaceID_None, nsGkAtoms::style, oldValueStr); + if (modification) { + aData.mOldValue.emplace(); + aData.mOldValue->SetTo(oldValueStr); + } + } else { + modification = HasAttr(kNameSpaceID_None, nsGkAtoms::style); + } + } + + aData.mModType = + modification ? static_cast<uint8_t>(MutationEvent_Binding::MODIFICATION) + : static_cast<uint8_t>(MutationEvent_Binding::ADDITION); + MutationObservers::NotifyAttributeWillChange( + this, kNameSpaceID_None, nsGkAtoms::style, aData.mModType); + + // XXXsmaug In order to make attribute handling more consistent, consider to + // call BeforeSetAttr and pass kCallAfterSetAttr to + // SetAttrAndNotify in SetInlineStyleDeclaration. + // Handling of mozAutoDocUpdate may require changes in that case. +} + +nsresult nsStyledElement::SetInlineStyleDeclaration( + DeclarationBlock& aDeclaration, MutationClosureData& aData) { + MOZ_ASSERT(OwnerDoc()->UpdateNestingLevel(), + "Should be inside document update!"); + + bool hasListeners = !StaticPrefs::dom_mutation_events_cssom_disabled() && + nsContentUtils::HasMutationListeners( + this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED, this); + + nsAttrValue attrValue(do_AddRef(&aDeclaration), nullptr); + SetMayHaveStyle(); + + Document* document = GetComposedDoc(); + mozAutoDocUpdate updateBatch(document, true); + return SetAttrAndNotify(kNameSpaceID_None, nsGkAtoms::style, nullptr, + aData.mOldValue.ptrOr(nullptr), attrValue, nullptr, + aData.mModType, hasListeners, true, + kDontCallAfterSetAttr, document, updateBatch); +} + +// --------------------------------------------------------------- +// Others and helpers + +nsICSSDeclaration* nsStyledElement::Style() { + Element::nsDOMSlots* slots = DOMSlots(); + + if (!slots->mStyle) { + // Just in case... + ReparseStyleAttribute(/* aForceInDataDoc */ true); + + slots->mStyle = new nsDOMCSSAttributeDeclaration(this, false); + SetMayHaveStyle(); + } + + return slots->mStyle; +} + +nsresult nsStyledElement::ReparseStyleAttribute(bool aForceInDataDoc) { + if (!MayHaveStyle()) { + return NS_OK; + } + const nsAttrValue* oldVal = mAttrs.GetAttr(nsGkAtoms::style); + if (oldVal && oldVal->Type() != nsAttrValue::eCSSDeclaration) { + nsAttrValue attrValue; + nsAutoString stringValue; + oldVal->ToString(stringValue); + ParseStyleAttribute(stringValue, nullptr, attrValue, aForceInDataDoc); + // Don't bother going through SetInlineStyleDeclaration; we don't + // want to fire off mutation events or document notifications anyway + bool oldValueSet; + nsresult rv = + mAttrs.SetAndSwapAttr(nsGkAtoms::style, attrValue, &oldValueSet); + NS_ENSURE_SUCCESS(rv, rv); + } + + return NS_OK; +} + +nsICSSDeclaration* nsStyledElement::GetExistingStyle() { + Element::nsDOMSlots* slots = GetExistingDOMSlots(); + if (!slots) { + return nullptr; + } + + return slots->mStyle; +} + +void nsStyledElement::ParseStyleAttribute(const nsAString& aValue, + nsIPrincipal* aMaybeScriptedPrincipal, + nsAttrValue& aResult, + bool aForceInDataDoc) { + Document* doc = OwnerDoc(); + bool isNativeAnon = IsInNativeAnonymousSubtree(); + + if (!isNativeAnon && + !nsStyleUtil::CSPAllowsInlineStyle(this, doc, aMaybeScriptedPrincipal, 0, + 0, aValue, nullptr)) + return; + + if (aForceInDataDoc || !doc->IsLoadedAsData() || GetExistingStyle() || + doc->IsStaticDocument()) { + bool isCSS = true; // assume CSS until proven otherwise + + if (!isNativeAnon) { // native anonymous content always assumes CSS + nsAutoString styleType; + doc->GetHeaderData(nsGkAtoms::headerContentStyleType, styleType); + if (!styleType.IsEmpty()) { + isCSS = StringBeginsWith(styleType, u"text/css"_ns, + nsASCIICaseInsensitiveStringComparator); + } + } + + if (isCSS && + aResult.ParseStyleAttribute(aValue, aMaybeScriptedPrincipal, this)) { + return; + } + } + + aResult.SetTo(aValue); +} + +nsresult nsStyledElement::BindToTree(BindContext& aContext, nsINode& aParent) { + nsresult rv = nsStyledElementBase::BindToTree(aContext, aParent); + NS_ENSURE_SUCCESS(rv, rv); + + if (HasAttr(nsGkAtoms::autofocus) && aContext.AllowsAutoFocus() && + (!IsSVGElement() || IsFocusable())) { + aContext.OwnerDoc().SetAutoFocusElement(this); + } + + return NS_OK; +} |