|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* vim:set tw=80 expandtab softtabstop=2 ts=2 sw=2: */ |
|
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/css/StyleRule.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 |
|
28 //---------------------------------------------------------------------- |
|
29 // nsIContent methods |
|
30 |
|
31 nsIAtom* |
|
32 nsStyledElementNotElementCSSInlineStyle::GetClassAttributeName() const |
|
33 { |
|
34 return nsGkAtoms::_class; |
|
35 } |
|
36 |
|
37 nsIAtom* |
|
38 nsStyledElementNotElementCSSInlineStyle::GetIDAttributeName() const |
|
39 { |
|
40 return nsGkAtoms::id; |
|
41 } |
|
42 |
|
43 nsIAtom* |
|
44 nsStyledElementNotElementCSSInlineStyle::DoGetID() const |
|
45 { |
|
46 NS_ASSERTION(HasID(), "Unexpected call"); |
|
47 |
|
48 // The nullcheck here is needed because Element::UnsetAttr calls |
|
49 // out to various code between removing the attribute and we get a chance to |
|
50 // ClearHasID(). |
|
51 |
|
52 const nsAttrValue* attr = mAttrsAndChildren.GetAttr(nsGkAtoms::id); |
|
53 |
|
54 return attr ? attr->GetAtomValue() : nullptr; |
|
55 } |
|
56 |
|
57 const nsAttrValue* |
|
58 nsStyledElementNotElementCSSInlineStyle::DoGetClasses() const |
|
59 { |
|
60 NS_ASSERTION(HasFlag(NODE_MAY_HAVE_CLASS), "Unexpected call"); |
|
61 return mAttrsAndChildren.GetAttr(nsGkAtoms::_class); |
|
62 } |
|
63 |
|
64 bool |
|
65 nsStyledElementNotElementCSSInlineStyle::ParseAttribute(int32_t aNamespaceID, |
|
66 nsIAtom* aAttribute, |
|
67 const nsAString& aValue, |
|
68 nsAttrValue& aResult) |
|
69 { |
|
70 if (aNamespaceID == kNameSpaceID_None) { |
|
71 if (aAttribute == nsGkAtoms::style) { |
|
72 SetMayHaveStyle(); |
|
73 ParseStyleAttribute(aValue, aResult, false); |
|
74 return true; |
|
75 } |
|
76 if (aAttribute == nsGkAtoms::_class) { |
|
77 SetFlags(NODE_MAY_HAVE_CLASS); |
|
78 aResult.ParseAtomArray(aValue); |
|
79 return true; |
|
80 } |
|
81 if (aAttribute == nsGkAtoms::id) { |
|
82 // Store id as an atom. id="" means that the element has no id, |
|
83 // not that it has an emptystring as the id. |
|
84 RemoveFromIdTable(); |
|
85 if (aValue.IsEmpty()) { |
|
86 ClearHasID(); |
|
87 return false; |
|
88 } |
|
89 aResult.ParseAtom(aValue); |
|
90 SetHasID(); |
|
91 AddToIdTable(aResult.GetAtomValue()); |
|
92 return true; |
|
93 } |
|
94 } |
|
95 |
|
96 return nsStyledElementBase::ParseAttribute(aNamespaceID, aAttribute, aValue, |
|
97 aResult); |
|
98 } |
|
99 |
|
100 nsresult |
|
101 nsStyledElementNotElementCSSInlineStyle::UnsetAttr(int32_t aNameSpaceID, |
|
102 nsIAtom* aAttribute, |
|
103 bool aNotify) |
|
104 { |
|
105 nsAutoScriptBlocker scriptBlocker; |
|
106 if (aAttribute == nsGkAtoms::id && aNameSpaceID == kNameSpaceID_None) { |
|
107 // Have to do this before clearing flag. See RemoveFromIdTable |
|
108 RemoveFromIdTable(); |
|
109 } |
|
110 |
|
111 return Element::UnsetAttr(aNameSpaceID, aAttribute, aNotify); |
|
112 } |
|
113 |
|
114 nsresult |
|
115 nsStyledElementNotElementCSSInlineStyle::AfterSetAttr(int32_t aNamespaceID, |
|
116 nsIAtom* aAttribute, |
|
117 const nsAttrValue* aValue, |
|
118 bool aNotify) |
|
119 { |
|
120 if (aNamespaceID == kNameSpaceID_None && !aValue && |
|
121 aAttribute == nsGkAtoms::id) { |
|
122 // The id has been removed when calling UnsetAttr but we kept it because |
|
123 // the id is used for some layout stuff between UnsetAttr and AfterSetAttr. |
|
124 // Now. the id is really removed so it would not be safe to keep this flag. |
|
125 ClearHasID(); |
|
126 } |
|
127 |
|
128 return Element::AfterSetAttr(aNamespaceID, aAttribute, aValue, aNotify); |
|
129 } |
|
130 |
|
131 nsresult |
|
132 nsStyledElementNotElementCSSInlineStyle::SetInlineStyleRule(css::StyleRule* aStyleRule, |
|
133 const nsAString* aSerialized, |
|
134 bool aNotify) |
|
135 { |
|
136 SetMayHaveStyle(); |
|
137 bool modification = false; |
|
138 nsAttrValue oldValue; |
|
139 |
|
140 bool hasListeners = aNotify && |
|
141 nsContentUtils::HasMutationListeners(this, |
|
142 NS_EVENT_BITS_MUTATION_ATTRMODIFIED, |
|
143 this); |
|
144 |
|
145 // There's no point in comparing the stylerule pointers since we're always |
|
146 // getting a new stylerule here. And we can't compare the stringvalues of |
|
147 // the old and the new rules since both will point to the same declaration |
|
148 // and thus will be the same. |
|
149 if (hasListeners) { |
|
150 // save the old attribute so we can set up the mutation event properly |
|
151 // XXXbz if the old rule points to the same declaration as the new one, |
|
152 // this is getting the new attr value, not the old one.... |
|
153 nsAutoString oldValueStr; |
|
154 modification = GetAttr(kNameSpaceID_None, nsGkAtoms::style, |
|
155 oldValueStr); |
|
156 if (modification) { |
|
157 oldValue.SetTo(oldValueStr); |
|
158 } |
|
159 } |
|
160 else if (aNotify && IsInDoc()) { |
|
161 modification = !!mAttrsAndChildren.GetAttr(nsGkAtoms::style); |
|
162 } |
|
163 |
|
164 nsAttrValue attrValue(aStyleRule, aSerialized); |
|
165 |
|
166 // XXXbz do we ever end up with ADDITION here? I doubt it. |
|
167 uint8_t modType = modification ? |
|
168 static_cast<uint8_t>(nsIDOMMutationEvent::MODIFICATION) : |
|
169 static_cast<uint8_t>(nsIDOMMutationEvent::ADDITION); |
|
170 |
|
171 return SetAttrAndNotify(kNameSpaceID_None, nsGkAtoms::style, nullptr, |
|
172 oldValue, attrValue, modType, hasListeners, |
|
173 aNotify, kDontCallAfterSetAttr); |
|
174 } |
|
175 |
|
176 css::StyleRule* |
|
177 nsStyledElementNotElementCSSInlineStyle::GetInlineStyleRule() |
|
178 { |
|
179 if (!MayHaveStyle()) { |
|
180 return nullptr; |
|
181 } |
|
182 const nsAttrValue* attrVal = mAttrsAndChildren.GetAttr(nsGkAtoms::style); |
|
183 |
|
184 if (attrVal && attrVal->Type() == nsAttrValue::eCSSStyleRule) { |
|
185 return attrVal->GetCSSStyleRuleValue(); |
|
186 } |
|
187 |
|
188 return nullptr; |
|
189 } |
|
190 |
|
191 // --------------------------------------------------------------- |
|
192 // Others and helpers |
|
193 |
|
194 nsICSSDeclaration* |
|
195 nsStyledElementNotElementCSSInlineStyle::Style() |
|
196 { |
|
197 Element::nsDOMSlots *slots = DOMSlots(); |
|
198 |
|
199 if (!slots->mStyle) { |
|
200 // Just in case... |
|
201 ReparseStyleAttribute(true); |
|
202 |
|
203 slots->mStyle = new nsDOMCSSAttributeDeclaration(this, false); |
|
204 SetMayHaveStyle(); |
|
205 } |
|
206 |
|
207 return slots->mStyle; |
|
208 } |
|
209 |
|
210 nsresult |
|
211 nsStyledElementNotElementCSSInlineStyle::ReparseStyleAttribute(bool aForceInDataDoc) |
|
212 { |
|
213 if (!MayHaveStyle()) { |
|
214 return NS_OK; |
|
215 } |
|
216 const nsAttrValue* oldVal = mAttrsAndChildren.GetAttr(nsGkAtoms::style); |
|
217 |
|
218 if (oldVal && oldVal->Type() != nsAttrValue::eCSSStyleRule) { |
|
219 nsAttrValue attrValue; |
|
220 nsAutoString stringValue; |
|
221 oldVal->ToString(stringValue); |
|
222 ParseStyleAttribute(stringValue, attrValue, aForceInDataDoc); |
|
223 // Don't bother going through SetInlineStyleRule, we don't want to fire off |
|
224 // mutation events or document notifications anyway |
|
225 nsresult rv = mAttrsAndChildren.SetAndTakeAttr(nsGkAtoms::style, attrValue); |
|
226 NS_ENSURE_SUCCESS(rv, rv); |
|
227 } |
|
228 |
|
229 return NS_OK; |
|
230 } |
|
231 |
|
232 void |
|
233 nsStyledElementNotElementCSSInlineStyle::ParseStyleAttribute(const nsAString& aValue, |
|
234 nsAttrValue& aResult, |
|
235 bool aForceInDataDoc) |
|
236 { |
|
237 nsIDocument* doc = OwnerDoc(); |
|
238 |
|
239 if (!nsStyleUtil::CSPAllowsInlineStyle(nullptr, NodePrincipal(), |
|
240 doc->GetDocumentURI(), 0, aValue, |
|
241 nullptr)) |
|
242 return; |
|
243 |
|
244 if (aForceInDataDoc || |
|
245 !doc->IsLoadedAsData() || |
|
246 doc->IsStaticDocument()) { |
|
247 bool isCSS = true; // assume CSS until proven otherwise |
|
248 |
|
249 if (!IsInNativeAnonymousSubtree()) { // native anonymous content |
|
250 // always assumes CSS |
|
251 nsAutoString styleType; |
|
252 doc->GetHeaderData(nsGkAtoms::headerContentStyleType, styleType); |
|
253 if (!styleType.IsEmpty()) { |
|
254 static const char textCssStr[] = "text/css"; |
|
255 isCSS = (styleType.EqualsIgnoreCase(textCssStr, sizeof(textCssStr) - 1)); |
|
256 } |
|
257 } |
|
258 |
|
259 if (isCSS && aResult.ParseStyleAttribute(aValue, this)) { |
|
260 return; |
|
261 } |
|
262 } |
|
263 |
|
264 aResult.SetTo(aValue); |
|
265 } |