tor-browser

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

PrototypeDocumentContentSink.cpp (40066B)


      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 https://mozilla.org/MPL/2.0/. */
      6 
      7 #include "mozilla/dom/PrototypeDocumentContentSink.h"
      8 
      9 #include "js/CompilationAndEvaluation.h"
     10 #include "js/Utility.h"  // JS::FreePolicy
     11 #include "js/experimental/JSStencil.h"
     12 #include "mozAutoDocUpdate.h"
     13 #include "mozilla/CycleCollectedJSContext.h"
     14 #include "mozilla/LoadInfo.h"
     15 #include "mozilla/Logging.h"
     16 #include "mozilla/PresShell.h"
     17 #include "mozilla/ProfilerLabels.h"
     18 #include "mozilla/RefPtr.h"
     19 #include "mozilla/StyleSheetInlines.h"
     20 #include "mozilla/Try.h"
     21 #include "mozilla/css/Loader.h"
     22 #include "mozilla/dom/AutoEntryScript.h"
     23 #include "mozilla/dom/CDATASection.h"
     24 #include "mozilla/dom/Comment.h"
     25 #include "mozilla/dom/Document.h"
     26 #include "mozilla/dom/DocumentType.h"
     27 #include "mozilla/dom/Element.h"
     28 #include "mozilla/dom/HTMLTemplateElement.h"
     29 #include "mozilla/dom/PolicyContainer.h"
     30 #include "mozilla/dom/ProcessingInstruction.h"
     31 #include "mozilla/dom/ScriptLoader.h"
     32 #include "mozilla/dom/XMLStylesheetProcessingInstruction.h"
     33 #include "mozilla/dom/nsCSPUtils.h"
     34 #include "nsCOMPtr.h"
     35 #include "nsCRT.h"
     36 #include "nsContentCreatorFunctions.h"
     37 #include "nsContentPolicyUtils.h"
     38 #include "nsContentUtils.h"
     39 #include "nsDocElementCreatedNotificationRunner.h"
     40 #include "nsError.h"
     41 #include "nsGkAtoms.h"
     42 #include "nsHTMLParts.h"
     43 #include "nsHtml5SVGLoadDispatcher.h"
     44 #include "nsIChannel.h"
     45 #include "nsIContent.h"
     46 #include "nsIContentPolicy.h"
     47 #include "nsIParser.h"
     48 #include "nsIScriptContext.h"
     49 #include "nsIScriptElement.h"
     50 #include "nsIScriptError.h"
     51 #include "nsIScriptGlobalObject.h"
     52 #include "nsIURI.h"
     53 #include "nsMimeTypes.h"
     54 #include "nsNameSpaceManager.h"
     55 #include "nsNetUtil.h"
     56 #include "nsNodeInfoManager.h"
     57 #include "nsReadableUtils.h"
     58 #include "nsRect.h"
     59 #include "nsTextNode.h"
     60 #include "nsUnicharUtils.h"
     61 #include "nsXULElement.h"
     62 #include "nsXULPrototypeCache.h"
     63 #include "prtime.h"
     64 
     65 using namespace mozilla;
     66 using namespace mozilla::dom;
     67 
     68 LazyLogModule PrototypeDocumentContentSink::gLog("PrototypeDocument");
     69 
     70 nsresult NS_NewPrototypeDocumentContentSink(nsIContentSink** aResult,
     71                                            Document* aDoc, nsIURI* aURI,
     72                                            nsISupports* aContainer,
     73                                            nsIChannel* aChannel) {
     74  MOZ_ASSERT(nullptr != aResult, "null ptr");
     75  if (nullptr == aResult) {
     76    return NS_ERROR_NULL_POINTER;
     77  }
     78  RefPtr<PrototypeDocumentContentSink> it = new PrototypeDocumentContentSink();
     79 
     80  nsresult rv = it->Init(aDoc, aURI, aContainer, aChannel);
     81  NS_ENSURE_SUCCESS(rv, rv);
     82 
     83  it.forget(aResult);
     84  return NS_OK;
     85 }
     86 
     87 namespace mozilla::dom {
     88 
     89 PrototypeDocumentContentSink::PrototypeDocumentContentSink()
     90    : mNextSrcLoadWaiter(nullptr),
     91      mCurrentScriptProto(nullptr),
     92      mOffThreadCompiling(false),
     93      mStillWalking(false),
     94      mPendingSheets(0) {}
     95 
     96 PrototypeDocumentContentSink::~PrototypeDocumentContentSink() {
     97  NS_ASSERTION(
     98      mNextSrcLoadWaiter == nullptr,
     99      "unreferenced document still waiting for script source to load?");
    100 }
    101 
    102 nsresult PrototypeDocumentContentSink::Init(Document* aDoc, nsIURI* aURI,
    103                                            nsISupports* aContainer,
    104                                            nsIChannel* aChannel) {
    105  MOZ_ASSERT(aDoc, "null ptr");
    106  MOZ_ASSERT(aURI, "null ptr");
    107 
    108  mDocument = aDoc;
    109 
    110  mDocument->SetDelayFrameLoaderInitialization(true);
    111  mDocument->SetMayStartLayout(false);
    112 
    113  // Get the URI.  this should match the uri used for the OnNewURI call in
    114  // nsDocShell::CreateDocumentViewer.
    115  nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(mDocumentURI));
    116  NS_ENSURE_SUCCESS(rv, rv);
    117 
    118  mScriptLoader = mDocument->GetScriptLoader();
    119 
    120  return NS_OK;
    121 }
    122 
    123 NS_IMPL_CYCLE_COLLECTION(PrototypeDocumentContentSink, mParser, mDocumentURI,
    124                         mDocument, mScriptLoader, mContextStack,
    125                         mCurrentPrototype)
    126 
    127 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PrototypeDocumentContentSink)
    128  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentSink)
    129  NS_INTERFACE_MAP_ENTRY(nsIContentSink)
    130  NS_INTERFACE_MAP_ENTRY(nsIStreamLoaderObserver)
    131  NS_INTERFACE_MAP_ENTRY(nsICSSLoaderObserver)
    132  NS_INTERFACE_MAP_ENTRY(nsIOffThreadScriptReceiver)
    133 NS_INTERFACE_MAP_END
    134 
    135 NS_IMPL_CYCLE_COLLECTING_ADDREF(PrototypeDocumentContentSink)
    136 NS_IMPL_CYCLE_COLLECTING_RELEASE(PrototypeDocumentContentSink)
    137 
    138 //----------------------------------------------------------------------
    139 //
    140 // nsIContentSink interface
    141 //
    142 
    143 void PrototypeDocumentContentSink::SetDocumentCharset(
    144    NotNull<const Encoding*> aEncoding) {
    145  if (mDocument) {
    146    mDocument->SetDocumentCharacterSet(aEncoding);
    147  }
    148 }
    149 
    150 nsISupports* PrototypeDocumentContentSink::GetTarget() {
    151  return ToSupports(mDocument);
    152 }
    153 
    154 bool PrototypeDocumentContentSink::IsScriptExecuting() {
    155  if (!mScriptLoader) {
    156    MOZ_ASSERT(false, "Can't load prototype docs as data");
    157    return false;
    158  }
    159  return !!mScriptLoader->GetCurrentScript();
    160 }
    161 
    162 NS_IMETHODIMP
    163 PrototypeDocumentContentSink::SetParser(nsParserBase* aParser) {
    164  MOZ_ASSERT(aParser, "Should have a parser here!");
    165  mParser = aParser;
    166  return NS_OK;
    167 }
    168 
    169 nsIParser* PrototypeDocumentContentSink::GetParser() {
    170  return static_cast<nsIParser*>(mParser.get());
    171 }
    172 
    173 void PrototypeDocumentContentSink::ContinueInterruptedParsingIfEnabled() {
    174  if (mParser && mParser->IsParserEnabled()) {
    175    GetParser()->ContinueInterruptedParsing();
    176  }
    177 }
    178 
    179 void PrototypeDocumentContentSink::ContinueInterruptedParsingAsync() {
    180  nsCOMPtr<nsIRunnable> ev = NewRunnableMethod(
    181      "PrototypeDocumentContentSink::ContinueInterruptedParsingIfEnabled", this,
    182      &PrototypeDocumentContentSink::ContinueInterruptedParsingIfEnabled);
    183  mDocument->Dispatch(ev.forget());
    184 }
    185 
    186 //----------------------------------------------------------------------
    187 //
    188 // PrototypeDocumentContentSink::ContextStack
    189 //
    190 
    191 PrototypeDocumentContentSink::ContextStack::ContextStack()
    192    : mTop(nullptr), mDepth(0) {}
    193 
    194 PrototypeDocumentContentSink::ContextStack::~ContextStack() { Clear(); }
    195 
    196 void PrototypeDocumentContentSink::ContextStack::Traverse(
    197    nsCycleCollectionTraversalCallback& aCallback, const char* aName,
    198    uint32_t aFlags) {
    199  aFlags |= CycleCollectionEdgeNameArrayFlag;
    200  Entry* current = mTop;
    201  while (current) {
    202    CycleCollectionNoteChild(aCallback, current->mElement, aName, aFlags);
    203    current = current->mNext;
    204  }
    205 }
    206 
    207 void PrototypeDocumentContentSink::ContextStack::Clear() {
    208  while (mTop) {
    209    Entry* doomed = mTop;
    210    mTop = mTop->mNext;
    211    NS_IF_RELEASE(doomed->mElement);
    212    delete doomed;
    213  }
    214  mDepth = 0;
    215 }
    216 
    217 nsresult PrototypeDocumentContentSink::ContextStack::Push(
    218    nsXULPrototypeElement* aPrototype, nsIContent* aElement) {
    219  Entry* entry = new Entry;
    220  entry->mPrototype = aPrototype;
    221  entry->mElement = aElement;
    222  NS_IF_ADDREF(entry->mElement);
    223  entry->mIndex = 0;
    224 
    225  entry->mNext = mTop;
    226  mTop = entry;
    227 
    228  ++mDepth;
    229  return NS_OK;
    230 }
    231 
    232 nsresult PrototypeDocumentContentSink::ContextStack::Pop() {
    233  if (mDepth == 0) return NS_ERROR_UNEXPECTED;
    234 
    235  Entry* doomed = mTop;
    236  mTop = mTop->mNext;
    237  --mDepth;
    238 
    239  NS_IF_RELEASE(doomed->mElement);
    240  delete doomed;
    241  return NS_OK;
    242 }
    243 
    244 nsresult PrototypeDocumentContentSink::ContextStack::Peek(
    245    nsXULPrototypeElement** aPrototype, nsIContent** aElement,
    246    int32_t* aIndex) {
    247  if (mDepth == 0) return NS_ERROR_UNEXPECTED;
    248 
    249  *aPrototype = mTop->mPrototype;
    250  *aElement = mTop->mElement;
    251  NS_IF_ADDREF(*aElement);
    252  *aIndex = mTop->mIndex;
    253 
    254  return NS_OK;
    255 }
    256 
    257 nsresult PrototypeDocumentContentSink::ContextStack::SetTopIndex(
    258    int32_t aIndex) {
    259  if (mDepth == 0) return NS_ERROR_UNEXPECTED;
    260 
    261  mTop->mIndex = aIndex;
    262  return NS_OK;
    263 }
    264 
    265 //----------------------------------------------------------------------
    266 //
    267 // Content model walking routines
    268 //
    269 
    270 nsresult PrototypeDocumentContentSink::OnPrototypeLoadDone(
    271    nsXULPrototypeDocument* aPrototype) {
    272  mCurrentPrototype = aPrototype;
    273  mDocument->SetPrototypeDocument(aPrototype);
    274 
    275  nsresult rv = PrepareToWalk();
    276  NS_ENSURE_SUCCESS(rv, rv);
    277 
    278  rv = ResumeWalk();
    279 
    280  return rv;
    281 }
    282 
    283 nsresult PrototypeDocumentContentSink::PrepareToWalk() {
    284  MOZ_ASSERT(mCurrentPrototype);
    285  nsresult rv;
    286 
    287  mStillWalking = true;
    288 
    289  // Notify document that the load is beginning
    290  mDocument->BeginLoad();
    291  MOZ_ASSERT(!mDocument->HasChildren());
    292 
    293  // Get the prototype's root element and initialize the context
    294  // stack for the prototype walk.
    295  nsXULPrototypeElement* proto = mCurrentPrototype->GetRootElement();
    296 
    297  if (!proto) {
    298    if (MOZ_LOG_TEST(gLog, LogLevel::Error)) {
    299      nsCOMPtr<nsIURI> url = mCurrentPrototype->GetURI();
    300 
    301      nsAutoCString urlspec;
    302      rv = url->GetSpec(urlspec);
    303      if (NS_FAILED(rv)) return rv;
    304 
    305      MOZ_LOG(gLog, LogLevel::Error,
    306              ("prototype: error parsing '%s'", urlspec.get()));
    307    }
    308 
    309    return NS_OK;
    310  }
    311 
    312  const nsTArray<RefPtr<nsXULPrototypePI> >& processingInstructions =
    313      mCurrentPrototype->GetProcessingInstructions();
    314 
    315  uint32_t total = processingInstructions.Length();
    316  for (uint32_t i = 0; i < total; ++i) {
    317    rv = CreateAndInsertPI(processingInstructions[i], mDocument,
    318                           /* aInProlog */ true);
    319    if (NS_FAILED(rv)) return rv;
    320  }
    321 
    322  // Do one-time initialization.
    323  RefPtr<Element> root;
    324 
    325  // Add the root element
    326  rv = CreateElementFromPrototype(proto, getter_AddRefs(root), nullptr);
    327  if (NS_FAILED(rv)) return rv;
    328 
    329  ErrorResult error;
    330  mDocument->AppendChildTo(root, false, error);
    331  if (error.Failed()) {
    332    return error.StealNSResult();
    333  }
    334 
    335  // TODO(emilio): Should this really notify? We don't notify of appends anyhow,
    336  // and we just appended the root so no styles can possibly depend on it.
    337  mDocument->UpdateDocumentStates(DocumentState::RTL_LOCALE, true);
    338 
    339  nsContentUtils::AddScriptRunner(
    340      new nsDocElementCreatedNotificationRunner(mDocument));
    341 
    342  // There'd better not be anything on the context stack at this
    343  // point! This is the basis case for our "induction" in
    344  // ResumeWalk(), below, which'll assume that there's always a
    345  // content element on the context stack if we're in the document.
    346  NS_ASSERTION(mContextStack.Depth() == 0,
    347               "something's on the context stack already");
    348  if (mContextStack.Depth() != 0) return NS_ERROR_UNEXPECTED;
    349 
    350  rv = mContextStack.Push(proto, root);
    351  if (NS_FAILED(rv)) return rv;
    352 
    353  return NS_OK;
    354 }
    355 
    356 nsresult PrototypeDocumentContentSink::CreateAndInsertPI(
    357    const nsXULPrototypePI* aProtoPI, nsINode* aParent, bool aInProlog) {
    358  MOZ_ASSERT(aProtoPI, "null ptr");
    359  MOZ_ASSERT(aParent, "null ptr");
    360 
    361  RefPtr<ProcessingInstruction> node =
    362      NS_NewXMLProcessingInstruction(aParent->OwnerDoc()->NodeInfoManager(),
    363                                     aProtoPI->mTarget, aProtoPI->mData);
    364 
    365  nsresult rv;
    366  if (aProtoPI->mTarget.EqualsLiteral("xml-stylesheet")) {
    367    MOZ_ASSERT(LinkStyle::FromNode(*node),
    368               "XML Stylesheet node does not implement LinkStyle!");
    369    auto* pi = static_cast<XMLStylesheetProcessingInstruction*>(node.get());
    370    rv = InsertXMLStylesheetPI(aProtoPI, aParent, pi);
    371  } else {
    372    // Handles the special <?csp ?> PI, which will be handled before
    373    // creating any element with potential inline style or scripts.
    374    if (aInProlog && aProtoPI->mTarget.EqualsLiteral("csp")) {
    375      CSP_ApplyMetaCSPToDoc(*aParent->OwnerDoc(), aProtoPI->mData);
    376    }
    377 
    378    // No special processing, just add the PI to the document.
    379    ErrorResult error;
    380    aParent->AppendChildTo(node->AsContent(), false, error);
    381    rv = error.StealNSResult();
    382  }
    383 
    384  return rv;
    385 }
    386 
    387 nsresult PrototypeDocumentContentSink::InsertXMLStylesheetPI(
    388    const nsXULPrototypePI* aProtoPI, nsINode* aParent,
    389    XMLStylesheetProcessingInstruction* aPINode) {
    390  // We want to be notified when the style sheet finishes loading, so
    391  // disable style sheet loading for now.
    392  aPINode->DisableUpdates();
    393  aPINode->OverrideBaseURI(mCurrentPrototype->GetURI());
    394 
    395  ErrorResult rv;
    396  aParent->AppendChildTo(aPINode, false, rv);
    397  if (rv.Failed()) {
    398    return rv.StealNSResult();
    399  }
    400 
    401  // load the stylesheet if necessary, passing ourselves as
    402  // nsICSSObserver
    403  auto result = aPINode->EnableUpdatesAndUpdateStyleSheet(this);
    404  if (result.isErr()) {
    405    // Ignore errors from UpdateStyleSheet; we don't want failure to
    406    // do that to break the XUL document load.  But do propagate out
    407    // NS_ERROR_OUT_OF_MEMORY.
    408    if (result.unwrapErr() == NS_ERROR_OUT_OF_MEMORY) {
    409      return result.unwrapErr();
    410    }
    411    return NS_OK;
    412  }
    413 
    414  auto update = result.unwrap();
    415  if (update.ShouldBlock()) {
    416    ++mPendingSheets;
    417  }
    418 
    419  return NS_OK;
    420 }
    421 
    422 void PrototypeDocumentContentSink::CloseElement(Element* aElement,
    423                                                bool aHadChildren) {
    424  if (nsIContent::RequiresDoneAddingChildren(
    425          aElement->NodeInfo()->NamespaceID(),
    426          aElement->NodeInfo()->NameAtom())) {
    427    aElement->DoneAddingChildren(false);
    428  }
    429 
    430  if (auto* linkStyle = LinkStyle::FromNode(*aElement)) {
    431    auto result = linkStyle->EnableUpdatesAndUpdateStyleSheet(this);
    432    if (result.isOk() && result.unwrap().ShouldBlock()) {
    433      ++mPendingSheets;
    434    }
    435    return;
    436  }
    437 
    438  if (!aHadChildren) {
    439    return;
    440  }
    441 
    442  // See bug 370111 and bug 1495946. We don't cache inline styles nor module
    443  // scripts in the prototype cache, and we don't notify on node insertion, so
    444  // we need to do this for the stylesheet / script to be properly processed.
    445  // This kinda sucks, but notifying was a pretty sizeable perf regression so...
    446  if (aElement->IsHTMLElement(nsGkAtoms::script) ||
    447      aElement->IsSVGElement(nsGkAtoms::script)) {
    448    nsCOMPtr<nsIScriptElement> sele = do_QueryInterface(aElement);
    449    MOZ_ASSERT(sele, "Node didn't QI to script.");
    450    if (sele->GetScriptIsModule()) {
    451      // https://html.spec.whatwg.org/#parsing-main-incdata
    452      // An end tag whose tag name is "script"
    453      //  - If the active speculative HTML parser is null and the JavaScript
    454      // execution context stack is empty, then perform a microtask checkpoint.
    455      {
    456        nsAutoMicroTask mt;
    457      }
    458      sele->AttemptToExecute(nullptr /* aParser */);
    459    }
    460  }
    461 }
    462 
    463 nsresult PrototypeDocumentContentSink::ResumeWalk() {
    464  nsresult rv = ResumeWalkInternal();
    465  if (NS_FAILED(rv)) {
    466    nsContentUtils::ReportToConsoleNonLocalized(
    467        u"Failed to load document from prototype document."_ns,
    468        nsIScriptError::errorFlag, "Prototype Document"_ns, mDocument,
    469        SourceLocation{mDocumentURI.get()});
    470  }
    471  return rv;
    472 }
    473 
    474 nsresult PrototypeDocumentContentSink::ResumeWalkInternal() {
    475  MOZ_ASSERT(mStillWalking);
    476  // Walk the prototype and build the delegate content model. The
    477  // walk is performed in a top-down, left-to-right fashion. That
    478  // is, a parent is built before any of its children; a node is
    479  // only built after all of its siblings to the left are fully
    480  // constructed.
    481  //
    482  // It is interruptable so that transcluded documents (e.g.,
    483  // <html:script src="..." />) can be properly re-loaded if the
    484  // cached copy of the document becomes stale.
    485  nsresult rv;
    486  nsCOMPtr<nsIURI> docURI =
    487      mCurrentPrototype ? mCurrentPrototype->GetURI() : nullptr;
    488 
    489  while (true) {
    490    // Begin (or resume) walking the current prototype.
    491 
    492    while (mContextStack.Depth() > 0) {
    493      // Look at the top of the stack to determine what we're
    494      // currently working on.
    495      // This will always be a node already constructed and
    496      // inserted to the actual document.
    497      nsXULPrototypeElement* proto;
    498      nsCOMPtr<nsIContent> element;
    499      nsCOMPtr<nsIContent> nodeToPushTo;
    500      int32_t indx;  // all children of proto before indx (not
    501                     // inclusive) have already been constructed
    502      rv = mContextStack.Peek(&proto, getter_AddRefs(element), &indx);
    503      if (NS_FAILED(rv)) return rv;
    504 
    505      if (indx >= (int32_t)proto->mChildren.Length()) {
    506        if (element) {
    507          // We've processed all of the prototype's children.
    508          CloseElement(element->AsElement(), /* aHadChildren = */ true);
    509        }
    510        // Now pop the context stack back up to the parent
    511        // element and continue the prototype walk.
    512        mContextStack.Pop();
    513        continue;
    514      }
    515 
    516      nodeToPushTo = element;
    517      // For template elements append the content to the template's document
    518      // fragment.
    519      if (auto* templateElement = HTMLTemplateElement::FromNode(element)) {
    520        nodeToPushTo = templateElement->Content();
    521      }
    522 
    523      // Grab the next child, and advance the current context stack
    524      // to the next sibling to our right.
    525      nsXULPrototypeNode* childproto = proto->mChildren[indx];
    526      mContextStack.SetTopIndex(++indx);
    527 
    528      switch (childproto->mType) {
    529        case nsXULPrototypeNode::eType_Element: {
    530          // An 'element', which may contain more content.
    531          auto* protoele = static_cast<nsXULPrototypeElement*>(childproto);
    532 
    533          RefPtr<Element> child;
    534          MOZ_TRY(CreateElementFromPrototype(protoele, getter_AddRefs(child),
    535                                             nodeToPushTo));
    536 
    537          if (auto* linkStyle = LinkStyle::FromNode(*child)) {
    538            linkStyle->DisableUpdates();
    539          }
    540 
    541          // ...and append it to the content model.
    542          ErrorResult error;
    543          nodeToPushTo->AppendChildTo(child, false, error);
    544          if (error.Failed()) {
    545            return error.StealNSResult();
    546          }
    547 
    548          if (nsIContent::RequiresDoneCreatingElement(
    549                  protoele->mNodeInfo->NamespaceID(),
    550                  protoele->mNodeInfo->NameAtom())) {
    551            child->DoneCreatingElement();
    552          }
    553 
    554          // If it has children, push the element onto the context
    555          // stack and begin to process them.
    556          if (protoele->mChildren.Length() > 0) {
    557            rv = mContextStack.Push(protoele, child);
    558            if (NS_FAILED(rv)) return rv;
    559          } else {
    560            // If there are no children, close the element immediately.
    561            CloseElement(child, /* aHadChildren = */ false);
    562          }
    563        } break;
    564 
    565        case nsXULPrototypeNode::eType_Script: {
    566          // A script reference. Execute the script immediately;
    567          // this may have side effects in the content model.
    568          auto* scriptproto = static_cast<nsXULPrototypeScript*>(childproto);
    569          if (scriptproto->mSrcURI) {
    570            // A transcluded script reference; this may
    571            // "block" our prototype walk if the script isn't
    572            // cached, or the cached copy of the script is
    573            // stale and must be reloaded.
    574            bool blocked;
    575            rv = LoadScript(scriptproto, &blocked);
    576            // If the script cannot be loaded, just keep going!
    577 
    578            if (NS_SUCCEEDED(rv) && blocked) return NS_OK;
    579          } else if (scriptproto->HasStencil()) {
    580            // An inline script
    581            rv = ExecuteScript(scriptproto);
    582            if (NS_FAILED(rv)) return rv;
    583          }
    584        } break;
    585 
    586        case nsXULPrototypeNode::eType_Text: {
    587          nsNodeInfoManager* nim = nodeToPushTo->NodeInfo()->NodeInfoManager();
    588          // A simple text node.
    589          RefPtr<nsTextNode> text = new (nim) nsTextNode(nim);
    590 
    591          auto* textproto = static_cast<nsXULPrototypeText*>(childproto);
    592          text->SetText(textproto->mValue, false);
    593 
    594          ErrorResult error;
    595          nodeToPushTo->AppendChildTo(text, false, error);
    596          if (error.Failed()) {
    597            return error.StealNSResult();
    598          }
    599        } break;
    600 
    601        case nsXULPrototypeNode::eType_PI: {
    602          auto* piProto = static_cast<nsXULPrototypePI*>(childproto);
    603 
    604          // <?xml-stylesheet?> and <?csp?> don't have an effect
    605          // outside the prolog, issue a warning.
    606 
    607          if (piProto->mTarget.EqualsLiteral("xml-stylesheet") ||
    608              piProto->mTarget.EqualsLiteral("csp")) {
    609            AutoTArray<nsString, 1> params = {piProto->mTarget};
    610 
    611            nsContentUtils::ReportToConsole(
    612                nsIScriptError::warningFlag, "XUL Document"_ns, nullptr,
    613                nsContentUtils::eXUL_PROPERTIES, "PINotInProlog2", params,
    614                SourceLocation(docURI.get()));
    615          }
    616 
    617          if (nsIContent* parent = element.get()) {
    618            // an inline script could have removed the root element
    619            rv = CreateAndInsertPI(piProto, parent, /* aInProlog */ false);
    620            NS_ENSURE_SUCCESS(rv, rv);
    621          }
    622        } break;
    623 
    624        default:
    625          MOZ_ASSERT_UNREACHABLE("Unexpected nsXULPrototypeNode::Type");
    626      }
    627    }
    628 
    629    // Once we get here, the context stack will have been
    630    // depleted. That means that the entire prototype has been
    631    // walked and content has been constructed.
    632    break;
    633  }
    634 
    635  mStillWalking = false;
    636  return MaybeDoneWalking();
    637 }
    638 
    639 void PrototypeDocumentContentSink::InitialTranslationCompleted() {
    640  MaybeDoneWalking();
    641 }
    642 
    643 nsresult PrototypeDocumentContentSink::MaybeDoneWalking() {
    644  if (mPendingSheets > 0 || mStillWalking) {
    645    return NS_OK;
    646  }
    647 
    648  if (mDocument->HasPendingInitialTranslation()) {
    649    mDocument->OnParsingCompleted();
    650    return NS_OK;
    651  }
    652 
    653  return DoneWalking();
    654 }
    655 
    656 nsresult PrototypeDocumentContentSink::DoneWalking() {
    657  MOZ_ASSERT(mPendingSheets == 0, "there are sheets to be loaded");
    658  MOZ_ASSERT(!mStillWalking, "walk not done");
    659  MOZ_ASSERT(!mDocument->HasPendingInitialTranslation(), "translation pending");
    660 
    661  if (mDocument) {
    662    MOZ_ASSERT(mDocument->GetReadyStateEnum() == Document::READYSTATE_LOADING,
    663               "Bad readyState");
    664    mDocument->SetReadyStateInternal(Document::READYSTATE_INTERACTIVE);
    665    mDocument->NotifyPossibleTitleChange(false);
    666 
    667    nsContentUtils::DispatchEventOnlyToChrome(mDocument, mDocument,
    668                                              u"MozBeforeInitialXULLayout"_ns,
    669                                              CanBubble::eYes, Cancelable::eNo);
    670  }
    671 
    672  if (mScriptLoader) {
    673    mScriptLoader->ParsingComplete(false);
    674    mScriptLoader->DeferCheckpointReached();
    675  }
    676 
    677  StartLayout();
    678 
    679  if (mDocumentURI->SchemeIs("chrome") &&
    680      nsXULPrototypeCache::GetInstance()->IsEnabled()) {
    681    bool isCachedOnDisk;
    682    nsXULPrototypeCache::GetInstance()->HasPrototype(mDocumentURI,
    683                                                     &isCachedOnDisk);
    684    if (!isCachedOnDisk) {
    685      if (!mDocument->GetDocumentElement() ||
    686          (mDocument->GetDocumentElement()->NodeInfo()->Equals(
    687               nsGkAtoms::parsererror) &&
    688           mDocument->GetDocumentElement()->NodeInfo()->NamespaceEquals(
    689               nsDependentAtomString(nsGkAtoms::nsuri_parsererror)))) {
    690        nsXULPrototypeCache::GetInstance()->RemovePrototype(mDocumentURI);
    691      } else {
    692        nsXULPrototypeCache::GetInstance()->WritePrototype(mCurrentPrototype);
    693      }
    694    }
    695  }
    696 
    697  mDocument->SetDelayFrameLoaderInitialization(false);
    698  RefPtr<Document> doc = mDocument;
    699  doc->MaybeInitializeFinalizeFrameLoaders();
    700 
    701  // If the document we are loading has a reference or it is a
    702  // frameset document, disable the scroll bars on the views.
    703 
    704  doc->SetScrollToRef(mDocument->GetDocumentURI());
    705 
    706  doc->EndLoad();
    707 
    708  return NS_OK;
    709 }
    710 
    711 void PrototypeDocumentContentSink::StartLayout() {
    712  AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING(
    713      "PrototypeDocumentContentSink::StartLayout", LAYOUT,
    714      mDocumentURI->GetSpecOrDefault());
    715  mDocument->SetMayStartLayout(true);
    716  RefPtr<PresShell> presShell = mDocument->GetPresShell();
    717  if (presShell && !presShell->DidInitialize()) {
    718    nsresult rv = presShell->Initialize();
    719    if (NS_FAILED(rv)) {
    720      return;
    721    }
    722  }
    723 }
    724 
    725 NS_IMETHODIMP
    726 PrototypeDocumentContentSink::StyleSheetLoaded(StyleSheet* aSheet,
    727                                               bool aWasDeferred,
    728                                               nsresult aStatus) {
    729  if (!aWasDeferred) {
    730    // Don't care about when alternate sheets finish loading
    731    MOZ_ASSERT(mPendingSheets > 0, "Unexpected StyleSheetLoaded notification");
    732 
    733    --mPendingSheets;
    734 
    735    return MaybeDoneWalking();
    736  }
    737 
    738  return NS_OK;
    739 }
    740 
    741 nsresult PrototypeDocumentContentSink::LoadScript(
    742    nsXULPrototypeScript* aScriptProto, bool* aBlock) {
    743  // Load a transcluded script
    744  nsresult rv;
    745 
    746  bool isChromeDoc = mDocumentURI->SchemeIs("chrome");
    747 
    748  if (isChromeDoc && aScriptProto->HasStencil()) {
    749    rv = ExecuteScript(aScriptProto);
    750 
    751    // Ignore return value from execution, and don't block
    752    *aBlock = false;
    753    return NS_OK;
    754  }
    755 
    756  // Try the XUL script cache, in case two XUL documents source the same
    757  // .js file (e.g., strres.js from navigator.xul and utilityOverlay.xul).
    758  // XXXbe the cache relies on aScriptProto's GC root!
    759  bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled();
    760 
    761  if (isChromeDoc && useXULCache) {
    762    RefPtr<JS::Stencil> newStencil =
    763        nsXULPrototypeCache::GetInstance()->GetStencil(aScriptProto->mSrcURI);
    764    if (newStencil) {
    765      // The script language for a proto must remain constant - we
    766      // can't just change it for this unexpected language.
    767      aScriptProto->Set(newStencil);
    768    }
    769 
    770    if (aScriptProto->HasStencil()) {
    771      rv = ExecuteScript(aScriptProto);
    772 
    773      // Ignore return value from execution, and don't block
    774      *aBlock = false;
    775      return NS_OK;
    776    }
    777  }
    778 
    779  // Release stencil from FastLoad since we decided against using them
    780  aScriptProto->Set(nullptr);
    781 
    782  // Set the current script prototype so that OnStreamComplete can report
    783  // the right file if there are errors in the script.
    784  NS_ASSERTION(!mCurrentScriptProto,
    785               "still loading a script when starting another load?");
    786  mCurrentScriptProto = aScriptProto;
    787 
    788  if (isChromeDoc && aScriptProto->mSrcLoading) {
    789    // Another document load has started, which is still in progress.
    790    // Remember to ResumeWalk this document when the load completes.
    791    mNextSrcLoadWaiter = aScriptProto->mSrcLoadWaiters;
    792    aScriptProto->mSrcLoadWaiters = this;
    793    NS_ADDREF_THIS();
    794  } else {
    795    nsCOMPtr<nsILoadGroup> group =
    796        mDocument
    797            ->GetDocumentLoadGroup();  // found in
    798                                       // mozilla::dom::Document::SetScriptGlobalObject
    799 
    800    // Note: the loader will keep itself alive while it's loading.
    801    nsCOMPtr<nsIStreamLoader> loader;
    802    rv = NS_NewStreamLoader(
    803        getter_AddRefs(loader), aScriptProto->mSrcURI,
    804        this,       // aObserver
    805        mDocument,  // aRequestingContext
    806        nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT,
    807        nsIContentPolicy::TYPE_INTERNAL_SCRIPT, group);
    808 
    809    if (NS_FAILED(rv)) {
    810      mCurrentScriptProto = nullptr;
    811      return rv;
    812    }
    813 
    814    aScriptProto->mSrcLoading = true;
    815  }
    816 
    817  // Block until OnStreamComplete resumes us.
    818  *aBlock = true;
    819  return NS_OK;
    820 }
    821 
    822 NS_IMETHODIMP
    823 PrototypeDocumentContentSink::OnStreamComplete(nsIStreamLoader* aLoader,
    824                                               nsISupports* context,
    825                                               nsresult aStatus,
    826                                               uint32_t stringLen,
    827                                               const uint8_t* string) {
    828  nsCOMPtr<nsIRequest> request;
    829  aLoader->GetRequest(getter_AddRefs(request));
    830  nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
    831 
    832 #ifdef DEBUG
    833  // print a load error on bad status
    834  if (NS_FAILED(aStatus)) {
    835    if (channel) {
    836      nsCOMPtr<nsIURI> uri;
    837      channel->GetURI(getter_AddRefs(uri));
    838      if (uri) {
    839        printf("Failed to load %s\n", uri->GetSpecOrDefault().get());
    840      }
    841    }
    842  }
    843 #endif
    844 
    845  // This is the completion routine that will be called when a
    846  // transcluded script completes. Compile and execute the script
    847  // if the load was successful, then continue building content
    848  // from the prototype.
    849  nsresult rv = aStatus;
    850 
    851  NS_ASSERTION(mCurrentScriptProto && mCurrentScriptProto->mSrcLoading,
    852               "script source not loading on unichar stream complete?");
    853  if (!mCurrentScriptProto) {
    854    // XXX Wallpaper for bug 270042
    855    return NS_OK;
    856  }
    857 
    858  if (NS_SUCCEEDED(aStatus)) {
    859    // If the including document is a FastLoad document, and we're
    860    // compiling an out-of-line script (one with src=...), then we must
    861    // be writing a new FastLoad file.  If we were reading this script
    862    // from the FastLoad file, XULContentSinkImpl::OpenScript (over in
    863    // nsXULContentSink.cpp) would have already deserialized a non-null
    864    // script->mStencil, causing control flow at the top of LoadScript
    865    // not to reach here.
    866    nsCOMPtr<nsIURI> uri = mCurrentScriptProto->mSrcURI;
    867 
    868    // XXX should also check nsIHttpChannel::requestSucceeded
    869 
    870    MOZ_ASSERT(!mOffThreadCompiling,
    871               "PrototypeDocument can't load multiple scripts at once");
    872 
    873    UniquePtr<Utf8Unit[], JS::FreePolicy> units;
    874    size_t unitsLength = 0;
    875 
    876    rv = ScriptLoader::ConvertToUTF8(channel, string, stringLen, u""_ns,
    877                                     mDocument, units, unitsLength);
    878    if (NS_SUCCEEDED(rv)) {
    879      rv = mCurrentScriptProto->CompileMaybeOffThread(
    880          std::move(units), unitsLength, uri, 1, mDocument, this);
    881      if (NS_SUCCEEDED(rv) && !mCurrentScriptProto->HasStencil()) {
    882        mOffThreadCompiling = true;
    883        mDocument->BlockOnload();
    884        return NS_OK;
    885      }
    886    }
    887  }
    888 
    889  return OnScriptCompileComplete(mCurrentScriptProto->GetStencil(), rv);
    890 }
    891 
    892 NS_IMETHODIMP
    893 PrototypeDocumentContentSink::OnScriptCompileComplete(JS::Stencil* aStencil,
    894                                                      nsresult aStatus) {
    895  // The mCurrentScriptProto may have been cleared out by another
    896  // PrototypeDocumentContentSink.
    897  if (!mCurrentScriptProto) {
    898    return NS_OK;
    899  }
    900 
    901  // When compiling off thread the script will not have been attached to the
    902  // script proto yet.
    903  if (aStencil && !mCurrentScriptProto->HasStencil()) {
    904    mCurrentScriptProto->Set(aStencil);
    905  }
    906 
    907  // Allow load events to be fired once off thread compilation finishes.
    908  if (mOffThreadCompiling) {
    909    mOffThreadCompiling = false;
    910    mDocument->UnblockOnload(false);
    911  }
    912 
    913  // Clear mCurrentScriptProto now, but save it first for use below in
    914  // the execute code, and in the while loop that resumes walks of other
    915  // documents that raced to load this script.
    916  nsXULPrototypeScript* scriptProto = mCurrentScriptProto;
    917  mCurrentScriptProto = nullptr;
    918 
    919  // Clear the prototype's loading flag before executing the script or
    920  // resuming document walks, in case any of those control flows starts a
    921  // new script load.
    922  scriptProto->mSrcLoading = false;
    923 
    924  nsresult rv = aStatus;
    925  if (NS_SUCCEEDED(rv)) {
    926    rv = ExecuteScript(scriptProto);
    927 
    928    // If the XUL cache is enabled, save the script object there in
    929    // case different XUL documents source the same script.
    930    //
    931    // But don't save the script in the cache unless the master XUL
    932    // document URL is a chrome: URL.  It is valid for a URL such as
    933    // about:config to translate into a master document URL, whose
    934    // prototype document nodes -- including prototype scripts that
    935    // hold GC roots protecting their mJSObject pointers -- are not
    936    // cached in the XUL prototype cache.  See StartDocumentLoad,
    937    // the fillXULCache logic.
    938    //
    939    // A document such as about:config is free to load a script via
    940    // a URL such as chrome://global/content/config.js, and we must
    941    // not cache that script object without a prototype cache entry
    942    // containing a companion nsXULPrototypeScript node that owns a
    943    // GC root protecting the script object.  Otherwise, the script
    944    // cache entry will dangle once the uncached prototype document
    945    // is released when its owning document is unloaded.
    946    //
    947    // (See http://bugzilla.mozilla.org/show_bug.cgi?id=98207 for
    948    // the true crime story.)
    949    bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled();
    950 
    951    if (useXULCache && mDocumentURI->SchemeIs("chrome") &&
    952        scriptProto->HasStencil()) {
    953      nsXULPrototypeCache::GetInstance()->PutStencil(scriptProto->mSrcURI,
    954                                                     scriptProto->GetStencil());
    955    }
    956    // ignore any evaluation errors
    957  }
    958 
    959  rv = ResumeWalk();
    960 
    961  // Load a pointer to the prototype-script's list of documents who
    962  // raced to load the same script
    963  PrototypeDocumentContentSink** docp = &scriptProto->mSrcLoadWaiters;
    964 
    965  // Resume walking other documents that waited for this one's load, first
    966  // executing the script we just compiled, in each doc's script context
    967  PrototypeDocumentContentSink* doc;
    968  while ((doc = *docp) != nullptr) {
    969    NS_ASSERTION(doc->mCurrentScriptProto == scriptProto,
    970                 "waiting for wrong script to load?");
    971    doc->mCurrentScriptProto = nullptr;
    972 
    973    // Unlink doc from scriptProto's list before executing and resuming
    974    *docp = doc->mNextSrcLoadWaiter;
    975    doc->mNextSrcLoadWaiter = nullptr;
    976 
    977    if (aStatus == NS_BINDING_ABORTED && !scriptProto->HasStencil()) {
    978      // If the previous doc load was aborted, we want to try loading
    979      // again for the next doc. Otherwise, one abort would lead to all
    980      // subsequent waiting docs to abort as well.
    981      bool block = false;
    982      doc->LoadScript(scriptProto, &block);
    983      NS_RELEASE(doc);
    984      return rv;
    985    }
    986 
    987    // Execute only if we loaded and compiled successfully, then resume
    988    if (NS_SUCCEEDED(aStatus) && scriptProto->HasStencil()) {
    989      doc->ExecuteScript(scriptProto);
    990    }
    991    doc->ResumeWalk();
    992    NS_RELEASE(doc);
    993  }
    994 
    995  return rv;
    996 }
    997 
    998 nsresult PrototypeDocumentContentSink::ExecuteScript(
    999    nsXULPrototypeScript* aScript) {
   1000  MOZ_ASSERT(aScript != nullptr, "null ptr");
   1001  NS_ENSURE_TRUE(aScript, NS_ERROR_NULL_POINTER);
   1002 
   1003  nsIScriptGlobalObject* scriptGlobalObject;
   1004  bool aHasHadScriptHandlingObject;
   1005  scriptGlobalObject =
   1006      mDocument->GetScriptHandlingObject(aHasHadScriptHandlingObject);
   1007 
   1008  NS_ENSURE_TRUE(scriptGlobalObject, NS_ERROR_NOT_INITIALIZED);
   1009 
   1010  nsresult rv;
   1011  rv = scriptGlobalObject->EnsureScriptEnvironment();
   1012  NS_ENSURE_SUCCESS(rv, rv);
   1013 
   1014  // Execute the precompiled script with the given version
   1015  nsAutoMicroTask mt;
   1016 
   1017  // We're about to run script via JS_ExecuteScript, so we need an
   1018  // AutoEntryScript. This is Gecko specific and not in any spec.
   1019  AutoEntryScript aes(scriptGlobalObject, "precompiled XUL <script> element");
   1020  JSContext* cx = aes.cx();
   1021 
   1022  JS::Rooted<JSScript*> scriptObject(cx);
   1023  rv = aScript->InstantiateScript(cx, &scriptObject);
   1024  NS_ENSURE_SUCCESS(rv, rv);
   1025 
   1026  JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
   1027  NS_ENSURE_TRUE(xpc::Scriptability::Get(global).Allowed(), NS_OK);
   1028 
   1029  if (!aScript->mOutOfLine) {
   1030    // Check if CSP allows loading of inline scripts.
   1031    if (nsCOMPtr<nsIContentSecurityPolicy> csp =
   1032            PolicyContainer::GetCSP(mDocument->GetPolicyContainer())) {
   1033      nsAutoJSString content;
   1034      JS::Rooted<JSString*> decompiled(cx,
   1035                                       JS_DecompileScript(cx, scriptObject));
   1036      if (NS_WARN_IF(!decompiled || !content.init(cx, decompiled))) {
   1037        JS_ClearPendingException(cx);
   1038      }
   1039 
   1040      bool allowInlineScript = false;
   1041      rv = csp->GetAllowsInline(
   1042          nsIContentSecurityPolicy::SCRIPT_SRC_ELEM_DIRECTIVE,
   1043          /* aHasUnsafeHash */ false, /* aNonce */ u""_ns,
   1044          /* aParserCreated */ true,
   1045          /* aTriggeringElement */ nullptr,
   1046          /* nsICSPEventListener */ nullptr,
   1047          /* aContentOfPseudoScript */ content, aScript->mLineNo,
   1048          /* aColumnNumber */ 0, &allowInlineScript);
   1049      if (NS_FAILED(rv) || !allowInlineScript) {
   1050        return NS_OK;
   1051      }
   1052    }
   1053  }
   1054 
   1055  // On failure, ~AutoScriptEntry will handle exceptions, so
   1056  // there is no need to manually check the return value.
   1057  JS::Rooted<JS::Value> rval(cx);
   1058  (void)JS_ExecuteScript(cx, scriptObject, &rval);
   1059 
   1060  return NS_OK;
   1061 }
   1062 
   1063 nsresult PrototypeDocumentContentSink::CreateElementFromPrototype(
   1064    nsXULPrototypeElement* aPrototype, Element** aResult, nsIContent* aParent) {
   1065  // Create a content model element from a prototype element.
   1066  MOZ_ASSERT(aPrototype, "null ptr");
   1067  if (!aPrototype) return NS_ERROR_NULL_POINTER;
   1068 
   1069  *aResult = nullptr;
   1070  nsresult rv = NS_OK;
   1071 
   1072  if (MOZ_LOG_TEST(gLog, LogLevel::Debug)) {
   1073    MOZ_LOG(
   1074        gLog, LogLevel::Debug,
   1075        ("prototype: creating <%s> from prototype",
   1076         NS_ConvertUTF16toUTF8(aPrototype->mNodeInfo->QualifiedName()).get()));
   1077  }
   1078 
   1079  RefPtr<Element> result;
   1080 
   1081  Document* doc = aParent ? aParent->OwnerDoc() : mDocument.get();
   1082  if (aPrototype->mNodeInfo->NamespaceEquals(kNameSpaceID_XUL)) {
   1083    const bool isRoot = !aParent;
   1084    // If it's a XUL element, it'll be lightweight until somebody
   1085    // monkeys with it.
   1086    result = nsXULElement::CreateFromPrototype(aPrototype, doc, isRoot);
   1087    if (!result) {
   1088      return NS_ERROR_OUT_OF_MEMORY;
   1089    }
   1090  } else {
   1091    // If it's not a XUL element, it's gonna be heavyweight no matter
   1092    // what. So we need to copy everything out of the prototype
   1093    // into the element.  Get a nodeinfo from our nodeinfo manager
   1094    // for this node.
   1095    RefPtr<NodeInfo> newNodeInfo = doc->NodeInfoManager()->GetNodeInfo(
   1096        aPrototype->mNodeInfo->NameAtom(),
   1097        aPrototype->mNodeInfo->GetPrefixAtom(),
   1098        aPrototype->mNodeInfo->NamespaceID(), nsINode::ELEMENT_NODE);
   1099    if (!newNodeInfo) {
   1100      return NS_ERROR_OUT_OF_MEMORY;
   1101    }
   1102    const bool isScript =
   1103        newNodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_XHTML) ||
   1104        newNodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_SVG);
   1105    if (aPrototype->mIsAtom &&
   1106        newNodeInfo->NamespaceID() == kNameSpaceID_XHTML) {
   1107      rv = NS_NewHTMLElement(getter_AddRefs(result), newNodeInfo.forget(),
   1108                             NOT_FROM_PARSER, aPrototype->mIsAtom);
   1109    } else {
   1110      rv = NS_NewElement(getter_AddRefs(result), newNodeInfo.forget(),
   1111                         NOT_FROM_PARSER);
   1112    }
   1113    if (NS_FAILED(rv)) return rv;
   1114 
   1115    rv = AddAttributes(aPrototype, result);
   1116    if (NS_FAILED(rv)) return rv;
   1117 
   1118    if (isScript) {
   1119      nsCOMPtr<nsIScriptElement> sele = do_QueryInterface(result);
   1120      MOZ_ASSERT(sele, "Node didn't QI to script.");
   1121 
   1122      sele->FreezeExecutionAttrs(doc);
   1123      // Script loading is handled by the this content sink, so prevent the
   1124      // script from loading when it is bound to the document.
   1125      //
   1126      // NOTE(emilio): This is only done for non-module scripts, because we
   1127      // don't support caching modules properly yet, see the comment in
   1128      // XULContentSinkImpl::OpenScript. For non-inline scripts, this is enough,
   1129      // since we can start the load when the node is inserted. Non-inline
   1130      // scripts need another special-case in CloseElement.
   1131      if (!sele->GetScriptIsModule()) {
   1132        sele->PreventExecution();
   1133      }
   1134    }
   1135  }
   1136 
   1137  // FIXME(bug 1627474): Is this right if this is inside an <html:template>?
   1138  if (result->HasAttr(nsGkAtoms::datal10nid)) {
   1139    mDocument->mL10nProtoElements.InsertOrUpdate(result, RefPtr{aPrototype});
   1140    result->SetElementCreatedFromPrototypeAndHasUnmodifiedL10n();
   1141  }
   1142  result.forget(aResult);
   1143  return NS_OK;
   1144 }
   1145 
   1146 nsresult PrototypeDocumentContentSink::AddAttributes(
   1147    nsXULPrototypeElement* aPrototype, Element* aElement) {
   1148  nsresult rv;
   1149 
   1150  for (size_t i = 0; i < aPrototype->mAttributes.Length(); ++i) {
   1151    nsXULPrototypeAttribute* protoattr = &(aPrototype->mAttributes[i]);
   1152    nsAutoString valueStr;
   1153    protoattr->mValue.ToString(valueStr);
   1154 
   1155    rv = aElement->SetAttr(protoattr->mName.NamespaceID(),
   1156                           protoattr->mName.LocalName(),
   1157                           protoattr->mName.GetPrefix(), valueStr, false);
   1158    if (NS_FAILED(rv)) return rv;
   1159  }
   1160 
   1161  return NS_OK;
   1162 }
   1163 
   1164 }  // namespace mozilla::dom