tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }