Backed out changeset 9d8b4c0b99ed (bug 1945683) for causing btime failures. CLOSED...
[gecko.git] / dom / xslt / xslt / txMozillaXMLOutput.cpp
blob75f9f4e69a35804d621ce34142d10bf16abae094
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"
18 #include "txLog.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"
35 #include "nsINode.h"
36 #include "nsContentCreatorFunctions.h"
37 #include "nsError.h"
38 #include "nsStringFlags.h"
39 #include "nsStyleUtil.h"
40 #include "nsIFrame.h"
41 #include <algorithm>
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)
57 : mTreeDepth(0),
58 mBadChildLevel(0),
59 mTableState(NORMAL),
60 mCreatingNewDocument(true),
61 mOpenedElementIsHTML(false),
62 mRootContentCreated(false),
63 mNoFixup(false) {
64 MOZ_COUNT_CTOR(txMozillaXMLOutput);
65 if (aObserver) {
66 mNotifier = new txTransformNotifier(aSourceDocument);
67 if (mNotifier) {
68 mNotifier->Init(aObserver);
72 mOutputFormat.merge(*aFormat);
73 mOutputFormat.setFromDefaults();
76 txMozillaXMLOutput::txMozillaXMLOutput(txOutputFormat* aFormat,
77 DocumentFragment* aFragment,
78 bool aNoFixup)
79 : mTreeDepth(0),
80 mBadChildLevel(0),
81 mTableState(NORMAL),
82 mCreatingNewDocument(false),
83 mOpenedElementIsHTML(false),
84 mRootContentCreated(false),
85 mNoFixup(aNoFixup) {
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,
101 const int32_t aNsID,
102 const nsString& aValue) {
103 RefPtr<nsAtom> owner;
104 if (mOpenedElementIsHTML && aNsID == kNameSpaceID_None) {
105 if (aLowercaseLocalName) {
106 aLocalName = aLowercaseLocalName;
107 } else {
108 owner = TX_ToLowerCaseAtom(aLocalName);
109 NS_ENSURE_TRUE(owner, NS_ERROR_OUT_OF_MEMORY);
111 aLocalName = owner;
115 return attributeInternal(aPrefix, aLocalName, aNsID, aValue);
118 nsresult txMozillaXMLOutput::attribute(nsAtom* aPrefix,
119 const nsAString& aLocalName,
120 const int32_t aNsID,
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);
128 } else {
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
137 aPrefix = nullptr;
138 if (!nsContentUtils::IsValidNodeName(lname, aPrefix, aNsID)) {
139 // Don't return error here since the callers don't deal
140 return NS_OK;
144 return attributeInternal(aPrefix, lname, aNsID, aValue);
147 nsresult txMozillaXMLOutput::attributeInternal(nsAtom* aPrefix,
148 nsAtom* aLocalName,
149 int32_t aNsID,
150 const nsString& aValue) {
151 if (!mOpenedElement) {
152 // XXX Signal this? (can't add attributes after element closed)
153 return NS_OK;
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) {
166 mText.Append(aData);
169 return NS_OK;
172 nsresult txMozillaXMLOutput::comment(const nsString& aData) {
173 nsresult rv = closePrevious(true);
174 NS_ENSURE_SUCCESS(rv, rv);
176 if (mBadChildLevel) {
177 return NS_OK;
180 TX_ENSURE_CURRENTNODE;
182 RefPtr<Comment> comment = new (mNodeInfoManager) Comment(mNodeInfoManager);
184 rv = comment->SetText(aData, false);
185 NS_ENSURE_SUCCESS(rv, rv);
187 ErrorResult error;
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)) {
196 if (mNotifier) {
197 mNotifier->OnTransformEnd(aResult);
200 return NS_OK;
203 nsresult rv = closePrevious(true);
204 if (NS_FAILED(rv)) {
205 if (mNotifier) {
206 mNotifier->OnTransformEnd(rv);
209 return rv;
212 if (mCreatingNewDocument) {
213 // This should really be handled by Document::EndLoad
214 MOZ_ASSERT(mDocument->GetReadyStateEnum() == Document::READYSTATE_LOADING,
215 "Bad readyState");
216 mDocument->SetReadyStateInternal(Document::READYSTATE_INTERACTIVE);
217 if (ScriptLoader* loader = mDocument->ScriptLoader()) {
218 loader->ParsingComplete(false);
222 if (mNotifier) {
223 mNotifier->OnTransformEnd();
226 return NS_OK;
229 nsresult txMozillaXMLOutput::endElement() {
230 TX_ENSURE_CURRENTNODE;
232 if (mBadChildLevel) {
233 --mBadChildLevel;
234 MOZ_LOG(txLog::xslt, LogLevel::Debug,
235 ("endElement, mBadChildLevel = %d\n", mBadChildLevel));
236 return NS_OK;
239 --mTreeDepth;
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
250 if (!mNoFixup) {
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);
263 if (sele) {
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.
267 if (block) {
268 mNotifier->AddScriptElement(sele);
270 } else {
271 MOZ_ASSERT(nsNameSpaceManager::GetInstance()->mSVGDisabled,
272 "Script elements need to implement nsIScriptElement and SVG "
273 "wasn't disabled.");
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)) {
285 auto updateOrError =
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
295 // up the tree
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
311 // else in the tree
312 if (!mCurrentNode->GetParentNode()) {
313 parent->AppendChildTo(mNonAddedNode, true, IgnoreErrors());
315 mNonAddedNode = nullptr;
318 mCurrentNode = parent;
320 mTableState =
321 static_cast<TableState>(NS_PTR_TO_INT32(mTableStateStack.pop()));
323 return NS_OK;
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);
348 if (linkStyle) {
349 linkStyle->DisableUpdates();
353 ErrorResult error;
354 mCurrentNode->AppendChildTo(pi, true, error);
355 if (error.Failed()) {
356 return error.StealNSResult();
359 if (linkStyle) {
360 auto updateOrError = linkStyle->EnableUpdatesAndUpdateStyleSheet(mNotifier);
361 if (mNotifier && updateOrError.isOk() &&
362 updateOrError.unwrap().ShouldBlock()) {
363 mNotifier->AddPendingStylesheet();
367 return NS_OK;
370 nsresult txMozillaXMLOutput::startDocument() {
371 if (mNotifier) {
372 mNotifier->OnTransformStart();
375 if (mCreatingNewDocument) {
376 ScriptLoader* loader = mDocument->ScriptLoader();
377 if (loader) {
378 loader->BeginDeferringScripts();
382 return NS_OK;
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,
400 kNameSpaceID_XHTML);
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);
418 } else {
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
428 aPrefix = nullptr;
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,
438 nsAtom* aLocalName,
439 int32_t aNsID) {
440 TX_ENSURE_CURRENTNODE;
442 if (mBadChildLevel) {
443 ++mBadChildLevel;
444 MOZ_LOG(txLog::xslt, LogLevel::Debug,
445 ("startElement, mBadChildLevel = %d\n", mBadChildLevel));
446 return NS_OK;
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.
455 ++mBadChildLevel;
456 MOZ_LOG(txLog::xslt, LogLevel::Debug,
457 ("startElement, mBadChildLevel = %d\n", mBadChildLevel));
458 return NS_OK;
461 ++mTreeDepth;
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();
490 return NS_OK;
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
501 // document element.
503 MOZ_TRY(createTxWrapper());
506 ErrorResult error;
507 mCurrentNode->AppendChildTo(mOpenedElement, true, error);
508 if (error.Failed()) {
509 return error.StealNSResult();
512 if (currentIsDoc) {
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)) {
524 mText.Truncate();
526 return NS_OK;
529 MOZ_TRY(createTxWrapper());
531 RefPtr<nsTextNode> text =
532 new (mNodeInfoManager) nsTextNode(mNodeInfoManager);
534 MOZ_TRY(text->SetText(mText, false));
536 ErrorResult error;
537 mCurrentNode->AppendChildTo(text, true, error);
538 if (error.Failed()) {
539 return error.StealNSResult();
542 mText.Truncate();
545 return NS_OK;
548 nsresult txMozillaXMLOutput::createTxWrapper() {
549 NS_ASSERTION(mDocument == mCurrentNode,
550 "creating wrapper when document isn't parent");
552 int32_t namespaceID;
553 MOZ_TRY(nsNameSpaceManager::GetInstance()->RegisterNameSpace(
554 nsLiteralString(kTXNameSpaceURI), namespaceID));
556 nsCOMPtr<Element> wrapper =
557 mDocument->CreateElem(nsDependentAtomString(nsGkAtoms::result),
558 nsGkAtoms::transformiix, namespaceID);
560 #ifdef DEBUG
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;
564 #endif
565 for (nsCOMPtr<nsIContent> childContent = mDocument->GetFirstChild();
566 childContent; childContent = childContent->GetNextSibling()) {
567 #ifdef DEBUG
568 if (childContent->IsElement()) {
569 rootLocation = j;
571 #endif
573 if (childContent->NodeInfo()->NameAtom() ==
574 nsGkAtoms::documentTypeNodeName) {
575 #ifdef DEBUG
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);
580 ++j;
581 #endif
582 } else {
583 mDocument->RemoveChildNode(childContent, true);
585 ErrorResult error;
586 wrapper->AppendChildTo(childContent, true, error);
587 if (error.Failed()) {
588 return error.StealNSResult();
590 break;
594 mCurrentNodeStack.AppendElement(wrapper);
595 mCurrentNode = wrapper;
596 mRootContentCreated = true;
597 NS_ASSERTION(rootLocation == mDocument->GetChildCount(),
598 "Incorrect root location");
599 ErrorResult error;
600 mDocument->AppendChildTo(wrapper, true, error);
601 return error.StealNSResult();
604 nsresult txMozillaXMLOutput::startHTMLElement(nsIContent* aElement,
605 bool aIsHTML) {
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;
611 } else {
612 mCurrentNode = mCurrentNodeStack.PopLastElement();
614 mTableStateStack.pop();
617 if (aElement->IsHTMLElement(nsGkAtoms::table) && aIsHTML) {
618 mTableState = TABLE;
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)));
624 ErrorResult error;
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,
649 false));
651 // No need to notify since aElement hasn't been inserted yet
652 NS_ASSERTION(!aElement->IsInUncomposedDoc(), "should not be in doc");
653 ErrorResult error;
654 aElement->AppendChildTo(meta, false, error);
655 if (error.Failed()) {
656 return error.StealNSResult();
660 return NS_OK;
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;
670 } else {
671 mCurrentNode = mCurrentNodeStack.PopLastElement();
673 mTableState =
674 static_cast<TableState>(NS_PTR_TO_INT32(mTableStateStack.pop()));
678 nsresult txMozillaXMLOutput::createResultDocument(const nsAString& aName,
679 int32_t aNsID,
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,
685 aLoadedAsData));
686 } else {
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,
690 aLoadedAsData));
692 // This should really be handled by Document::BeginLoad
693 MOZ_ASSERT(
694 mDocument->GetReadyStateEnum() == Document::READYSTATE_UNINITIALIZED,
695 "Bad readyState");
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);
715 // Set the charset
716 if (!mOutputFormat.mEncoding.IsEmpty()) {
717 const Encoding* encoding = Encoding::ForLabel(mOutputFormat.mEncoding);
718 if (encoding) {
719 mDocument->SetDocumentCharacterSetSource(kCharsetFromOtherComponent);
720 mDocument->SetDocumentCharacterSet(WrapNotNull(encoding));
724 // Set the mime-type
725 if (!mOutputFormat.mMediaType.IsEmpty()) {
726 mDocument->SetContentType(mOutputFormat.mMediaType);
727 } else if (mOutputFormat.mMethod == eHTMLOutput) {
728 mDocument->SetContentType("text/html"_ns);
729 } else {
730 mDocument->SetContentType("application/xml"_ns);
733 if (mOutputFormat.mMethod == eXMLOutput &&
734 mOutputFormat.mOmitXMLDeclaration != eTrue) {
735 int32_t standalone;
736 if (mOutputFormat.mStandalone == eNotSet) {
737 standalone = -1;
738 } else if (mOutputFormat.mStandalone == eFalse) {
739 standalone = 0;
740 } else {
741 standalone = 1;
744 // Could use mOutputFormat.mVersion.get() when we support
745 // versions > 1.0.
746 static const char16_t kOneDotZero[] = {'1', '.', '0', '\0'};
747 mDocument->SetXMLDeclaration(kOneDotZero, mOutputFormat.mEncoding.get(),
748 standalone);
751 // Set up script loader of the result document.
752 ScriptLoader* loader = mDocument->ScriptLoader();
753 if (mNotifier) {
754 loader->AddObserver(mNotifier);
755 } else {
756 // Don't load scripts, we can't notify the caller when they're loaded.
757 loader->SetEnabled(false);
760 if (mNotifier) {
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()) {
771 nsAutoString qName;
772 if (mOutputFormat.mMethod == eHTMLOutput) {
773 qName.AssignLiteral("html");
774 } else {
775 qName.Assign(aName);
778 nsresult rv = nsContentUtils::CheckQName(qName);
779 if (NS_SUCCEEDED(rv)) {
780 RefPtr<nsAtom> doctypeName = NS_Atomize(qName);
781 if (!doctypeName) {
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());
790 ErrorResult error;
791 mDocument->AppendChildTo(documentType, true, error);
792 if (error.Failed()) {
793 return error.StealNSResult();
798 return NS_OK;
801 nsresult txMozillaXMLOutput::createHTMLElement(nsAtom* aName,
802 Element** aResult) {
803 NS_ASSERTION(mOutputFormat.mMethod == eHTMLOutput,
804 "need to adjust createHTMLElement");
806 *aResult = nullptr;
808 RefPtr<NodeInfo> ni;
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);
816 el.forget(aResult);
817 return rv;
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)
830 NS_IMETHODIMP
831 txTransformNotifier::ScriptAvailable(nsresult aResult,
832 nsIScriptElement* aElement,
833 bool aIsInlineClassicScript, nsIURI* aURI,
834 uint32_t aLineNo) {
835 if (NS_FAILED(aResult) && mScriptElements.RemoveElement(aElement)) {
836 SignalTransformEnd();
839 return NS_OK;
842 NS_IMETHODIMP
843 txTransformNotifier::ScriptEvaluated(nsresult aResult,
844 nsIScriptElement* aElement,
845 bool aIsInline) {
846 if (mScriptElements.RemoveElement(aElement)) {
847 SignalTransformEnd();
850 return NS_OK;
853 NS_IMETHODIMP
854 txTransformNotifier::StyleSheetLoaded(StyleSheet* aSheet, bool aWasDeferred,
855 nsresult aStatus) {
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.
860 return NS_OK;
863 // We're never waiting for alternate stylesheets
864 if (!aWasDeferred) {
865 --mPendingStylesheetCount;
866 SignalTransformEnd();
869 return NS_OK;
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) {
897 if (mInTransform ||
898 (NS_SUCCEEDED(aResult) &&
899 (!mScriptElements.IsEmpty() || mPendingStylesheetCount > 0))) {
900 return;
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
905 // CSSLoader.
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);
913 if (mDocument) {
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);