nsXMLContentSink.cpp (53077B)
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 "nsXMLContentSink.h" 8 9 #include "js/ColumnNumber.h" // JS::ColumnNumberOneOrigin 10 #include "mozAutoDocUpdate.h" 11 #include "mozilla/CycleCollectedJSContext.h" 12 #include "mozilla/LoadInfo.h" 13 #include "mozilla/Logging.h" 14 #include "mozilla/PodOperations.h" 15 #include "mozilla/StyleSheetInlines.h" 16 #include "mozilla/UseCounter.h" 17 #include "mozilla/css/Loader.h" 18 #include "mozilla/dom/CDATASection.h" 19 #include "mozilla/dom/Comment.h" 20 #include "mozilla/dom/CustomElementRegistry.h" 21 #include "mozilla/dom/DocGroup.h" 22 #include "mozilla/dom/Document.h" 23 #include "mozilla/dom/DocumentType.h" 24 #include "mozilla/dom/Element.h" 25 #include "mozilla/dom/HTMLTemplateElement.h" 26 #include "mozilla/dom/MutationObservers.h" 27 #include "mozilla/dom/NameSpaceConstants.h" 28 #include "mozilla/dom/ProcessingInstruction.h" 29 #include "mozilla/dom/ScriptLoader.h" 30 #include "mozilla/dom/nsCSPUtils.h" 31 #include "mozilla/dom/txMozillaXSLTProcessor.h" 32 #include "mozilla/intl/LocaleService.h" 33 #include "nsCOMPtr.h" 34 #include "nsCRT.h" 35 #include "nsContentCreatorFunctions.h" 36 #include "nsContentPolicyUtils.h" 37 #include "nsContentUtils.h" 38 #include "nsDocElementCreatedNotificationRunner.h" 39 #include "nsError.h" 40 #include "nsGkAtoms.h" 41 #include "nsHTMLParts.h" 42 #include "nsHtml5SVGLoadDispatcher.h" 43 #include "nsIChannel.h" 44 #include "nsIContent.h" 45 #include "nsIContentPolicy.h" 46 #include "nsIDocShell.h" 47 #include "nsIDocumentViewer.h" 48 #include "nsIParser.h" 49 #include "nsIScriptContext.h" 50 #include "nsIScriptElement.h" 51 #include "nsIScriptGlobalObject.h" 52 #include "nsIScriptSecurityManager.h" 53 #include "nsIURI.h" 54 #include "nsMimeTypes.h" 55 #include "nsNameSpaceManager.h" 56 #include "nsNetUtil.h" 57 #include "nsNodeInfoManager.h" 58 #include "nsReadableUtils.h" 59 #include "nsRect.h" 60 #include "nsTextNode.h" 61 #include "nsUnicharUtils.h" 62 #include "nsXMLPrettyPrinter.h" 63 #include "prtime.h" 64 65 using namespace mozilla; 66 using namespace mozilla::dom; 67 68 // XXX Open Issues: 69 // 1) what's not allowed - We need to figure out which HTML tags 70 // (prefixed with a HTML namespace qualifier) are explicitly not 71 // allowed (if any). 72 73 nsresult NS_NewXMLContentSink(nsIXMLContentSink** aResult, Document* aDoc, 74 nsIURI* aURI, nsISupports* aContainer, 75 nsIChannel* aChannel) { 76 MOZ_ASSERT(nullptr != aResult, "null ptr"); 77 if (nullptr == aResult) { 78 return NS_ERROR_NULL_POINTER; 79 } 80 RefPtr<nsXMLContentSink> it = new nsXMLContentSink(); 81 82 nsresult rv = it->Init(aDoc, aURI, aContainer, aChannel); 83 NS_ENSURE_SUCCESS(rv, rv); 84 85 it.forget(aResult); 86 return NS_OK; 87 } 88 89 nsXMLContentSink::nsXMLContentSink() 90 : mState(eXMLContentSinkState_InProlog), 91 mTextLength(0), 92 mNotifyLevel(0), 93 mPrettyPrintXML(true), 94 mPrettyPrintHasSpecialRoot(0), 95 mPrettyPrintHasFactoredElements(0), 96 mPrettyPrinting(0), 97 mPreventScriptExecution(0) { 98 PodArrayZero(mText); 99 } 100 101 nsXMLContentSink::~nsXMLContentSink() = default; 102 103 nsresult nsXMLContentSink::Init(Document* aDoc, nsIURI* aURI, 104 nsISupports* aContainer, nsIChannel* aChannel) { 105 nsresult rv = nsContentSink::Init(aDoc, aURI, aContainer, aChannel); 106 NS_ENSURE_SUCCESS(rv, rv); 107 108 aDoc->AddObserver(this); 109 mIsDocumentObserver = true; 110 111 if (!mDocShell) { 112 mPrettyPrintXML = false; 113 } 114 115 mState = eXMLContentSinkState_InProlog; 116 mDocElement = nullptr; 117 118 return NS_OK; 119 } 120 121 inline void ImplCycleCollectionTraverse( 122 nsCycleCollectionTraversalCallback& aCallback, 123 nsXMLContentSink::StackNode& aField, const char* aName, 124 uint32_t aFlags = 0) { 125 ImplCycleCollectionTraverse(aCallback, aField.mContent, aName, aFlags); 126 } 127 128 inline void ImplCycleCollectionUnlink(nsXMLContentSink::StackNode& aField) { 129 ImplCycleCollectionUnlink(aField.mContent); 130 } 131 132 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXMLContentSink) 133 NS_INTERFACE_MAP_ENTRY(nsIContentSink) 134 NS_INTERFACE_MAP_ENTRY(nsIXMLContentSink) 135 NS_INTERFACE_MAP_ENTRY(nsIExpatSink) 136 NS_INTERFACE_MAP_ENTRY(nsITransformObserver) 137 NS_INTERFACE_MAP_END_INHERITING(nsContentSink) 138 139 NS_IMPL_ADDREF_INHERITED(nsXMLContentSink, nsContentSink) 140 NS_IMPL_RELEASE_INHERITED(nsXMLContentSink, nsContentSink) 141 142 NS_IMPL_CYCLE_COLLECTION_INHERITED(nsXMLContentSink, nsContentSink, 143 mCurrentHead, mDocElement, mLastTextNode, 144 mContentStack, mDocumentChildren) 145 146 // nsIContentSink 147 NS_IMETHODIMP 148 nsXMLContentSink::WillParse(void) { return WillParseImpl(); } 149 150 NS_IMETHODIMP 151 nsXMLContentSink::WillBuildModel() { 152 WillBuildModelImpl(); 153 154 // Notify document that the load is beginning 155 mDocument->BeginLoad(); 156 157 // Check for correct load-command for maybe prettyprinting 158 if (mPrettyPrintXML) { 159 nsAutoCString command; 160 GetParser()->GetCommand(command); 161 if (!command.EqualsLiteral("view")) { 162 mPrettyPrintXML = false; 163 } 164 } 165 166 return NS_OK; 167 } 168 169 bool nsXMLContentSink::CanStillPrettyPrint() { 170 return mPrettyPrintXML && 171 (!mPrettyPrintHasFactoredElements || mPrettyPrintHasSpecialRoot); 172 } 173 174 nsresult nsXMLContentSink::MaybePrettyPrint() { 175 if (!CanStillPrettyPrint()) { 176 mPrettyPrintXML = false; 177 178 return NS_OK; 179 } 180 181 { 182 // Try to perform a microtask checkpoint; this avoids always breaking 183 // pretty-printing if webextensions insert new content right after the 184 // document loads. 185 nsAutoMicroTask mt; 186 } 187 188 // stop observing in order to avoid crashing when replacing content 189 mDocument->RemoveObserver(this); 190 mIsDocumentObserver = false; 191 192 // Reenable the CSSLoader so that the prettyprinting stylesheets can load 193 mDocument->EnsureCSSLoader().SetEnabled(true); 194 195 RefPtr<nsXMLPrettyPrinter> printer; 196 nsresult rv = NS_NewXMLPrettyPrinter(getter_AddRefs(printer)); 197 NS_ENSURE_SUCCESS(rv, rv); 198 199 bool isPrettyPrinting; 200 rv = printer->PrettyPrint(mDocument, &isPrettyPrinting); 201 NS_ENSURE_SUCCESS(rv, rv); 202 203 mPrettyPrinting = isPrettyPrinting; 204 return NS_OK; 205 } 206 207 static void CheckXSLTParamPI(ProcessingInstruction* aPi, 208 nsIDocumentTransformer* aProcessor, 209 nsINode* aSource) { 210 nsAutoString target, data; 211 aPi->GetTarget(target); 212 213 // Check for namespace declarations 214 if (target.EqualsLiteral("xslt-param-namespace")) { 215 aPi->GetData(data); 216 nsAutoString prefix, namespaceAttr; 217 nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::prefix, prefix); 218 if (!prefix.IsEmpty() && nsContentUtils::GetPseudoAttributeValue( 219 data, nsGkAtoms::_namespace, namespaceAttr)) { 220 aProcessor->AddXSLTParamNamespace(prefix, namespaceAttr); 221 } 222 } 223 224 // Check for actual parameters 225 else if (target.EqualsLiteral("xslt-param")) { 226 aPi->GetData(data); 227 nsAutoString name, namespaceAttr, select, value; 228 nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::name, name); 229 nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::_namespace, 230 namespaceAttr); 231 if (!nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::select, 232 select)) { 233 select.SetIsVoid(true); 234 } 235 if (!nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::value, 236 value)) { 237 value.SetIsVoid(true); 238 } 239 if (!name.IsEmpty()) { 240 aProcessor->AddXSLTParam(name, namespaceAttr, select, value, aSource); 241 } 242 } 243 } 244 245 NS_IMETHODIMP 246 nsXMLContentSink::DidBuildModel(bool aTerminated) { 247 if (!mParser) { 248 // If mParser is null, this parse has already been terminated and must 249 // not been terminated again. However, Document may still think that 250 // the parse has not been terminated and call back into here in the case 251 // where the XML parser has finished but the XSLT transform associated 252 // with the document has not. 253 return NS_OK; 254 } 255 256 FlushTags(); 257 258 DidBuildModelImpl(aTerminated); 259 260 if (mXSLTProcessor) { 261 // stop observing in order to avoid crashing when replacing content 262 mDocument->RemoveObserver(this); 263 mIsDocumentObserver = false; 264 265 ErrorResult rv; 266 RefPtr<DocumentFragment> source = mDocument->CreateDocumentFragment(); 267 for (nsIContent* child : mDocumentChildren) { 268 // XPath data model doesn't have DocumentType nodes. 269 if (child->NodeType() != nsINode::DOCUMENT_TYPE_NODE) { 270 source->AppendChild(*child, rv); 271 if (rv.Failed()) { 272 return rv.StealNSResult(); 273 } 274 } 275 } 276 277 // Check for xslt-param and xslt-param-namespace PIs 278 for (nsIContent* child : mDocumentChildren) { 279 if (auto pi = ProcessingInstruction::FromNode(child)) { 280 CheckXSLTParamPI(pi, mXSLTProcessor, source); 281 } else if (child->IsElement()) { 282 // Only honor PIs in the prolog 283 break; 284 } 285 } 286 287 mXSLTProcessor->SetSourceContentModel(source); 288 // Since the processor now holds a reference to us we drop our reference 289 // to it to avoid owning cycles 290 mXSLTProcessor = nullptr; 291 } else { 292 // Kick off layout for non-XSLT transformed documents. 293 294 // Check if we want to prettyprint 295 MaybePrettyPrint(); 296 297 bool startLayout = true; 298 299 if (mPrettyPrinting) { 300 NS_ASSERTION(!mPendingSheetCount, "Shouldn't have pending sheets here!"); 301 302 // We're pretty-printing now. See whether we should wait up on 303 // stylesheet loads 304 css::Loader* cssLoader = mDocument->GetExistingCSSLoader(); 305 if (cssLoader && cssLoader->HasPendingLoads()) { 306 cssLoader->AddObserver(this); 307 // wait for those sheets to load 308 startLayout = false; 309 } 310 } 311 312 if (startLayout) { 313 StartLayout(false); 314 315 ScrollToRef(); 316 } 317 318 mDocument->RemoveObserver(this); 319 mIsDocumentObserver = false; 320 321 RefPtr<Document> doc = mDocument; 322 if (!mDeferredLayoutStart && doc->IsBeingUsedAsImage()) { 323 // Eagerly layout image documents, so that layout-triggered loads have a 324 // chance of blocking the load event, see bug 1901414. 325 doc->FlushPendingNotifications(FlushType::Layout); 326 } 327 328 doc->EndLoad(); 329 330 DropParserAndPerfHint(); 331 } 332 333 return NS_OK; 334 } 335 336 nsresult nsXMLContentSink::OnDocumentCreated(Document* aSourceDocument, 337 Document* aResultDocument) { 338 aResultDocument->SetDocWriteDisabled(true); 339 340 nsCOMPtr<nsIDocumentViewer> viewer; 341 mDocShell->GetDocViewer(getter_AddRefs(viewer)); 342 // Make sure that we haven't loaded a new document into the documentviewer 343 // after starting the XSLT transform. 344 if (viewer && viewer->GetDocument() == aSourceDocument) { 345 return viewer->SetDocumentInternal(aResultDocument, true); 346 } 347 return NS_OK; 348 } 349 350 nsresult nsXMLContentSink::OnTransformDone(Document* aSourceDocument, 351 nsresult aResult, 352 Document* aResultDocument) { 353 MOZ_ASSERT(aResultDocument, 354 "Don't notify about transform end without a document."); 355 356 mDocumentChildren.Clear(); 357 358 nsCOMPtr<nsIDocumentViewer> viewer; 359 mDocShell->GetDocViewer(getter_AddRefs(viewer)); 360 361 RefPtr<Document> originalDocument = mDocument; 362 bool blockingOnload = mIsBlockingOnload; 363 364 auto IsXSLTError = [](nsresult aResult, nsIDocumentViewer* aViewer, 365 Document* aResultDocument) -> bool { 366 return NS_FAILED(aResult) && aViewer->GetDocument() && aResultDocument && 367 aViewer->GetDocument()->GetPrincipal() == 368 aResultDocument->GetPrincipal() && 369 aResultDocument->GetDocumentElement() && 370 aResultDocument->GetDocumentElement()->NodeInfo()->Equals( 371 nsGkAtoms::parsererror) && 372 aResultDocument->GetDocumentElement()->NodeInfo()->NamespaceEquals( 373 nsDependentAtomString(nsGkAtoms::nsuri_parsererror)); 374 }; 375 376 // Make sure that we haven't loaded a new document into the documentviewer 377 // after starting the XSLT transform. 378 if (viewer && (viewer->GetDocument() == aSourceDocument || 379 viewer->GetDocument() == aResultDocument || 380 IsXSLTError(aResult, viewer, aResultDocument))) { 381 if (NS_FAILED(aResult)) { 382 // Transform failed. 383 aResultDocument->SetMayStartLayout(false); 384 // We have an error document. 385 viewer->SetDocument(aResultDocument); 386 } 387 388 if (!mRunsToCompletion) { 389 // This BlockOnload call corresponds to the UnblockOnload call in 390 // nsContentSink::DropParserAndPerfHint. 391 aResultDocument->BlockOnload(); 392 mIsBlockingOnload = true; 393 } 394 // Transform succeeded, or it failed and we have an error document to 395 // display. 396 mDocument = aResultDocument; 397 aResultDocument->SetDocWriteDisabled(false); 398 399 // Notify document observers that all the content has been stuck 400 // into the document. 401 // XXX do we need to notify for things like PIs? Or just the 402 // documentElement? 403 nsIContent* rootElement = mDocument->GetRootElement(); 404 if (rootElement) { 405 NS_ASSERTION(mDocument->ComputeIndexOf(rootElement).isSome(), 406 "rootElement not in doc?"); 407 mDocument->BeginUpdate(); 408 MutationObservers::NotifyContentInserted(mDocument, rootElement, {}); 409 mDocument->EndUpdate(); 410 } 411 412 // Start the layout process 413 StartLayout(false); 414 415 ScrollToRef(); 416 } 417 418 originalDocument->EndLoad(); 419 if (blockingOnload) { 420 // This UnblockOnload call corresponds to the BlockOnload call in 421 // nsContentSink::WillBuildModelImpl. 422 originalDocument->UnblockOnload(true); 423 } 424 425 DropParserAndPerfHint(); 426 427 // By this point, the result document has been set in the content viewer. But 428 // the content viewer does not call Destroy on the original document, so we 429 // won't end up reporting document use counters. It's possible we should be 430 // detaching the document from the window, but for now, we call 431 // ReportDocumentUseCounters on the original document here, to avoid 432 // assertions in ~Document about not having reported them. 433 originalDocument->ReportDocumentUseCounters(); 434 435 return NS_OK; 436 } 437 438 NS_IMETHODIMP 439 nsXMLContentSink::StyleSheetLoaded(StyleSheet* aSheet, bool aWasDeferred, 440 nsresult aStatus) { 441 if (!mPrettyPrinting) { 442 return nsContentSink::StyleSheetLoaded(aSheet, aWasDeferred, aStatus); 443 } 444 445 if (mDocument->GetExistingCSSLoader() && 446 !mDocument->GetExistingCSSLoader()->HasPendingLoads()) { 447 mDocument->GetExistingCSSLoader()->RemoveObserver(this); 448 StartLayout(false); 449 ScrollToRef(); 450 } 451 452 return NS_OK; 453 } 454 455 NS_IMETHODIMP 456 nsXMLContentSink::WillInterrupt(void) { return WillInterruptImpl(); } 457 458 void nsXMLContentSink::WillResume() { WillResumeImpl(); } 459 460 NS_IMETHODIMP 461 nsXMLContentSink::SetParser(nsParserBase* aParser) { 462 MOZ_ASSERT(aParser, "Should have a parser here!"); 463 mParser = aParser; 464 return NS_OK; 465 } 466 467 static bool FindIsAttrValue(const char16_t** aAtts, const char16_t** aResult) { 468 RefPtr<nsAtom> prefix, localName; 469 for (; *aAtts; aAtts += 2) { 470 int32_t nameSpaceID; 471 nsContentUtils::SplitExpatName(aAtts[0], getter_AddRefs(prefix), 472 getter_AddRefs(localName), &nameSpaceID); 473 if (nameSpaceID == kNameSpaceID_None && localName == nsGkAtoms::is) { 474 *aResult = aAtts[1]; 475 476 return true; 477 } 478 } 479 480 return false; 481 } 482 483 nsresult nsXMLContentSink::CreateElement( 484 const char16_t** aAtts, uint32_t aAttsCount, 485 mozilla::dom::NodeInfo* aNodeInfo, uint32_t aLineNumber, 486 uint32_t aColumnNumber, nsIContent** aResult, bool* aAppendContent, 487 FromParser aFromParser) { 488 NS_ASSERTION(aNodeInfo, "can't create element without nodeinfo"); 489 490 *aResult = nullptr; 491 *aAppendContent = true; 492 nsresult rv = NS_OK; 493 494 RefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo; 495 RefPtr<Element> element; 496 497 // https://html.spec.whatwg.org/#create-an-element-for-the-token 498 // Step 5: Let is be the value of the "is" attribute in the given token, if 499 // such an attribute exists, or null otherwise. 500 const char16_t* is = nullptr; 501 RefPtr<nsAtom> isAtom; 502 uint32_t namespaceID = ni->NamespaceID(); 503 bool isXHTMLOrXUL = 504 namespaceID == kNameSpaceID_XHTML || namespaceID == kNameSpaceID_XUL; 505 if (isXHTMLOrXUL && FindIsAttrValue(aAtts, &is)) { 506 isAtom = NS_AtomizeMainThread(nsDependentString(is)); 507 } 508 509 // Step 6: Let definition be the result of looking up a custom element 510 // definition given document, given namespace, local name, and is. 511 // Step 7: If definition is non-null and the parser was not created as part of 512 // the HTML fragment parsing algorithm, then let will execute script be true. 513 // Otherwise, let it be false. 514 // 515 // Note that the check that the parser was not created as part of the HTML 516 // fragment parsing algorithm is done by the check for a non-null mDocument. 517 CustomElementDefinition* customElementDefinition = nullptr; 518 nsAtom* nameAtom = ni->NameAtom(); 519 if (mDocument && !mDocument->IsLoadedAsData() && isXHTMLOrXUL && 520 (isAtom || nsContentUtils::IsCustomElementName(nameAtom, namespaceID))) { 521 nsAtom* typeAtom = is ? isAtom.get() : nameAtom; 522 523 MOZ_ASSERT(nameAtom->Equals(ni->LocalName())); 524 customElementDefinition = nsContentUtils::LookupCustomElementDefinition( 525 mDocument, nameAtom, namespaceID, typeAtom); 526 } 527 528 if (customElementDefinition) { 529 // Since we are possibly going to run a script for the custom element 530 // constructor, we should first flush any remaining elements. 531 FlushTags(); 532 { 533 nsAutoMicroTask mt; 534 } 535 536 Maybe<AutoCEReaction> autoCEReaction; 537 if (auto* docGroup = mDocument->GetDocGroup()) { 538 autoCEReaction.emplace(docGroup->CustomElementReactionsStack(), nullptr); 539 } 540 rv = NS_NewElement(getter_AddRefs(element), ni.forget(), aFromParser, 541 isAtom, customElementDefinition); 542 } else { 543 rv = NS_NewElement(getter_AddRefs(element), ni.forget(), aFromParser, 544 isAtom); 545 } 546 NS_ENSURE_SUCCESS(rv, rv); 547 548 if (aNodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_XHTML) || 549 aNodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_SVG)) { 550 if (nsCOMPtr<nsIScriptElement> sele = do_QueryInterface(element)) { 551 sele->SetScriptLineNumber(aLineNumber); 552 sele->SetScriptColumnNumber( 553 JS::ColumnNumberOneOrigin::fromZeroOrigin(aColumnNumber)); 554 sele->SetCreatorParser(GetParser()); 555 } else { 556 MOZ_ASSERT(nsNameSpaceManager::GetInstance()->mSVGDisabled, 557 "Node didn't QI to script, but SVG wasn't disabled."); 558 } 559 } 560 561 // XHTML needs some special attention 562 if (aNodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) { 563 mPrettyPrintHasFactoredElements = true; 564 } else { 565 // If we care, find out if we just used a special factory. 566 if (!mPrettyPrintHasFactoredElements && !mPrettyPrintHasSpecialRoot && 567 mPrettyPrintXML) { 568 mPrettyPrintHasFactoredElements = 569 nsNameSpaceManager::GetInstance()->HasElementCreator( 570 aNodeInfo->NamespaceID()); 571 } 572 573 if (!aNodeInfo->NamespaceEquals(kNameSpaceID_SVG)) { 574 element.forget(aResult); 575 return NS_OK; 576 } 577 } 578 579 if (auto* linkStyle = LinkStyle::FromNode(*element)) { 580 if (aFromParser) { 581 linkStyle->DisableUpdates(); 582 } 583 if (!aNodeInfo->Equals(nsGkAtoms::link, kNameSpaceID_XHTML)) { 584 linkStyle->SetLineNumber(aFromParser ? aLineNumber : 0); 585 linkStyle->SetColumnNumber(aFromParser ? aColumnNumber + 1 : 1); 586 } 587 } 588 589 element.forget(aResult); 590 return NS_OK; 591 } 592 593 nsresult nsXMLContentSink::CloseElement(nsIContent* aContent) { 594 NS_ASSERTION(aContent, "missing element to close"); 595 596 mozilla::dom::NodeInfo* nodeInfo = aContent->NodeInfo(); 597 598 // Some HTML nodes need DoneAddingChildren() called to initialize 599 // properly (eg form state restoration). 600 if (nsIContent::RequiresDoneAddingChildren(nodeInfo->NamespaceID(), 601 nodeInfo->NameAtom())) { 602 aContent->DoneAddingChildren(HaveNotifiedForCurrentContent()); 603 } 604 605 if (IsMonolithicContainer(nodeInfo)) { 606 mInMonolithicContainer--; 607 } 608 609 if (!nodeInfo->NamespaceEquals(kNameSpaceID_XHTML) && 610 !nodeInfo->NamespaceEquals(kNameSpaceID_SVG)) { 611 return NS_OK; 612 } 613 614 if (nodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_XHTML) || 615 nodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_SVG)) { 616 nsCOMPtr<nsIScriptElement> sele = do_QueryInterface(aContent); 617 if (!sele) { 618 MOZ_ASSERT(nsNameSpaceManager::GetInstance()->mSVGDisabled, 619 "Node didn't QI to script, but SVG wasn't disabled."); 620 return NS_OK; 621 } 622 623 if (mPreventScriptExecution) { 624 sele->PreventExecution(); 625 return NS_OK; 626 } 627 628 // Always check the clock in nsContentSink right after a script 629 StopDeflecting(); 630 631 // Flush any previously parsed elements before executing a script, in order 632 // to prevent a script that adds a mutation observer from observing that 633 // script element being adding to the tree. 634 FlushTags(); 635 636 // https://html.spec.whatwg.org/#parsing-xhtml-documents 637 // When the element's end tag is subsequently parsed, the user agent must 638 // perform a microtask checkpoint, and then prepare the script element. 639 { 640 nsAutoMicroTask mt; 641 } 642 643 // Now tell the script that it's ready to go. This may execute the script 644 // or return true, or neither if the script doesn't need executing. 645 bool block = sele->AttemptToExecute(GetParser()); 646 647 // If the parser got blocked, make sure to return the appropriate rv. 648 // I'm not sure if this is actually needed or not. 649 if (mParser && !mParser->IsParserEnabled()) { 650 block = true; 651 } 652 653 return block ? NS_ERROR_HTMLPARSER_BLOCK : NS_OK; 654 } 655 656 nsresult rv = NS_OK; 657 if (auto* linkStyle = LinkStyle::FromNode(*aContent)) { 658 auto updateOrError = linkStyle->EnableUpdatesAndUpdateStyleSheet( 659 mRunsToCompletion ? nullptr : this); 660 if (updateOrError.isErr()) { 661 rv = updateOrError.unwrapErr(); 662 } else if (updateOrError.unwrap().ShouldBlock() && !mRunsToCompletion) { 663 ++mPendingSheetCount; 664 if (mScriptLoader) { 665 mScriptLoader->AddParserBlockingScriptExecutionBlocker(); 666 } 667 } 668 } 669 670 return rv; 671 } 672 673 nsresult nsXMLContentSink::AddContentAsLeaf(nsIContent* aContent) { 674 nsresult result = NS_OK; 675 676 if (mState == eXMLContentSinkState_InProlog) { 677 NS_ASSERTION(mDocument, "Fragments have no prolog"); 678 mDocumentChildren.AppendElement(aContent); 679 } else if (mState == eXMLContentSinkState_InEpilog) { 680 NS_ASSERTION(mDocument, "Fragments have no epilog"); 681 if (mXSLTProcessor) { 682 mDocumentChildren.AppendElement(aContent); 683 } else { 684 mDocument->AppendChildTo(aContent, false, IgnoreErrors()); 685 } 686 } else { 687 nsCOMPtr<nsIContent> parent = GetCurrentContent(); 688 689 if (parent) { 690 ErrorResult rv; 691 parent->AppendChildTo(aContent, false, rv); 692 result = rv.StealNSResult(); 693 } 694 } 695 return result; 696 } 697 698 // Create an XML parser and an XSL content sink and start parsing 699 // the XSL stylesheet located at the given URI. 700 nsresult nsXMLContentSink::LoadXSLStyleSheet(nsIURI* aUrl) { 701 nsCOMPtr<nsIDocumentTransformer> processor = new txMozillaXSLTProcessor(); 702 mDocument->SetUseCounter(eUseCounter_custom_XSLStylesheet); 703 mDocument->WarnOnceAbout(DeprecatedOperations::eXSLTDeprecated); 704 705 processor->SetTransformObserver(this); 706 707 if (NS_SUCCEEDED(processor->LoadStyleSheet(aUrl, mDocument))) { 708 mXSLTProcessor.swap(processor); 709 } 710 711 // Intentionally ignore errors here, we should continue loading the 712 // XML document whether we're able to load the XSLT stylesheet or 713 // not. 714 715 return NS_OK; 716 } 717 718 nsresult nsXMLContentSink::ProcessStyleLinkFromHeader( 719 const nsAString& aHref, bool aAlternate, const nsAString& aTitle, 720 const nsAString& aIntegrity, const nsAString& aType, 721 const nsAString& aMedia, const nsAString& aReferrerPolicy, 722 const nsAString& aFetchPriority) { 723 mPrettyPrintXML = false; 724 725 nsAutoCString cmd; 726 if (mParser) GetParser()->GetCommand(cmd); 727 if (cmd.EqualsASCII(kLoadAsData)) 728 return NS_OK; // Do not load stylesheets when loading as data 729 730 bool wasXSLT; 731 nsresult rv = MaybeProcessXSLTLink(nullptr, aHref, aAlternate, aType, aType, 732 aMedia, aReferrerPolicy, &wasXSLT); 733 NS_ENSURE_SUCCESS(rv, rv); 734 if (wasXSLT) { 735 // We're done here. 736 return NS_OK; 737 } 738 739 // Otherwise fall through to nsContentSink to handle CSS Link headers. 740 return nsContentSink::ProcessStyleLinkFromHeader( 741 aHref, aAlternate, aTitle, aIntegrity, aType, aMedia, aReferrerPolicy, 742 aFetchPriority); 743 } 744 745 nsresult nsXMLContentSink::MaybeProcessXSLTLink( 746 ProcessingInstruction* aProcessingInstruction, const nsAString& aHref, 747 bool aAlternate, const nsAString& aTitle, const nsAString& aType, 748 const nsAString& aMedia, const nsAString& aReferrerPolicy, bool* aWasXSLT) { 749 bool wasXSLT = aType.LowerCaseEqualsLiteral(TEXT_XSL) || 750 aType.LowerCaseEqualsLiteral(APPLICATION_XSLT_XML) || 751 aType.LowerCaseEqualsLiteral(TEXT_XML) || 752 aType.LowerCaseEqualsLiteral(APPLICATION_XML); 753 754 if (aWasXSLT) { 755 *aWasXSLT = wasXSLT; 756 } 757 758 if (!wasXSLT) { 759 return NS_OK; 760 } 761 762 if (aAlternate) { 763 // don't load alternate XSLT 764 return NS_OK; 765 } 766 // LoadXSLStyleSheet needs a mDocShell. 767 if (!mDocShell) { 768 return NS_OK; 769 } 770 771 nsCOMPtr<nsIURI> url; 772 nsresult rv = NS_NewURI(getter_AddRefs(url), aHref, nullptr, 773 mDocument->GetDocBaseURI()); 774 NS_ENSURE_SUCCESS(rv, rv); 775 776 // Do security check 777 nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager(); 778 rv = secMan->CheckLoadURIWithPrincipal(mDocument->NodePrincipal(), url, 779 nsIScriptSecurityManager::ALLOW_CHROME, 780 mDocument->InnerWindowID()); 781 NS_ENSURE_SUCCESS(rv, NS_OK); 782 783 nsCOMPtr<nsILoadInfo> secCheckLoadInfo = MOZ_TRY( 784 net::LoadInfo::Create(mDocument->NodePrincipal(), // loading principal 785 mDocument->NodePrincipal(), // triggering principal 786 aProcessingInstruction, 787 nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK, 788 nsIContentPolicy::TYPE_XSLT)); 789 790 // Do content policy check 791 int16_t decision = nsIContentPolicy::ACCEPT; 792 rv = NS_CheckContentLoadPolicy(url, secCheckLoadInfo, &decision, 793 nsContentUtils::GetContentPolicy()); 794 795 NS_ENSURE_SUCCESS(rv, rv); 796 797 if (NS_CP_REJECTED(decision)) { 798 return NS_OK; 799 } 800 801 return LoadXSLStyleSheet(url); 802 } 803 804 void nsXMLContentSink::SetDocumentCharset(NotNull<const Encoding*> aEncoding) { 805 if (mDocument) { 806 mDocument->SetDocumentCharacterSet(aEncoding); 807 } 808 } 809 810 nsISupports* nsXMLContentSink::GetTarget() { return ToSupports(mDocument); } 811 812 nsresult nsXMLContentSink::FlushText(bool aReleaseTextNode) { 813 nsresult rv = NS_OK; 814 815 if (mTextLength != 0) { 816 if (mLastTextNode) { 817 bool notify = HaveNotifiedForCurrentContent(); 818 // We could probably always increase mInNotification here since 819 // if AppendText doesn't notify it shouldn't trigger evil code. 820 // But just in case it does, we don't want to mask any notifications. 821 if (notify) { 822 ++mInNotification; 823 } 824 rv = mLastTextNode->AppendText(mText, mTextLength, notify); 825 if (notify) { 826 --mInNotification; 827 } 828 829 mTextLength = 0; 830 } else { 831 RefPtr<nsTextNode> textContent = 832 new (mNodeInfoManager) nsTextNode(mNodeInfoManager); 833 834 mLastTextNode = textContent; 835 836 // Set the text in the text node 837 textContent->SetText(mText, mTextLength, false); 838 mTextLength = 0; 839 840 // Add text to its parent 841 rv = AddContentAsLeaf(textContent); 842 } 843 } 844 845 if (aReleaseTextNode) { 846 mLastTextNode = nullptr; 847 } 848 849 return rv; 850 } 851 852 nsIContent* nsXMLContentSink::GetCurrentContent() { 853 if (mContentStack.Length() == 0) { 854 return nullptr; 855 } 856 return GetCurrentStackNode()->mContent; 857 } 858 859 nsXMLContentSink::StackNode* nsXMLContentSink::GetCurrentStackNode() { 860 int32_t count = mContentStack.Length(); 861 return count != 0 ? &mContentStack[count - 1] : nullptr; 862 } 863 864 nsresult nsXMLContentSink::PushContent(nsIContent* aContent) { 865 MOZ_ASSERT(aContent, "Null content being pushed!"); 866 StackNode* sn = mContentStack.AppendElement(); 867 NS_ENSURE_TRUE(sn, NS_ERROR_OUT_OF_MEMORY); 868 869 nsIContent* contentToPush = aContent; 870 871 // When an XML parser would append a node to a template element, it 872 // must instead append it to the template element's template contents. 873 if (contentToPush->IsHTMLElement(nsGkAtoms::_template)) { 874 HTMLTemplateElement* templateElement = 875 static_cast<HTMLTemplateElement*>(contentToPush); 876 contentToPush = templateElement->Content(); 877 } 878 879 sn->mContent = contentToPush; 880 sn->mNumFlushed = 0; 881 return NS_OK; 882 } 883 884 void nsXMLContentSink::PopContent() { 885 if (mContentStack.IsEmpty()) { 886 NS_WARNING("Popping empty stack"); 887 return; 888 } 889 890 mContentStack.RemoveLastElement(); 891 } 892 893 bool nsXMLContentSink::HaveNotifiedForCurrentContent() const { 894 uint32_t stackLength = mContentStack.Length(); 895 if (stackLength) { 896 const StackNode& stackNode = mContentStack[stackLength - 1]; 897 nsIContent* parent = stackNode.mContent; 898 return stackNode.mNumFlushed == parent->GetChildCount(); 899 } 900 return true; 901 } 902 903 void nsXMLContentSink::MaybeStartLayout(bool aIgnorePendingSheets) { 904 // XXXbz if aIgnorePendingSheets is true, what should we do when 905 // mXSLTProcessor or CanStillPrettyPrint()? 906 if (mLayoutStarted || mXSLTProcessor || CanStillPrettyPrint()) { 907 return; 908 } 909 StartLayout(aIgnorePendingSheets); 910 } 911 912 //////////////////////////////////////////////////////////////////////// 913 914 bool nsXMLContentSink::SetDocElement(int32_t aNameSpaceID, nsAtom* aTagName, 915 nsIContent* aContent) { 916 if (mDocElement) return false; 917 918 mDocElement = aContent; 919 920 if (mXSLTProcessor) { 921 mDocumentChildren.AppendElement(aContent); 922 return true; 923 } 924 925 auto documentChildren = std::move(mDocumentChildren); 926 MOZ_ASSERT(mDocumentChildren.IsEmpty()); 927 for (nsIContent* child : documentChildren) { 928 auto* linkStyle = LinkStyle::FromNode(*child); 929 if (linkStyle) { 930 linkStyle->DisableUpdates(); 931 } 932 mDocument->AppendChildTo(child, false, IgnoreErrors()); 933 if (linkStyle) { 934 auto updateOrError = linkStyle->EnableUpdatesAndUpdateStyleSheet( 935 mRunsToCompletion ? nullptr : this); 936 if (updateOrError.isErr()) { 937 continue; 938 } 939 auto update = updateOrError.unwrap(); 940 // Successfully started a stylesheet load 941 if (update.ShouldBlock() && !mRunsToCompletion) { 942 ++mPendingSheetCount; 943 if (mScriptLoader) { 944 mScriptLoader->AddParserBlockingScriptExecutionBlocker(); 945 } 946 } 947 } 948 } 949 950 // check for root elements that needs special handling for 951 // prettyprinting 952 if (aNameSpaceID == kNameSpaceID_XSLT && 953 (aTagName == nsGkAtoms::stylesheet || aTagName == nsGkAtoms::transform)) { 954 mPrettyPrintHasSpecialRoot = true; 955 if (mPrettyPrintXML) { 956 // In this case, disable script execution, stylesheet 957 // loading, and auto XLinks since we plan to prettyprint. 958 if (dom::ScriptLoader* scriptLoader = mDocument->GetScriptLoader()) { 959 scriptLoader->SetEnabled(false); 960 } 961 // Sadly, we need to create the CSSLoader to disable it so that 962 // something else doesn't create it in an enabled state after 963 // this point but before it is OK to re-enable. 964 mDocument->EnsureCSSLoader().SetEnabled(false); 965 } 966 } 967 968 IgnoredErrorResult rv; 969 mDocument->AppendChildTo(mDocElement, NotifyForDocElement(), rv); 970 if (rv.Failed()) { 971 // If we return false here, the caller will bail out because it won't 972 // find a parent content node to append to, which is fine. 973 return false; 974 } 975 976 return true; 977 } 978 979 NS_IMETHODIMP 980 nsXMLContentSink::HandleStartElement(const char16_t* aName, 981 const char16_t** aAtts, 982 uint32_t aAttsCount, uint32_t aLineNumber, 983 uint32_t aColumnNumber) { 984 return HandleStartElement(aName, aAtts, aAttsCount, aLineNumber, 985 aColumnNumber, true); 986 } 987 988 nsresult nsXMLContentSink::HandleStartElement( 989 const char16_t* aName, const char16_t** aAtts, uint32_t aAttsCount, 990 uint32_t aLineNumber, uint32_t aColumnNumber, bool aInterruptable) { 991 MOZ_ASSERT(aAttsCount % 2 == 0, "incorrect aAttsCount"); 992 // Adjust aAttsCount so it's the actual number of attributes 993 aAttsCount /= 2; 994 995 nsresult result = NS_OK; 996 bool appendContent = true; 997 nsCOMPtr<nsIContent> content; 998 999 // XXX Hopefully the parser will flag this before we get 1000 // here. If we're in the epilog, there should be no 1001 // new elements 1002 MOZ_ASSERT(eXMLContentSinkState_InEpilog != mState); 1003 1004 FlushText(); 1005 DidAddContent(); 1006 1007 mState = eXMLContentSinkState_InDocumentElement; 1008 1009 int32_t nameSpaceID; 1010 RefPtr<nsAtom> prefix, localName; 1011 nsContentUtils::SplitExpatName(aName, getter_AddRefs(prefix), 1012 getter_AddRefs(localName), &nameSpaceID); 1013 1014 if (!OnOpenContainer(aAtts, aAttsCount, nameSpaceID, localName, 1015 aLineNumber)) { 1016 return NS_OK; 1017 } 1018 1019 RefPtr<mozilla::dom::NodeInfo> nodeInfo; 1020 nodeInfo = mNodeInfoManager->GetNodeInfo(localName, prefix, nameSpaceID, 1021 nsINode::ELEMENT_NODE); 1022 1023 result = CreateElement(aAtts, aAttsCount, nodeInfo, aLineNumber, 1024 aColumnNumber, getter_AddRefs(content), &appendContent, 1025 FROM_PARSER_NETWORK); 1026 NS_ENSURE_SUCCESS(result, result); 1027 1028 // Have to do this before we push the new content on the stack... and have to 1029 // do that before we set attributes, call BindToTree, etc. Ideally we'd push 1030 // on the stack inside CreateElement (which is effectively what the HTML sink 1031 // does), but that's hard with all the subclass overrides going on. 1032 nsCOMPtr<nsIContent> parent = GetCurrentContent(); 1033 1034 result = PushContent(content); 1035 NS_ENSURE_SUCCESS(result, result); 1036 1037 // Set the attributes on the new content element 1038 result = AddAttributes(aAtts, content->AsElement()); 1039 1040 if (NS_OK == result) { 1041 // Store the element 1042 if (!SetDocElement(nameSpaceID, localName, content) && appendContent) { 1043 NS_ENSURE_TRUE(parent, NS_ERROR_UNEXPECTED); 1044 1045 parent->AppendChildTo(content, false, IgnoreErrors()); 1046 } 1047 } 1048 1049 // Some HTML nodes need DoneCreatingElement() called to initialize 1050 // properly (eg form state restoration). 1051 if (nsIContent::RequiresDoneCreatingElement(nodeInfo->NamespaceID(), 1052 nodeInfo->NameAtom())) { 1053 content->DoneCreatingElement(); 1054 } 1055 1056 if (nodeInfo->NamespaceID() == kNameSpaceID_XHTML && 1057 nodeInfo->NameAtom() == nsGkAtoms::head && !mCurrentHead) { 1058 mCurrentHead = content; 1059 } 1060 1061 if (IsMonolithicContainer(nodeInfo)) { 1062 mInMonolithicContainer++; 1063 } 1064 1065 if (!mXSLTProcessor) { 1066 if (content == mDocElement) { 1067 nsContentUtils::AddScriptRunner( 1068 new nsDocElementCreatedNotificationRunner(mDocument)); 1069 1070 if (aInterruptable && NS_SUCCEEDED(result) && mParser && 1071 !mParser->IsParserEnabled()) { 1072 return NS_ERROR_HTMLPARSER_BLOCK; 1073 } 1074 } else if (!mCurrentHead) { 1075 // This isn't the root and we're not inside an XHTML <head>. 1076 // Might need to start layout 1077 MaybeStartLayout(false); 1078 } 1079 } 1080 1081 return aInterruptable && NS_SUCCEEDED(result) ? DidProcessATokenImpl() 1082 : result; 1083 } 1084 1085 NS_IMETHODIMP 1086 nsXMLContentSink::HandleEndElement(const char16_t* aName) { 1087 return HandleEndElement(aName, true); 1088 } 1089 1090 nsresult nsXMLContentSink::HandleEndElement(const char16_t* aName, 1091 bool aInterruptable) { 1092 nsresult result = NS_OK; 1093 1094 // XXX Hopefully the parser will flag this before we get 1095 // here. If we're in the prolog or epilog, there should be 1096 // no close tags for elements. 1097 MOZ_ASSERT(eXMLContentSinkState_InDocumentElement == mState); 1098 1099 FlushText(); 1100 1101 StackNode* sn = GetCurrentStackNode(); 1102 if (!sn) { 1103 return NS_ERROR_UNEXPECTED; 1104 } 1105 1106 nsCOMPtr<nsIContent> content; 1107 sn->mContent.swap(content); 1108 uint32_t numFlushed = sn->mNumFlushed; 1109 1110 PopContent(); 1111 NS_ASSERTION(content, "failed to pop content"); 1112 #ifdef DEBUG 1113 // Check that we're closing the right thing 1114 RefPtr<nsAtom> debugNameSpacePrefix, debugTagAtom; 1115 int32_t debugNameSpaceID; 1116 nsContentUtils::SplitExpatName(aName, getter_AddRefs(debugNameSpacePrefix), 1117 getter_AddRefs(debugTagAtom), 1118 &debugNameSpaceID); 1119 // Check if we are closing a template element because template 1120 // elements do not get pushed on the stack, the template 1121 // element content is pushed instead. 1122 bool isTemplateElement = debugTagAtom == nsGkAtoms::_template && 1123 debugNameSpaceID == kNameSpaceID_XHTML; 1124 NS_ASSERTION( 1125 content->NodeInfo()->Equals(debugTagAtom, debugNameSpaceID) || 1126 (debugNameSpaceID == kNameSpaceID_MathML && 1127 content->NodeInfo()->NamespaceID() == kNameSpaceID_disabled_MathML && 1128 content->NodeInfo()->Equals(debugTagAtom)) || 1129 (debugNameSpaceID == kNameSpaceID_SVG && 1130 content->NodeInfo()->NamespaceID() == kNameSpaceID_disabled_SVG && 1131 content->NodeInfo()->Equals(debugTagAtom)) || 1132 isTemplateElement, 1133 "Wrong element being closed"); 1134 #endif 1135 1136 // Make sure to notify on our kids before we call out to any other code that 1137 // might reenter us and call FlushTags, in a state in which we've already 1138 // popped "content" from the stack but haven't notified on its kids yet. 1139 int32_t stackLen = mContentStack.Length(); 1140 if (mNotifyLevel >= stackLen) { 1141 if (numFlushed < content->GetChildCount()) { 1142 NotifyAppend(content, numFlushed); 1143 } 1144 mNotifyLevel = stackLen - 1; 1145 } 1146 1147 result = CloseElement(content); 1148 1149 if (mCurrentHead == content) { 1150 mCurrentHead = nullptr; 1151 } 1152 1153 if (mDocElement == content) { 1154 // XXXbz for roots that don't want to be appended on open, we 1155 // probably need to deal here.... (and stop appending them on open). 1156 mState = eXMLContentSinkState_InEpilog; 1157 1158 mDocument->OnParsingCompleted(); 1159 1160 // We might have had no occasion to start layout yet. Do so now. 1161 MaybeStartLayout(false); 1162 } 1163 1164 DidAddContent(); 1165 1166 if (content->IsSVGElement(nsGkAtoms::svg)) { 1167 FlushTags(); 1168 nsCOMPtr<nsIRunnable> event = new nsHtml5SVGLoadDispatcher(content); 1169 if (NS_FAILED(content->OwnerDoc()->Dispatch(event.forget()))) { 1170 NS_WARNING("failed to dispatch svg load dispatcher"); 1171 } 1172 } 1173 1174 return aInterruptable && NS_SUCCEEDED(result) ? DidProcessATokenImpl() 1175 : result; 1176 } 1177 1178 NS_IMETHODIMP 1179 nsXMLContentSink::HandleComment(const char16_t* aName) { 1180 FlushText(); 1181 1182 RefPtr<Comment> comment = new (mNodeInfoManager) Comment(mNodeInfoManager); 1183 comment->SetText(nsDependentString(aName), false); 1184 nsresult rv = AddContentAsLeaf(comment); 1185 DidAddContent(); 1186 1187 return NS_SUCCEEDED(rv) ? DidProcessATokenImpl() : rv; 1188 } 1189 1190 NS_IMETHODIMP 1191 nsXMLContentSink::HandleCDataSection(const char16_t* aData, uint32_t aLength) { 1192 // XSLT doesn't differentiate between text and cdata and wants adjacent 1193 // textnodes merged, so add as text. 1194 if (mXSLTProcessor) { 1195 return AddText(aData, aLength); 1196 } 1197 1198 FlushText(); 1199 1200 RefPtr<CDATASection> cdata = 1201 new (mNodeInfoManager) CDATASection(mNodeInfoManager); 1202 cdata->SetText(aData, aLength, false); 1203 nsresult rv = AddContentAsLeaf(cdata); 1204 DidAddContent(); 1205 1206 return NS_SUCCEEDED(rv) ? DidProcessATokenImpl() : rv; 1207 } 1208 1209 NS_IMETHODIMP 1210 nsXMLContentSink::HandleDoctypeDecl(const nsAString& aSubset, 1211 const nsAString& aName, 1212 const nsAString& aSystemId, 1213 const nsAString& aPublicId, 1214 nsISupports* aCatalogData) { 1215 FlushText(); 1216 1217 NS_ASSERTION(mDocument, "Shouldn't get here from a document fragment"); 1218 1219 RefPtr<nsAtom> name = NS_Atomize(aName); 1220 NS_ENSURE_TRUE(name, NS_ERROR_OUT_OF_MEMORY); 1221 1222 // Create a new doctype node 1223 RefPtr<DocumentType> docType = NS_NewDOMDocumentType( 1224 mNodeInfoManager, name, aPublicId, aSystemId, aSubset); 1225 1226 MOZ_ASSERT(!aCatalogData, 1227 "Need to add back support for catalog style " 1228 "sheets"); 1229 1230 mDocumentChildren.AppendElement(docType); 1231 DidAddContent(); 1232 return DidProcessATokenImpl(); 1233 } 1234 1235 NS_IMETHODIMP 1236 nsXMLContentSink::HandleCharacterData(const char16_t* aData, uint32_t aLength) { 1237 return HandleCharacterData(aData, aLength, true); 1238 } 1239 1240 nsresult nsXMLContentSink::HandleCharacterData(const char16_t* aData, 1241 uint32_t aLength, 1242 bool aInterruptable) { 1243 nsresult rv = NS_OK; 1244 if (aData && mState != eXMLContentSinkState_InProlog && 1245 mState != eXMLContentSinkState_InEpilog) { 1246 rv = AddText(aData, aLength); 1247 } 1248 return aInterruptable && NS_SUCCEEDED(rv) ? DidProcessATokenImpl() : rv; 1249 } 1250 1251 NS_IMETHODIMP 1252 nsXMLContentSink::HandleProcessingInstruction(const char16_t* aTarget, 1253 const char16_t* aData) { 1254 FlushText(); 1255 1256 const nsDependentString target(aTarget); 1257 const nsDependentString data(aData); 1258 1259 RefPtr<ProcessingInstruction> node = 1260 NS_NewXMLProcessingInstruction(mNodeInfoManager, target, data); 1261 1262 if (LinkStyle::FromNode(*node)) { 1263 // TODO(emilio): can we move this check to SetDocElement? 1264 mPrettyPrintXML = false; 1265 } 1266 1267 nsresult rv = AddContentAsLeaf(node); 1268 NS_ENSURE_SUCCESS(rv, rv); 1269 DidAddContent(); 1270 1271 // Handles the special chrome-only <?csp ?> PI, which will be handled before 1272 // creating any element with potential inline style or scripts. 1273 if (mState == eXMLContentSinkState_InProlog && target.EqualsLiteral("csp") && 1274 mDocument->NodePrincipal()->IsSystemPrincipal()) { 1275 CSP_ApplyMetaCSPToDoc(*mDocument, data); 1276 } 1277 1278 // Check whether this is a CSS stylesheet PI. Make sure the type 1279 // handling here matches 1280 // XMLStylesheetProcessingInstruction::GetStyleSheetInfo. 1281 nsAutoString type; 1282 nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::type, type); 1283 nsAutoString mimeType, notUsed; 1284 nsContentUtils::SplitMimeType(type, mimeType, notUsed); 1285 1286 if (mState != eXMLContentSinkState_InProlog || 1287 !target.EqualsLiteral("xml-stylesheet") || mimeType.IsEmpty() || 1288 mimeType.LowerCaseEqualsLiteral("text/css")) { 1289 // Either not a useful stylesheet PI, or a regular CSS stylesheet PI that 1290 // will get handled when appending mDocumentChildren. 1291 return DidProcessATokenImpl(); 1292 } 1293 1294 // If it's not a CSS stylesheet PI... 1295 nsAutoString href, title, media; 1296 bool isAlternate = false; 1297 1298 // If there was no href, we can't do anything with this PI 1299 if (!ParsePIData(data, href, title, media, isAlternate)) { 1300 return DidProcessATokenImpl(); 1301 } 1302 1303 // <?xml-stylesheet?> processing instructions don't have a referrerpolicy 1304 // pseudo-attribute, so we pass in an empty string 1305 rv = 1306 MaybeProcessXSLTLink(node, href, isAlternate, title, type, media, u""_ns); 1307 return NS_SUCCEEDED(rv) ? DidProcessATokenImpl() : rv; 1308 } 1309 1310 /* static */ 1311 bool nsXMLContentSink::ParsePIData(const nsString& aData, nsString& aHref, 1312 nsString& aTitle, nsString& aMedia, 1313 bool& aIsAlternate) { 1314 // If there was no href, we can't do anything with this PI 1315 if (!nsContentUtils::GetPseudoAttributeValue(aData, nsGkAtoms::href, aHref)) { 1316 return false; 1317 } 1318 1319 nsContentUtils::GetPseudoAttributeValue(aData, nsGkAtoms::title, aTitle); 1320 1321 nsContentUtils::GetPseudoAttributeValue(aData, nsGkAtoms::media, aMedia); 1322 1323 nsAutoString alternate; 1324 nsContentUtils::GetPseudoAttributeValue(aData, nsGkAtoms::alternate, 1325 alternate); 1326 1327 aIsAlternate = alternate.EqualsLiteral("yes"); 1328 1329 return true; 1330 } 1331 1332 NS_IMETHODIMP 1333 nsXMLContentSink::HandleXMLDeclaration(const char16_t* aVersion, 1334 const char16_t* aEncoding, 1335 int32_t aStandalone) { 1336 mDocument->SetXMLDeclaration(aVersion, aEncoding, aStandalone); 1337 1338 return DidProcessATokenImpl(); 1339 } 1340 1341 NS_IMETHODIMP 1342 nsXMLContentSink::ReportError(const char16_t* aErrorText, 1343 const char16_t* aSourceText, 1344 nsIScriptError* aError, bool* _retval) { 1345 MOZ_ASSERT(aError && aSourceText && aErrorText, "Check arguments!!!"); 1346 nsresult rv = NS_OK; 1347 1348 // The expat driver should report the error. We're just cleaning up the mess. 1349 *_retval = true; 1350 1351 mPrettyPrintXML = false; 1352 1353 mState = eXMLContentSinkState_InProlog; 1354 1355 // XXX need to stop scripts here -- hsivonen 1356 1357 // stop observing in order to avoid crashing when removing content 1358 mDocument->RemoveObserver(this); 1359 mIsDocumentObserver = false; 1360 1361 // Clear the current content 1362 mDocumentChildren.Clear(); 1363 while (mDocument->GetLastChild()) { 1364 mDocument->GetLastChild()->Remove(); 1365 } 1366 mDocElement = nullptr; 1367 1368 // Clear any buffered-up text we have. It's enough to set the length to 0. 1369 // The buffer itself is allocated when we're created and deleted in our 1370 // destructor, so don't mess with it. 1371 mTextLength = 0; 1372 1373 if (mXSLTProcessor) { 1374 // Get rid of the XSLT processor. 1375 mXSLTProcessor->CancelLoads(); 1376 mXSLTProcessor = nullptr; 1377 } 1378 1379 // release the nodes on stack 1380 mContentStack.Clear(); 1381 mNotifyLevel = 0; 1382 1383 // return leaving the document empty if we're asked to not add a <parsererror> 1384 // root node 1385 if (mDocument->SuppressParserErrorElement()) { 1386 return NS_OK; 1387 } 1388 1389 // prepare to set <parsererror> as the document root 1390 1391 constexpr auto errorNs = 1392 u"http://www.mozilla.org/newlayout/xml/parsererror.xml"_ns; 1393 1394 nsAutoString parsererror(errorNs); 1395 parsererror.Append((char16_t)0xFFFF); 1396 parsererror.AppendLiteral("parsererror"); 1397 1398 const char16_t* dirAttr[] = {u"dir", u"ltr", 0, 0}; 1399 if (intl::LocaleService::GetInstance()->IsAppLocaleRTL() && 1400 !mDocument->ShouldResistFingerprinting(RFPTarget::JSLocale)) { 1401 dirAttr[1] = u"rtl"; 1402 } 1403 rv = HandleStartElement(parsererror.get(), dirAttr, 0, 2, 0); 1404 NS_ENSURE_SUCCESS(rv, rv); 1405 1406 rv = HandleCharacterData(aErrorText, NS_strlen(aErrorText), false); 1407 NS_ENSURE_SUCCESS(rv, rv); 1408 1409 nsAutoString sourcetext(errorNs); 1410 sourcetext.Append((char16_t)0xFFFF); 1411 sourcetext.AppendLiteral("sourcetext"); 1412 1413 const char16_t* noAtts[] = {0, 0}; 1414 rv = HandleStartElement(sourcetext.get(), noAtts, 0, (uint32_t)-1, 0); 1415 NS_ENSURE_SUCCESS(rv, rv); 1416 1417 rv = HandleCharacterData(aSourceText, NS_strlen(aSourceText), false); 1418 NS_ENSURE_SUCCESS(rv, rv); 1419 1420 rv = HandleEndElement(sourcetext.get(), false); 1421 NS_ENSURE_SUCCESS(rv, rv); 1422 1423 rv = HandleEndElement(parsererror.get(), false); 1424 NS_ENSURE_SUCCESS(rv, rv); 1425 1426 FlushTags(); 1427 1428 return NS_OK; 1429 } 1430 1431 nsresult nsXMLContentSink::AddAttributes(const char16_t** aAtts, 1432 Element* aContent) { 1433 // Add tag attributes to the content attributes 1434 RefPtr<nsAtom> prefix, localName; 1435 while (*aAtts) { 1436 int32_t nameSpaceID; 1437 nsContentUtils::SplitExpatName(aAtts[0], getter_AddRefs(prefix), 1438 getter_AddRefs(localName), &nameSpaceID); 1439 1440 // Add attribute to content 1441 aContent->SetAttr(nameSpaceID, localName, prefix, 1442 nsDependentString(aAtts[1]), false); 1443 aAtts += 2; 1444 } 1445 1446 return NS_OK; 1447 } 1448 1449 #define NS_ACCUMULATION_BUFFER_SIZE 4096 1450 1451 nsresult nsXMLContentSink::AddText(const char16_t* aText, int32_t aLength) { 1452 // Copy data from string into our buffer; flush buffer when it fills up. 1453 int32_t offset = 0; 1454 while (0 != aLength) { 1455 int32_t amount = NS_ACCUMULATION_BUFFER_SIZE - mTextLength; 1456 if (0 == amount) { 1457 nsresult rv = FlushText(false); 1458 if (NS_WARN_IF(NS_FAILED(rv))) { 1459 return rv; 1460 } 1461 MOZ_ASSERT(mTextLength == 0); 1462 amount = NS_ACCUMULATION_BUFFER_SIZE; 1463 } 1464 1465 if (amount > aLength) { 1466 amount = aLength; 1467 } 1468 memcpy(&mText[mTextLength], &aText[offset], sizeof(char16_t) * amount); 1469 mTextLength += amount; 1470 offset += amount; 1471 aLength -= amount; 1472 } 1473 1474 return NS_OK; 1475 } 1476 1477 void nsXMLContentSink::InitialTranslationCompleted() { StartLayout(false); } 1478 1479 void nsXMLContentSink::FlushPendingNotifications(FlushType aType) { 1480 // Only flush tags if we're not doing the notification ourselves 1481 // (since we aren't reentrant) 1482 if (!mInNotification) { 1483 if (mIsDocumentObserver) { 1484 // Only flush if we're still a document observer (so that our child 1485 // counts should be correct). 1486 if (aType >= FlushType::ContentAndNotify) { 1487 FlushTags(); 1488 } else { 1489 FlushText(false); 1490 } 1491 } 1492 if (aType >= FlushType::EnsurePresShellInitAndFrames) { 1493 // Make sure that layout has started so that the reflow flush 1494 // will actually happen. 1495 MaybeStartLayout(true); 1496 } 1497 } 1498 } 1499 1500 /** 1501 * NOTE!! Forked from SinkContext. Please keep in sync. 1502 * 1503 * Flush all elements that have been seen so far such that 1504 * they are visible in the tree. Specifically, make sure 1505 * that they are all added to their respective parents. 1506 * Also, do notification at the top for all content that 1507 * has been newly added so that the frame tree is complete. 1508 */ 1509 nsresult nsXMLContentSink::FlushTags() { 1510 mDeferredFlushTags = false; 1511 uint32_t oldUpdates = mUpdatesInNotification; 1512 1513 mUpdatesInNotification = 0; 1514 ++mInNotification; 1515 { 1516 // Scope so we call EndUpdate before we decrease mInNotification 1517 mozAutoDocUpdate updateBatch(mDocument, true); 1518 1519 // Don't release last text node in case we need to add to it again 1520 FlushText(false); 1521 1522 // Start from the base of the stack (growing downward) and do 1523 // a notification from the node that is closest to the root of 1524 // tree for any content that has been added. 1525 1526 int32_t stackPos; 1527 int32_t stackLen = mContentStack.Length(); 1528 bool flushed = false; 1529 uint32_t childCount; 1530 nsIContent* content; 1531 1532 for (stackPos = 0; stackPos < stackLen; ++stackPos) { 1533 content = mContentStack[stackPos].mContent; 1534 childCount = content->GetChildCount(); 1535 1536 if (!flushed && (mContentStack[stackPos].mNumFlushed < childCount)) { 1537 NotifyAppend(content, mContentStack[stackPos].mNumFlushed); 1538 flushed = true; 1539 } 1540 1541 mContentStack[stackPos].mNumFlushed = childCount; 1542 } 1543 mNotifyLevel = stackLen - 1; 1544 } 1545 --mInNotification; 1546 1547 if (mUpdatesInNotification > 1) { 1548 UpdateChildCounts(); 1549 } 1550 1551 mUpdatesInNotification = oldUpdates; 1552 return NS_OK; 1553 } 1554 1555 /** 1556 * NOTE!! Forked from SinkContext. Please keep in sync. 1557 */ 1558 void nsXMLContentSink::UpdateChildCounts() { 1559 // Start from the top of the stack (growing upwards) and see if any 1560 // new content has been appended. If so, we recognize that reflows 1561 // have been generated for it and we should make sure that no 1562 // further reflows occur. Note that we have to include stackPos == 0 1563 // to properly notify on kids of <html>. 1564 int32_t stackLen = mContentStack.Length(); 1565 int32_t stackPos = stackLen - 1; 1566 while (stackPos >= 0) { 1567 StackNode& node = mContentStack[stackPos]; 1568 node.mNumFlushed = node.mContent->GetChildCount(); 1569 1570 stackPos--; 1571 } 1572 mNotifyLevel = stackLen - 1; 1573 } 1574 1575 bool nsXMLContentSink::IsMonolithicContainer( 1576 mozilla::dom::NodeInfo* aNodeInfo) { 1577 return ((aNodeInfo->NamespaceID() == kNameSpaceID_XHTML && 1578 (aNodeInfo->NameAtom() == nsGkAtoms::tr || 1579 aNodeInfo->NameAtom() == nsGkAtoms::select || 1580 aNodeInfo->NameAtom() == nsGkAtoms::object)) || 1581 (aNodeInfo->NamespaceID() == kNameSpaceID_MathML && 1582 (aNodeInfo->NameAtom() == nsGkAtoms::math))); 1583 } 1584 1585 void nsXMLContentSink::ContinueInterruptedParsingIfEnabled() { 1586 if (mParser && mParser->IsParserEnabled()) { 1587 GetParser()->ContinueInterruptedParsing(); 1588 } 1589 } 1590 1591 void nsXMLContentSink::ContinueInterruptedParsingAsync() { 1592 nsCOMPtr<nsIRunnable> ev = NewRunnableMethod( 1593 "nsXMLContentSink::ContinueInterruptedParsingIfEnabled", this, 1594 &nsXMLContentSink::ContinueInterruptedParsingIfEnabled); 1595 mDocument->Dispatch(ev.forget()); 1596 } 1597 1598 nsIParser* nsXMLContentSink::GetParser() { 1599 return static_cast<nsIParser*>(mParser.get()); 1600 }