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 /*
8 * A class for managing namespace IDs and mapping back and forth
9 * between namespace IDs and namespace URIs.
10 */
11
12 #include "nsNameSpaceManager.h"
13
14 #include "nscore.h"
15 #include "mozilla/dom/NodeInfo.h"
16 #include "nsCOMArray.h"
17 #include "nsContentCreatorFunctions.h"
18 #include "nsContentUtils.h"
19 #include "nsGkAtoms.h"
20 #include "nsIDocument.h"
21 #include "nsString.h"
22 #include "mozilla/dom/NodeInfo.h"
23 #include "mozilla/ClearOnShutdown.h"
24 #include "mozilla/dom/XBLChildrenElement.h"
25 #include "mozilla/dom/Element.h"
26 #include "mozilla/Preferences.h"
27
28 using namespace mozilla;
29 using namespace mozilla::dom;
30
31 static const char* kPrefMathMLDisabled = "mathml.disabled";
32 static const char* kObservedPrefs[] = {
33 kPrefMathMLDisabled,
34 nullptr
35 };
36 StaticRefPtr<nsNameSpaceManager> nsNameSpaceManager::sInstance;
37
38 /* static */ nsNameSpaceManager*
GetInstance()39 nsNameSpaceManager::GetInstance() {
40 if (!sInstance) {
41 sInstance = new nsNameSpaceManager();
42 if (sInstance->Init()) {
43 ClearOnShutdown(&sInstance);
44 } else {
45 delete sInstance;
46 sInstance = nullptr;
47 }
48 }
49
50 return sInstance;
51 }
52
Init()53 bool nsNameSpaceManager::Init()
54 {
55 nsresult rv;
56 #define REGISTER_NAMESPACE(uri, id) \
57 rv = AddNameSpace(dont_AddRef(uri), id); \
58 NS_ENSURE_SUCCESS(rv, false)
59
60 #define REGISTER_DISABLED_NAMESPACE(uri, id) \
61 rv = AddDisabledNameSpace(dont_AddRef(uri), id); \
62 NS_ENSURE_SUCCESS(rv, false)
63
64 mozilla::Preferences::AddStrongObservers(this, kObservedPrefs);
65 mMathMLDisabled = mozilla::Preferences::GetBool(kPrefMathMLDisabled);
66
67
68 // Need to be ordered according to ID.
69 MOZ_ASSERT(mURIArray.IsEmpty());
70 REGISTER_NAMESPACE(nsGkAtoms::empty, kNameSpaceID_None);
71 REGISTER_NAMESPACE(nsGkAtoms::nsuri_xmlns, kNameSpaceID_XMLNS);
72 REGISTER_NAMESPACE(nsGkAtoms::nsuri_xml, kNameSpaceID_XML);
73 REGISTER_NAMESPACE(nsGkAtoms::nsuri_xhtml, kNameSpaceID_XHTML);
74 REGISTER_NAMESPACE(nsGkAtoms::nsuri_xlink, kNameSpaceID_XLink);
75 REGISTER_NAMESPACE(nsGkAtoms::nsuri_xslt, kNameSpaceID_XSLT);
76 REGISTER_NAMESPACE(nsGkAtoms::nsuri_xbl, kNameSpaceID_XBL);
77 REGISTER_NAMESPACE(nsGkAtoms::nsuri_mathml, kNameSpaceID_MathML);
78 REGISTER_NAMESPACE(nsGkAtoms::nsuri_rdf, kNameSpaceID_RDF);
79 REGISTER_NAMESPACE(nsGkAtoms::nsuri_xul, kNameSpaceID_XUL);
80 REGISTER_NAMESPACE(nsGkAtoms::nsuri_svg, kNameSpaceID_SVG);
81 REGISTER_DISABLED_NAMESPACE(nsGkAtoms::nsuri_mathml, kNameSpaceID_disabled_MathML);
82
83 #undef REGISTER_NAMESPACE
84 #undef REGISTER_DISABLED_NAMESPACE
85
86 return true;
87 }
88
89 nsresult
RegisterNameSpace(const nsAString & aURI,int32_t & aNameSpaceID)90 nsNameSpaceManager::RegisterNameSpace(const nsAString& aURI,
91 int32_t& aNameSpaceID)
92 {
93 if (aURI.IsEmpty()) {
94 aNameSpaceID = kNameSpaceID_None; // xmlns="", see bug 75700 for details
95
96 return NS_OK;
97 }
98
99 nsCOMPtr<nsIAtom> atom = NS_Atomize(aURI);
100 nsresult rv = NS_OK;
101 if (!mURIToIDTable.Get(atom, &aNameSpaceID)) {
102 aNameSpaceID = mURIArray.Length();
103
104 rv = AddNameSpace(atom.forget(), aNameSpaceID);
105 if (NS_FAILED(rv)) {
106 aNameSpaceID = kNameSpaceID_Unknown;
107 }
108 }
109
110 NS_POSTCONDITION(aNameSpaceID >= -1, "Bogus namespace ID");
111
112 return rv;
113 }
114
115 nsresult
GetNameSpaceURI(int32_t aNameSpaceID,nsAString & aURI)116 nsNameSpaceManager::GetNameSpaceURI(int32_t aNameSpaceID, nsAString& aURI)
117 {
118 NS_PRECONDITION(aNameSpaceID >= 0, "Bogus namespace ID");
119
120 // We have historically treated GetNameSpaceURI calls for kNameSpaceID_None
121 // as erroneous.
122 if (aNameSpaceID <= 0 || aNameSpaceID >= int32_t(mURIArray.Length())) {
123 aURI.Truncate();
124
125 return NS_ERROR_ILLEGAL_VALUE;
126 }
127
128 mURIArray.ElementAt(aNameSpaceID)->ToString(aURI);
129
130 return NS_OK;
131 }
132
133 int32_t
GetNameSpaceID(const nsAString & aURI,bool aInChromeDoc)134 nsNameSpaceManager::GetNameSpaceID(const nsAString& aURI,
135 bool aInChromeDoc)
136 {
137 if (aURI.IsEmpty()) {
138 return kNameSpaceID_None; // xmlns="", see bug 75700 for details
139 }
140
141 nsCOMPtr<nsIAtom> atom = NS_Atomize(aURI);
142 return GetNameSpaceID(atom, aInChromeDoc);
143 }
144
145 int32_t
GetNameSpaceID(nsIAtom * aURI,bool aInChromeDoc)146 nsNameSpaceManager::GetNameSpaceID(nsIAtom* aURI,
147 bool aInChromeDoc)
148 {
149 if (aURI == nsGkAtoms::_empty) {
150 return kNameSpaceID_None; // xmlns="", see bug 75700 for details
151 }
152
153 int32_t nameSpaceID;
154 if (mMathMLDisabled &&
155 mDisabledURIToIDTable.Get(aURI, &nameSpaceID) &&
156 !aInChromeDoc) {
157 NS_POSTCONDITION(nameSpaceID >= 0, "Bogus namespace ID");
158 return nameSpaceID;
159 }
160 if (mURIToIDTable.Get(aURI, &nameSpaceID)) {
161 NS_POSTCONDITION(nameSpaceID >= 0, "Bogus namespace ID");
162 return nameSpaceID;
163 }
164
165 return kNameSpaceID_Unknown;
166 }
167
168 nsresult
NS_NewElement(Element ** aResult,already_AddRefed<mozilla::dom::NodeInfo> && aNodeInfo,FromParser aFromParser,const nsAString * aIs)169 NS_NewElement(Element** aResult,
170 already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
171 FromParser aFromParser,
172 const nsAString* aIs)
173 {
174 RefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo;
175 int32_t ns = ni->NamespaceID();
176 if (ns == kNameSpaceID_XHTML) {
177 return NS_NewHTMLElement(aResult, ni.forget(), aFromParser, aIs);
178 }
179 #ifdef MOZ_XUL
180 if (ns == kNameSpaceID_XUL) {
181 return NS_NewXULElement(aResult, ni.forget());
182 }
183 #endif
184 if (ns == kNameSpaceID_MathML) {
185 // If the mathml.disabled pref. is true, convert all MathML nodes into
186 // disabled MathML nodes by swapping the namespace.
187 nsNameSpaceManager* nsmgr = nsNameSpaceManager::GetInstance();
188 if ((nsmgr && !nsmgr->mMathMLDisabled) ||
189 nsContentUtils::IsChromeDoc(ni->GetDocument())) {
190 return NS_NewMathMLElement(aResult, ni.forget());
191 }
192
193 RefPtr<mozilla::dom::NodeInfo> genericXMLNI =
194 ni->NodeInfoManager()->
195 GetNodeInfo(ni->NameAtom(), ni->GetPrefixAtom(),
196 kNameSpaceID_disabled_MathML, ni->NodeType(), ni->GetExtraName());
197 return NS_NewXMLElement(aResult, genericXMLNI.forget());
198 }
199 if (ns == kNameSpaceID_SVG) {
200 return NS_NewSVGElement(aResult, ni.forget(), aFromParser);
201 }
202 if (ns == kNameSpaceID_XBL && ni->Equals(nsGkAtoms::children)) {
203 NS_ADDREF(*aResult = new XBLChildrenElement(ni.forget()));
204 return NS_OK;
205 }
206
207 return NS_NewXMLElement(aResult, ni.forget());
208 }
209
210 bool
HasElementCreator(int32_t aNameSpaceID)211 nsNameSpaceManager::HasElementCreator(int32_t aNameSpaceID)
212 {
213 return aNameSpaceID == kNameSpaceID_XHTML ||
214 #ifdef MOZ_XUL
215 aNameSpaceID == kNameSpaceID_XUL ||
216 #endif
217 aNameSpaceID == kNameSpaceID_MathML ||
218 aNameSpaceID == kNameSpaceID_SVG ||
219 false;
220 }
221
AddNameSpace(already_AddRefed<nsIAtom> aURI,const int32_t aNameSpaceID)222 nsresult nsNameSpaceManager::AddNameSpace(already_AddRefed<nsIAtom> aURI,
223 const int32_t aNameSpaceID)
224 {
225 nsCOMPtr<nsIAtom> uri = aURI;
226 if (aNameSpaceID < 0) {
227 // We've wrapped... Can't do anything else here; just bail.
228 return NS_ERROR_OUT_OF_MEMORY;
229 }
230
231 MOZ_ASSERT(aNameSpaceID == (int32_t) mURIArray.Length());
232 mURIArray.AppendElement(uri.forget());
233 mURIToIDTable.Put(mURIArray.LastElement(), aNameSpaceID);
234
235 return NS_OK;
236 }
237
238 nsresult
AddDisabledNameSpace(already_AddRefed<nsIAtom> aURI,const int32_t aNameSpaceID)239 nsNameSpaceManager::AddDisabledNameSpace(already_AddRefed<nsIAtom> aURI,
240 const int32_t aNameSpaceID)
241 {
242 nsCOMPtr<nsIAtom> uri = aURI;
243 if (aNameSpaceID < 0) {
244 // We've wrapped... Can't do anything else here; just bail.
245 return NS_ERROR_OUT_OF_MEMORY;
246 }
247
248 MOZ_ASSERT(aNameSpaceID == (int32_t) mURIArray.Length());
249 mURIArray.AppendElement(uri.forget());
250 mDisabledURIToIDTable.Put(mURIArray.LastElement(), aNameSpaceID);
251
252 return NS_OK;
253 }
254
255 // nsISupports
NS_IMPL_ISUPPORTS(nsNameSpaceManager,nsIObserver)256 NS_IMPL_ISUPPORTS(nsNameSpaceManager,
257 nsIObserver)
258
259 // nsIObserver
260 NS_IMETHODIMP
261 nsNameSpaceManager::Observe(nsISupports* aObject, const char* aTopic,
262 const char16_t* aMessage)
263 {
264 mMathMLDisabled = mozilla::Preferences::GetBool(kPrefMathMLDisabled);
265 return NS_OK;
266 }
267