1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "txMozillaXMLOutput.h"
8 #include "mozilla/dom/Document.h"
9 #include "mozilla/dom/FeaturePolicy.h"
10 #include "nsIDocShell.h"
11 #include "nsIScriptElement.h"
12 #include "nsCharsetSource.h"
13 #include "nsIRefreshURI.h"
14 #include "nsPIDOMWindow.h"
15 #include "nsIContent.h"
16 #include "nsUnicharUtils.h"
17 #include "nsGkAtoms.h"
19 #include "nsNameSpaceManager.h"
20 #include "txStringUtils.h"
21 #include "txURIUtils.h"
22 #include "nsIDocumentTransformer.h"
23 #include "mozilla/StyleSheetInlines.h"
24 #include "mozilla/css/Loader.h"
25 #include "mozilla/dom/DocumentType.h"
26 #include "mozilla/dom/DocumentFragment.h"
27 #include "mozilla/dom/Element.h"
28 #include "mozilla/dom/ScriptLoader.h"
29 #include "mozilla/Encoding.h"
30 #include "mozilla/Try.h"
31 #include "nsContentUtils.h"
32 #include "nsDocElementCreatedNotificationRunner.h"
33 #include "txXMLUtils.h"
34 #include "nsContentSink.h"
36 #include "nsContentCreatorFunctions.h"
38 #include "nsStringFlags.h"
39 #include "nsStyleUtil.h"
42 #include "nsTextNode.h"
43 #include "nsDocShell.h"
44 #include "mozilla/dom/Comment.h"
45 #include "mozilla/dom/ProcessingInstruction.h"
47 using namespace mozilla
;
48 using namespace mozilla::dom
;
50 #define TX_ENSURE_CURRENTNODE \
51 NS_ASSERTION(mCurrentNode, "mCurrentNode is nullptr"); \
52 if (!mCurrentNode) return NS_ERROR_UNEXPECTED
54 txMozillaXMLOutput::txMozillaXMLOutput(Document
* aSourceDocument
,
55 txOutputFormat
* aFormat
,
56 nsITransformObserver
* aObserver
)
60 mCreatingNewDocument(true),
61 mOpenedElementIsHTML(false),
62 mRootContentCreated(false),
64 MOZ_COUNT_CTOR(txMozillaXMLOutput
);
66 mNotifier
= new txTransformNotifier(aSourceDocument
);
68 mNotifier
->Init(aObserver
);
72 mOutputFormat
.merge(*aFormat
);
73 mOutputFormat
.setFromDefaults();
76 txMozillaXMLOutput::txMozillaXMLOutput(txOutputFormat
* aFormat
,
77 DocumentFragment
* aFragment
,
82 mCreatingNewDocument(false),
83 mOpenedElementIsHTML(false),
84 mRootContentCreated(false),
86 MOZ_COUNT_CTOR(txMozillaXMLOutput
);
87 mOutputFormat
.merge(*aFormat
);
88 mOutputFormat
.setFromDefaults();
90 mCurrentNode
= aFragment
;
91 mDocument
= mCurrentNode
->OwnerDoc();
92 mNodeInfoManager
= mDocument
->NodeInfoManager();
95 txMozillaXMLOutput::~txMozillaXMLOutput() {
96 MOZ_COUNT_DTOR(txMozillaXMLOutput
);
99 nsresult
txMozillaXMLOutput::attribute(nsAtom
* aPrefix
, nsAtom
* aLocalName
,
100 nsAtom
* aLowercaseLocalName
,
102 const nsString
& aValue
) {
103 RefPtr
<nsAtom
> owner
;
104 if (mOpenedElementIsHTML
&& aNsID
== kNameSpaceID_None
) {
105 if (aLowercaseLocalName
) {
106 aLocalName
= aLowercaseLocalName
;
108 owner
= TX_ToLowerCaseAtom(aLocalName
);
109 NS_ENSURE_TRUE(owner
, NS_ERROR_OUT_OF_MEMORY
);
115 return attributeInternal(aPrefix
, aLocalName
, aNsID
, aValue
);
118 nsresult
txMozillaXMLOutput::attribute(nsAtom
* aPrefix
,
119 const nsAString
& aLocalName
,
121 const nsString
& aValue
) {
122 RefPtr
<nsAtom
> lname
;
124 if (mOpenedElementIsHTML
&& aNsID
== kNameSpaceID_None
) {
125 nsAutoString lnameStr
;
126 nsContentUtils::ASCIIToLower(aLocalName
, lnameStr
);
127 lname
= NS_Atomize(lnameStr
);
129 lname
= NS_Atomize(aLocalName
);
132 NS_ENSURE_TRUE(lname
, NS_ERROR_OUT_OF_MEMORY
);
134 // Check that it's a valid name
135 if (!nsContentUtils::IsValidNodeName(lname
, aPrefix
, aNsID
)) {
136 // Try without prefix
138 if (!nsContentUtils::IsValidNodeName(lname
, aPrefix
, aNsID
)) {
139 // Don't return error here since the callers don't deal
144 return attributeInternal(aPrefix
, lname
, aNsID
, aValue
);
147 nsresult
txMozillaXMLOutput::attributeInternal(nsAtom
* aPrefix
,
150 const nsString
& aValue
) {
151 if (!mOpenedElement
) {
152 // XXX Signal this? (can't add attributes after element closed)
156 NS_ASSERTION(!mBadChildLevel
, "mBadChildLevel set when element is opened");
158 return mOpenedElement
->SetAttr(aNsID
, aLocalName
, aPrefix
, aValue
, false);
161 nsresult
txMozillaXMLOutput::characters(const nsAString
& aData
, bool aDOE
) {
162 nsresult rv
= closePrevious(false);
163 NS_ENSURE_SUCCESS(rv
, rv
);
165 if (!mBadChildLevel
) {
172 nsresult
txMozillaXMLOutput::comment(const nsString
& aData
) {
173 nsresult rv
= closePrevious(true);
174 NS_ENSURE_SUCCESS(rv
, rv
);
176 if (mBadChildLevel
) {
180 TX_ENSURE_CURRENTNODE
;
182 RefPtr
<Comment
> comment
= new (mNodeInfoManager
) Comment(mNodeInfoManager
);
184 rv
= comment
->SetText(aData
, false);
185 NS_ENSURE_SUCCESS(rv
, rv
);
188 mCurrentNode
->AppendChildTo(comment
, true, error
);
189 return error
.StealNSResult();
192 nsresult
txMozillaXMLOutput::endDocument(nsresult aResult
) {
193 TX_ENSURE_CURRENTNODE
;
195 if (NS_FAILED(aResult
)) {
197 mNotifier
->OnTransformEnd(aResult
);
203 nsresult rv
= closePrevious(true);
206 mNotifier
->OnTransformEnd(rv
);
212 if (mCreatingNewDocument
) {
213 // This should really be handled by Document::EndLoad
214 MOZ_ASSERT(mDocument
->GetReadyStateEnum() == Document::READYSTATE_LOADING
,
216 mDocument
->SetReadyStateInternal(Document::READYSTATE_INTERACTIVE
);
217 if (ScriptLoader
* loader
= mDocument
->ScriptLoader()) {
218 loader
->ParsingComplete(false);
223 mNotifier
->OnTransformEnd();
229 nsresult
txMozillaXMLOutput::endElement() {
230 TX_ENSURE_CURRENTNODE
;
232 if (mBadChildLevel
) {
234 MOZ_LOG(txLog::xslt
, LogLevel::Debug
,
235 ("endElement, mBadChildLevel = %d\n", mBadChildLevel
));
241 nsresult rv
= closePrevious(true);
242 NS_ENSURE_SUCCESS(rv
, rv
);
244 NS_ASSERTION(mCurrentNode
->IsElement(), "borked mCurrentNode");
245 NS_ENSURE_TRUE(mCurrentNode
->IsElement(), NS_ERROR_UNEXPECTED
);
247 Element
* element
= mCurrentNode
->AsElement();
249 // Handle html-elements
251 if (element
->IsHTMLElement()) {
252 endHTMLElement(element
);
255 // Handle elements that are different when parser-created
256 if (nsIContent::RequiresDoneCreatingElement(
257 element
->NodeInfo()->NamespaceID(),
258 element
->NodeInfo()->NameAtom())) {
259 element
->DoneCreatingElement();
260 } else if (element
->IsSVGElement(nsGkAtoms::script
) ||
261 element
->IsHTMLElement(nsGkAtoms::script
)) {
262 nsCOMPtr
<nsIScriptElement
> sele
= do_QueryInterface(element
);
264 bool block
= sele
->AttemptToExecute();
265 // If the act of insertion evaluated the script, we're fine.
266 // Else, add this script element to the array of loading scripts.
268 mNotifier
->AddScriptElement(sele
);
271 MOZ_ASSERT(nsNameSpaceManager::GetInstance()->mSVGDisabled
,
272 "Script elements need to implement nsIScriptElement and SVG "
275 } else if (nsIContent::RequiresDoneAddingChildren(
276 element
->NodeInfo()->NamespaceID(),
277 element
->NodeInfo()->NameAtom())) {
278 element
->DoneAddingChildren(true);
282 if (mCreatingNewDocument
) {
283 // Handle all sorts of stylesheets
284 if (auto* linkStyle
= LinkStyle::FromNode(*mCurrentNode
)) {
286 linkStyle
->EnableUpdatesAndUpdateStyleSheet(mNotifier
);
287 if (mNotifier
&& updateOrError
.isOk() &&
288 updateOrError
.unwrap().ShouldBlock()) {
289 mNotifier
->AddPendingStylesheet();
294 // Add the element to the tree if it wasn't added before and take one step
296 MOZ_ASSERT(!mCurrentNodeStack
.IsEmpty(), "empty stack");
297 nsCOMPtr
<nsINode
> parent
;
298 if (!mCurrentNodeStack
.IsEmpty()) {
299 parent
= mCurrentNodeStack
.PopLastElement();
302 if (mCurrentNode
== mNonAddedNode
) {
303 if (parent
== mDocument
) {
304 NS_ASSERTION(!mRootContentCreated
,
305 "Parent to add to shouldn't be a document if we "
306 "have a root content");
307 mRootContentCreated
= true;
310 // Check to make sure that script hasn't inserted the node somewhere
312 if (!mCurrentNode
->GetParentNode()) {
313 parent
->AppendChildTo(mNonAddedNode
, true, IgnoreErrors());
315 mNonAddedNode
= nullptr;
318 mCurrentNode
= parent
;
321 static_cast<TableState
>(NS_PTR_TO_INT32(mTableStateStack
.pop()));
326 void txMozillaXMLOutput::getOutputDocument(Document
** aDocument
) {
327 NS_IF_ADDREF(*aDocument
= mDocument
);
330 nsresult
txMozillaXMLOutput::processingInstruction(const nsString
& aTarget
,
331 const nsString
& aData
) {
332 nsresult rv
= closePrevious(true);
333 NS_ENSURE_SUCCESS(rv
, rv
);
335 if (mOutputFormat
.mMethod
== eHTMLOutput
) return NS_OK
;
337 TX_ENSURE_CURRENTNODE
;
339 rv
= nsContentUtils::CheckQName(aTarget
, false);
340 NS_ENSURE_SUCCESS(rv
, rv
);
342 nsCOMPtr
<nsIContent
> pi
=
343 NS_NewXMLProcessingInstruction(mNodeInfoManager
, aTarget
, aData
);
345 LinkStyle
* linkStyle
= nullptr;
346 if (mCreatingNewDocument
) {
347 linkStyle
= LinkStyle::FromNode(*pi
);
349 linkStyle
->DisableUpdates();
354 mCurrentNode
->AppendChildTo(pi
, true, error
);
355 if (error
.Failed()) {
356 return error
.StealNSResult();
360 auto updateOrError
= linkStyle
->EnableUpdatesAndUpdateStyleSheet(mNotifier
);
361 if (mNotifier
&& updateOrError
.isOk() &&
362 updateOrError
.unwrap().ShouldBlock()) {
363 mNotifier
->AddPendingStylesheet();
370 nsresult
txMozillaXMLOutput::startDocument() {
372 mNotifier
->OnTransformStart();
375 if (mCreatingNewDocument
) {
376 ScriptLoader
* loader
= mDocument
->ScriptLoader();
378 loader
->BeginDeferringScripts();
385 nsresult
txMozillaXMLOutput::startElement(nsAtom
* aPrefix
, nsAtom
* aLocalName
,
386 nsAtom
* aLowercaseLocalName
,
387 const int32_t aNsID
) {
388 MOZ_ASSERT(aNsID
!= kNameSpaceID_None
|| !aPrefix
,
389 "Can't have prefix without namespace");
391 if (mOutputFormat
.mMethod
== eHTMLOutput
&& aNsID
== kNameSpaceID_None
) {
392 RefPtr
<nsAtom
> owner
;
393 if (!aLowercaseLocalName
) {
394 owner
= TX_ToLowerCaseAtom(aLocalName
);
395 NS_ENSURE_TRUE(owner
, NS_ERROR_OUT_OF_MEMORY
);
397 aLowercaseLocalName
= owner
;
399 return startElementInternal(nullptr, aLowercaseLocalName
,
403 return startElementInternal(aPrefix
, aLocalName
, aNsID
);
406 nsresult
txMozillaXMLOutput::startElement(nsAtom
* aPrefix
,
407 const nsAString
& aLocalName
,
408 const int32_t aNsID
) {
409 int32_t nsId
= aNsID
;
410 RefPtr
<nsAtom
> lname
;
412 if (mOutputFormat
.mMethod
== eHTMLOutput
&& aNsID
== kNameSpaceID_None
) {
413 nsId
= kNameSpaceID_XHTML
;
415 nsAutoString lnameStr
;
416 nsContentUtils::ASCIIToLower(aLocalName
, lnameStr
);
417 lname
= NS_Atomize(lnameStr
);
419 lname
= NS_Atomize(aLocalName
);
422 // No biggie if we lose the prefix due to OOM
423 NS_ENSURE_TRUE(lname
, NS_ERROR_OUT_OF_MEMORY
);
425 // Check that it's a valid name
426 if (!nsContentUtils::IsValidNodeName(lname
, aPrefix
, nsId
)) {
427 // Try without prefix
429 if (!nsContentUtils::IsValidNodeName(lname
, aPrefix
, nsId
)) {
430 return NS_ERROR_XSLT_BAD_NODE_NAME
;
434 return startElementInternal(aPrefix
, lname
, nsId
);
437 nsresult
txMozillaXMLOutput::startElementInternal(nsAtom
* aPrefix
,
440 TX_ENSURE_CURRENTNODE
;
442 if (mBadChildLevel
) {
444 MOZ_LOG(txLog::xslt
, LogLevel::Debug
,
445 ("startElement, mBadChildLevel = %d\n", mBadChildLevel
));
449 MOZ_TRY(closePrevious(true));
451 // Push and init state
452 if (mTreeDepth
== MAX_REFLOW_DEPTH
) {
453 // eCloseElement couldn't add the parent so we fail as well or we've
454 // reached the limit of the depth of the tree that we allow.
456 MOZ_LOG(txLog::xslt
, LogLevel::Debug
,
457 ("startElement, mBadChildLevel = %d\n", mBadChildLevel
));
463 mTableStateStack
.push(NS_INT32_TO_PTR(mTableState
));
465 mCurrentNodeStack
.AppendElement(mCurrentNode
);
467 mTableState
= NORMAL
;
468 mOpenedElementIsHTML
= false;
470 // Create the element
471 RefPtr
<NodeInfo
> ni
= mNodeInfoManager
->GetNodeInfo(
472 aLocalName
, aPrefix
, aNsID
, nsINode::ELEMENT_NODE
);
474 NS_NewElement(getter_AddRefs(mOpenedElement
), ni
.forget(),
475 mCreatingNewDocument
? FROM_PARSER_XSLT
: FROM_PARSER_FRAGMENT
);
477 // Set up the element and adjust state
478 if (!mNoFixup
&& aNsID
== kNameSpaceID_XHTML
) {
479 mOpenedElementIsHTML
= (mOutputFormat
.mMethod
== eHTMLOutput
);
480 MOZ_TRY(startHTMLElement(mOpenedElement
, mOpenedElementIsHTML
));
483 if (mCreatingNewDocument
) {
484 // Handle all sorts of stylesheets
485 if (auto* linkStyle
= LinkStyle::FromNode(*mOpenedElement
)) {
486 linkStyle
->DisableUpdates();
493 nsresult
txMozillaXMLOutput::closePrevious(bool aFlushText
) {
494 TX_ENSURE_CURRENTNODE
;
496 if (mOpenedElement
) {
497 bool currentIsDoc
= mCurrentNode
== mDocument
;
498 if (currentIsDoc
&& mRootContentCreated
) {
499 // We already have a document element, but the XSLT spec allows this.
500 // As a workaround, create a wrapper object and use that as the
503 MOZ_TRY(createTxWrapper());
507 mCurrentNode
->AppendChildTo(mOpenedElement
, true, error
);
508 if (error
.Failed()) {
509 return error
.StealNSResult();
513 mRootContentCreated
= true;
514 nsContentUtils::AddScriptRunner(
515 new nsDocElementCreatedNotificationRunner(mDocument
));
518 mCurrentNode
= mOpenedElement
;
519 mOpenedElement
= nullptr;
520 } else if (aFlushText
&& !mText
.IsEmpty()) {
521 // Text can't appear in the root of a document
522 if (mDocument
== mCurrentNode
) {
523 if (XMLUtils::isWhitespace(mText
)) {
529 MOZ_TRY(createTxWrapper());
531 RefPtr
<nsTextNode
> text
=
532 new (mNodeInfoManager
) nsTextNode(mNodeInfoManager
);
534 MOZ_TRY(text
->SetText(mText
, false));
537 mCurrentNode
->AppendChildTo(text
, true, error
);
538 if (error
.Failed()) {
539 return error
.StealNSResult();
548 nsresult
txMozillaXMLOutput::createTxWrapper() {
549 NS_ASSERTION(mDocument
== mCurrentNode
,
550 "creating wrapper when document isn't parent");
553 MOZ_TRY(nsNameSpaceManager::GetInstance()->RegisterNameSpace(
554 nsLiteralString(kTXNameSpaceURI
), namespaceID
));
556 nsCOMPtr
<Element
> wrapper
=
557 mDocument
->CreateElem(nsDependentAtomString(nsGkAtoms::result
),
558 nsGkAtoms::transformiix
, namespaceID
);
561 // Keep track of the location of the current documentElement, if there is
562 // one, so we can verify later
563 uint32_t j
= 0, rootLocation
= 0;
565 for (nsCOMPtr
<nsIContent
> childContent
= mDocument
->GetFirstChild();
566 childContent
; childContent
= childContent
->GetNextSibling()) {
568 if (childContent
->IsElement()) {
573 if (childContent
->NodeInfo()->NameAtom() ==
574 nsGkAtoms::documentTypeNodeName
) {
576 // The new documentElement should go after the document type.
577 // This is needed for cases when there is no existing
578 // documentElement in the document.
579 rootLocation
= std::max(rootLocation
, j
+ 1);
583 mDocument
->RemoveChildNode(childContent
, true);
586 wrapper
->AppendChildTo(childContent
, true, error
);
587 if (error
.Failed()) {
588 return error
.StealNSResult();
594 mCurrentNodeStack
.AppendElement(wrapper
);
595 mCurrentNode
= wrapper
;
596 mRootContentCreated
= true;
597 NS_ASSERTION(rootLocation
== mDocument
->GetChildCount(),
598 "Incorrect root location");
600 mDocument
->AppendChildTo(wrapper
, true, error
);
601 return error
.StealNSResult();
604 nsresult
txMozillaXMLOutput::startHTMLElement(nsIContent
* aElement
,
606 if ((!aElement
->IsHTMLElement(nsGkAtoms::tr
) || !aIsHTML
) &&
607 NS_PTR_TO_INT32(mTableStateStack
.peek()) == ADDED_TBODY
) {
608 MOZ_ASSERT(!mCurrentNodeStack
.IsEmpty(), "empty stack");
609 if (mCurrentNodeStack
.IsEmpty()) {
610 mCurrentNode
= nullptr;
612 mCurrentNode
= mCurrentNodeStack
.PopLastElement();
614 mTableStateStack
.pop();
617 if (aElement
->IsHTMLElement(nsGkAtoms::table
) && aIsHTML
) {
619 } else if (aElement
->IsHTMLElement(nsGkAtoms::tr
) && aIsHTML
&&
620 NS_PTR_TO_INT32(mTableStateStack
.peek()) == TABLE
) {
621 RefPtr
<Element
> tbody
;
622 MOZ_TRY(createHTMLElement(nsGkAtoms::tbody
, getter_AddRefs(tbody
)));
625 mCurrentNode
->AppendChildTo(tbody
, true, error
);
626 if (error
.Failed()) {
627 return error
.StealNSResult();
630 mTableStateStack
.push(NS_INT32_TO_PTR(ADDED_TBODY
));
632 mCurrentNodeStack
.AppendElement(tbody
);
633 mCurrentNode
= tbody
;
634 } else if (aElement
->IsHTMLElement(nsGkAtoms::head
) &&
635 mOutputFormat
.mMethod
== eHTMLOutput
) {
636 // Insert META tag, according to spec, 16.2, like
637 // <META http-equiv="Content-Type" content="text/html; charset=EUC-JP">
638 RefPtr
<Element
> meta
;
639 MOZ_TRY(createHTMLElement(nsGkAtoms::meta
, getter_AddRefs(meta
)));
641 MOZ_TRY(meta
->SetAttr(kNameSpaceID_None
, nsGkAtoms::httpEquiv
,
642 u
"Content-Type"_ns
, false));
644 nsAutoString metacontent
;
645 CopyUTF8toUTF16(mOutputFormat
.mMediaType
, metacontent
);
646 metacontent
.AppendLiteral("; charset=");
647 metacontent
.Append(mOutputFormat
.mEncoding
);
648 MOZ_TRY(meta
->SetAttr(kNameSpaceID_None
, nsGkAtoms::content
, metacontent
,
651 // No need to notify since aElement hasn't been inserted yet
652 NS_ASSERTION(!aElement
->IsInUncomposedDoc(), "should not be in doc");
654 aElement
->AppendChildTo(meta
, false, error
);
655 if (error
.Failed()) {
656 return error
.StealNSResult();
663 void txMozillaXMLOutput::endHTMLElement(nsIContent
* aElement
) {
664 if (mTableState
== ADDED_TBODY
) {
665 NS_ASSERTION(aElement
->IsHTMLElement(nsGkAtoms::tbody
),
666 "Element flagged as added tbody isn't a tbody");
667 MOZ_ASSERT(!mCurrentNodeStack
.IsEmpty(), "empty stack");
668 if (mCurrentNodeStack
.IsEmpty()) {
669 mCurrentNode
= nullptr;
671 mCurrentNode
= mCurrentNodeStack
.PopLastElement();
674 static_cast<TableState
>(NS_PTR_TO_INT32(mTableStateStack
.pop()));
678 nsresult
txMozillaXMLOutput::createResultDocument(const nsAString
& aName
,
680 Document
* aSourceDocument
,
681 bool aLoadedAsData
) {
682 // Create the document
683 if (mOutputFormat
.mMethod
== eHTMLOutput
) {
684 MOZ_TRY(NS_NewHTMLDocument(getter_AddRefs(mDocument
), nullptr, nullptr,
687 // We should check the root name/namespace here and create the
688 // appropriate document
689 MOZ_TRY(NS_NewXMLDocument(getter_AddRefs(mDocument
), nullptr, nullptr,
692 // This should really be handled by Document::BeginLoad
694 mDocument
->GetReadyStateEnum() == Document::READYSTATE_UNINITIALIZED
,
696 mDocument
->SetReadyStateInternal(Document::READYSTATE_LOADING
);
697 mDocument
->SetMayStartLayout(false);
698 bool hasHadScriptObject
= false;
699 nsIScriptGlobalObject
* sgo
=
700 aSourceDocument
->GetScriptHandlingObject(hasHadScriptObject
);
701 NS_ENSURE_STATE(sgo
|| !hasHadScriptObject
);
703 mCurrentNode
= mDocument
;
704 mNodeInfoManager
= mDocument
->NodeInfoManager();
706 // Reset and set up the document
707 URIUtils::ResetWithSource(mDocument
, aSourceDocument
);
709 // Make sure we set the script handling object after resetting with the
710 // source, so that we have the right principal.
711 mDocument
->SetScriptHandlingObject(sgo
);
713 mDocument
->SetStateObjectFrom(aSourceDocument
);
716 if (!mOutputFormat
.mEncoding
.IsEmpty()) {
717 const Encoding
* encoding
= Encoding::ForLabel(mOutputFormat
.mEncoding
);
719 mDocument
->SetDocumentCharacterSetSource(kCharsetFromOtherComponent
);
720 mDocument
->SetDocumentCharacterSet(WrapNotNull(encoding
));
725 if (!mOutputFormat
.mMediaType
.IsEmpty()) {
726 mDocument
->SetContentType(mOutputFormat
.mMediaType
);
727 } else if (mOutputFormat
.mMethod
== eHTMLOutput
) {
728 mDocument
->SetContentType("text/html"_ns
);
730 mDocument
->SetContentType("application/xml"_ns
);
733 if (mOutputFormat
.mMethod
== eXMLOutput
&&
734 mOutputFormat
.mOmitXMLDeclaration
!= eTrue
) {
736 if (mOutputFormat
.mStandalone
== eNotSet
) {
738 } else if (mOutputFormat
.mStandalone
== eFalse
) {
744 // Could use mOutputFormat.mVersion.get() when we support
746 static const char16_t kOneDotZero
[] = {'1', '.', '0', '\0'};
747 mDocument
->SetXMLDeclaration(kOneDotZero
, mOutputFormat
.mEncoding
.get(),
751 // Set up script loader of the result document.
752 ScriptLoader
* loader
= mDocument
->ScriptLoader();
754 loader
->AddObserver(mNotifier
);
756 // Don't load scripts, we can't notify the caller when they're loaded.
757 loader
->SetEnabled(false);
761 MOZ_TRY(mNotifier
->SetOutputDocument(mDocument
));
762 MOZ_TRY(mDocument
->InitFeaturePolicy(mDocument
->GetChannel()));
765 // Do this after calling OnDocumentCreated to ensure that the
766 // PresShell/PresContext has been hooked up and get notified.
767 mDocument
->SetCompatibilityMode(eCompatibility_FullStandards
);
769 // Add a doc-type if requested
770 if (!mOutputFormat
.mSystemId
.IsEmpty()) {
772 if (mOutputFormat
.mMethod
== eHTMLOutput
) {
773 qName
.AssignLiteral("html");
778 nsresult rv
= nsContentUtils::CheckQName(qName
);
779 if (NS_SUCCEEDED(rv
)) {
780 RefPtr
<nsAtom
> doctypeName
= NS_Atomize(qName
);
782 return NS_ERROR_OUT_OF_MEMORY
;
785 // Indicate that there is no internal subset (not just an empty one)
786 RefPtr
<DocumentType
> documentType
= NS_NewDOMDocumentType(
787 mNodeInfoManager
, doctypeName
, mOutputFormat
.mPublicId
,
788 mOutputFormat
.mSystemId
, VoidString());
791 mDocument
->AppendChildTo(documentType
, true, error
);
792 if (error
.Failed()) {
793 return error
.StealNSResult();
801 nsresult
txMozillaXMLOutput::createHTMLElement(nsAtom
* aName
,
803 NS_ASSERTION(mOutputFormat
.mMethod
== eHTMLOutput
,
804 "need to adjust createHTMLElement");
809 ni
= mNodeInfoManager
->GetNodeInfo(aName
, nullptr, kNameSpaceID_XHTML
,
810 nsINode::ELEMENT_NODE
);
812 nsCOMPtr
<Element
> el
;
813 nsresult rv
= NS_NewHTMLElement(
814 getter_AddRefs(el
), ni
.forget(),
815 mCreatingNewDocument
? FROM_PARSER_XSLT
: FROM_PARSER_FRAGMENT
);
820 txTransformNotifier::txTransformNotifier(Document
* aSourceDocument
)
821 : mSourceDocument(aSourceDocument
),
822 mPendingStylesheetCount(0),
823 mInTransform(false) {}
825 txTransformNotifier::~txTransformNotifier() = default;
827 NS_IMPL_ISUPPORTS(txTransformNotifier
, nsIScriptLoaderObserver
,
828 nsICSSLoaderObserver
)
831 txTransformNotifier::ScriptAvailable(nsresult aResult
,
832 nsIScriptElement
* aElement
,
833 bool aIsInlineClassicScript
, nsIURI
* aURI
,
835 if (NS_FAILED(aResult
) && mScriptElements
.RemoveElement(aElement
)) {
836 SignalTransformEnd();
843 txTransformNotifier::ScriptEvaluated(nsresult aResult
,
844 nsIScriptElement
* aElement
,
846 if (mScriptElements
.RemoveElement(aElement
)) {
847 SignalTransformEnd();
854 txTransformNotifier::StyleSheetLoaded(StyleSheet
* aSheet
, bool aWasDeferred
,
856 if (mPendingStylesheetCount
== 0) {
857 // We weren't waiting on this stylesheet anyway. This can happen if
858 // SignalTransformEnd got called with an error aResult. See
859 // http://bugzilla.mozilla.org/show_bug.cgi?id=215465.
863 // We're never waiting for alternate stylesheets
865 --mPendingStylesheetCount
;
866 SignalTransformEnd();
872 void txTransformNotifier::Init(nsITransformObserver
* aObserver
) {
873 mObserver
= aObserver
;
876 void txTransformNotifier::AddScriptElement(nsIScriptElement
* aElement
) {
877 mScriptElements
.AppendElement(aElement
);
880 void txTransformNotifier::AddPendingStylesheet() { ++mPendingStylesheetCount
; }
882 void txTransformNotifier::OnTransformEnd(nsresult aResult
) {
883 mInTransform
= false;
884 SignalTransformEnd(aResult
);
887 void txTransformNotifier::OnTransformStart() { mInTransform
= true; }
889 nsresult
txTransformNotifier::SetOutputDocument(Document
* aDocument
) {
890 mDocument
= aDocument
;
892 // Notify the contentsink that the document is created
893 return mObserver
->OnDocumentCreated(mSourceDocument
, mDocument
);
896 void txTransformNotifier::SignalTransformEnd(nsresult aResult
) {
898 (NS_SUCCEEDED(aResult
) &&
899 (!mScriptElements
.IsEmpty() || mPendingStylesheetCount
> 0))) {
903 // mPendingStylesheetCount is nonzero at this point only if aResult is an
904 // error. Set it to 0 so we won't reenter this code when we stop the
906 mPendingStylesheetCount
= 0;
907 mScriptElements
.Clear();
909 // Make sure that we don't get deleted while this function is executed and
910 // we remove ourselfs from the scriptloader
911 nsCOMPtr
<nsIScriptLoaderObserver
> kungFuDeathGrip(this);
914 mDocument
->ScriptLoader()->DeferCheckpointReached();
915 mDocument
->ScriptLoader()->RemoveObserver(this);
916 // XXX Maybe we want to cancel script loads if NS_FAILED(rv)?
918 if (NS_FAILED(aResult
)) {
919 mDocument
->CSSLoader()->Stop();
923 if (NS_SUCCEEDED(aResult
)) {
924 mObserver
->OnTransformDone(mSourceDocument
, aResult
, mDocument
);