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/. */
5 
6 #include "txMozillaTextOutput.h"
7 #include "nsContentCID.h"
8 #include "nsIContent.h"
9 #include "mozilla/dom/Document.h"
10 #include "nsIDocumentTransformer.h"
11 #include "nsCharsetSource.h"
12 #include "txURIUtils.h"
13 #include "nsContentCreatorFunctions.h"
14 #include "nsContentUtils.h"
15 #include "nsGkAtoms.h"
16 #include "mozilla/Encoding.h"
17 #include "nsTextNode.h"
18 #include "nsNameSpaceManager.h"
19 #include "mozilla/dom/DocumentFragment.h"
20 
21 using namespace mozilla;
22 using namespace mozilla::dom;
23 
txMozillaTextOutput(nsITransformObserver * aObserver)24 txMozillaTextOutput::txMozillaTextOutput(nsITransformObserver* aObserver)
25     : mObserver(do_GetWeakReference(aObserver)), mCreatedDocument(false) {
26   MOZ_COUNT_CTOR(txMozillaTextOutput);
27 }
28 
txMozillaTextOutput(DocumentFragment * aDest)29 txMozillaTextOutput::txMozillaTextOutput(DocumentFragment* aDest)
30     : mTextParent(aDest),
31       mDocument(mTextParent->OwnerDoc()),
32       mCreatedDocument(false) {
33   MOZ_COUNT_CTOR(txMozillaTextOutput);
34   mTextParent = aDest;
35   mDocument = mTextParent->OwnerDoc();
36 }
37 
~txMozillaTextOutput()38 txMozillaTextOutput::~txMozillaTextOutput() {
39   MOZ_COUNT_DTOR(txMozillaTextOutput);
40 }
41 
attribute(nsAtom * aPrefix,nsAtom * aLocalName,nsAtom * aLowercaseLocalName,int32_t aNsID,const nsString & aValue)42 nsresult txMozillaTextOutput::attribute(nsAtom* aPrefix, nsAtom* aLocalName,
43                                         nsAtom* aLowercaseLocalName,
44                                         int32_t aNsID, const nsString& aValue) {
45   return NS_OK;
46 }
47 
attribute(nsAtom * aPrefix,const nsAString & aName,const int32_t aNsID,const nsString & aValue)48 nsresult txMozillaTextOutput::attribute(nsAtom* aPrefix, const nsAString& aName,
49                                         const int32_t aNsID,
50                                         const nsString& aValue) {
51   return NS_OK;
52 }
53 
characters(const nsAString & aData,bool aDOE)54 nsresult txMozillaTextOutput::characters(const nsAString& aData, bool aDOE) {
55   mText.Append(aData);
56 
57   return NS_OK;
58 }
59 
comment(const nsString & aData)60 nsresult txMozillaTextOutput::comment(const nsString& aData) { return NS_OK; }
61 
endDocument(nsresult aResult)62 nsresult txMozillaTextOutput::endDocument(nsresult aResult) {
63   NS_ENSURE_TRUE(mDocument && mTextParent, NS_ERROR_FAILURE);
64 
65   RefPtr<nsTextNode> text = new (mDocument->NodeInfoManager())
66       nsTextNode(mDocument->NodeInfoManager());
67 
68   ErrorResult rv;
69   text->SetText(mText, false);
70   mTextParent->AppendChildTo(text, true, rv);
71   if (rv.Failed()) {
72     return rv.StealNSResult();
73   }
74 
75   // This should really be handled by Document::EndLoad
76   if (mCreatedDocument) {
77     MOZ_ASSERT(mDocument->GetReadyStateEnum() == Document::READYSTATE_LOADING,
78                "Bad readyState");
79   } else {
80     MOZ_ASSERT(
81         mDocument->GetReadyStateEnum() == Document::READYSTATE_INTERACTIVE,
82         "Bad readyState");
83   }
84   mDocument->SetReadyStateInternal(Document::READYSTATE_INTERACTIVE);
85 
86   if (NS_SUCCEEDED(aResult)) {
87     nsCOMPtr<nsITransformObserver> observer = do_QueryReferent(mObserver);
88     if (observer) {
89       observer->OnTransformDone(aResult, mDocument);
90     }
91   }
92 
93   return NS_OK;
94 }
95 
endElement()96 nsresult txMozillaTextOutput::endElement() { return NS_OK; }
97 
processingInstruction(const nsString & aTarget,const nsString & aData)98 nsresult txMozillaTextOutput::processingInstruction(const nsString& aTarget,
99                                                     const nsString& aData) {
100   return NS_OK;
101 }
102 
startDocument()103 nsresult txMozillaTextOutput::startDocument() { return NS_OK; }
104 
createResultDocument(Document * aSourceDocument,bool aLoadedAsData)105 nsresult txMozillaTextOutput::createResultDocument(Document* aSourceDocument,
106                                                    bool aLoadedAsData) {
107   /*
108    * Create an XHTML document to hold the text.
109    *
110    * <html>
111    *   <head />
112    *   <body>
113    *     <pre id="transformiixResult"> * The text comes here * </pre>
114    *   <body>
115    * </html>
116    *
117    * Except if we are transforming into a non-displayed document we create
118    * the following DOM
119    *
120    * <transformiix:result> * The text comes here * </transformiix:result>
121    */
122 
123   // Create the document
124   nsresult rv = NS_NewXMLDocument(getter_AddRefs(mDocument), aLoadedAsData);
125   NS_ENSURE_SUCCESS(rv, rv);
126   mCreatedDocument = true;
127   // This should really be handled by Document::BeginLoad
128   MOZ_ASSERT(
129       mDocument->GetReadyStateEnum() == Document::READYSTATE_UNINITIALIZED,
130       "Bad readyState");
131   mDocument->SetReadyStateInternal(Document::READYSTATE_LOADING);
132   bool hasHadScriptObject = false;
133   nsIScriptGlobalObject* sgo =
134       aSourceDocument->GetScriptHandlingObject(hasHadScriptObject);
135   NS_ENSURE_STATE(sgo || !hasHadScriptObject);
136 
137   NS_ASSERTION(mDocument, "Need document");
138 
139   // Reset and set up document
140   URIUtils::ResetWithSource(mDocument, aSourceDocument);
141   // Only do this after resetting the document to ensure we have the
142   // correct principal.
143   mDocument->SetScriptHandlingObject(sgo);
144 
145   // Set the charset
146   if (!mOutputFormat.mEncoding.IsEmpty()) {
147     const Encoding* encoding = Encoding::ForLabel(mOutputFormat.mEncoding);
148     if (encoding) {
149       mDocument->SetDocumentCharacterSetSource(kCharsetFromOtherComponent);
150       mDocument->SetDocumentCharacterSet(WrapNotNull(encoding));
151     }
152   }
153 
154   // Notify the contentsink that the document is created
155   nsCOMPtr<nsITransformObserver> observer = do_QueryReferent(mObserver);
156   if (observer) {
157     rv = observer->OnDocumentCreated(mDocument);
158     NS_ENSURE_SUCCESS(rv, rv);
159   }
160 
161   // Create the content
162 
163   // When transforming into a non-displayed document (i.e. when there is no
164   // observer) we only create a transformiix:result root element.
165   if (!observer) {
166     int32_t namespaceID;
167     rv = nsNameSpaceManager::GetInstance()->RegisterNameSpace(
168         nsLiteralString(kTXNameSpaceURI), namespaceID);
169     NS_ENSURE_SUCCESS(rv, rv);
170 
171     mTextParent =
172         mDocument->CreateElem(nsDependentAtomString(nsGkAtoms::result),
173                               nsGkAtoms::transformiix, namespaceID);
174 
175     ErrorResult error;
176     mDocument->AppendChildTo(mTextParent, true, error);
177     if (error.Failed()) {
178       return error.StealNSResult();
179     }
180   } else {
181     RefPtr<Element> html, head, body;
182     rv = createXHTMLElement(nsGkAtoms::html, getter_AddRefs(html));
183     NS_ENSURE_SUCCESS(rv, rv);
184 
185     rv = createXHTMLElement(nsGkAtoms::head, getter_AddRefs(head));
186     NS_ENSURE_SUCCESS(rv, rv);
187 
188     ErrorResult error;
189     html->AppendChildTo(head, false, error);
190     if (error.Failed()) {
191       return error.StealNSResult();
192     }
193 
194     rv = createXHTMLElement(nsGkAtoms::body, getter_AddRefs(body));
195     NS_ENSURE_SUCCESS(rv, rv);
196 
197     html->AppendChildTo(body, false, error);
198     if (error.Failed()) {
199       return error.StealNSResult();
200     }
201 
202     {
203       RefPtr<Element> textParent;
204       rv = createXHTMLElement(nsGkAtoms::pre, getter_AddRefs(textParent));
205       NS_ENSURE_SUCCESS(rv, rv);
206       mTextParent = std::move(textParent);
207     }
208 
209     rv = mTextParent->AsElement()->SetAttr(kNameSpaceID_None, nsGkAtoms::id,
210                                            u"transformiixResult"_ns, false);
211     NS_ENSURE_SUCCESS(rv, rv);
212 
213     body->AppendChildTo(mTextParent, false, error);
214     if (error.Failed()) {
215       return error.StealNSResult();
216     }
217 
218     mDocument->AppendChildTo(html, true, error);
219     if (error.Failed()) {
220       return error.StealNSResult();
221     }
222   }
223 
224   return NS_OK;
225 }
226 
startElement(nsAtom * aPrefix,nsAtom * aLocalName,nsAtom * aLowercaseLocalName,int32_t aNsID)227 nsresult txMozillaTextOutput::startElement(nsAtom* aPrefix, nsAtom* aLocalName,
228                                            nsAtom* aLowercaseLocalName,
229                                            int32_t aNsID) {
230   return NS_OK;
231 }
232 
startElement(nsAtom * aPrefix,const nsAString & aName,const int32_t aNsID)233 nsresult txMozillaTextOutput::startElement(nsAtom* aPrefix,
234                                            const nsAString& aName,
235                                            const int32_t aNsID) {
236   return NS_OK;
237 }
238 
getOutputDocument(Document ** aDocument)239 void txMozillaTextOutput::getOutputDocument(Document** aDocument) {
240   NS_IF_ADDREF(*aDocument = mDocument);
241 }
242 
createXHTMLElement(nsAtom * aName,Element ** aResult)243 nsresult txMozillaTextOutput::createXHTMLElement(nsAtom* aName,
244                                                  Element** aResult) {
245   nsCOMPtr<Element> element = mDocument->CreateHTMLElement(aName);
246   element.forget(aResult);
247   return NS_OK;
248 }
249