1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "nsCOMPtr.h"
8 #include "nsXMLContentSink.h"
9 #include "nsIParser.h"
10 #include "mozilla/dom/Document.h"
11 #include "nsIContent.h"
12 #include "nsIURI.h"
13 #include "nsNetUtil.h"
14 #include "nsHTMLParts.h"
15 #include "nsCRT.h"
16 #include "mozilla/StyleSheetInlines.h"
17 #include "mozilla/css/Loader.h"
18 #include "nsGkAtoms.h"
19 #include "nsContentUtils.h"
20 #include "nsDocElementCreatedNotificationRunner.h"
21 #include "nsIDocShell.h"
22 #include "nsIScriptContext.h"
23 #include "nsNameSpaceManager.h"
24 #include "nsIScriptSecurityManager.h"
25 #include "nsIContentViewer.h"
26 #include "prtime.h"
27 #include "mozilla/Logging.h"
28 #include "nsRect.h"
29 #include "nsIScriptElement.h"
30 #include "nsReadableUtils.h"
31 #include "nsUnicharUtils.h"
32 #include "nsIChannel.h"
33 #include "nsXMLPrettyPrinter.h"
34 #include "nsNodeInfoManager.h"
35 #include "nsContentCreatorFunctions.h"
36 #include "nsIContentPolicy.h"
37 #include "nsContentPolicyUtils.h"
38 #include "nsError.h"
39 #include "nsIScriptGlobalObject.h"
40 #include "mozAutoDocUpdate.h"
41 #include "nsMimeTypes.h"
42 #include "nsHtml5SVGLoadDispatcher.h"
43 #include "nsTextNode.h"
44 #include "mozilla/dom/CDATASection.h"
45 #include "mozilla/dom/Comment.h"
46 #include "mozilla/dom/DocumentType.h"
47 #include "mozilla/dom/Element.h"
48 #include "mozilla/dom/HTMLTemplateElement.h"
49 #include "mozilla/dom/MutationObservers.h"
50 #include "mozilla/dom/ProcessingInstruction.h"
51 #include "mozilla/dom/ScriptLoader.h"
52 #include "mozilla/dom/txMozillaXSLTProcessor.h"
53 #include "mozilla/CycleCollectedJSContext.h"
54 #include "mozilla/LoadInfo.h"
55 
56 using namespace mozilla;
57 using namespace mozilla::dom;
58 
59 // XXX Open Issues:
60 // 1) what's not allowed - We need to figure out which HTML tags
61 //    (prefixed with a HTML namespace qualifier) are explicitly not
62 //    allowed (if any).
63 // 2) factoring code with nsHTMLContentSink - There's some amount of
64 //    common code between this and the HTML content sink. This will
65 //    increase as we support more and more HTML elements. How can code
66 //    from the code be factored?
67 
NS_NewXMLContentSink(nsIXMLContentSink ** aResult,Document * aDoc,nsIURI * aURI,nsISupports * aContainer,nsIChannel * aChannel)68 nsresult NS_NewXMLContentSink(nsIXMLContentSink** aResult, Document* aDoc,
69                               nsIURI* aURI, nsISupports* aContainer,
70                               nsIChannel* aChannel) {
71   MOZ_ASSERT(nullptr != aResult, "null ptr");
72   if (nullptr == aResult) {
73     return NS_ERROR_NULL_POINTER;
74   }
75   RefPtr<nsXMLContentSink> it = new nsXMLContentSink();
76 
77   nsresult rv = it->Init(aDoc, aURI, aContainer, aChannel);
78   NS_ENSURE_SUCCESS(rv, rv);
79 
80   it.forget(aResult);
81   return NS_OK;
82 }
83 
nsXMLContentSink()84 nsXMLContentSink::nsXMLContentSink()
85     : mState(eXMLContentSinkState_InProlog),
86       mTextLength(0),
87       mNotifyLevel(0),
88       mPrettyPrintXML(true),
89       mPrettyPrintHasSpecialRoot(0),
90       mPrettyPrintHasFactoredElements(0),
91       mPrettyPrinting(0),
92       mPreventScriptExecution(0) {
93   PodArrayZero(mText);
94 }
95 
96 nsXMLContentSink::~nsXMLContentSink() = default;
97 
Init(Document * aDoc,nsIURI * aURI,nsISupports * aContainer,nsIChannel * aChannel)98 nsresult nsXMLContentSink::Init(Document* aDoc, nsIURI* aURI,
99                                 nsISupports* aContainer, nsIChannel* aChannel) {
100   nsresult rv = nsContentSink::Init(aDoc, aURI, aContainer, aChannel);
101   NS_ENSURE_SUCCESS(rv, rv);
102 
103   aDoc->AddObserver(this);
104   mIsDocumentObserver = true;
105 
106   if (!mDocShell) {
107     mPrettyPrintXML = false;
108   }
109 
110   mState = eXMLContentSinkState_InProlog;
111   mDocElement = nullptr;
112 
113   return NS_OK;
114 }
115 
ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback & aCallback,nsXMLContentSink::StackNode & aField,const char * aName,uint32_t aFlags=0)116 inline void ImplCycleCollectionTraverse(
117     nsCycleCollectionTraversalCallback& aCallback,
118     nsXMLContentSink::StackNode& aField, const char* aName,
119     uint32_t aFlags = 0) {
120   ImplCycleCollectionTraverse(aCallback, aField.mContent, aName, aFlags);
121 }
122 
ImplCycleCollectionUnlink(nsXMLContentSink::StackNode & aField)123 inline void ImplCycleCollectionUnlink(nsXMLContentSink::StackNode& aField) {
124   ImplCycleCollectionUnlink(aField.mContent);
125 }
126 
127 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXMLContentSink)
NS_INTERFACE_MAP_ENTRY(nsIContentSink)128   NS_INTERFACE_MAP_ENTRY(nsIContentSink)
129   NS_INTERFACE_MAP_ENTRY(nsIXMLContentSink)
130   NS_INTERFACE_MAP_ENTRY(nsIExpatSink)
131   NS_INTERFACE_MAP_ENTRY(nsITransformObserver)
132 NS_INTERFACE_MAP_END_INHERITING(nsContentSink)
133 
134 NS_IMPL_ADDREF_INHERITED(nsXMLContentSink, nsContentSink)
135 NS_IMPL_RELEASE_INHERITED(nsXMLContentSink, nsContentSink)
136 
137 NS_IMPL_CYCLE_COLLECTION_INHERITED(nsXMLContentSink, nsContentSink,
138                                    mCurrentHead, mDocElement, mLastTextNode,
139                                    mContentStack, mDocumentChildren)
140 
141 // nsIContentSink
142 NS_IMETHODIMP
143 nsXMLContentSink::WillParse(void) { return WillParseImpl(); }
144 
145 NS_IMETHODIMP
WillBuildModel(nsDTDMode aDTDMode)146 nsXMLContentSink::WillBuildModel(nsDTDMode aDTDMode) {
147   WillBuildModelImpl();
148 
149   // Notify document that the load is beginning
150   mDocument->BeginLoad();
151 
152   // Check for correct load-command for maybe prettyprinting
153   if (mPrettyPrintXML) {
154     nsAutoCString command;
155     GetParser()->GetCommand(command);
156     if (!command.EqualsLiteral("view")) {
157       mPrettyPrintXML = false;
158     }
159   }
160 
161   return NS_OK;
162 }
163 
CanStillPrettyPrint()164 bool nsXMLContentSink::CanStillPrettyPrint() {
165   return mPrettyPrintXML &&
166          (!mPrettyPrintHasFactoredElements || mPrettyPrintHasSpecialRoot);
167 }
168 
MaybePrettyPrint()169 nsresult nsXMLContentSink::MaybePrettyPrint() {
170   if (!CanStillPrettyPrint()) {
171     mPrettyPrintXML = false;
172 
173     return NS_OK;
174   }
175 
176   {
177     // Try to perform a microtask checkpoint; this avoids always breaking
178     // pretty-printing if webextensions insert new content right after the
179     // document loads.
180     nsAutoMicroTask mt;
181   }
182 
183   // stop observing in order to avoid crashing when replacing content
184   mDocument->RemoveObserver(this);
185   mIsDocumentObserver = false;
186 
187   // Reenable the CSSLoader so that the prettyprinting stylesheets can load
188   if (mCSSLoader) {
189     mCSSLoader->SetEnabled(true);
190   }
191 
192   RefPtr<nsXMLPrettyPrinter> printer;
193   nsresult rv = NS_NewXMLPrettyPrinter(getter_AddRefs(printer));
194   NS_ENSURE_SUCCESS(rv, rv);
195 
196   bool isPrettyPrinting;
197   rv = printer->PrettyPrint(mDocument, &isPrettyPrinting);
198   NS_ENSURE_SUCCESS(rv, rv);
199 
200   mPrettyPrinting = isPrettyPrinting;
201   return NS_OK;
202 }
203 
CheckXSLTParamPI(ProcessingInstruction * aPi,nsIDocumentTransformer * aProcessor,nsINode * aSource)204 static void CheckXSLTParamPI(ProcessingInstruction* aPi,
205                              nsIDocumentTransformer* aProcessor,
206                              nsINode* aSource) {
207   nsAutoString target, data;
208   aPi->GetTarget(target);
209 
210   // Check for namespace declarations
211   if (target.EqualsLiteral("xslt-param-namespace")) {
212     aPi->GetData(data);
213     nsAutoString prefix, namespaceAttr;
214     nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::prefix, prefix);
215     if (!prefix.IsEmpty() && nsContentUtils::GetPseudoAttributeValue(
216                                  data, nsGkAtoms::_namespace, namespaceAttr)) {
217       aProcessor->AddXSLTParamNamespace(prefix, namespaceAttr);
218     }
219   }
220 
221   // Check for actual parameters
222   else if (target.EqualsLiteral("xslt-param")) {
223     aPi->GetData(data);
224     nsAutoString name, namespaceAttr, select, value;
225     nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::name, name);
226     nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::_namespace,
227                                             namespaceAttr);
228     if (!nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::select,
229                                                  select)) {
230       select.SetIsVoid(true);
231     }
232     if (!nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::value,
233                                                  value)) {
234       value.SetIsVoid(true);
235     }
236     if (!name.IsEmpty()) {
237       aProcessor->AddXSLTParam(name, namespaceAttr, select, value, aSource);
238     }
239   }
240 }
241 
242 NS_IMETHODIMP
DidBuildModel(bool aTerminated)243 nsXMLContentSink::DidBuildModel(bool aTerminated) {
244   if (!mParser) {
245     // If mParser is null, this parse has already been terminated and must
246     // not been terminated again. However, Document may still think that
247     // the parse has not been terminated and call back into here in the case
248     // where the XML parser has finished but the XSLT transform associated
249     // with the document has not.
250     return NS_OK;
251   }
252 
253   FlushTags();
254 
255   DidBuildModelImpl(aTerminated);
256 
257   if (mXSLTProcessor) {
258     // stop observing in order to avoid crashing when replacing content
259     mDocument->RemoveObserver(this);
260     mIsDocumentObserver = false;
261 
262     ErrorResult rv;
263     RefPtr<DocumentFragment> source = mDocument->CreateDocumentFragment();
264     for (nsIContent* child : mDocumentChildren) {
265       // XPath data model doesn't have DocumentType nodes.
266       if (child->NodeType() != nsINode::DOCUMENT_TYPE_NODE) {
267         source->AppendChild(*child, rv);
268         if (rv.Failed()) {
269           return rv.StealNSResult();
270         }
271       }
272     }
273 
274     // Check for xslt-param and xslt-param-namespace PIs
275     for (nsIContent* child : mDocumentChildren) {
276       if (auto pi = ProcessingInstruction::FromNode(child)) {
277         CheckXSLTParamPI(pi, mXSLTProcessor, source);
278       } else if (child->IsElement()) {
279         // Only honor PIs in the prolog
280         break;
281       }
282     }
283 
284     mXSLTProcessor->SetSourceContentModel(source);
285     // Since the processor now holds a reference to us we drop our reference
286     // to it to avoid owning cycles
287     mXSLTProcessor = nullptr;
288   } else {
289     // Kick off layout for non-XSLT transformed documents.
290 
291     // Check if we want to prettyprint
292     MaybePrettyPrint();
293 
294     bool startLayout = true;
295 
296     if (mPrettyPrinting) {
297       NS_ASSERTION(!mPendingSheetCount, "Shouldn't have pending sheets here!");
298 
299       // We're pretty-printing now.  See whether we should wait up on
300       // stylesheet loads
301       if (mDocument->CSSLoader()->HasPendingLoads()) {
302         mDocument->CSSLoader()->AddObserver(this);
303         // wait for those sheets to load
304         startLayout = false;
305       }
306     }
307 
308     if (startLayout) {
309       StartLayout(false);
310 
311       ScrollToRef();
312     }
313 
314     mDocument->RemoveObserver(this);
315     mIsDocumentObserver = false;
316 
317     mDocument->EndLoad();
318 
319     DropParserAndPerfHint();
320   }
321 
322   return NS_OK;
323 }
324 
325 NS_IMETHODIMP
OnDocumentCreated(Document * aResultDocument)326 nsXMLContentSink::OnDocumentCreated(Document* aResultDocument) {
327   NS_ENSURE_ARG(aResultDocument);
328 
329   aResultDocument->SetDocWriteDisabled(true);
330 
331   nsCOMPtr<nsIContentViewer> contentViewer;
332   mDocShell->GetContentViewer(getter_AddRefs(contentViewer));
333   if (contentViewer) {
334     return contentViewer->SetDocumentInternal(aResultDocument, true);
335   }
336   return NS_OK;
337 }
338 
339 NS_IMETHODIMP
OnTransformDone(nsresult aResult,Document * aResultDocument)340 nsXMLContentSink::OnTransformDone(nsresult aResult, Document* aResultDocument) {
341   MOZ_ASSERT(aResultDocument,
342              "Don't notify about transform end without a document.");
343 
344   mDocumentChildren.Clear();
345 
346   nsCOMPtr<nsIContentViewer> contentViewer;
347   mDocShell->GetContentViewer(getter_AddRefs(contentViewer));
348 
349   if (NS_FAILED(aResult) && contentViewer) {
350     // Transform failed.
351     aResultDocument->SetMayStartLayout(false);
352     // We have an error document.
353     contentViewer->SetDocument(aResultDocument);
354   }
355 
356   RefPtr<Document> originalDocument = mDocument;
357   bool blockingOnload = mIsBlockingOnload;
358   if (!mRunsToCompletion) {
359     // This BlockOnload call corresponds to the UnblockOnload call in
360     // nsContentSink::DropParserAndPerfHint.
361     aResultDocument->BlockOnload();
362     mIsBlockingOnload = true;
363   }
364   // Transform succeeded, or it failed and we have an error document to display.
365   mDocument = aResultDocument;
366   aResultDocument->SetDocWriteDisabled(false);
367 
368   // Notify document observers that all the content has been stuck
369   // into the document.
370   // XXX do we need to notify for things like PIs?  Or just the
371   // documentElement?
372   nsIContent* rootElement = mDocument->GetRootElement();
373   if (rootElement) {
374     NS_ASSERTION(mDocument->ComputeIndexOf(rootElement) != -1,
375                  "rootElement not in doc?");
376     mDocument->BeginUpdate();
377     MutationObservers::NotifyContentInserted(mDocument, rootElement);
378     mDocument->EndUpdate();
379   }
380 
381   // Start the layout process
382   StartLayout(false);
383 
384   ScrollToRef();
385 
386   originalDocument->EndLoad();
387   if (blockingOnload) {
388     // This UnblockOnload call corresponds to the BlockOnload call in
389     // nsContentSink::WillBuildModelImpl.
390     originalDocument->UnblockOnload(true);
391   }
392 
393   DropParserAndPerfHint();
394 
395   // By this point, the result document has been set in the content viewer.  But
396   // the content viewer does not call Destroy on the original document, so we
397   // won't end up reporting document use counters.  It's possible we should be
398   // detaching the document from the window, but for now, we call
399   // ReportDocumentUseCounters on the original document here, to avoid
400   // assertions in ~Document about not having reported them.
401   originalDocument->ReportDocumentUseCounters();
402 
403   return NS_OK;
404 }
405 
406 NS_IMETHODIMP
StyleSheetLoaded(StyleSheet * aSheet,bool aWasDeferred,nsresult aStatus)407 nsXMLContentSink::StyleSheetLoaded(StyleSheet* aSheet, bool aWasDeferred,
408                                    nsresult aStatus) {
409   if (!mPrettyPrinting) {
410     return nsContentSink::StyleSheetLoaded(aSheet, aWasDeferred, aStatus);
411   }
412 
413   if (!mDocument->CSSLoader()->HasPendingLoads()) {
414     mDocument->CSSLoader()->RemoveObserver(this);
415     StartLayout(false);
416     ScrollToRef();
417   }
418 
419   return NS_OK;
420 }
421 
422 NS_IMETHODIMP
WillInterrupt(void)423 nsXMLContentSink::WillInterrupt(void) { return WillInterruptImpl(); }
424 
425 NS_IMETHODIMP
WillResume(void)426 nsXMLContentSink::WillResume(void) { return WillResumeImpl(); }
427 
428 NS_IMETHODIMP
SetParser(nsParserBase * aParser)429 nsXMLContentSink::SetParser(nsParserBase* aParser) {
430   MOZ_ASSERT(aParser, "Should have a parser here!");
431   mParser = aParser;
432   return NS_OK;
433 }
434 
FindIsAttrValue(const char16_t ** aAtts,const char16_t ** aResult)435 static bool FindIsAttrValue(const char16_t** aAtts, const char16_t** aResult) {
436   RefPtr<nsAtom> prefix, localName;
437   for (; *aAtts; aAtts += 2) {
438     int32_t nameSpaceID;
439     nsContentUtils::SplitExpatName(aAtts[0], getter_AddRefs(prefix),
440                                    getter_AddRefs(localName), &nameSpaceID);
441     if (nameSpaceID == kNameSpaceID_None && localName == nsGkAtoms::is) {
442       *aResult = aAtts[1];
443 
444       return true;
445     }
446   }
447 
448   return false;
449 }
450 
CreateElement(const char16_t ** aAtts,uint32_t aAttsCount,mozilla::dom::NodeInfo * aNodeInfo,uint32_t aLineNumber,uint32_t aColumnNumber,nsIContent ** aResult,bool * aAppendContent,FromParser aFromParser)451 nsresult nsXMLContentSink::CreateElement(
452     const char16_t** aAtts, uint32_t aAttsCount,
453     mozilla::dom::NodeInfo* aNodeInfo, uint32_t aLineNumber,
454     uint32_t aColumnNumber, nsIContent** aResult, bool* aAppendContent,
455     FromParser aFromParser) {
456   NS_ASSERTION(aNodeInfo, "can't create element without nodeinfo");
457 
458   *aResult = nullptr;
459   *aAppendContent = true;
460   nsresult rv = NS_OK;
461 
462   RefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo;
463   RefPtr<Element> content;
464 
465   const char16_t* is = nullptr;
466   if ((aNodeInfo->NamespaceEquals(kNameSpaceID_XHTML) ||
467        aNodeInfo->NamespaceEquals(kNameSpaceID_XUL)) &&
468       FindIsAttrValue(aAtts, &is)) {
469     const nsDependentString isStr(is);
470     rv = NS_NewElement(getter_AddRefs(content), ni.forget(), aFromParser,
471                        &isStr);
472   } else {
473     rv = NS_NewElement(getter_AddRefs(content), ni.forget(), aFromParser);
474   }
475 
476   NS_ENSURE_SUCCESS(rv, rv);
477 
478   if (aNodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_XHTML) ||
479       aNodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_SVG)) {
480     nsCOMPtr<nsIScriptElement> sele = do_QueryInterface(content);
481     if (sele) {
482       sele->SetScriptLineNumber(aLineNumber);
483       sele->SetScriptColumnNumber(aColumnNumber);
484       sele->SetCreatorParser(GetParser());
485     } else {
486       MOZ_ASSERT(nsNameSpaceManager::GetInstance()->mSVGDisabled,
487                  "Node didn't QI to script, but SVG wasn't disabled.");
488     }
489   }
490 
491   // XHTML needs some special attention
492   if (aNodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
493     mPrettyPrintHasFactoredElements = true;
494   } else {
495     // If we care, find out if we just used a special factory.
496     if (!mPrettyPrintHasFactoredElements && !mPrettyPrintHasSpecialRoot &&
497         mPrettyPrintXML) {
498       mPrettyPrintHasFactoredElements =
499           nsContentUtils::NameSpaceManager()->HasElementCreator(
500               aNodeInfo->NamespaceID());
501     }
502 
503     if (!aNodeInfo->NamespaceEquals(kNameSpaceID_SVG)) {
504       content.forget(aResult);
505 
506       return NS_OK;
507     }
508   }
509 
510   if (aNodeInfo->Equals(nsGkAtoms::link, kNameSpaceID_XHTML) ||
511       aNodeInfo->Equals(nsGkAtoms::style, kNameSpaceID_XHTML) ||
512       aNodeInfo->Equals(nsGkAtoms::style, kNameSpaceID_SVG)) {
513     if (auto* linkStyle = LinkStyle::FromNode(*content)) {
514       if (aFromParser) {
515         linkStyle->SetEnableUpdates(false);
516       }
517       if (!aNodeInfo->Equals(nsGkAtoms::link, kNameSpaceID_XHTML)) {
518         linkStyle->SetLineNumber(aFromParser ? aLineNumber : 0);
519         linkStyle->SetColumnNumber(aFromParser ? aColumnNumber : 0);
520       }
521     }
522   }
523 
524   content.forget(aResult);
525 
526   return NS_OK;
527 }
528 
CloseElement(nsIContent * aContent)529 nsresult nsXMLContentSink::CloseElement(nsIContent* aContent) {
530   NS_ASSERTION(aContent, "missing element to close");
531 
532   mozilla::dom::NodeInfo* nodeInfo = aContent->NodeInfo();
533 
534   // Some HTML nodes need DoneAddingChildren() called to initialize
535   // properly (eg form state restoration).
536   if (nsIContent::RequiresDoneAddingChildren(nodeInfo->NamespaceID(),
537                                              nodeInfo->NameAtom())) {
538     aContent->DoneAddingChildren(HaveNotifiedForCurrentContent());
539   }
540 
541   if (IsMonolithicContainer(nodeInfo)) {
542     mInMonolithicContainer--;
543   }
544 
545   if (!nodeInfo->NamespaceEquals(kNameSpaceID_XHTML) &&
546       !nodeInfo->NamespaceEquals(kNameSpaceID_SVG)) {
547     return NS_OK;
548   }
549 
550   if (nodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_XHTML) ||
551       nodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_SVG)) {
552     nsCOMPtr<nsIScriptElement> sele = do_QueryInterface(aContent);
553     if (!sele) {
554       MOZ_ASSERT(nsNameSpaceManager::GetInstance()->mSVGDisabled,
555                  "Node didn't QI to script, but SVG wasn't disabled.");
556       return NS_OK;
557     }
558 
559     if (mPreventScriptExecution) {
560       sele->PreventExecution();
561       return NS_OK;
562     }
563 
564     // Always check the clock in nsContentSink right after a script
565     StopDeflecting();
566 
567     // Now tell the script that it's ready to go. This may execute the script
568     // or return true, or neither if the script doesn't need executing.
569     bool block = sele->AttemptToExecute();
570 
571     // If the parser got blocked, make sure to return the appropriate rv.
572     // I'm not sure if this is actually needed or not.
573     if (mParser && !mParser->IsParserEnabled()) {
574       block = true;
575     }
576 
577     return block ? NS_ERROR_HTMLPARSER_BLOCK : NS_OK;
578   }
579 
580   nsresult rv = NS_OK;
581   if (nodeInfo->Equals(nsGkAtoms::link, kNameSpaceID_XHTML) ||
582       nodeInfo->Equals(nsGkAtoms::style, kNameSpaceID_XHTML) ||
583       nodeInfo->Equals(nsGkAtoms::style, kNameSpaceID_SVG)) {
584     if (auto* linkStyle = LinkStyle::FromNode(*aContent)) {
585       linkStyle->SetEnableUpdates(true);
586       auto updateOrError =
587           linkStyle->UpdateStyleSheet(mRunsToCompletion ? nullptr : this);
588       if (updateOrError.isErr()) {
589         rv = updateOrError.unwrapErr();
590       } else if (updateOrError.unwrap().ShouldBlock() && !mRunsToCompletion) {
591         ++mPendingSheetCount;
592         mScriptLoader->AddParserBlockingScriptExecutionBlocker();
593       }
594     }
595   }
596 
597   return rv;
598 }
599 
AddContentAsLeaf(nsIContent * aContent)600 nsresult nsXMLContentSink::AddContentAsLeaf(nsIContent* aContent) {
601   nsresult result = NS_OK;
602 
603   if (mState == eXMLContentSinkState_InProlog) {
604     NS_ASSERTION(mDocument, "Fragments have no prolog");
605     mDocumentChildren.AppendElement(aContent);
606   } else if (mState == eXMLContentSinkState_InEpilog) {
607     NS_ASSERTION(mDocument, "Fragments have no epilog");
608     if (mXSLTProcessor) {
609       mDocumentChildren.AppendElement(aContent);
610     } else {
611       mDocument->AppendChildTo(aContent, false, IgnoreErrors());
612     }
613   } else {
614     nsCOMPtr<nsIContent> parent = GetCurrentContent();
615 
616     if (parent) {
617       ErrorResult rv;
618       parent->AppendChildTo(aContent, false, rv);
619       result = rv.StealNSResult();
620     }
621   }
622   return result;
623 }
624 
625 // Create an XML parser and an XSL content sink and start parsing
626 // the XSL stylesheet located at the given URI.
LoadXSLStyleSheet(nsIURI * aUrl)627 nsresult nsXMLContentSink::LoadXSLStyleSheet(nsIURI* aUrl) {
628   nsCOMPtr<nsIDocumentTransformer> processor = new txMozillaXSLTProcessor();
629 
630   processor->SetTransformObserver(this);
631 
632   if (NS_SUCCEEDED(processor->LoadStyleSheet(aUrl, mDocument))) {
633     mXSLTProcessor.swap(processor);
634   }
635 
636   // Intentionally ignore errors here, we should continue loading the
637   // XML document whether we're able to load the XSLT stylesheet or
638   // not.
639 
640   return NS_OK;
641 }
642 
ProcessStyleLinkFromHeader(const nsAString & aHref,bool aAlternate,const nsAString & aTitle,const nsAString & aIntegrity,const nsAString & aType,const nsAString & aMedia,const nsAString & aReferrerPolicy)643 nsresult nsXMLContentSink::ProcessStyleLinkFromHeader(
644     const nsAString& aHref, bool aAlternate, const nsAString& aTitle,
645     const nsAString& aIntegrity, const nsAString& aType,
646     const nsAString& aMedia, const nsAString& aReferrerPolicy) {
647   mPrettyPrintXML = false;
648 
649   nsAutoCString cmd;
650   if (mParser) GetParser()->GetCommand(cmd);
651   if (cmd.EqualsASCII(kLoadAsData))
652     return NS_OK;  // Do not load stylesheets when loading as data
653 
654   bool wasXSLT;
655   nsresult rv = MaybeProcessXSLTLink(nullptr, aHref, aAlternate, aType, aType,
656                                      aMedia, aReferrerPolicy, &wasXSLT);
657   NS_ENSURE_SUCCESS(rv, rv);
658   if (wasXSLT) {
659     // We're done here.
660     return NS_OK;
661   }
662 
663   // Otherwise fall through to nsContentSink to handle CSS Link headers.
664   return nsContentSink::ProcessStyleLinkFromHeader(
665       aHref, aAlternate, aTitle, aIntegrity, aType, aMedia, aReferrerPolicy);
666 }
667 
MaybeProcessXSLTLink(ProcessingInstruction * aProcessingInstruction,const nsAString & aHref,bool aAlternate,const nsAString & aTitle,const nsAString & aType,const nsAString & aMedia,const nsAString & aReferrerPolicy,bool * aWasXSLT)668 nsresult nsXMLContentSink::MaybeProcessXSLTLink(
669     ProcessingInstruction* aProcessingInstruction, const nsAString& aHref,
670     bool aAlternate, const nsAString& aTitle, const nsAString& aType,
671     const nsAString& aMedia, const nsAString& aReferrerPolicy, bool* aWasXSLT) {
672   bool wasXSLT = aType.LowerCaseEqualsLiteral(TEXT_XSL) ||
673                  aType.LowerCaseEqualsLiteral(APPLICATION_XSLT_XML) ||
674                  aType.LowerCaseEqualsLiteral(TEXT_XML) ||
675                  aType.LowerCaseEqualsLiteral(APPLICATION_XML);
676 
677   if (aWasXSLT) {
678     *aWasXSLT = wasXSLT;
679   }
680 
681   if (!wasXSLT) {
682     return NS_OK;
683   }
684 
685   if (aAlternate) {
686     // don't load alternate XSLT
687     return NS_OK;
688   }
689   // LoadXSLStyleSheet needs a mDocShell.
690   if (!mDocShell) {
691     return NS_OK;
692   }
693 
694   nsCOMPtr<nsIURI> url;
695   nsresult rv = NS_NewURI(getter_AddRefs(url), aHref, nullptr,
696                           mDocument->GetDocBaseURI());
697   NS_ENSURE_SUCCESS(rv, rv);
698 
699   // Do security check
700   nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
701   rv = secMan->CheckLoadURIWithPrincipal(mDocument->NodePrincipal(), url,
702                                          nsIScriptSecurityManager::ALLOW_CHROME,
703                                          mDocument->InnerWindowID());
704   NS_ENSURE_SUCCESS(rv, NS_OK);
705 
706   nsCOMPtr<nsILoadInfo> secCheckLoadInfo =
707       new net::LoadInfo(mDocument->NodePrincipal(),  // loading principal
708                         mDocument->NodePrincipal(),  // triggering principal
709                         aProcessingInstruction,
710                         nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK,
711                         nsIContentPolicy::TYPE_XSLT);
712 
713   // Do content policy check
714   int16_t decision = nsIContentPolicy::ACCEPT;
715   rv = NS_CheckContentLoadPolicy(url, secCheckLoadInfo,
716                                  NS_ConvertUTF16toUTF8(aType), &decision,
717                                  nsContentUtils::GetContentPolicy());
718 
719   NS_ENSURE_SUCCESS(rv, rv);
720 
721   if (NS_CP_REJECTED(decision)) {
722     return NS_OK;
723   }
724 
725   return LoadXSLStyleSheet(url);
726 }
727 
SetDocumentCharset(NotNull<const Encoding * > aEncoding)728 void nsXMLContentSink::SetDocumentCharset(NotNull<const Encoding*> aEncoding) {
729   if (mDocument) {
730     mDocument->SetDocumentCharacterSet(aEncoding);
731   }
732 }
733 
GetTarget()734 nsISupports* nsXMLContentSink::GetTarget() { return ToSupports(mDocument); }
735 
IsScriptExecuting()736 bool nsXMLContentSink::IsScriptExecuting() { return IsScriptExecutingImpl(); }
737 
FlushText(bool aReleaseTextNode)738 nsresult nsXMLContentSink::FlushText(bool aReleaseTextNode) {
739   nsresult rv = NS_OK;
740 
741   if (mTextLength != 0) {
742     if (mLastTextNode) {
743       bool notify = HaveNotifiedForCurrentContent();
744       // We could probably always increase mInNotification here since
745       // if AppendText doesn't notify it shouldn't trigger evil code.
746       // But just in case it does, we don't want to mask any notifications.
747       if (notify) {
748         ++mInNotification;
749       }
750       rv = mLastTextNode->AppendText(mText, mTextLength, notify);
751       if (notify) {
752         --mInNotification;
753       }
754 
755       mTextLength = 0;
756     } else {
757       RefPtr<nsTextNode> textContent =
758           new (mNodeInfoManager) nsTextNode(mNodeInfoManager);
759 
760       mLastTextNode = textContent;
761 
762       // Set the text in the text node
763       textContent->SetText(mText, mTextLength, false);
764       mTextLength = 0;
765 
766       // Add text to its parent
767       rv = AddContentAsLeaf(textContent);
768     }
769   }
770 
771   if (aReleaseTextNode) {
772     mLastTextNode = nullptr;
773   }
774 
775   return rv;
776 }
777 
GetCurrentContent()778 nsIContent* nsXMLContentSink::GetCurrentContent() {
779   if (mContentStack.Length() == 0) {
780     return nullptr;
781   }
782   return GetCurrentStackNode()->mContent;
783 }
784 
GetCurrentStackNode()785 nsXMLContentSink::StackNode* nsXMLContentSink::GetCurrentStackNode() {
786   int32_t count = mContentStack.Length();
787   return count != 0 ? &mContentStack[count - 1] : nullptr;
788 }
789 
PushContent(nsIContent * aContent)790 nsresult nsXMLContentSink::PushContent(nsIContent* aContent) {
791   MOZ_ASSERT(aContent, "Null content being pushed!");
792   StackNode* sn = mContentStack.AppendElement();
793   NS_ENSURE_TRUE(sn, NS_ERROR_OUT_OF_MEMORY);
794 
795   nsIContent* contentToPush = aContent;
796 
797   // When an XML parser would append a node to a template element, it
798   // must instead append it to the template element's template contents.
799   if (contentToPush->IsHTMLElement(nsGkAtoms::_template)) {
800     HTMLTemplateElement* templateElement =
801         static_cast<HTMLTemplateElement*>(contentToPush);
802     contentToPush = templateElement->Content();
803   }
804 
805   sn->mContent = contentToPush;
806   sn->mNumFlushed = 0;
807   return NS_OK;
808 }
809 
PopContent()810 void nsXMLContentSink::PopContent() {
811   if (mContentStack.IsEmpty()) {
812     NS_WARNING("Popping empty stack");
813     return;
814   }
815 
816   mContentStack.RemoveLastElement();
817 }
818 
HaveNotifiedForCurrentContent() const819 bool nsXMLContentSink::HaveNotifiedForCurrentContent() const {
820   uint32_t stackLength = mContentStack.Length();
821   if (stackLength) {
822     const StackNode& stackNode = mContentStack[stackLength - 1];
823     nsIContent* parent = stackNode.mContent;
824     return stackNode.mNumFlushed == parent->GetChildCount();
825   }
826   return true;
827 }
828 
MaybeStartLayout(bool aIgnorePendingSheets)829 void nsXMLContentSink::MaybeStartLayout(bool aIgnorePendingSheets) {
830   // XXXbz if aIgnorePendingSheets is true, what should we do when
831   // mXSLTProcessor or CanStillPrettyPrint()?
832   if (mLayoutStarted || mXSLTProcessor || CanStillPrettyPrint()) {
833     return;
834   }
835   StartLayout(aIgnorePendingSheets);
836 }
837 
838 ////////////////////////////////////////////////////////////////////////
839 
SetDocElement(int32_t aNameSpaceID,nsAtom * aTagName,nsIContent * aContent)840 bool nsXMLContentSink::SetDocElement(int32_t aNameSpaceID, nsAtom* aTagName,
841                                      nsIContent* aContent) {
842   if (mDocElement) return false;
843 
844   mDocElement = aContent;
845 
846   if (mXSLTProcessor) {
847     mDocumentChildren.AppendElement(aContent);
848     return true;
849   }
850 
851   if (!mDocumentChildren.IsEmpty()) {
852     for (nsIContent* child : mDocumentChildren) {
853       mDocument->AppendChildTo(child, false, IgnoreErrors());
854     }
855     mDocumentChildren.Clear();
856   }
857 
858   // check for root elements that needs special handling for
859   // prettyprinting
860   if (aNameSpaceID == kNameSpaceID_XSLT &&
861       (aTagName == nsGkAtoms::stylesheet || aTagName == nsGkAtoms::transform)) {
862     mPrettyPrintHasSpecialRoot = true;
863     if (mPrettyPrintXML) {
864       // In this case, disable script execution, stylesheet
865       // loading, and auto XLinks since we plan to prettyprint.
866       mDocument->ScriptLoader()->SetEnabled(false);
867       if (mCSSLoader) {
868         mCSSLoader->SetEnabled(false);
869       }
870     }
871   }
872 
873   IgnoredErrorResult rv;
874   mDocument->AppendChildTo(mDocElement, NotifyForDocElement(), rv);
875   if (rv.Failed()) {
876     // If we return false here, the caller will bail out because it won't
877     // find a parent content node to append to, which is fine.
878     return false;
879   }
880 
881   return true;
882 }
883 
884 NS_IMETHODIMP
HandleStartElement(const char16_t * aName,const char16_t ** aAtts,uint32_t aAttsCount,uint32_t aLineNumber,uint32_t aColumnNumber)885 nsXMLContentSink::HandleStartElement(const char16_t* aName,
886                                      const char16_t** aAtts,
887                                      uint32_t aAttsCount, uint32_t aLineNumber,
888                                      uint32_t aColumnNumber) {
889   return HandleStartElement(aName, aAtts, aAttsCount, aLineNumber,
890                             aColumnNumber, true);
891 }
892 
HandleStartElement(const char16_t * aName,const char16_t ** aAtts,uint32_t aAttsCount,uint32_t aLineNumber,uint32_t aColumnNumber,bool aInterruptable)893 nsresult nsXMLContentSink::HandleStartElement(
894     const char16_t* aName, const char16_t** aAtts, uint32_t aAttsCount,
895     uint32_t aLineNumber, uint32_t aColumnNumber, bool aInterruptable) {
896   MOZ_ASSERT(aAttsCount % 2 == 0, "incorrect aAttsCount");
897   // Adjust aAttsCount so it's the actual number of attributes
898   aAttsCount /= 2;
899 
900   nsresult result = NS_OK;
901   bool appendContent = true;
902   nsCOMPtr<nsIContent> content;
903 
904   // XXX Hopefully the parser will flag this before we get
905   // here. If we're in the epilog, there should be no
906   // new elements
907   MOZ_ASSERT(eXMLContentSinkState_InEpilog != mState);
908 
909   FlushText();
910   DidAddContent();
911 
912   mState = eXMLContentSinkState_InDocumentElement;
913 
914   int32_t nameSpaceID;
915   RefPtr<nsAtom> prefix, localName;
916   nsContentUtils::SplitExpatName(aName, getter_AddRefs(prefix),
917                                  getter_AddRefs(localName), &nameSpaceID);
918 
919   if (!OnOpenContainer(aAtts, aAttsCount, nameSpaceID, localName,
920                        aLineNumber)) {
921     return NS_OK;
922   }
923 
924   RefPtr<mozilla::dom::NodeInfo> nodeInfo;
925   nodeInfo = mNodeInfoManager->GetNodeInfo(localName, prefix, nameSpaceID,
926                                            nsINode::ELEMENT_NODE);
927 
928   result = CreateElement(aAtts, aAttsCount, nodeInfo, aLineNumber,
929                          aColumnNumber, getter_AddRefs(content), &appendContent,
930                          FROM_PARSER_NETWORK);
931   NS_ENSURE_SUCCESS(result, result);
932 
933   // Have to do this before we push the new content on the stack... and have to
934   // do that before we set attributes, call BindToTree, etc.  Ideally we'd push
935   // on the stack inside CreateElement (which is effectively what the HTML sink
936   // does), but that's hard with all the subclass overrides going on.
937   nsCOMPtr<nsIContent> parent = GetCurrentContent();
938 
939   result = PushContent(content);
940   NS_ENSURE_SUCCESS(result, result);
941 
942   // Set the attributes on the new content element
943   result = AddAttributes(aAtts, content->AsElement());
944 
945   if (NS_OK == result) {
946     // Store the element
947     if (!SetDocElement(nameSpaceID, localName, content) && appendContent) {
948       NS_ENSURE_TRUE(parent, NS_ERROR_UNEXPECTED);
949 
950       parent->AppendChildTo(content, false, IgnoreErrors());
951     }
952   }
953 
954   // Some HTML nodes need DoneCreatingElement() called to initialize
955   // properly (eg form state restoration).
956   if (nsIContent::RequiresDoneCreatingElement(nodeInfo->NamespaceID(),
957                                               nodeInfo->NameAtom())) {
958     content->DoneCreatingElement();
959   }
960 
961   if (nodeInfo->NamespaceID() == kNameSpaceID_XHTML &&
962       nodeInfo->NameAtom() == nsGkAtoms::head && !mCurrentHead) {
963     mCurrentHead = content;
964   }
965 
966   if (IsMonolithicContainer(nodeInfo)) {
967     mInMonolithicContainer++;
968   }
969 
970   if (!mXSLTProcessor) {
971     if (content == mDocElement) {
972       nsContentUtils::AddScriptRunner(
973           new nsDocElementCreatedNotificationRunner(mDocument));
974 
975       if (aInterruptable && NS_SUCCEEDED(result) && mParser &&
976           !mParser->IsParserEnabled()) {
977         return NS_ERROR_HTMLPARSER_BLOCK;
978       }
979     } else if (!mCurrentHead) {
980       // This isn't the root and we're not inside an XHTML <head>.
981       // Might need to start layout
982       MaybeStartLayout(false);
983     }
984   }
985 
986   return aInterruptable && NS_SUCCEEDED(result) ? DidProcessATokenImpl()
987                                                 : result;
988 }
989 
990 NS_IMETHODIMP
HandleEndElement(const char16_t * aName)991 nsXMLContentSink::HandleEndElement(const char16_t* aName) {
992   return HandleEndElement(aName, true);
993 }
994 
HandleEndElement(const char16_t * aName,bool aInterruptable)995 nsresult nsXMLContentSink::HandleEndElement(const char16_t* aName,
996                                             bool aInterruptable) {
997   nsresult result = NS_OK;
998 
999   // XXX Hopefully the parser will flag this before we get
1000   // here. If we're in the prolog or epilog, there should be
1001   // no close tags for elements.
1002   MOZ_ASSERT(eXMLContentSinkState_InDocumentElement == mState);
1003 
1004   FlushText();
1005 
1006   StackNode* sn = GetCurrentStackNode();
1007   if (!sn) {
1008     return NS_ERROR_UNEXPECTED;
1009   }
1010 
1011   nsCOMPtr<nsIContent> content;
1012   sn->mContent.swap(content);
1013   uint32_t numFlushed = sn->mNumFlushed;
1014 
1015   PopContent();
1016   NS_ASSERTION(content, "failed to pop content");
1017 #ifdef DEBUG
1018   // Check that we're closing the right thing
1019   RefPtr<nsAtom> debugNameSpacePrefix, debugTagAtom;
1020   int32_t debugNameSpaceID;
1021   nsContentUtils::SplitExpatName(aName, getter_AddRefs(debugNameSpacePrefix),
1022                                  getter_AddRefs(debugTagAtom),
1023                                  &debugNameSpaceID);
1024   // Check if we are closing a template element because template
1025   // elements do not get pushed on the stack, the template
1026   // element content is pushed instead.
1027   bool isTemplateElement = debugTagAtom == nsGkAtoms::_template &&
1028                            debugNameSpaceID == kNameSpaceID_XHTML;
1029   NS_ASSERTION(
1030       content->NodeInfo()->Equals(debugTagAtom, debugNameSpaceID) ||
1031           (debugNameSpaceID == kNameSpaceID_MathML &&
1032            content->NodeInfo()->NamespaceID() == kNameSpaceID_disabled_MathML &&
1033            content->NodeInfo()->Equals(debugTagAtom)) ||
1034           (debugNameSpaceID == kNameSpaceID_SVG &&
1035            content->NodeInfo()->NamespaceID() == kNameSpaceID_disabled_SVG &&
1036            content->NodeInfo()->Equals(debugTagAtom)) ||
1037           isTemplateElement,
1038       "Wrong element being closed");
1039 #endif
1040 
1041   // Make sure to notify on our kids before we call out to any other code that
1042   // might reenter us and call FlushTags, in a state in which we've already
1043   // popped "content" from the stack but haven't notified on its kids yet.
1044   int32_t stackLen = mContentStack.Length();
1045   if (mNotifyLevel >= stackLen) {
1046     if (numFlushed < content->GetChildCount()) {
1047       NotifyAppend(content, numFlushed);
1048     }
1049     mNotifyLevel = stackLen - 1;
1050   }
1051 
1052   result = CloseElement(content);
1053 
1054   if (mCurrentHead == content) {
1055     mCurrentHead = nullptr;
1056   }
1057 
1058   if (mDocElement == content) {
1059     // XXXbz for roots that don't want to be appended on open, we
1060     // probably need to deal here.... (and stop appending them on open).
1061     mState = eXMLContentSinkState_InEpilog;
1062 
1063     mDocument->OnParsingCompleted();
1064 
1065     // We might have had no occasion to start layout yet.  Do so now.
1066     MaybeStartLayout(false);
1067   }
1068 
1069   DidAddContent();
1070 
1071   if (content->IsSVGElement(nsGkAtoms::svg)) {
1072     FlushTags();
1073     nsCOMPtr<nsIRunnable> event = new nsHtml5SVGLoadDispatcher(content);
1074     if (NS_FAILED(content->OwnerDoc()->Dispatch(TaskCategory::Other,
1075                                                 event.forget()))) {
1076       NS_WARNING("failed to dispatch svg load dispatcher");
1077     }
1078   }
1079 
1080   return aInterruptable && NS_SUCCEEDED(result) ? DidProcessATokenImpl()
1081                                                 : result;
1082 }
1083 
1084 NS_IMETHODIMP
HandleComment(const char16_t * aName)1085 nsXMLContentSink::HandleComment(const char16_t* aName) {
1086   FlushText();
1087 
1088   RefPtr<Comment> comment = new (mNodeInfoManager) Comment(mNodeInfoManager);
1089   comment->SetText(nsDependentString(aName), false);
1090   nsresult rv = AddContentAsLeaf(comment);
1091   DidAddContent();
1092 
1093   return NS_SUCCEEDED(rv) ? DidProcessATokenImpl() : rv;
1094 }
1095 
1096 NS_IMETHODIMP
HandleCDataSection(const char16_t * aData,uint32_t aLength)1097 nsXMLContentSink::HandleCDataSection(const char16_t* aData, uint32_t aLength) {
1098   // XSLT doesn't differentiate between text and cdata and wants adjacent
1099   // textnodes merged, so add as text.
1100   if (mXSLTProcessor) {
1101     return AddText(aData, aLength);
1102   }
1103 
1104   FlushText();
1105 
1106   RefPtr<CDATASection> cdata =
1107       new (mNodeInfoManager) CDATASection(mNodeInfoManager);
1108   cdata->SetText(aData, aLength, false);
1109   nsresult rv = AddContentAsLeaf(cdata);
1110   DidAddContent();
1111 
1112   return NS_SUCCEEDED(rv) ? DidProcessATokenImpl() : rv;
1113 }
1114 
1115 NS_IMETHODIMP
HandleDoctypeDecl(const nsAString & aSubset,const nsAString & aName,const nsAString & aSystemId,const nsAString & aPublicId,nsISupports * aCatalogData)1116 nsXMLContentSink::HandleDoctypeDecl(const nsAString& aSubset,
1117                                     const nsAString& aName,
1118                                     const nsAString& aSystemId,
1119                                     const nsAString& aPublicId,
1120                                     nsISupports* aCatalogData) {
1121   FlushText();
1122 
1123   NS_ASSERTION(mDocument, "Shouldn't get here from a document fragment");
1124 
1125   RefPtr<nsAtom> name = NS_Atomize(aName);
1126   NS_ENSURE_TRUE(name, NS_ERROR_OUT_OF_MEMORY);
1127 
1128   // Create a new doctype node
1129   RefPtr<DocumentType> docType = NS_NewDOMDocumentType(
1130       mNodeInfoManager, name, aPublicId, aSystemId, aSubset);
1131 
1132   MOZ_ASSERT(!aCatalogData,
1133              "Need to add back support for catalog style "
1134              "sheets");
1135 
1136   mDocumentChildren.AppendElement(docType);
1137   DidAddContent();
1138   return DidProcessATokenImpl();
1139 }
1140 
1141 NS_IMETHODIMP
HandleCharacterData(const char16_t * aData,uint32_t aLength)1142 nsXMLContentSink::HandleCharacterData(const char16_t* aData, uint32_t aLength) {
1143   return HandleCharacterData(aData, aLength, true);
1144 }
1145 
HandleCharacterData(const char16_t * aData,uint32_t aLength,bool aInterruptable)1146 nsresult nsXMLContentSink::HandleCharacterData(const char16_t* aData,
1147                                                uint32_t aLength,
1148                                                bool aInterruptable) {
1149   nsresult rv = NS_OK;
1150   if (aData && mState != eXMLContentSinkState_InProlog &&
1151       mState != eXMLContentSinkState_InEpilog) {
1152     rv = AddText(aData, aLength);
1153   }
1154   return aInterruptable && NS_SUCCEEDED(rv) ? DidProcessATokenImpl() : rv;
1155 }
1156 
1157 NS_IMETHODIMP
HandleProcessingInstruction(const char16_t * aTarget,const char16_t * aData)1158 nsXMLContentSink::HandleProcessingInstruction(const char16_t* aTarget,
1159                                               const char16_t* aData) {
1160   FlushText();
1161 
1162   const nsDependentString target(aTarget);
1163   const nsDependentString data(aData);
1164 
1165   RefPtr<ProcessingInstruction> node =
1166       NS_NewXMLProcessingInstruction(mNodeInfoManager, target, data);
1167 
1168   auto* linkStyle = LinkStyle::FromNode(*node);
1169   if (linkStyle) {
1170     linkStyle->SetEnableUpdates(false);
1171     mPrettyPrintXML = false;
1172   }
1173 
1174   nsresult rv = AddContentAsLeaf(node);
1175   NS_ENSURE_SUCCESS(rv, rv);
1176   DidAddContent();
1177 
1178   if (linkStyle) {
1179     // This is an xml-stylesheet processing instruction... but it might not be
1180     // a CSS one if the type is set to something else.
1181     linkStyle->SetEnableUpdates(true);
1182     auto updateOrError =
1183         linkStyle->UpdateStyleSheet(mRunsToCompletion ? nullptr : this);
1184     if (updateOrError.isErr()) {
1185       return updateOrError.unwrapErr();
1186     }
1187 
1188     auto update = updateOrError.unwrap();
1189     if (update.WillNotify()) {
1190       // Successfully started a stylesheet load
1191       if (update.ShouldBlock() && !mRunsToCompletion) {
1192         ++mPendingSheetCount;
1193         mScriptLoader->AddParserBlockingScriptExecutionBlocker();
1194       }
1195       return NS_OK;
1196     }
1197   }
1198 
1199   // Check whether this is a CSS stylesheet PI.  Make sure the type
1200   // handling here matches
1201   // XMLStylesheetProcessingInstruction::GetStyleSheetInfo.
1202   nsAutoString type;
1203   nsContentUtils::GetPseudoAttributeValue(data, nsGkAtoms::type, type);
1204   nsAutoString mimeType, notUsed;
1205   nsContentUtils::SplitMimeType(type, mimeType, notUsed);
1206 
1207   if (mState != eXMLContentSinkState_InProlog ||
1208       !target.EqualsLiteral("xml-stylesheet") || mimeType.IsEmpty() ||
1209       mimeType.LowerCaseEqualsLiteral("text/css")) {
1210     // Either not a useful stylesheet PI, or a CSS stylesheet PI that
1211     // got handled above by the "ssle" bits.  We're done here.
1212     return DidProcessATokenImpl();
1213   }
1214 
1215   // If it's not a CSS stylesheet PI...
1216   nsAutoString href, title, media;
1217   bool isAlternate = false;
1218 
1219   // If there was no href, we can't do anything with this PI
1220   if (!ParsePIData(data, href, title, media, isAlternate)) {
1221     return DidProcessATokenImpl();
1222   }
1223 
1224   // <?xml-stylesheet?> processing instructions don't have a referrerpolicy
1225   // pseudo-attribute, so we pass in an empty string
1226   rv =
1227       MaybeProcessXSLTLink(node, href, isAlternate, title, type, media, u""_ns);
1228   return NS_SUCCEEDED(rv) ? DidProcessATokenImpl() : rv;
1229 }
1230 
1231 /* static */
ParsePIData(const nsString & aData,nsString & aHref,nsString & aTitle,nsString & aMedia,bool & aIsAlternate)1232 bool nsXMLContentSink::ParsePIData(const nsString& aData, nsString& aHref,
1233                                    nsString& aTitle, nsString& aMedia,
1234                                    bool& aIsAlternate) {
1235   // If there was no href, we can't do anything with this PI
1236   if (!nsContentUtils::GetPseudoAttributeValue(aData, nsGkAtoms::href, aHref)) {
1237     return false;
1238   }
1239 
1240   nsContentUtils::GetPseudoAttributeValue(aData, nsGkAtoms::title, aTitle);
1241 
1242   nsContentUtils::GetPseudoAttributeValue(aData, nsGkAtoms::media, aMedia);
1243 
1244   nsAutoString alternate;
1245   nsContentUtils::GetPseudoAttributeValue(aData, nsGkAtoms::alternate,
1246                                           alternate);
1247 
1248   aIsAlternate = alternate.EqualsLiteral("yes");
1249 
1250   return true;
1251 }
1252 
1253 NS_IMETHODIMP
HandleXMLDeclaration(const char16_t * aVersion,const char16_t * aEncoding,int32_t aStandalone)1254 nsXMLContentSink::HandleXMLDeclaration(const char16_t* aVersion,
1255                                        const char16_t* aEncoding,
1256                                        int32_t aStandalone) {
1257   mDocument->SetXMLDeclaration(aVersion, aEncoding, aStandalone);
1258 
1259   return DidProcessATokenImpl();
1260 }
1261 
1262 NS_IMETHODIMP
ReportError(const char16_t * aErrorText,const char16_t * aSourceText,nsIScriptError * aError,bool * _retval)1263 nsXMLContentSink::ReportError(const char16_t* aErrorText,
1264                               const char16_t* aSourceText,
1265                               nsIScriptError* aError, bool* _retval) {
1266   MOZ_ASSERT(aError && aSourceText && aErrorText, "Check arguments!!!");
1267   nsresult rv = NS_OK;
1268 
1269   // The expat driver should report the error.  We're just cleaning up the mess.
1270   *_retval = true;
1271 
1272   mPrettyPrintXML = false;
1273 
1274   mState = eXMLContentSinkState_InProlog;
1275 
1276   // XXX need to stop scripts here -- hsivonen
1277 
1278   // stop observing in order to avoid crashing when removing content
1279   mDocument->RemoveObserver(this);
1280   mIsDocumentObserver = false;
1281 
1282   // Clear the current content
1283   mDocumentChildren.Clear();
1284   while (mDocument->GetLastChild()) {
1285     mDocument->GetLastChild()->Remove();
1286   }
1287   mDocElement = nullptr;
1288 
1289   // Clear any buffered-up text we have.  It's enough to set the length to 0.
1290   // The buffer itself is allocated when we're created and deleted in our
1291   // destructor, so don't mess with it.
1292   mTextLength = 0;
1293 
1294   if (mXSLTProcessor) {
1295     // Get rid of the XSLT processor.
1296     mXSLTProcessor->CancelLoads();
1297     mXSLTProcessor = nullptr;
1298   }
1299 
1300   // release the nodes on stack
1301   mContentStack.Clear();
1302   mNotifyLevel = 0;
1303 
1304   // return leaving the document empty if we're asked to not add a <parsererror>
1305   // root node
1306   if (mDocument->SuppressParserErrorElement()) {
1307     return NS_OK;
1308   }
1309 
1310   // prepare to set <parsererror> as the document root
1311   rv = HandleProcessingInstruction(
1312       u"xml-stylesheet",
1313       u"href=\"chrome://global/locale/intl.css\" type=\"text/css\"");
1314   NS_ENSURE_SUCCESS(rv, rv);
1315 
1316   const char16_t* noAtts[] = {0, 0};
1317 
1318   constexpr auto errorNs =
1319       u"http://www.mozilla.org/newlayout/xml/parsererror.xml"_ns;
1320 
1321   nsAutoString parsererror(errorNs);
1322   parsererror.Append((char16_t)0xFFFF);
1323   parsererror.AppendLiteral("parsererror");
1324 
1325   rv = HandleStartElement(parsererror.get(), noAtts, 0, (uint32_t)-1, false);
1326   NS_ENSURE_SUCCESS(rv, rv);
1327 
1328   rv = HandleCharacterData(aErrorText, NS_strlen(aErrorText), false);
1329   NS_ENSURE_SUCCESS(rv, rv);
1330 
1331   nsAutoString sourcetext(errorNs);
1332   sourcetext.Append((char16_t)0xFFFF);
1333   sourcetext.AppendLiteral("sourcetext");
1334 
1335   rv = HandleStartElement(sourcetext.get(), noAtts, 0, (uint32_t)-1, false);
1336   NS_ENSURE_SUCCESS(rv, rv);
1337 
1338   rv = HandleCharacterData(aSourceText, NS_strlen(aSourceText), false);
1339   NS_ENSURE_SUCCESS(rv, rv);
1340 
1341   rv = HandleEndElement(sourcetext.get(), false);
1342   NS_ENSURE_SUCCESS(rv, rv);
1343 
1344   rv = HandleEndElement(parsererror.get(), false);
1345   NS_ENSURE_SUCCESS(rv, rv);
1346 
1347   FlushTags();
1348 
1349   return NS_OK;
1350 }
1351 
AddAttributes(const char16_t ** aAtts,Element * aContent)1352 nsresult nsXMLContentSink::AddAttributes(const char16_t** aAtts,
1353                                          Element* aContent) {
1354   // Add tag attributes to the content attributes
1355   RefPtr<nsAtom> prefix, localName;
1356   while (*aAtts) {
1357     int32_t nameSpaceID;
1358     nsContentUtils::SplitExpatName(aAtts[0], getter_AddRefs(prefix),
1359                                    getter_AddRefs(localName), &nameSpaceID);
1360 
1361     // Add attribute to content
1362     aContent->SetAttr(nameSpaceID, localName, prefix,
1363                       nsDependentString(aAtts[1]), false);
1364     aAtts += 2;
1365   }
1366 
1367   return NS_OK;
1368 }
1369 
1370 #define NS_ACCUMULATION_BUFFER_SIZE 4096
1371 
AddText(const char16_t * aText,int32_t aLength)1372 nsresult nsXMLContentSink::AddText(const char16_t* aText, int32_t aLength) {
1373   // Copy data from string into our buffer; flush buffer when it fills up.
1374   int32_t offset = 0;
1375   while (0 != aLength) {
1376     int32_t amount = NS_ACCUMULATION_BUFFER_SIZE - mTextLength;
1377     if (0 == amount) {
1378       nsresult rv = FlushText(false);
1379       if (NS_WARN_IF(NS_FAILED(rv))) {
1380         return rv;
1381       }
1382       MOZ_ASSERT(mTextLength == 0);
1383       amount = NS_ACCUMULATION_BUFFER_SIZE;
1384     }
1385 
1386     if (amount > aLength) {
1387       amount = aLength;
1388     }
1389     memcpy(&mText[mTextLength], &aText[offset], sizeof(char16_t) * amount);
1390     mTextLength += amount;
1391     offset += amount;
1392     aLength -= amount;
1393   }
1394 
1395   return NS_OK;
1396 }
1397 
InitialTranslationCompleted()1398 void nsXMLContentSink::InitialTranslationCompleted() { StartLayout(false); }
1399 
FlushPendingNotifications(FlushType aType)1400 void nsXMLContentSink::FlushPendingNotifications(FlushType aType) {
1401   // Only flush tags if we're not doing the notification ourselves
1402   // (since we aren't reentrant)
1403   if (!mInNotification) {
1404     if (mIsDocumentObserver) {
1405       // Only flush if we're still a document observer (so that our child
1406       // counts should be correct).
1407       if (aType >= FlushType::ContentAndNotify) {
1408         FlushTags();
1409       } else {
1410         FlushText(false);
1411       }
1412     }
1413     if (aType >= FlushType::EnsurePresShellInitAndFrames) {
1414       // Make sure that layout has started so that the reflow flush
1415       // will actually happen.
1416       MaybeStartLayout(true);
1417     }
1418   }
1419 }
1420 
1421 /**
1422  * NOTE!! Forked from SinkContext. Please keep in sync.
1423  *
1424  * Flush all elements that have been seen so far such that
1425  * they are visible in the tree. Specifically, make sure
1426  * that they are all added to their respective parents.
1427  * Also, do notification at the top for all content that
1428  * has been newly added so that the frame tree is complete.
1429  */
FlushTags()1430 nsresult nsXMLContentSink::FlushTags() {
1431   mDeferredFlushTags = false;
1432   uint32_t oldUpdates = mUpdatesInNotification;
1433 
1434   mUpdatesInNotification = 0;
1435   ++mInNotification;
1436   {
1437     // Scope so we call EndUpdate before we decrease mInNotification
1438     mozAutoDocUpdate updateBatch(mDocument, true);
1439 
1440     // Don't release last text node in case we need to add to it again
1441     FlushText(false);
1442 
1443     // Start from the base of the stack (growing downward) and do
1444     // a notification from the node that is closest to the root of
1445     // tree for any content that has been added.
1446 
1447     int32_t stackPos;
1448     int32_t stackLen = mContentStack.Length();
1449     bool flushed = false;
1450     uint32_t childCount;
1451     nsIContent* content;
1452 
1453     for (stackPos = 0; stackPos < stackLen; ++stackPos) {
1454       content = mContentStack[stackPos].mContent;
1455       childCount = content->GetChildCount();
1456 
1457       if (!flushed && (mContentStack[stackPos].mNumFlushed < childCount)) {
1458         NotifyAppend(content, mContentStack[stackPos].mNumFlushed);
1459         flushed = true;
1460       }
1461 
1462       mContentStack[stackPos].mNumFlushed = childCount;
1463     }
1464     mNotifyLevel = stackLen - 1;
1465   }
1466   --mInNotification;
1467 
1468   if (mUpdatesInNotification > 1) {
1469     UpdateChildCounts();
1470   }
1471 
1472   mUpdatesInNotification = oldUpdates;
1473   return NS_OK;
1474 }
1475 
1476 /**
1477  * NOTE!! Forked from SinkContext. Please keep in sync.
1478  */
UpdateChildCounts()1479 void nsXMLContentSink::UpdateChildCounts() {
1480   // Start from the top of the stack (growing upwards) and see if any
1481   // new content has been appended. If so, we recognize that reflows
1482   // have been generated for it and we should make sure that no
1483   // further reflows occur.  Note that we have to include stackPos == 0
1484   // to properly notify on kids of <html>.
1485   int32_t stackLen = mContentStack.Length();
1486   int32_t stackPos = stackLen - 1;
1487   while (stackPos >= 0) {
1488     StackNode& node = mContentStack[stackPos];
1489     node.mNumFlushed = node.mContent->GetChildCount();
1490 
1491     stackPos--;
1492   }
1493   mNotifyLevel = stackLen - 1;
1494 }
1495 
IsMonolithicContainer(mozilla::dom::NodeInfo * aNodeInfo)1496 bool nsXMLContentSink::IsMonolithicContainer(
1497     mozilla::dom::NodeInfo* aNodeInfo) {
1498   return ((aNodeInfo->NamespaceID() == kNameSpaceID_XHTML &&
1499            (aNodeInfo->NameAtom() == nsGkAtoms::tr ||
1500             aNodeInfo->NameAtom() == nsGkAtoms::select ||
1501             aNodeInfo->NameAtom() == nsGkAtoms::object)) ||
1502           (aNodeInfo->NamespaceID() == kNameSpaceID_MathML &&
1503            (aNodeInfo->NameAtom() == nsGkAtoms::math)));
1504 }
1505 
ContinueInterruptedParsingIfEnabled()1506 void nsXMLContentSink::ContinueInterruptedParsingIfEnabled() {
1507   if (mParser && mParser->IsParserEnabled()) {
1508     GetParser()->ContinueInterruptedParsing();
1509   }
1510 }
1511 
ContinueInterruptedParsingAsync()1512 void nsXMLContentSink::ContinueInterruptedParsingAsync() {
1513   nsCOMPtr<nsIRunnable> ev = NewRunnableMethod(
1514       "nsXMLContentSink::ContinueInterruptedParsingIfEnabled", this,
1515       &nsXMLContentSink::ContinueInterruptedParsingIfEnabled);
1516 
1517   mDocument->Dispatch(mozilla::TaskCategory::Other, ev.forget());
1518 }
1519 
GetParser()1520 nsIParser* nsXMLContentSink::GetParser() {
1521   return static_cast<nsIParser*>(mParser.get());
1522 }
1523