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