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