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