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 #include "nsGkAtoms.h"
9 #include "nsAttrValue.h"
10 #include "nsAttrValueInlines.h"
11 #include "mozilla/dom/ElementInlines.h"
12 #include "mozilla/InternalMutationEvent.h"
13 #include "nsDOMCSSDeclaration.h"
14 #include "nsDOMCSSAttrDeclaration.h"
15 #include "nsServiceManagerUtils.h"
16 #include "nsIDocument.h"
17 #include "mozilla/DeclarationBlockInlines.h"
18 #include "nsCSSParser.h"
19 #include "mozilla/css/Loader.h"
20 #include "nsIDOMMutationEvent.h"
21 #include "nsXULElement.h"
22 #include "nsContentUtils.h"
23 #include "nsStyleUtil.h"
24
25 using namespace mozilla;
26 using namespace mozilla::dom;
27
NS_IMPL_QUERY_INTERFACE_INHERITED(nsStyledElement,nsStyledElementBase,nsStyledElement)28 NS_IMPL_QUERY_INTERFACE_INHERITED(nsStyledElement,
29 nsStyledElementBase,
30 nsStyledElement)
31
32 //----------------------------------------------------------------------
33 // nsIContent methods
34
35 bool
36 nsStyledElement::ParseAttribute(int32_t aNamespaceID,
37 nsIAtom* aAttribute,
38 const nsAString& aValue,
39 nsAttrValue& aResult)
40 {
41 if (aAttribute == nsGkAtoms::style && aNamespaceID == kNameSpaceID_None) {
42 SetMayHaveStyle();
43 ParseStyleAttribute(aValue, aResult, false);
44 return true;
45 }
46
47 return nsStyledElementBase::ParseAttribute(aNamespaceID, aAttribute, aValue,
48 aResult);
49 }
50
51 nsresult
SetInlineStyleDeclaration(DeclarationBlock * aDeclaration,const nsAString * aSerialized,bool aNotify)52 nsStyledElement::SetInlineStyleDeclaration(DeclarationBlock* aDeclaration,
53 const nsAString* aSerialized,
54 bool aNotify)
55 {
56 SetMayHaveStyle();
57 bool modification = false;
58 nsAttrValue oldValue;
59
60 bool hasListeners = aNotify &&
61 nsContentUtils::HasMutationListeners(this,
62 NS_EVENT_BITS_MUTATION_ATTRMODIFIED,
63 this);
64
65 // There's no point in comparing the stylerule pointers since we're always
66 // getting a new stylerule here. And we can't compare the stringvalues of
67 // the old and the new rules since both will point to the same declaration
68 // and thus will be the same.
69 if (hasListeners) {
70 // save the old attribute so we can set up the mutation event properly
71 // XXXbz if the old rule points to the same declaration as the new one,
72 // this is getting the new attr value, not the old one....
73 nsAutoString oldValueStr;
74 modification = GetAttr(kNameSpaceID_None, nsGkAtoms::style,
75 oldValueStr);
76 if (modification) {
77 oldValue.SetTo(oldValueStr);
78 }
79 }
80 else if (aNotify && IsInUncomposedDoc()) {
81 modification = !!mAttrsAndChildren.GetAttr(nsGkAtoms::style);
82 }
83
84 nsAttrValue attrValue(do_AddRef(aDeclaration), aSerialized);
85
86 // XXXbz do we ever end up with ADDITION here? I doubt it.
87 uint8_t modType = modification ?
88 static_cast<uint8_t>(nsIDOMMutationEvent::MODIFICATION) :
89 static_cast<uint8_t>(nsIDOMMutationEvent::ADDITION);
90
91 return SetAttrAndNotify(kNameSpaceID_None, nsGkAtoms::style, nullptr,
92 oldValue, attrValue, modType, hasListeners,
93 aNotify, kDontCallAfterSetAttr);
94 }
95
96 DeclarationBlock*
GetInlineStyleDeclaration()97 nsStyledElement::GetInlineStyleDeclaration()
98 {
99 if (!MayHaveStyle()) {
100 return nullptr;
101 }
102 const nsAttrValue* attrVal = mAttrsAndChildren.GetAttr(nsGkAtoms::style);
103
104 if (attrVal && attrVal->Type() == nsAttrValue::eCSSDeclaration) {
105 return attrVal->GetCSSDeclarationValue();
106 }
107
108 return nullptr;
109 }
110
111 // ---------------------------------------------------------------
112 // Others and helpers
113
114 nsICSSDeclaration*
Style()115 nsStyledElement::Style()
116 {
117 Element::nsDOMSlots *slots = DOMSlots();
118
119 if (!slots->mStyle) {
120 // Just in case...
121 ReparseStyleAttribute(true);
122
123 slots->mStyle = new nsDOMCSSAttributeDeclaration(this, false);
124 SetMayHaveStyle();
125 }
126
127 return slots->mStyle;
128 }
129
130 nsresult
ReparseStyleAttribute(bool aForceInDataDoc)131 nsStyledElement::ReparseStyleAttribute(bool aForceInDataDoc)
132 {
133 if (!MayHaveStyle()) {
134 return NS_OK;
135 }
136 const nsAttrValue* oldVal = mAttrsAndChildren.GetAttr(nsGkAtoms::style);
137 if (oldVal && oldVal->Type() != nsAttrValue::eCSSDeclaration) {
138 nsAttrValue attrValue;
139 nsAutoString stringValue;
140 oldVal->ToString(stringValue);
141 ParseStyleAttribute(stringValue, attrValue, aForceInDataDoc);
142 // Don't bother going through SetInlineStyleDeclaration; we don't
143 // want to fire off mutation events or document notifications anyway
144 nsresult rv = mAttrsAndChildren.SetAndSwapAttr(nsGkAtoms::style, attrValue);
145 NS_ENSURE_SUCCESS(rv, rv);
146 }
147
148 return NS_OK;
149 }
150
151 nsICSSDeclaration*
GetExistingStyle()152 nsStyledElement::GetExistingStyle()
153 {
154 Element::nsDOMSlots* slots = GetExistingDOMSlots();
155 if (!slots) {
156 return nullptr;
157 }
158
159 return slots->mStyle;
160 }
161
162 void
ParseStyleAttribute(const nsAString & aValue,nsAttrValue & aResult,bool aForceInDataDoc)163 nsStyledElement::ParseStyleAttribute(const nsAString& aValue,
164 nsAttrValue& aResult,
165 bool aForceInDataDoc)
166 {
167 nsIDocument* doc = OwnerDoc();
168 bool isNativeAnon = IsInNativeAnonymousSubtree();
169
170 if (!isNativeAnon &&
171 !nsStyleUtil::CSPAllowsInlineStyle(nullptr, NodePrincipal(),
172 doc->GetDocumentURI(), 0, aValue,
173 nullptr))
174 return;
175
176 if (aForceInDataDoc ||
177 !doc->IsLoadedAsData() ||
178 GetExistingStyle() ||
179 doc->IsStaticDocument()) {
180 bool isCSS = true; // assume CSS until proven otherwise
181
182 if (!isNativeAnon) { // native anonymous content always assumes CSS
183 nsAutoString styleType;
184 doc->GetHeaderData(nsGkAtoms::headerContentStyleType, styleType);
185 if (!styleType.IsEmpty()) {
186 static const char textCssStr[] = "text/css";
187 isCSS = (styleType.EqualsIgnoreCase(textCssStr, sizeof(textCssStr) - 1));
188 }
189 }
190
191 if (isCSS && aResult.ParseStyleAttribute(aValue, this)) {
192 return;
193 }
194 }
195
196 aResult.SetTo(aValue);
197 }
198