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