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