nsStyledElement.cpp (7711B)
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 #include "nsStyledElement.h" 8 9 #include "mozAutoDocUpdate.h" 10 #include "mozilla/DeclarationBlock.h" 11 #include "mozilla/RefPtr.h" 12 #include "mozilla/css/Loader.h" 13 #include "mozilla/dom/BindContext.h" 14 #include "mozilla/dom/CustomElementRegistry.h" 15 #include "mozilla/dom/Document.h" 16 #include "mozilla/dom/ElementInlines.h" 17 #include "mozilla/dom/MutationObservers.h" 18 #include "mozilla/dom/StylePropertyMap.h" 19 #include "nsAttrValue.h" 20 #include "nsAttrValueInlines.h" 21 #include "nsContentUtils.h" 22 #include "nsDOMCSSAttrDeclaration.h" 23 #include "nsDOMCSSDeclaration.h" 24 #include "nsGkAtoms.h" 25 #include "nsIMutationObserver.h" 26 #include "nsServiceManagerUtils.h" 27 #include "nsStyleUtil.h" 28 #include "nsXULElement.h" 29 30 using namespace mozilla; 31 using namespace mozilla::dom; 32 33 // Use the CC variant of this, even though this class does not define 34 // a new CC participant, to make QIing to the CC interfaces faster. 35 NS_IMPL_QUERY_INTERFACE_CYCLE_COLLECTION_INHERITED(nsStyledElement, 36 nsStyledElementBase, 37 nsStyledElement) 38 39 //---------------------------------------------------------------------- 40 // nsIContent methods 41 42 bool nsStyledElement::ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute, 43 const nsAString& aValue, 44 nsIPrincipal* aMaybeScriptedPrincipal, 45 nsAttrValue& aResult) { 46 if (aAttribute == nsGkAtoms::style && aNamespaceID == kNameSpaceID_None) { 47 ParseStyleAttribute(aValue, aMaybeScriptedPrincipal, aResult, false); 48 return true; 49 } 50 51 return nsStyledElementBase::ParseAttribute(aNamespaceID, aAttribute, aValue, 52 aMaybeScriptedPrincipal, aResult); 53 } 54 55 void nsStyledElement::BeforeSetAttr(int32_t aNamespaceID, nsAtom* aName, 56 const nsAttrValue* aValue, bool aNotify) { 57 if (aNamespaceID == kNameSpaceID_None && aName == nsGkAtoms::style && 58 aValue) { 59 SetMayHaveStyle(); 60 } 61 62 return nsStyledElementBase::BeforeSetAttr(aNamespaceID, aName, aValue, 63 aNotify); 64 } 65 66 void nsStyledElement::InlineStyleDeclarationWillChange( 67 MutationClosureData& aData) { 68 MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript()); 69 MOZ_ASSERT(OwnerDoc()->UpdateNestingLevel() > 0, 70 "Should be inside document update!"); 71 bool modification = false; 72 if (MayHaveStyle()) { 73 CustomElementDefinition* definition = GetCustomElementDefinition(); 74 if (definition && definition->IsInObservedAttributeList(nsGkAtoms::style)) { 75 nsAutoString oldValueStr; 76 modification = GetAttr(nsGkAtoms::style, oldValueStr); 77 if (modification) { 78 aData.mOldValue.emplace(); 79 aData.mOldValue->SetTo(oldValueStr); 80 } 81 } else { 82 modification = HasAttr(nsGkAtoms::style); 83 } 84 } 85 86 aData.mModType = 87 modification ? AttrModType::Modification : AttrModType::Addition; 88 MutationObservers::NotifyAttributeWillChange( 89 this, kNameSpaceID_None, nsGkAtoms::style, aData.mModType); 90 91 // XXXsmaug In order to make attribute handling more consistent, consider to 92 // call BeforeSetAttr and pass kCallAfterSetAttr to 93 // SetAttrAndNotify in SetInlineStyleDeclaration. 94 // Handling of mozAutoDocUpdate may require changes in that case. 95 } 96 97 nsresult nsStyledElement::SetInlineStyleDeclaration( 98 DeclarationBlock& aDeclaration, MutationClosureData& aData) { 99 MOZ_ASSERT(OwnerDoc()->UpdateNestingLevel(), 100 "Should be inside document update!"); 101 102 nsAttrValue attrValue(do_AddRef(&aDeclaration), nullptr); 103 SetMayHaveStyle(); 104 105 Document* document = GetComposedDoc(); 106 mozAutoDocUpdate updateBatch(document, true); 107 return SetAttrAndNotify(kNameSpaceID_None, nsGkAtoms::style, nullptr, 108 aData.mOldValue.ptrOr(nullptr), attrValue, nullptr, 109 aData.mModType, true, kDontCallAfterSetAttr, document, 110 updateBatch); 111 } 112 113 // --------------------------------------------------------------- 114 // Others and helpers 115 116 nsDOMCSSDeclaration* nsStyledElement::Style() { 117 Element::nsDOMSlots* slots = DOMSlots(); 118 119 if (!slots->mStyle) { 120 // Just in case... 121 ReparseStyleAttribute(/* aForceInDataDoc */ true); 122 123 slots->mStyle = new nsDOMCSSAttributeDeclaration(this, false); 124 SetMayHaveStyle(); 125 } 126 127 return slots->mStyle; 128 } 129 130 StylePropertyMap* nsStyledElement::AttributeStyleMap() { 131 nsDOMSlots* slots = DOMSlots(); 132 133 if (!slots->mAttributeStyleMap) { 134 slots->mAttributeStyleMap = 135 MakeRefPtr<StylePropertyMap>(this, /* aComputed */ false); 136 } 137 138 return slots->mAttributeStyleMap; 139 } 140 141 nsresult nsStyledElement::ReparseStyleAttribute(bool aForceInDataDoc) { 142 if (!MayHaveStyle()) { 143 return NS_OK; 144 } 145 const nsAttrValue* oldVal = mAttrs.GetAttr(nsGkAtoms::style); 146 if (oldVal && oldVal->Type() != nsAttrValue::eCSSDeclaration) { 147 nsAttrValue attrValue; 148 nsAutoString stringValue; 149 oldVal->ToString(stringValue); 150 if (!stringValue.IsEmpty()) { 151 // Prevent CSP errors from re-parsing a style that was already blocked. 152 ParseStyleAttribute(stringValue, nullptr, attrValue, aForceInDataDoc); 153 } 154 // Don't bother going through SetInlineStyleDeclaration; we don't 155 // want to fire off mutation events or document notifications anyway 156 bool oldValueSet; 157 nsresult rv = SetAndSwapAttr(nsGkAtoms::style, attrValue, &oldValueSet); 158 NS_ENSURE_SUCCESS(rv, rv); 159 } 160 161 return NS_OK; 162 } 163 164 nsDOMCSSDeclaration* nsStyledElement::GetExistingStyle() { 165 Element::nsDOMSlots* slots = GetExistingDOMSlots(); 166 if (!slots) { 167 return nullptr; 168 } 169 170 return slots->mStyle; 171 } 172 173 void nsStyledElement::ParseStyleAttribute(const nsAString& aValue, 174 nsIPrincipal* aMaybeScriptedPrincipal, 175 nsAttrValue& aResult, 176 bool aForceInDataDoc) { 177 Document* doc = OwnerDoc(); 178 bool isNativeAnon = IsInNativeAnonymousSubtree(); 179 180 if (!isNativeAnon && 181 !nsStyleUtil::CSPAllowsInlineStyle(this, doc, aMaybeScriptedPrincipal, 0, 182 1, aValue, nullptr)) { 183 return; 184 } 185 186 if (aForceInDataDoc || !doc->IsLoadedAsData() || GetExistingStyle() || 187 doc->IsStaticDocument()) { 188 bool isCSS = true; // assume CSS until proven otherwise 189 190 if (!isNativeAnon) { // native anonymous content always assumes CSS 191 nsAutoString styleType; 192 doc->GetHeaderData(nsGkAtoms::headerContentStyleType, styleType); 193 if (!styleType.IsEmpty()) { 194 isCSS = StringBeginsWith(styleType, u"text/css"_ns, 195 nsASCIICaseInsensitiveStringComparator); 196 } 197 } 198 199 if (isCSS && 200 aResult.ParseStyleAttribute(aValue, aMaybeScriptedPrincipal, this)) { 201 return; 202 } 203 } 204 205 aResult.SetTo(aValue); 206 } 207 208 nsresult nsStyledElement::BindToTree(BindContext& aContext, nsINode& aParent) { 209 nsresult rv = nsStyledElementBase::BindToTree(aContext, aParent); 210 NS_ENSURE_SUCCESS(rv, rv); 211 212 if (HasAttr(nsGkAtoms::autofocus) && aContext.AllowsAutoFocus() && 213 (!IsSVGElement() || IsFocusableWithoutStyle())) { 214 aContext.OwnerDoc().ElementWithAutoFocusInserted(this); 215 } 216 217 return NS_OK; 218 }