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 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is Mozilla Communicator client code.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Netscape Communications Corporation.
20 : * Portions created by the Initial Developer are Copyright (C) 1998
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Mats Palmgren <mats.palmgren@bredband.net>
25 : * Daniel Kraft <d@domob.eu>
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either of the GNU General Public License Version 2 or later (the "GPL"),
29 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : #include "nsStyledElement.h"
42 : #include "nsGkAtoms.h"
43 : #include "nsAttrValue.h"
44 : #include "nsGenericElement.h"
45 : #include "nsMutationEvent.h"
46 : #include "nsDOMCSSDeclaration.h"
47 : #include "nsDOMCSSAttrDeclaration.h"
48 : #include "nsServiceManagerUtils.h"
49 : #include "nsIDocument.h"
50 : #include "mozilla/css/StyleRule.h"
51 : #include "nsCSSParser.h"
52 : #include "mozilla/css/Loader.h"
53 : #include "nsIDOMMutationEvent.h"
54 : #include "nsXULElement.h"
55 : #include "nsIDOMSVGStylable.h"
56 : #include "nsContentUtils.h"
57 :
58 : namespace css = mozilla::css;
59 :
60 : //----------------------------------------------------------------------
61 : // nsIContent methods
62 :
63 : nsIAtom*
64 0 : nsStyledElementNotElementCSSInlineStyle::GetClassAttributeName() const
65 : {
66 0 : return nsGkAtoms::_class;
67 : }
68 :
69 : nsIAtom*
70 0 : nsStyledElementNotElementCSSInlineStyle::GetIDAttributeName() const
71 : {
72 0 : return nsGkAtoms::id;
73 : }
74 :
75 : nsIAtom*
76 0 : nsStyledElementNotElementCSSInlineStyle::DoGetID() const
77 : {
78 0 : NS_ASSERTION(HasID(), "Unexpected call");
79 :
80 : // The nullcheck here is needed because nsGenericElement::UnsetAttr calls
81 : // out to various code between removing the attribute and we get a chance to
82 : // ClearHasID().
83 :
84 0 : const nsAttrValue* attr = mAttrsAndChildren.GetAttr(nsGkAtoms::id);
85 :
86 0 : return attr ? attr->GetAtomValue() : nsnull;
87 : }
88 :
89 : const nsAttrValue*
90 0 : nsStyledElementNotElementCSSInlineStyle::DoGetClasses() const
91 : {
92 0 : NS_ASSERTION(HasFlag(NODE_MAY_HAVE_CLASS), "Unexpected call");
93 0 : return mAttrsAndChildren.GetAttr(nsGkAtoms::_class);
94 : }
95 :
96 : bool
97 195 : nsStyledElementNotElementCSSInlineStyle::ParseAttribute(PRInt32 aNamespaceID,
98 : nsIAtom* aAttribute,
99 : const nsAString& aValue,
100 : nsAttrValue& aResult)
101 : {
102 195 : if (aNamespaceID == kNameSpaceID_None) {
103 66 : if (aAttribute == nsGkAtoms::style) {
104 0 : SetMayHaveStyle();
105 0 : ParseStyleAttribute(aValue, aResult, false);
106 0 : return true;
107 : }
108 66 : if (aAttribute == nsGkAtoms::_class) {
109 0 : SetFlags(NODE_MAY_HAVE_CLASS);
110 0 : aResult.ParseAtomArray(aValue);
111 0 : return true;
112 : }
113 66 : if (aAttribute == nsGkAtoms::id) {
114 : // Store id as an atom. id="" means that the element has no id,
115 : // not that it has an emptystring as the id.
116 10 : RemoveFromIdTable();
117 10 : if (aValue.IsEmpty()) {
118 0 : ClearHasID();
119 0 : return false;
120 : }
121 10 : aResult.ParseAtom(aValue);
122 10 : SetHasID();
123 10 : AddToIdTable(aResult.GetAtomValue());
124 10 : return true;
125 : }
126 : }
127 :
128 : return nsStyledElementBase::ParseAttribute(aNamespaceID, aAttribute, aValue,
129 185 : aResult);
130 : }
131 :
132 : nsresult
133 0 : nsStyledElementNotElementCSSInlineStyle::UnsetAttr(PRInt32 aNameSpaceID,
134 : nsIAtom* aAttribute,
135 : bool aNotify)
136 : {
137 0 : nsAutoScriptBlocker scriptBlocker;
138 0 : if (aAttribute == nsGkAtoms::id && aNameSpaceID == kNameSpaceID_None) {
139 : // Have to do this before clearing flag. See RemoveFromIdTable
140 0 : RemoveFromIdTable();
141 : }
142 :
143 0 : return nsGenericElement::UnsetAttr(aNameSpaceID, aAttribute, aNotify);
144 : }
145 :
146 : nsresult
147 195 : nsStyledElementNotElementCSSInlineStyle::AfterSetAttr(PRInt32 aNamespaceID,
148 : nsIAtom* aAttribute,
149 : const nsAttrValue* aValue,
150 : bool aNotify)
151 : {
152 195 : if (aNamespaceID == kNameSpaceID_None && !aValue &&
153 : aAttribute == nsGkAtoms::id) {
154 : // The id has been removed when calling UnsetAttr but we kept it because
155 : // the id is used for some layout stuff between UnsetAttr and AfterSetAttr.
156 : // Now. the id is really removed so it would not be safe to keep this flag.
157 0 : ClearHasID();
158 : }
159 :
160 : return nsGenericElement::AfterSetAttr(aNamespaceID, aAttribute, aValue,
161 195 : aNotify);
162 : }
163 :
164 : NS_IMETHODIMP
165 0 : nsStyledElementNotElementCSSInlineStyle::SetInlineStyleRule(css::StyleRule* aStyleRule,
166 : bool aNotify)
167 : {
168 0 : SetMayHaveStyle();
169 0 : bool modification = false;
170 0 : nsAttrValue oldValue;
171 :
172 : bool hasListeners = aNotify &&
173 : nsContentUtils::HasMutationListeners(this,
174 : NS_EVENT_BITS_MUTATION_ATTRMODIFIED,
175 0 : this);
176 :
177 : // There's no point in comparing the stylerule pointers since we're always
178 : // getting a new stylerule here. And we can't compare the stringvalues of
179 : // the old and the new rules since both will point to the same declaration
180 : // and thus will be the same.
181 0 : if (hasListeners) {
182 : // save the old attribute so we can set up the mutation event properly
183 : // XXXbz if the old rule points to the same declaration as the new one,
184 : // this is getting the new attr value, not the old one....
185 0 : nsAutoString oldValueStr;
186 : modification = GetAttr(kNameSpaceID_None, nsGkAtoms::style,
187 0 : oldValueStr);
188 0 : if (modification) {
189 0 : oldValue.SetTo(oldValueStr);
190 : }
191 : }
192 0 : else if (aNotify && IsInDoc()) {
193 0 : modification = !!mAttrsAndChildren.GetAttr(nsGkAtoms::style);
194 : }
195 :
196 0 : nsAttrValue attrValue(aStyleRule, nsnull);
197 :
198 : // XXXbz do we ever end up with ADDITION here? I doubt it.
199 : PRUint8 modType = modification ?
200 : static_cast<PRUint8>(nsIDOMMutationEvent::MODIFICATION) :
201 0 : static_cast<PRUint8>(nsIDOMMutationEvent::ADDITION);
202 :
203 : return SetAttrAndNotify(kNameSpaceID_None, nsGkAtoms::style, nsnull,
204 : oldValue, attrValue, modType, hasListeners,
205 0 : aNotify, kDontCallAfterSetAttr);
206 : }
207 :
208 : css::StyleRule*
209 0 : nsStyledElementNotElementCSSInlineStyle::GetInlineStyleRule()
210 : {
211 0 : if (!MayHaveStyle()) {
212 0 : return nsnull;
213 : }
214 0 : const nsAttrValue* attrVal = mAttrsAndChildren.GetAttr(nsGkAtoms::style);
215 :
216 0 : if (attrVal && attrVal->Type() == nsAttrValue::eCSSStyleRule) {
217 0 : return attrVal->GetCSSStyleRuleValue();
218 : }
219 :
220 0 : return nsnull;
221 : }
222 :
223 : nsresult
224 934 : nsStyledElementNotElementCSSInlineStyle::BindToTree(nsIDocument* aDocument,
225 : nsIContent* aParent,
226 : nsIContent* aBindingParent,
227 : bool aCompileEventHandlers)
228 : {
229 : nsresult rv = nsStyledElementBase::BindToTree(aDocument, aParent,
230 : aBindingParent,
231 934 : aCompileEventHandlers);
232 934 : NS_ENSURE_SUCCESS(rv, rv);
233 :
234 934 : if (aDocument && HasID() && !GetBindingParent()) {
235 10 : aDocument->AddToIdTable(this, DoGetID());
236 : }
237 :
238 934 : if (!IsXUL()) {
239 : // XXXbz if we already have a style attr parsed, this won't do
240 : // anything... need to fix that.
241 790 : ReparseStyleAttribute(false);
242 : }
243 :
244 934 : return NS_OK;
245 : }
246 :
247 : void
248 1684 : nsStyledElementNotElementCSSInlineStyle::UnbindFromTree(bool aDeep,
249 : bool aNullParent)
250 : {
251 1684 : RemoveFromIdTable();
252 :
253 1684 : nsStyledElementBase::UnbindFromTree(aDeep, aNullParent);
254 1684 : }
255 :
256 :
257 : // ---------------------------------------------------------------
258 : // Others and helpers
259 :
260 : nsIDOMCSSStyleDeclaration*
261 0 : nsStyledElementNotElementCSSInlineStyle::GetStyle(nsresult* retval)
262 : {
263 0 : nsXULElement* xulElement = nsXULElement::FromContent(this);
264 0 : if (xulElement) {
265 0 : nsresult rv = xulElement->EnsureLocalStyle();
266 0 : if (NS_FAILED(rv)) {
267 0 : *retval = rv;
268 0 : return nsnull;
269 : }
270 : }
271 :
272 0 : nsGenericElement::nsDOMSlots *slots = DOMSlots();
273 :
274 0 : if (!slots->mStyle) {
275 : // Just in case...
276 0 : ReparseStyleAttribute(true);
277 :
278 0 : slots->mStyle = new nsDOMCSSAttributeDeclaration(this, false);
279 0 : SetMayHaveStyle();
280 : }
281 :
282 0 : *retval = NS_OK;
283 0 : return slots->mStyle;
284 : }
285 :
286 : nsresult
287 790 : nsStyledElementNotElementCSSInlineStyle::ReparseStyleAttribute(bool aForceInDataDoc)
288 : {
289 790 : if (!MayHaveStyle()) {
290 790 : return NS_OK;
291 : }
292 0 : const nsAttrValue* oldVal = mAttrsAndChildren.GetAttr(nsGkAtoms::style);
293 :
294 0 : if (oldVal && oldVal->Type() != nsAttrValue::eCSSStyleRule) {
295 0 : nsAttrValue attrValue;
296 0 : nsAutoString stringValue;
297 0 : oldVal->ToString(stringValue);
298 0 : ParseStyleAttribute(stringValue, attrValue, aForceInDataDoc);
299 : // Don't bother going through SetInlineStyleRule, we don't want to fire off
300 : // mutation events or document notifications anyway
301 0 : nsresult rv = mAttrsAndChildren.SetAndTakeAttr(nsGkAtoms::style, attrValue);
302 0 : NS_ENSURE_SUCCESS(rv, rv);
303 : }
304 :
305 0 : return NS_OK;
306 : }
307 :
308 : void
309 0 : nsStyledElementNotElementCSSInlineStyle::ParseStyleAttribute(const nsAString& aValue,
310 : nsAttrValue& aResult,
311 : bool aForceInDataDoc)
312 : {
313 0 : nsIDocument* doc = OwnerDoc();
314 :
315 0 : if (aForceInDataDoc ||
316 0 : !doc->IsLoadedAsData() ||
317 0 : doc->IsStaticDocument()) {
318 0 : bool isCSS = true; // assume CSS until proven otherwise
319 :
320 0 : if (!IsInNativeAnonymousSubtree()) { // native anonymous content
321 : // always assumes CSS
322 0 : nsAutoString styleType;
323 0 : doc->GetHeaderData(nsGkAtoms::headerContentStyleType, styleType);
324 0 : if (!styleType.IsEmpty()) {
325 : static const char textCssStr[] = "text/css";
326 0 : isCSS = (styleType.EqualsIgnoreCase(textCssStr, sizeof(textCssStr) - 1));
327 : }
328 : }
329 :
330 0 : if (isCSS) {
331 0 : css::Loader* cssLoader = doc->CSSLoader();
332 0 : nsCSSParser cssParser(cssLoader);
333 :
334 0 : nsCOMPtr<nsIURI> baseURI = GetBaseURI();
335 :
336 0 : nsRefPtr<css::StyleRule> rule;
337 : cssParser.ParseStyleAttribute(aValue, doc->GetDocumentURI(),
338 : baseURI,
339 : NodePrincipal(),
340 0 : getter_AddRefs(rule));
341 0 : if (rule) {
342 0 : aResult.SetTo(rule, &aValue);
343 : return;
344 : }
345 : }
346 : }
347 :
348 0 : aResult.SetTo(aValue);
349 : }
|