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 "mozilla/dom/XMLDocument.h"
8 #include "nsParserCIID.h"
9 #include "nsCharsetSource.h"
10 #include "nsIXMLContentSink.h"
11 #include "nsPresContext.h"
12 #include "nsIContent.h"
13 #include "nsIDocShell.h"
14 #include "nsHTMLParts.h"
15 #include "nsCOMPtr.h"
16 #include "nsString.h"
17 #include "nsIURI.h"
18 #include "nsNetUtil.h"
19 #include "nsError.h"
20 #include "nsIPrincipal.h"
21 #include "nsLayoutCID.h"
22 #include "mozilla/dom/Attr.h"
23 #include "nsCExternalHandlerService.h"
24 #include "nsMimeTypes.h"
25 #include "nsContentUtils.h"
26 #include "nsThreadUtils.h"
27 #include "nsJSUtils.h"
28 #include "nsCRT.h"
29 #include "nsComponentManagerUtils.h"
30 #include "nsContentCreatorFunctions.h"
31 #include "nsContentPolicyUtils.h"
32 #include "nsIConsoleService.h"
33 #include "nsIScriptError.h"
34 #include "nsHTMLDocument.h"
35 #include "mozilla/BasicEvents.h"
36 #include "mozilla/EventDispatcher.h"
37 #include "mozilla/Encoding.h"
38 #include "mozilla/dom/DocumentType.h"
39 #include "mozilla/dom/Element.h"
40 #include "mozilla/dom/DocGroup.h"
41 #include "mozilla/dom/XMLDocumentBinding.h"
42 #include "mozilla/dom/DocumentBinding.h"
43 
44 using namespace mozilla;
45 using namespace mozilla::dom;
46 
47 // ==================================================================
48 // =
49 // ==================================================================
50 
NS_NewDOMDocument(Document ** aInstancePtrResult,const nsAString & aNamespaceURI,const nsAString & aQualifiedName,DocumentType * aDoctype,nsIURI * aDocumentURI,nsIURI * aBaseURI,nsIPrincipal * aPrincipal,bool aLoadedAsData,nsIGlobalObject * aEventObject,DocumentFlavor aFlavor)51 nsresult NS_NewDOMDocument(Document** aInstancePtrResult,
52                            const nsAString& aNamespaceURI,
53                            const nsAString& aQualifiedName,
54                            DocumentType* aDoctype, nsIURI* aDocumentURI,
55                            nsIURI* aBaseURI, nsIPrincipal* aPrincipal,
56                            bool aLoadedAsData, nsIGlobalObject* aEventObject,
57                            DocumentFlavor aFlavor) {
58   // Note: can't require that aDocumentURI/aBaseURI/aPrincipal be non-null,
59   // since at least one caller (XMLHttpRequest) doesn't have decent args to
60   // pass in.
61 
62   nsresult rv;
63 
64   *aInstancePtrResult = nullptr;
65 
66   nsCOMPtr<Document> d;
67   bool isHTML = false;
68   bool isXHTML = false;
69   if (aFlavor == DocumentFlavorSVG) {
70     rv = NS_NewSVGDocument(getter_AddRefs(d));
71   } else if (aFlavor == DocumentFlavorHTML) {
72     rv = NS_NewHTMLDocument(getter_AddRefs(d));
73     isHTML = true;
74   } else if (aFlavor == DocumentFlavorXML) {
75     rv = NS_NewXMLDocument(getter_AddRefs(d));
76   } else if (aFlavor == DocumentFlavorPlain) {
77     rv = NS_NewXMLDocument(getter_AddRefs(d), aLoadedAsData, true);
78   } else if (aDoctype) {
79     MOZ_ASSERT(aFlavor == DocumentFlavorLegacyGuess);
80     nsAutoString publicId, name;
81     aDoctype->GetPublicId(publicId);
82     if (publicId.IsEmpty()) {
83       aDoctype->GetName(name);
84     }
85     if (name.EqualsLiteral("html") ||
86         publicId.EqualsLiteral("-//W3C//DTD HTML 4.01//EN") ||
87         publicId.EqualsLiteral("-//W3C//DTD HTML 4.01 Frameset//EN") ||
88         publicId.EqualsLiteral("-//W3C//DTD HTML 4.01 Transitional//EN") ||
89         publicId.EqualsLiteral("-//W3C//DTD HTML 4.0//EN") ||
90         publicId.EqualsLiteral("-//W3C//DTD HTML 4.0 Frameset//EN") ||
91         publicId.EqualsLiteral("-//W3C//DTD HTML 4.0 Transitional//EN")) {
92       rv = NS_NewHTMLDocument(getter_AddRefs(d));
93       isHTML = true;
94     } else if (publicId.EqualsLiteral("-//W3C//DTD XHTML 1.0 Strict//EN") ||
95                publicId.EqualsLiteral(
96                    "-//W3C//DTD XHTML 1.0 Transitional//EN") ||
97                publicId.EqualsLiteral("-//W3C//DTD XHTML 1.0 Frameset//EN")) {
98       rv = NS_NewHTMLDocument(getter_AddRefs(d));
99       isHTML = true;
100       isXHTML = true;
101     } else if (publicId.EqualsLiteral("-//W3C//DTD SVG 1.1//EN")) {
102       rv = NS_NewSVGDocument(getter_AddRefs(d));
103     }
104     // XXX Add support for XUL documents.
105     else {
106       rv = NS_NewXMLDocument(getter_AddRefs(d));
107     }
108   } else {
109     MOZ_ASSERT(aFlavor == DocumentFlavorLegacyGuess);
110     rv = NS_NewXMLDocument(getter_AddRefs(d));
111   }
112 
113   if (NS_FAILED(rv)) {
114     return rv;
115   }
116 
117   if (isHTML) {
118     d->SetCompatibilityMode(eCompatibility_FullStandards);
119     d->AsHTMLDocument()->SetIsXHTML(isXHTML);
120   }
121   d->SetLoadedAsData(aLoadedAsData, /* aConsiderForMemoryReporting */ true);
122   d->SetDocumentURI(aDocumentURI);
123   // Must set the principal first, since SetBaseURI checks it.
124   d->SetPrincipals(aPrincipal, aPrincipal);
125   d->SetBaseURI(aBaseURI);
126 
127   // We need to set the script handling object after we set the principal such
128   // that the doc group is assigned correctly.
129   if (nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aEventObject)) {
130     d->SetScriptHandlingObject(sgo);
131   } else if (aEventObject) {
132     d->SetScopeObject(aEventObject);
133   }
134 
135   // XMLDocuments and documents "created in memory" get to be UTF-8 by default,
136   // unlike the legacy HTML mess
137   d->SetDocumentCharacterSet(UTF_8_ENCODING);
138 
139   if (aDoctype) {
140     ErrorResult result;
141     d->AppendChild(*aDoctype, result);
142     // Need to WouldReportJSException() if our callee can throw a JS
143     // exception (which it can) and we're neither propagating the
144     // error out nor unconditionally suppressing it.
145     result.WouldReportJSException();
146     if (NS_WARN_IF(result.Failed())) {
147       return result.StealNSResult();
148     }
149   }
150 
151   if (!aQualifiedName.IsEmpty()) {
152     ErrorResult result;
153     ElementCreationOptionsOrString options;
154     options.SetAsString();
155 
156     nsCOMPtr<Element> root =
157         d->CreateElementNS(aNamespaceURI, aQualifiedName, options, result);
158     if (NS_WARN_IF(result.Failed())) {
159       return result.StealNSResult();
160     }
161 
162     d->AppendChild(*root, result);
163     // Need to WouldReportJSException() if our callee can throw a JS
164     // exception (which it can) and we're neither propagating the
165     // error out nor unconditionally suppressing it.
166     result.WouldReportJSException();
167     if (NS_WARN_IF(result.Failed())) {
168       return result.StealNSResult();
169     }
170   }
171 
172   d.forget(aInstancePtrResult);
173 
174   return NS_OK;
175 }
176 
NS_NewXMLDocument(Document ** aInstancePtrResult,bool aLoadedAsData,bool aIsPlainDocument)177 nsresult NS_NewXMLDocument(Document** aInstancePtrResult, bool aLoadedAsData,
178                            bool aIsPlainDocument) {
179   RefPtr<XMLDocument> doc = new XMLDocument();
180 
181   nsresult rv = doc->Init();
182 
183   if (NS_FAILED(rv)) {
184     *aInstancePtrResult = nullptr;
185     return rv;
186   }
187 
188   doc->SetLoadedAsData(aLoadedAsData, /* aConsiderForMemoryReporting */ true);
189   doc->mIsPlainDocument = aIsPlainDocument;
190   doc.forget(aInstancePtrResult);
191 
192   return NS_OK;
193 }
194 
195 namespace mozilla {
196 namespace dom {
197 
XMLDocument(const char * aContentType)198 XMLDocument::XMLDocument(const char* aContentType)
199     : Document(aContentType),
200       mChannelIsPending(false),
201       mIsPlainDocument(false),
202       mSuppressParserErrorElement(false),
203       mSuppressParserErrorConsoleMessages(false) {
204   mType = eGenericXML;
205 }
206 
Init()207 nsresult XMLDocument::Init() {
208   nsresult rv = Document::Init();
209   NS_ENSURE_SUCCESS(rv, rv);
210 
211   return rv;
212 }
213 
Reset(nsIChannel * aChannel,nsILoadGroup * aLoadGroup)214 void XMLDocument::Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup) {
215   Document::Reset(aChannel, aLoadGroup);
216 }
217 
ResetToURI(nsIURI * aURI,nsILoadGroup * aLoadGroup,nsIPrincipal * aPrincipal,nsIPrincipal * aPartitionedPrincipal)218 void XMLDocument::ResetToURI(nsIURI* aURI, nsILoadGroup* aLoadGroup,
219                              nsIPrincipal* aPrincipal,
220                              nsIPrincipal* aPartitionedPrincipal) {
221   if (mChannelIsPending) {
222     StopDocumentLoad();
223     mChannel->Cancel(NS_BINDING_ABORTED);
224     mChannelIsPending = false;
225   }
226 
227   Document::ResetToURI(aURI, aLoadGroup, aPrincipal, aPartitionedPrincipal);
228 }
229 
SetSuppressParserErrorElement(bool aSuppress)230 void XMLDocument::SetSuppressParserErrorElement(bool aSuppress) {
231   mSuppressParserErrorElement = aSuppress;
232 }
233 
SuppressParserErrorElement()234 bool XMLDocument::SuppressParserErrorElement() {
235   return mSuppressParserErrorElement;
236 }
237 
SetSuppressParserErrorConsoleMessages(bool aSuppress)238 void XMLDocument::SetSuppressParserErrorConsoleMessages(bool aSuppress) {
239   mSuppressParserErrorConsoleMessages = aSuppress;
240 }
241 
SuppressParserErrorConsoleMessages()242 bool XMLDocument::SuppressParserErrorConsoleMessages() {
243   return mSuppressParserErrorConsoleMessages;
244 }
245 
StartDocumentLoad(const char * aCommand,nsIChannel * aChannel,nsILoadGroup * aLoadGroup,nsISupports * aContainer,nsIStreamListener ** aDocListener,bool aReset,nsIContentSink * aSink)246 nsresult XMLDocument::StartDocumentLoad(const char* aCommand,
247                                         nsIChannel* aChannel,
248                                         nsILoadGroup* aLoadGroup,
249                                         nsISupports* aContainer,
250                                         nsIStreamListener** aDocListener,
251                                         bool aReset, nsIContentSink* aSink) {
252   nsresult rv = Document::StartDocumentLoad(
253       aCommand, aChannel, aLoadGroup, aContainer, aDocListener, aReset, aSink);
254   if (NS_FAILED(rv)) return rv;
255 
256   int32_t charsetSource = kCharsetFromDocTypeDefault;
257   NotNull<const Encoding*> encoding = UTF_8_ENCODING;
258   TryChannelCharset(aChannel, charsetSource, encoding, nullptr);
259 
260   nsCOMPtr<nsIURI> aUrl;
261   rv = aChannel->GetURI(getter_AddRefs(aUrl));
262   if (NS_FAILED(rv)) return rv;
263 
264   static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID);
265 
266   mParser = do_CreateInstance(kCParserCID, &rv);
267   NS_ENSURE_SUCCESS(rv, rv);
268 
269   nsCOMPtr<nsIXMLContentSink> sink;
270 
271   if (aSink) {
272     sink = do_QueryInterface(aSink);
273   } else {
274     nsCOMPtr<nsIDocShell> docShell;
275     if (aContainer) {
276       docShell = do_QueryInterface(aContainer);
277       NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
278     }
279     rv = NS_NewXMLContentSink(getter_AddRefs(sink), this, aUrl, docShell,
280                               aChannel);
281     NS_ENSURE_SUCCESS(rv, rv);
282   }
283 
284   // Set the parser as the stream listener for the document loader...
285   rv = CallQueryInterface(mParser, aDocListener);
286   NS_ENSURE_SUCCESS(rv, rv);
287 
288   NS_ASSERTION(mChannel, "How can we not have a channel here?");
289   mChannelIsPending = true;
290 
291   SetDocumentCharacterSet(encoding);
292   mParser->SetDocumentCharset(encoding, charsetSource);
293   mParser->SetCommand(aCommand);
294   mParser->SetContentSink(sink);
295   mParser->Parse(aUrl, nullptr, (void*)this);
296 
297   return NS_OK;
298 }
299 
EndLoad()300 void XMLDocument::EndLoad() {
301   mChannelIsPending = false;
302 
303   mSynchronousDOMContentLoaded = mLoadedAsData;
304   Document::EndLoad();
305   if (mSynchronousDOMContentLoaded) {
306     mSynchronousDOMContentLoaded = false;
307     Document::SetReadyStateInternal(Document::READYSTATE_COMPLETE);
308     // Generate a document load event for the case when an XML
309     // document was loaded as pure data without any presentation
310     // attached to it.
311     WidgetEvent event(true, eLoad);
312     EventDispatcher::Dispatch(ToSupports(this), nullptr, &event);
313   }
314 }
315 
316 /* virtual */
DocAddSizeOfExcludingThis(nsWindowSizes & aWindowSizes) const317 void XMLDocument::DocAddSizeOfExcludingThis(nsWindowSizes& aWindowSizes) const {
318   Document::DocAddSizeOfExcludingThis(aWindowSizes);
319 }
320 
321 // Document interface
322 
Clone(dom::NodeInfo * aNodeInfo,nsINode ** aResult) const323 nsresult XMLDocument::Clone(dom::NodeInfo* aNodeInfo, nsINode** aResult) const {
324   NS_ASSERTION(aNodeInfo->NodeInfoManager() == mNodeInfoManager,
325                "Can't import this document into another document!");
326 
327   RefPtr<XMLDocument> clone = new XMLDocument();
328   nsresult rv = CloneDocHelper(clone);
329   NS_ENSURE_SUCCESS(rv, rv);
330 
331   // State from XMLDocument
332   clone->mIsPlainDocument = mIsPlainDocument;
333 
334   clone.forget(aResult);
335   return NS_OK;
336 }
337 
WrapNode(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)338 JSObject* XMLDocument::WrapNode(JSContext* aCx,
339                                 JS::Handle<JSObject*> aGivenProto) {
340   if (mIsPlainDocument) {
341     return Document_Binding::Wrap(aCx, this, aGivenProto);
342   }
343 
344   return XMLDocument_Binding::Wrap(aCx, this, aGivenProto);
345 }
346 
347 }  // namespace dom
348 }  // namespace mozilla
349