tor-browser

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

nsXULContentSink.cpp (25897B)


      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 /*
      8 * An implementation for a Gecko-style content sink that knows how
      9 * to build a content model (the "prototype" document) from XUL.
     10 *
     11 * For more information on XUL,
     12 * see http://developer.mozilla.org/en/docs/XUL
     13 */
     14 
     15 #include "nsXULContentSink.h"
     16 
     17 #include "jsfriendapi.h"
     18 #include "mozilla/Logging.h"
     19 #include "mozilla/css/Loader.h"
     20 #include "mozilla/dom/Document.h"
     21 #include "mozilla/dom/NodeInfo.h"
     22 #include "nsAttrName.h"
     23 #include "nsCOMPtr.h"
     24 #include "nsCRT.h"
     25 #include "nsContentTypeParser.h"
     26 #include "nsContentUtils.h"
     27 #include "nsGkAtoms.h"
     28 #include "nsIContentSink.h"
     29 #include "nsIFormControl.h"
     30 #include "nsIScriptContext.h"
     31 #include "nsIScriptError.h"
     32 #include "nsIScriptGlobalObject.h"
     33 #include "nsIScriptSecurityManager.h"
     34 #include "nsNameSpaceManager.h"
     35 #include "nsNetUtil.h"
     36 #include "nsParserBase.h"
     37 #include "nsReadableUtils.h"
     38 #include "nsString.h"
     39 #include "nsUnicharUtils.h"
     40 #include "nsXMLContentSink.h"
     41 #include "nsXULElement.h"
     42 #include "nsXULPrototypeDocument.h"  // XXXbe temporary
     43 
     44 static mozilla::LazyLogModule gContentSinkLog("nsXULContentSink");
     45 
     46 using namespace mozilla;
     47 using namespace mozilla::dom;
     48 //----------------------------------------------------------------------
     49 
     50 XULContentSinkImpl::ContextStack::ContextStack() : mTop(nullptr), mDepth(0) {}
     51 
     52 XULContentSinkImpl::ContextStack::~ContextStack() {
     53  while (mTop) {
     54    Entry* doomed = mTop;
     55    mTop = mTop->mNext;
     56    delete doomed;
     57  }
     58 }
     59 
     60 void XULContentSinkImpl::ContextStack::Push(RefPtr<nsXULPrototypeNode>&& aNode,
     61                                            State aState) {
     62  mTop = new Entry(std::move(aNode), aState, mTop);
     63  ++mDepth;
     64 }
     65 
     66 nsresult XULContentSinkImpl::ContextStack::Pop(State* aState) {
     67  if (mDepth == 0) return NS_ERROR_UNEXPECTED;
     68 
     69  Entry* entry = mTop;
     70  mTop = mTop->mNext;
     71  --mDepth;
     72 
     73  *aState = entry->mState;
     74  delete entry;
     75 
     76  return NS_OK;
     77 }
     78 
     79 nsresult XULContentSinkImpl::ContextStack::GetTopNode(
     80    RefPtr<nsXULPrototypeNode>& aNode) {
     81  if (mDepth == 0) return NS_ERROR_UNEXPECTED;
     82 
     83  aNode = mTop->mNode;
     84  return NS_OK;
     85 }
     86 
     87 nsresult XULContentSinkImpl::ContextStack::GetTopChildren(
     88    nsPrototypeArray** aChildren) {
     89  if (mDepth == 0) return NS_ERROR_UNEXPECTED;
     90 
     91  *aChildren = &(mTop->mChildren);
     92  return NS_OK;
     93 }
     94 
     95 void XULContentSinkImpl::ContextStack::Clear() {
     96  Entry* cur = mTop;
     97  while (cur) {
     98    // Release the root element (and its descendants).
     99    Entry* next = cur->mNext;
    100    delete cur;
    101    cur = next;
    102  }
    103 
    104  mTop = nullptr;
    105  mDepth = 0;
    106 }
    107 
    108 void XULContentSinkImpl::ContextStack::Traverse(
    109    nsCycleCollectionTraversalCallback& aCb) {
    110  nsCycleCollectionTraversalCallback& cb = aCb;
    111  for (ContextStack::Entry* tmp = mTop; tmp; tmp = tmp->mNext) {
    112    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNode)
    113    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildren)
    114  }
    115 }
    116 
    117 //----------------------------------------------------------------------
    118 
    119 XULContentSinkImpl::XULContentSinkImpl()
    120    : mText(nullptr),
    121      mTextLength(0),
    122      mTextSize(0),
    123      mConstrainSize(true),
    124      mState(eInProlog) {}
    125 
    126 XULContentSinkImpl::~XULContentSinkImpl() {
    127  // The context stack _should_ be empty, unless something has gone wrong.
    128  NS_ASSERTION(mContextStack.Depth() == 0, "Context stack not empty?");
    129  mContextStack.Clear();
    130 
    131  free(mText);
    132 }
    133 
    134 //----------------------------------------------------------------------
    135 // nsISupports interface
    136 
    137 NS_IMPL_CYCLE_COLLECTION_CLASS(XULContentSinkImpl)
    138 
    139 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(XULContentSinkImpl)
    140  NS_IMPL_CYCLE_COLLECTION_UNLINK(mNodeInfoManager)
    141  tmp->mContextStack.Clear();
    142  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrototype)
    143  NS_IMPL_CYCLE_COLLECTION_UNLINK(mParser)
    144 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    145 
    146 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(XULContentSinkImpl)
    147  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNodeInfoManager)
    148  tmp->mContextStack.Traverse(cb);
    149  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrototype)
    150  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParser)
    151 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    152 
    153 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(XULContentSinkImpl)
    154  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXMLContentSink)
    155  NS_INTERFACE_MAP_ENTRY(nsIXMLContentSink)
    156  NS_INTERFACE_MAP_ENTRY(nsIExpatSink)
    157  NS_INTERFACE_MAP_ENTRY(nsIContentSink)
    158 NS_INTERFACE_MAP_END
    159 
    160 NS_IMPL_CYCLE_COLLECTING_ADDREF(XULContentSinkImpl)
    161 NS_IMPL_CYCLE_COLLECTING_RELEASE(XULContentSinkImpl)
    162 
    163 //----------------------------------------------------------------------
    164 // nsIContentSink interface
    165 
    166 NS_IMETHODIMP
    167 XULContentSinkImpl::DidBuildModel(bool aTerminated) {
    168  nsCOMPtr<Document> doc(mDocument);
    169  if (doc) {
    170    mPrototype->NotifyLoadDone();
    171    mDocument = nullptr;
    172  }
    173 
    174  // Drop our reference to the parser to get rid of a circular
    175  // reference.
    176  mParser = nullptr;
    177  return NS_OK;
    178 }
    179 
    180 NS_IMETHODIMP
    181 XULContentSinkImpl::WillInterrupt(void) {
    182  // XXX Notify the docshell, if necessary
    183  return NS_OK;
    184 }
    185 
    186 void XULContentSinkImpl::WillResume() {
    187  // XXX Notify the docshell, if necessary
    188 }
    189 
    190 NS_IMETHODIMP
    191 XULContentSinkImpl::SetParser(nsParserBase* aParser) {
    192  mParser = aParser;
    193  return NS_OK;
    194 }
    195 
    196 void XULContentSinkImpl::SetDocumentCharset(
    197    NotNull<const Encoding*> aEncoding) {
    198  nsCOMPtr<Document> doc(mDocument);
    199  if (doc) {
    200    doc->SetDocumentCharacterSet(aEncoding);
    201  }
    202 }
    203 
    204 nsISupports* XULContentSinkImpl::GetTarget() { return ToSupports(mDocument); }
    205 
    206 //----------------------------------------------------------------------
    207 
    208 nsresult XULContentSinkImpl::Init(Document* aDocument,
    209                                  nsXULPrototypeDocument* aPrototype) {
    210  MOZ_ASSERT(aDocument != nullptr, "null ptr");
    211  if (!aDocument) return NS_ERROR_NULL_POINTER;
    212 
    213  mDocument = aDocument;
    214  mPrototype = aPrototype;
    215 
    216  mDocumentURL = mPrototype->GetURI();
    217  mNodeInfoManager = aPrototype->GetNodeInfoManager();
    218  if (!mNodeInfoManager) return NS_ERROR_UNEXPECTED;
    219 
    220  mState = eInProlog;
    221  return NS_OK;
    222 }
    223 
    224 //----------------------------------------------------------------------
    225 //
    226 // Text buffering
    227 //
    228 
    229 bool XULContentSinkImpl::IsDataInBuffer(char16_t* buffer, int32_t length) {
    230  for (int32_t i = 0; i < length; ++i) {
    231    if (buffer[i] == ' ' || buffer[i] == '\t' || buffer[i] == '\n' ||
    232        buffer[i] == '\r')
    233      continue;
    234 
    235    return true;
    236  }
    237  return false;
    238 }
    239 
    240 nsresult XULContentSinkImpl::FlushText(bool aCreateTextNode) {
    241  nsresult rv;
    242 
    243  do {
    244    // Don't do anything if there's no text to create a node from, or
    245    // if they've told us not to create a text node
    246    if (!mTextLength) break;
    247 
    248    if (!aCreateTextNode) break;
    249 
    250    RefPtr<nsXULPrototypeNode> node;
    251    rv = mContextStack.GetTopNode(node);
    252    if (NS_FAILED(rv)) return rv;
    253 
    254    bool stripWhitespace = false;
    255    if (node->mType == nsXULPrototypeNode::eType_Element) {
    256      mozilla::dom::NodeInfo* nodeInfo =
    257          static_cast<nsXULPrototypeElement*>(node.get())->mNodeInfo;
    258 
    259      if (nodeInfo->NamespaceEquals(kNameSpaceID_XUL))
    260        stripWhitespace = !nodeInfo->Equals(nsGkAtoms::label) &&
    261                          !nodeInfo->Equals(nsGkAtoms::description);
    262    }
    263 
    264    // Don't bother if there's nothing but whitespace.
    265    if (stripWhitespace && !IsDataInBuffer(mText, mTextLength)) break;
    266 
    267    // Don't bother if we're not in XUL document body
    268    if (mState != eInDocumentElement || mContextStack.Depth() == 0) break;
    269 
    270    RefPtr<nsXULPrototypeText> text = new nsXULPrototypeText();
    271    text->mValue.Assign(mText, mTextLength);
    272    if (stripWhitespace) text->mValue.Trim(" \t\n\r");
    273 
    274    // hook it up
    275    nsPrototypeArray* children = nullptr;
    276    rv = mContextStack.GetTopChildren(&children);
    277    if (NS_FAILED(rv)) return rv;
    278 
    279    children->AppendElement(text.forget());
    280  } while (0);
    281 
    282  // Reset our text buffer
    283  mTextLength = 0;
    284  return NS_OK;
    285 }
    286 
    287 //----------------------------------------------------------------------
    288 
    289 nsresult XULContentSinkImpl::NormalizeAttributeString(
    290    const char16_t* aExpatName, nsAttrName& aName) {
    291  int32_t nameSpaceID;
    292  RefPtr<nsAtom> prefix, localName;
    293  nsContentUtils::SplitExpatName(aExpatName, getter_AddRefs(prefix),
    294                                 getter_AddRefs(localName), &nameSpaceID);
    295 
    296  if (nameSpaceID == kNameSpaceID_None) {
    297    aName.SetTo(localName);
    298 
    299    return NS_OK;
    300  }
    301 
    302  RefPtr<mozilla::dom::NodeInfo> ni;
    303  ni = mNodeInfoManager->GetNodeInfo(localName, prefix, nameSpaceID,
    304                                     nsINode::ATTRIBUTE_NODE);
    305  aName.SetTo(ni);
    306 
    307  return NS_OK;
    308 }
    309 
    310 /**** BEGIN NEW APIs ****/
    311 
    312 NS_IMETHODIMP
    313 XULContentSinkImpl::HandleStartElement(const char16_t* aName,
    314                                       const char16_t** aAtts,
    315                                       uint32_t aAttsCount,
    316                                       uint32_t aLineNumber,
    317                                       uint32_t aColumnNumber) {
    318  // XXX Hopefully the parser will flag this before we get here. If
    319  // we're in the epilog, there should be no new elements
    320  MOZ_ASSERT(mState != eInEpilog, "tag in XUL doc epilog");
    321  MOZ_ASSERT(aAttsCount % 2 == 0, "incorrect aAttsCount");
    322 
    323  // Adjust aAttsCount so it's the actual number of attributes
    324  aAttsCount /= 2;
    325 
    326  if (mState == eInEpilog) return NS_ERROR_UNEXPECTED;
    327 
    328  if (mState != eInScript) {
    329    FlushText();
    330  }
    331 
    332  int32_t nameSpaceID;
    333  RefPtr<nsAtom> prefix, localName;
    334  nsContentUtils::SplitExpatName(aName, getter_AddRefs(prefix),
    335                                 getter_AddRefs(localName), &nameSpaceID);
    336 
    337  RefPtr<mozilla::dom::NodeInfo> nodeInfo;
    338  nodeInfo = mNodeInfoManager->GetNodeInfo(localName, prefix, nameSpaceID,
    339                                           nsINode::ELEMENT_NODE);
    340 
    341  nsresult rv = NS_OK;
    342  switch (mState) {
    343    case eInProlog:
    344      // We're the root document element
    345      rv = OpenRoot(aAtts, aAttsCount, nodeInfo);
    346      break;
    347 
    348    case eInDocumentElement:
    349      rv = OpenTag(aAtts, aAttsCount, aLineNumber, nodeInfo);
    350      break;
    351 
    352    case eInEpilog:
    353    case eInScript:
    354      MOZ_LOG(
    355          gContentSinkLog, LogLevel::Warning,
    356          ("xul: warning: unexpected tags in epilog at line %d", aLineNumber));
    357      rv = NS_ERROR_UNEXPECTED;  // XXX
    358      break;
    359  }
    360 
    361  return rv;
    362 }
    363 
    364 NS_IMETHODIMP
    365 XULContentSinkImpl::HandleEndElement(const char16_t* aName) {
    366  // Never EVER return anything but NS_OK or
    367  // NS_ERROR_HTMLPARSER_BLOCK from this method. Doing so will blow
    368  // the parser's little mind all over the planet.
    369  nsresult rv;
    370 
    371  RefPtr<nsXULPrototypeNode> node;
    372  rv = mContextStack.GetTopNode(node);
    373 
    374  if (NS_FAILED(rv)) {
    375    return NS_OK;
    376  }
    377 
    378  switch (node->mType) {
    379    case nsXULPrototypeNode::eType_Element: {
    380      // Flush any text _now_, so that we'll get text nodes created
    381      // before popping the stack.
    382      FlushText();
    383 
    384      // Pop the context stack and do prototype hookup.
    385      nsPrototypeArray* children = nullptr;
    386      rv = mContextStack.GetTopChildren(&children);
    387      if (NS_FAILED(rv)) return rv;
    388 
    389      nsXULPrototypeElement* element =
    390          static_cast<nsXULPrototypeElement*>(node.get());
    391 
    392      int32_t count = children->Length();
    393      if (count) {
    394        element->mChildren.SetCapacity(count);
    395 
    396        for (int32_t i = 0; i < count; ++i)
    397          element->mChildren.AppendElement(children->ElementAt(i));
    398      }
    399    } break;
    400 
    401    case nsXULPrototypeNode::eType_Script: {
    402      nsXULPrototypeScript* script =
    403          static_cast<nsXULPrototypeScript*>(node.get());
    404 
    405      // If given a src= attribute, we must ignore script tag content.
    406      if (!script->mSrcURI && !script->HasStencil()) {
    407        nsCOMPtr<Document> doc(mDocument);
    408 
    409        script->mOutOfLine = false;
    410        if (doc) {
    411          script->Compile(mText, mTextLength, mDocumentURL, script->mLineNo,
    412                          doc);
    413        }
    414      }
    415 
    416      FlushText(false);
    417    } break;
    418 
    419    default:
    420      NS_ERROR("didn't expect that");
    421      break;
    422  }
    423 
    424  rv = mContextStack.Pop(&mState);
    425  NS_ASSERTION(NS_SUCCEEDED(rv), "context stack corrupted");
    426  if (NS_FAILED(rv)) return rv;
    427 
    428  if (mContextStack.Depth() == 0) {
    429    // The root element should -always- be an element, because
    430    // it'll have been created via XULContentSinkImpl::OpenRoot().
    431    NS_ASSERTION(node->mType == nsXULPrototypeNode::eType_Element,
    432                 "root is not an element");
    433    if (node->mType != nsXULPrototypeNode::eType_Element)
    434      return NS_ERROR_UNEXPECTED;
    435 
    436    // Now that we're done parsing, set the prototype document's
    437    // root element. This transfers ownership of the prototype
    438    // element tree to the prototype document.
    439    nsXULPrototypeElement* element =
    440        static_cast<nsXULPrototypeElement*>(node.get());
    441 
    442    mPrototype->SetRootElement(element);
    443    mState = eInEpilog;
    444  }
    445 
    446  return NS_OK;
    447 }
    448 
    449 NS_IMETHODIMP
    450 XULContentSinkImpl::HandleComment(const char16_t* aName) {
    451  FlushText();
    452  return NS_OK;
    453 }
    454 
    455 NS_IMETHODIMP
    456 XULContentSinkImpl::HandleCDataSection(const char16_t* aData,
    457                                       uint32_t aLength) {
    458  FlushText();
    459  return AddText(aData, aLength);
    460 }
    461 
    462 NS_IMETHODIMP
    463 XULContentSinkImpl::HandleDoctypeDecl(const nsAString& aSubset,
    464                                      const nsAString& aName,
    465                                      const nsAString& aSystemId,
    466                                      const nsAString& aPublicId,
    467                                      nsISupports* aCatalogData) {
    468  return NS_OK;
    469 }
    470 
    471 NS_IMETHODIMP
    472 XULContentSinkImpl::HandleCharacterData(const char16_t* aData,
    473                                        uint32_t aLength) {
    474  if (aData && mState != eInProlog && mState != eInEpilog) {
    475    return AddText(aData, aLength);
    476  }
    477  return NS_OK;
    478 }
    479 
    480 NS_IMETHODIMP
    481 XULContentSinkImpl::HandleProcessingInstruction(const char16_t* aTarget,
    482                                                const char16_t* aData) {
    483  FlushText();
    484 
    485  const nsDependentString target(aTarget);
    486  const nsDependentString data(aData);
    487 
    488  // Note: the created nsXULPrototypePI has mRefCnt == 1
    489  RefPtr<nsXULPrototypePI> pi = new nsXULPrototypePI();
    490  pi->mTarget = target;
    491  pi->mData = data;
    492 
    493  if (mState == eInProlog) {
    494    // Note: passing in already addrefed pi
    495    return mPrototype->AddProcessingInstruction(pi);
    496  }
    497 
    498  nsresult rv;
    499  nsPrototypeArray* children = nullptr;
    500  rv = mContextStack.GetTopChildren(&children);
    501  if (NS_FAILED(rv)) {
    502    return rv;
    503  }
    504 
    505  // XXX(Bug 1631371) Check if this should use a fallible operation as it
    506  // pretended earlier.
    507  children->AppendElement(pi);
    508 
    509  return NS_OK;
    510 }
    511 
    512 NS_IMETHODIMP
    513 XULContentSinkImpl::HandleXMLDeclaration(const char16_t* aVersion,
    514                                         const char16_t* aEncoding,
    515                                         int32_t aStandalone) {
    516  return NS_OK;
    517 }
    518 
    519 NS_IMETHODIMP
    520 XULContentSinkImpl::ReportError(const char16_t* aErrorText,
    521                                const char16_t* aSourceText,
    522                                nsIScriptError* aError, bool* _retval) {
    523  MOZ_ASSERT(aError && aSourceText && aErrorText, "Check arguments!!!");
    524 
    525  // The expat driver should report the error.
    526  *_retval = true;
    527 
    528  nsresult rv = NS_OK;
    529 
    530  // make sure to empty the context stack so that
    531  // <parsererror> could become the root element.
    532  mContextStack.Clear();
    533 
    534  mState = eInProlog;
    535 
    536  // Clear any buffered-up text we have.  It's enough to set the length to 0.
    537  // The buffer itself is allocated when we're created and deleted in our
    538  // destructor, so don't mess with it.
    539  mTextLength = 0;
    540 
    541  // return leaving the document empty if we're asked to not add a <parsererror>
    542  // root node
    543  nsCOMPtr<Document> idoc(mDocument);
    544  if (idoc && idoc->SuppressParserErrorElement()) {
    545    return NS_OK;
    546  };
    547 
    548  const char16_t* noAtts[] = {0, 0};
    549 
    550  constexpr auto errorNs =
    551      u"http://www.mozilla.org/newlayout/xml/parsererror.xml"_ns;
    552 
    553  nsAutoString parsererror(errorNs);
    554  parsererror.Append((char16_t)0xFFFF);
    555  parsererror.AppendLiteral("parsererror");
    556 
    557  rv = HandleStartElement(parsererror.get(), noAtts, 0, 0, 0);
    558  NS_ENSURE_SUCCESS(rv, rv);
    559 
    560  rv = HandleCharacterData(aErrorText, NS_strlen(aErrorText));
    561  NS_ENSURE_SUCCESS(rv, rv);
    562 
    563  nsAutoString sourcetext(errorNs);
    564  sourcetext.Append((char16_t)0xFFFF);
    565  sourcetext.AppendLiteral("sourcetext");
    566 
    567  rv = HandleStartElement(sourcetext.get(), noAtts, 0, 0, 0);
    568  NS_ENSURE_SUCCESS(rv, rv);
    569 
    570  rv = HandleCharacterData(aSourceText, NS_strlen(aSourceText));
    571  NS_ENSURE_SUCCESS(rv, rv);
    572 
    573  rv = HandleEndElement(sourcetext.get());
    574  NS_ENSURE_SUCCESS(rv, rv);
    575 
    576  rv = HandleEndElement(parsererror.get());
    577  NS_ENSURE_SUCCESS(rv, rv);
    578 
    579  return rv;
    580 }
    581 
    582 nsresult XULContentSinkImpl::OpenRoot(const char16_t** aAttributes,
    583                                      const uint32_t aAttrLen,
    584                                      mozilla::dom::NodeInfo* aNodeInfo) {
    585  NS_ASSERTION(mState == eInProlog, "how'd we get here?");
    586  if (mState != eInProlog) return NS_ERROR_UNEXPECTED;
    587 
    588  if (aNodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_XHTML) ||
    589      aNodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_XUL)) {
    590    MOZ_LOG(gContentSinkLog, LogLevel::Error,
    591            ("xul: script tag not allowed as root content element"));
    592 
    593    return NS_ERROR_UNEXPECTED;
    594  }
    595 
    596  // Create the element
    597  RefPtr<nsXULPrototypeElement> element = new nsXULPrototypeElement(aNodeInfo);
    598 
    599  // Add the attributes
    600  nsresult rv = AddAttributes(aAttributes, aAttrLen, element);
    601  if (NS_FAILED(rv)) return rv;
    602 
    603  // Push the element onto the context stack, so that child
    604  // containers will hook up to us as their parent.
    605  mContextStack.Push(std::move(element), mState);
    606 
    607  mState = eInDocumentElement;
    608  return NS_OK;
    609 }
    610 
    611 nsresult XULContentSinkImpl::OpenTag(const char16_t** aAttributes,
    612                                     const uint32_t aAttrLen,
    613                                     const uint32_t aLineNumber,
    614                                     mozilla::dom::NodeInfo* aNodeInfo) {
    615  // Create the element
    616  RefPtr<nsXULPrototypeElement> element = new nsXULPrototypeElement(aNodeInfo);
    617 
    618  // Link this element to its parent.
    619  nsPrototypeArray* children = nullptr;
    620  nsresult rv = mContextStack.GetTopChildren(&children);
    621  if (NS_FAILED(rv)) {
    622    return rv;
    623  }
    624 
    625  // Add the attributes
    626  rv = AddAttributes(aAttributes, aAttrLen, element);
    627  if (NS_FAILED(rv)) return rv;
    628 
    629  children->AppendElement(element);
    630 
    631  if (aNodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_XHTML) ||
    632      aNodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_XUL)) {
    633    // Do scripty things now
    634    rv = OpenScript(aAttributes, aLineNumber);
    635    NS_ENSURE_SUCCESS(rv, rv);
    636 
    637    NS_ASSERTION(mState == eInScript || mState == eInDocumentElement,
    638                 "Unexpected state");
    639    if (mState == eInScript) {
    640      // OpenScript has pushed the nsPrototypeScriptElement onto the
    641      // stack, so we're done.
    642      return NS_OK;
    643    }
    644  }
    645 
    646  // Push the element onto the context stack, so that child
    647  // containers will hook up to us as their parent.
    648  mContextStack.Push(std::move(element), mState);
    649 
    650  mState = eInDocumentElement;
    651  return NS_OK;
    652 }
    653 
    654 nsresult XULContentSinkImpl::OpenScript(const char16_t** aAttributes,
    655                                        const uint32_t aLineNumber) {
    656  bool isJavaScript = true;
    657  nsresult rv;
    658 
    659  // Look for SRC attribute and look for a LANGUAGE attribute
    660  nsAutoString src;
    661  while (*aAttributes) {
    662    const nsDependentString key(aAttributes[0]);
    663    if (key.EqualsLiteral("src")) {
    664      src.Assign(aAttributes[1]);
    665    } else if (key.EqualsLiteral("type")) {
    666      nsDependentString str(aAttributes[1]);
    667      nsContentTypeParser parser(str);
    668      nsAutoString mimeType;
    669      rv = parser.GetType(mimeType);
    670      if (NS_FAILED(rv)) {
    671        if (rv == NS_ERROR_INVALID_ARG) {
    672          // Fail immediately rather than checking if later things
    673          // are okay.
    674          return NS_OK;
    675        }
    676        // We do want the warning here
    677        NS_ENSURE_SUCCESS(rv, rv);
    678      }
    679 
    680      // NOTE(emilio): Module scripts don't pass this test, aren't cached yet.
    681      // If they become cached, then we need to tweak
    682      // PrototypeDocumentContentSink and remove the special cases there.
    683      if (nsContentUtils::IsJavascriptMIMEType(mimeType)) {
    684        isJavaScript = true;
    685 
    686        // Get the version string, and ensure that JavaScript supports it.
    687        nsAutoString versionName;
    688        rv = parser.GetParameter("version", versionName);
    689 
    690        if (NS_SUCCEEDED(rv)) {
    691          nsContentUtils::ReportToConsoleNonLocalized(
    692              u"Versioned JavaScripts are no longer supported. "
    693              "Please remove the version parameter."_ns,
    694              nsIScriptError::errorFlag, "XUL Document"_ns, nullptr,
    695              SourceLocation(mDocumentURL.get()));
    696          isJavaScript = false;
    697        } else if (rv != NS_ERROR_INVALID_ARG) {
    698          return rv;
    699        }
    700      } else {
    701        isJavaScript = false;
    702      }
    703    } else if (key.EqualsLiteral("language")) {
    704      // Language is deprecated, and the impl in ScriptLoader ignores the
    705      // various version strings anyway.  So we make no attempt to support
    706      // languages other than JS for language=
    707      nsAutoString lang(aAttributes[1]);
    708      if (nsContentUtils::IsJavaScriptLanguage(lang)) {
    709        isJavaScript = true;
    710      }
    711    }
    712    aAttributes += 2;
    713  }
    714 
    715  // Don't process scripts that aren't JavaScript.
    716  if (!isJavaScript) {
    717    return NS_OK;
    718  }
    719 
    720  nsCOMPtr<Document> doc(mDocument);
    721  nsCOMPtr<nsIScriptGlobalObject> globalObject;
    722  if (doc) globalObject = do_QueryInterface(doc->GetWindow());
    723  RefPtr<nsXULPrototypeScript> script = new nsXULPrototypeScript(aLineNumber);
    724 
    725  // If there is a SRC attribute...
    726  if (!src.IsEmpty()) {
    727    // Use the SRC attribute value to load the URL
    728    rv = NS_NewURI(getter_AddRefs(script->mSrcURI), src, nullptr, mDocumentURL);
    729 
    730    // Check if this document is allowed to load a script from this source
    731    // NOTE: if we ever allow scripts added via the DOM to run, we need to
    732    // add a CheckLoadURI call for that as well.
    733    if (NS_SUCCEEDED(rv)) {
    734      if (!mSecMan)
    735        mSecMan = do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
    736      if (NS_SUCCEEDED(rv) && doc) {
    737        rv = mSecMan->CheckLoadURIWithPrincipal(
    738            doc->NodePrincipal(), script->mSrcURI,
    739            nsIScriptSecurityManager::ALLOW_CHROME, doc->InnerWindowID());
    740      }
    741    }
    742 
    743    if (NS_FAILED(rv)) {
    744      return rv;
    745    }
    746 
    747    // Attempt to deserialize an out-of-line script from the FastLoad
    748    // file right away.  Otherwise we'll end up reloading the script and
    749    // corrupting the FastLoad file trying to serialize it, in the case
    750    // where it's already there.
    751    script->DeserializeOutOfLine(nullptr, mPrototype);
    752  }
    753 
    754  nsPrototypeArray* children = nullptr;
    755  rv = mContextStack.GetTopChildren(&children);
    756  if (NS_FAILED(rv)) {
    757    return rv;
    758  }
    759 
    760  children->AppendElement(script);
    761 
    762  mConstrainSize = false;
    763 
    764  mContextStack.Push(script, mState);
    765  mState = eInScript;
    766 
    767  return NS_OK;
    768 }
    769 
    770 nsresult XULContentSinkImpl::AddAttributes(const char16_t** aAttributes,
    771                                           const uint32_t aAttrLen,
    772                                           nsXULPrototypeElement* aElement) {
    773  // Add tag attributes to the element
    774  nsresult rv;
    775 
    776  // Create storage for the attributes
    777  nsXULPrototypeAttribute* attrs = nullptr;
    778  if (aAttrLen > 0) {
    779    attrs = aElement->mAttributes.AppendElements(aAttrLen);
    780  }
    781 
    782  // Copy the attributes into the prototype
    783  uint32_t i;
    784  for (i = 0; i < aAttrLen; ++i) {
    785    rv = NormalizeAttributeString(aAttributes[i * 2], attrs[i].mName);
    786    NS_ENSURE_SUCCESS(rv, rv);
    787 
    788    rv = aElement->SetAttrAt(i, nsDependentString(aAttributes[i * 2 + 1]),
    789                             mDocumentURL);
    790    NS_ENSURE_SUCCESS(rv, rv);
    791 
    792    if (MOZ_LOG_TEST(gContentSinkLog, LogLevel::Debug)) {
    793      nsAutoString extraWhiteSpace;
    794      int32_t cnt = mContextStack.Depth();
    795      while (--cnt >= 0) extraWhiteSpace.AppendLiteral("  ");
    796      nsAutoString qnameC, valueC;
    797      qnameC.Assign(aAttributes[0]);
    798      valueC.Assign(aAttributes[1]);
    799      MOZ_LOG(gContentSinkLog, LogLevel::Debug,
    800              ("xul: %.5d. %s    %s=%s",
    801               -1,  // XXX pass in line number
    802               NS_ConvertUTF16toUTF8(extraWhiteSpace).get(),
    803               NS_ConvertUTF16toUTF8(qnameC).get(),
    804               NS_ConvertUTF16toUTF8(valueC).get()));
    805    }
    806  }
    807 
    808  return NS_OK;
    809 }
    810 
    811 nsresult XULContentSinkImpl::AddText(const char16_t* aText, int32_t aLength) {
    812  // Create buffer when we first need it
    813  if (0 == mTextSize) {
    814    mText = (char16_t*)malloc(sizeof(char16_t) * 4096);
    815    if (nullptr == mText) {
    816      return NS_ERROR_OUT_OF_MEMORY;
    817    }
    818    mTextSize = 4096;
    819  }
    820 
    821  // Copy data from string into our buffer; flush buffer when it fills up
    822  int32_t offset = 0;
    823  while (0 != aLength) {
    824    int32_t amount = mTextSize - mTextLength;
    825    if (amount > aLength) {
    826      amount = aLength;
    827    }
    828    if (0 == amount) {
    829      if (mConstrainSize) {
    830        nsresult rv = FlushText();
    831        if (NS_OK != rv) {
    832          return rv;
    833        }
    834      } else {
    835        CheckedInt32 size = mTextSize;
    836        size += aLength;
    837        if (!size.isValid()) {
    838          return NS_ERROR_OUT_OF_MEMORY;
    839        }
    840        mTextSize = size.value();
    841 
    842        mText = (char16_t*)realloc(mText, sizeof(char16_t) * mTextSize);
    843        if (nullptr == mText) {
    844          return NS_ERROR_OUT_OF_MEMORY;
    845        }
    846      }
    847    }
    848    memcpy(&mText[mTextLength], aText + offset, sizeof(char16_t) * amount);
    849 
    850    mTextLength += amount;
    851    offset += amount;
    852    aLength -= amount;
    853  }
    854 
    855  return NS_OK;
    856 }