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