Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 "txMozillaXMLOutput.h"
7 :
8 : #include "nsIDocument.h"
9 : #include "nsIDocShell.h"
10 : #include "nsIDOMDocument.h"
11 : #include "nsIDOMDocumentType.h"
12 : #include "nsIScriptElement.h"
13 : #include "nsCharsetSource.h"
14 : #include "nsIRefreshURI.h"
15 : #include "nsPIDOMWindow.h"
16 : #include "nsIContent.h"
17 : #include "nsContentCID.h"
18 : #include "nsUnicharUtils.h"
19 : #include "nsGkAtoms.h"
20 : #include "txLog.h"
21 : #include "nsIConsoleService.h"
22 : #include "nsIDOMDocumentFragment.h"
23 : #include "nsNameSpaceManager.h"
24 : #include "txStringUtils.h"
25 : #include "txURIUtils.h"
26 : #include "nsIHTMLDocument.h"
27 : #include "nsIStyleSheetLinkingElement.h"
28 : #include "nsIDocumentTransformer.h"
29 : #include "mozilla/StyleSheetInlines.h"
30 : #include "mozilla/css/Loader.h"
31 : #include "mozilla/dom/Element.h"
32 : #include "mozilla/dom/ScriptLoader.h"
33 : #include "mozilla/Encoding.h"
34 : #include "nsContentUtils.h"
35 : #include "txXMLUtils.h"
36 : #include "nsContentSink.h"
37 : #include "nsINode.h"
38 : #include "nsContentCreatorFunctions.h"
39 : #include "nsError.h"
40 : #include "nsIFrame.h"
41 : #include <algorithm>
42 : #include "nsTextNode.h"
43 : #include "mozilla/dom/Comment.h"
44 : #include "mozilla/dom/ProcessingInstruction.h"
45 :
46 : using namespace mozilla;
47 : using namespace mozilla::dom;
48 :
49 : #define TX_ENSURE_CURRENTNODE \
50 : NS_ASSERTION(mCurrentNode, "mCurrentNode is nullptr"); \
51 : if (!mCurrentNode) \
52 : return NS_ERROR_UNEXPECTED
53 :
54 0 : txMozillaXMLOutput::txMozillaXMLOutput(txOutputFormat* aFormat,
55 0 : nsITransformObserver* aObserver)
56 : : mTreeDepth(0),
57 : mBadChildLevel(0),
58 : mTableState(NORMAL),
59 : mCreatingNewDocument(true),
60 : mOpenedElementIsHTML(false),
61 : mRootContentCreated(false),
62 0 : mNoFixup(false)
63 : {
64 0 : MOZ_COUNT_CTOR(txMozillaXMLOutput);
65 0 : if (aObserver) {
66 0 : mNotifier = new txTransformNotifier();
67 0 : if (mNotifier) {
68 0 : mNotifier->Init(aObserver);
69 : }
70 : }
71 :
72 0 : mOutputFormat.merge(*aFormat);
73 0 : mOutputFormat.setFromDefaults();
74 0 : }
75 :
76 0 : txMozillaXMLOutput::txMozillaXMLOutput(txOutputFormat* aFormat,
77 : nsIDOMDocumentFragment* aFragment,
78 0 : bool aNoFixup)
79 : : mTreeDepth(0),
80 : mBadChildLevel(0),
81 : mTableState(NORMAL),
82 : mCreatingNewDocument(false),
83 : mOpenedElementIsHTML(false),
84 : mRootContentCreated(false),
85 0 : mNoFixup(aNoFixup)
86 : {
87 0 : MOZ_COUNT_CTOR(txMozillaXMLOutput);
88 0 : mOutputFormat.merge(*aFormat);
89 0 : mOutputFormat.setFromDefaults();
90 :
91 0 : mCurrentNode = do_QueryInterface(aFragment);
92 0 : mDocument = mCurrentNode->OwnerDoc();
93 0 : mNodeInfoManager = mDocument->NodeInfoManager();
94 0 : }
95 :
96 0 : txMozillaXMLOutput::~txMozillaXMLOutput()
97 : {
98 0 : MOZ_COUNT_DTOR(txMozillaXMLOutput);
99 0 : }
100 :
101 : nsresult
102 0 : txMozillaXMLOutput::attribute(nsIAtom* aPrefix,
103 : nsIAtom* aLocalName,
104 : nsIAtom* aLowercaseLocalName,
105 : const int32_t aNsID,
106 : const nsString& aValue)
107 : {
108 0 : nsCOMPtr<nsIAtom> owner;
109 0 : if (mOpenedElementIsHTML && aNsID == kNameSpaceID_None) {
110 0 : if (aLowercaseLocalName) {
111 0 : aLocalName = aLowercaseLocalName;
112 : }
113 : else {
114 0 : owner = TX_ToLowerCaseAtom(aLocalName);
115 0 : NS_ENSURE_TRUE(owner, NS_ERROR_OUT_OF_MEMORY);
116 :
117 0 : aLocalName = owner;
118 : }
119 : }
120 :
121 0 : return attributeInternal(aPrefix, aLocalName, aNsID, aValue);
122 : }
123 :
124 : nsresult
125 0 : txMozillaXMLOutput::attribute(nsIAtom* aPrefix,
126 : const nsAString& aLocalName,
127 : const int32_t aNsID,
128 : const nsString& aValue)
129 : {
130 0 : nsCOMPtr<nsIAtom> lname;
131 :
132 0 : if (mOpenedElementIsHTML && aNsID == kNameSpaceID_None) {
133 0 : nsAutoString lnameStr;
134 0 : nsContentUtils::ASCIIToLower(aLocalName, lnameStr);
135 0 : lname = NS_Atomize(lnameStr);
136 : }
137 : else {
138 0 : lname = NS_Atomize(aLocalName);
139 : }
140 :
141 0 : NS_ENSURE_TRUE(lname, NS_ERROR_OUT_OF_MEMORY);
142 :
143 : // Check that it's a valid name
144 0 : if (!nsContentUtils::IsValidNodeName(lname, aPrefix, aNsID)) {
145 : // Try without prefix
146 0 : aPrefix = nullptr;
147 0 : if (!nsContentUtils::IsValidNodeName(lname, aPrefix, aNsID)) {
148 : // Don't return error here since the callers don't deal
149 0 : return NS_OK;
150 : }
151 : }
152 :
153 0 : return attributeInternal(aPrefix, lname, aNsID, aValue);
154 : }
155 :
156 : nsresult
157 0 : txMozillaXMLOutput::attributeInternal(nsIAtom* aPrefix,
158 : nsIAtom* aLocalName,
159 : int32_t aNsID,
160 : const nsString& aValue)
161 : {
162 0 : if (!mOpenedElement) {
163 : // XXX Signal this? (can't add attributes after element closed)
164 0 : return NS_OK;
165 : }
166 :
167 0 : NS_ASSERTION(!mBadChildLevel, "mBadChildLevel set when element is opened");
168 :
169 0 : return mOpenedElement->SetAttr(aNsID, aLocalName, aPrefix, aValue,
170 0 : false);
171 : }
172 :
173 : nsresult
174 0 : txMozillaXMLOutput::characters(const nsAString& aData, bool aDOE)
175 : {
176 0 : nsresult rv = closePrevious(false);
177 0 : NS_ENSURE_SUCCESS(rv, rv);
178 :
179 0 : if (!mBadChildLevel) {
180 0 : mText.Append(aData);
181 : }
182 :
183 0 : return NS_OK;
184 : }
185 :
186 : nsresult
187 0 : txMozillaXMLOutput::comment(const nsString& aData)
188 : {
189 0 : nsresult rv = closePrevious(true);
190 0 : NS_ENSURE_SUCCESS(rv, rv);
191 :
192 0 : if (mBadChildLevel) {
193 0 : return NS_OK;
194 : }
195 :
196 0 : TX_ENSURE_CURRENTNODE;
197 :
198 0 : RefPtr<Comment> comment = new Comment(mNodeInfoManager);
199 :
200 0 : rv = comment->SetText(aData, false);
201 0 : NS_ENSURE_SUCCESS(rv, rv);
202 :
203 0 : return mCurrentNode->AppendChildTo(comment, true);
204 : }
205 :
206 : nsresult
207 0 : txMozillaXMLOutput::endDocument(nsresult aResult)
208 : {
209 0 : TX_ENSURE_CURRENTNODE;
210 :
211 0 : if (NS_FAILED(aResult)) {
212 0 : if (mNotifier) {
213 0 : mNotifier->OnTransformEnd(aResult);
214 : }
215 :
216 0 : return NS_OK;
217 : }
218 :
219 0 : nsresult rv = closePrevious(true);
220 0 : if (NS_FAILED(rv)) {
221 0 : if (mNotifier) {
222 0 : mNotifier->OnTransformEnd(rv);
223 : }
224 :
225 0 : return rv;
226 : }
227 :
228 0 : if (mCreatingNewDocument) {
229 : // This should really be handled by nsIDocument::EndLoad
230 0 : MOZ_ASSERT(mDocument->GetReadyStateEnum() ==
231 : nsIDocument::READYSTATE_LOADING, "Bad readyState");
232 0 : mDocument->SetReadyStateInternal(nsIDocument::READYSTATE_INTERACTIVE);
233 0 : ScriptLoader* loader = mDocument->ScriptLoader();
234 0 : if (loader) {
235 0 : loader->ParsingComplete(false);
236 : }
237 : }
238 :
239 0 : if (!mRefreshString.IsEmpty()) {
240 0 : nsPIDOMWindowOuter* win = mDocument->GetWindow();
241 0 : if (win) {
242 : nsCOMPtr<nsIRefreshURI> refURI =
243 0 : do_QueryInterface(win->GetDocShell());
244 0 : if (refURI) {
245 0 : refURI->SetupRefreshURIFromHeader(mDocument->GetDocBaseURI(),
246 0 : mDocument->NodePrincipal(),
247 0 : mRefreshString);
248 : }
249 : }
250 : }
251 :
252 0 : if (mNotifier) {
253 0 : mNotifier->OnTransformEnd();
254 : }
255 :
256 0 : return NS_OK;
257 : }
258 :
259 : nsresult
260 0 : txMozillaXMLOutput::endElement()
261 : {
262 0 : TX_ENSURE_CURRENTNODE;
263 :
264 0 : if (mBadChildLevel) {
265 0 : --mBadChildLevel;
266 0 : MOZ_LOG(txLog::xslt, LogLevel::Debug,
267 : ("endElement, mBadChildLevel = %d\n", mBadChildLevel));
268 0 : return NS_OK;
269 : }
270 :
271 0 : --mTreeDepth;
272 :
273 0 : nsresult rv = closePrevious(true);
274 0 : NS_ENSURE_SUCCESS(rv, rv);
275 :
276 0 : NS_ASSERTION(mCurrentNode->IsElement(), "borked mCurrentNode");
277 0 : NS_ENSURE_TRUE(mCurrentNode->IsElement(), NS_ERROR_UNEXPECTED);
278 :
279 0 : Element* element = mCurrentNode->AsElement();
280 :
281 : // Handle html-elements
282 0 : if (!mNoFixup) {
283 0 : if (element->IsHTMLElement()) {
284 0 : rv = endHTMLElement(element);
285 0 : NS_ENSURE_SUCCESS(rv, rv);
286 : }
287 :
288 : // Handle elements that are different when parser-created
289 0 : if (element->IsAnyOfHTMLElements(nsGkAtoms::title,
290 : nsGkAtoms::object,
291 : nsGkAtoms::applet,
292 : nsGkAtoms::select,
293 0 : nsGkAtoms::textarea) ||
294 0 : element->IsSVGElement(nsGkAtoms::title)) {
295 0 : element->DoneAddingChildren(true);
296 0 : } else if (element->IsSVGElement(nsGkAtoms::script) ||
297 0 : element->IsHTMLElement(nsGkAtoms::script)) {
298 0 : nsCOMPtr<nsIScriptElement> sele = do_QueryInterface(element);
299 0 : if (sele) {
300 0 : bool block = sele->AttemptToExecute();
301 : // If the act of insertion evaluated the script, we're fine.
302 : // Else, add this script element to the array of loading scripts.
303 0 : if (block) {
304 0 : rv = mNotifier->AddScriptElement(sele);
305 0 : NS_ENSURE_SUCCESS(rv, rv);
306 : }
307 : } else {
308 0 : MOZ_ASSERT(nsNameSpaceManager::GetInstance()->mSVGDisabled, "Script elements need to implement nsIScriptElement and SVG wasn't disabled.");
309 : }
310 0 : } else if (element->IsAnyOfHTMLElements(nsGkAtoms::input,
311 : nsGkAtoms::button,
312 : nsGkAtoms::menuitem,
313 : nsGkAtoms::audio,
314 : nsGkAtoms::video)) {
315 0 : element->DoneCreatingElement();
316 : }
317 : }
318 :
319 0 : if (mCreatingNewDocument) {
320 : // Handle all sorts of stylesheets
321 : nsCOMPtr<nsIStyleSheetLinkingElement> ssle =
322 0 : do_QueryInterface(mCurrentNode);
323 0 : if (ssle) {
324 0 : ssle->SetEnableUpdates(true);
325 : bool willNotify;
326 : bool isAlternate;
327 0 : nsresult rv = ssle->UpdateStyleSheet(mNotifier, &willNotify,
328 0 : &isAlternate);
329 0 : if (mNotifier && NS_SUCCEEDED(rv) && willNotify && !isAlternate) {
330 0 : mNotifier->AddPendingStylesheet();
331 : }
332 : }
333 : }
334 :
335 : // Add the element to the tree if it wasn't added before and take one step
336 : // up the tree
337 0 : uint32_t last = mCurrentNodeStack.Count() - 1;
338 0 : NS_ASSERTION(last != (uint32_t)-1, "empty stack");
339 :
340 0 : nsCOMPtr<nsINode> parent = mCurrentNodeStack.SafeObjectAt(last);
341 0 : mCurrentNodeStack.RemoveObjectAt(last);
342 :
343 0 : if (mCurrentNode == mNonAddedNode) {
344 0 : if (parent == mDocument) {
345 0 : NS_ASSERTION(!mRootContentCreated,
346 : "Parent to add to shouldn't be a document if we "
347 : "have a root content");
348 0 : mRootContentCreated = true;
349 : }
350 :
351 : // Check to make sure that script hasn't inserted the node somewhere
352 : // else in the tree
353 0 : if (!mCurrentNode->GetParentNode()) {
354 0 : parent->AppendChildTo(mNonAddedNode, true);
355 : }
356 0 : mNonAddedNode = nullptr;
357 : }
358 :
359 0 : mCurrentNode = parent;
360 :
361 0 : mTableState =
362 0 : static_cast<TableState>(NS_PTR_TO_INT32(mTableStateStack.pop()));
363 :
364 0 : return NS_OK;
365 : }
366 :
367 0 : void txMozillaXMLOutput::getOutputDocument(nsIDOMDocument** aDocument)
368 : {
369 0 : CallQueryInterface(mDocument, aDocument);
370 0 : }
371 :
372 : nsresult
373 0 : txMozillaXMLOutput::processingInstruction(const nsString& aTarget, const nsString& aData)
374 : {
375 0 : nsresult rv = closePrevious(true);
376 0 : NS_ENSURE_SUCCESS(rv, rv);
377 :
378 0 : if (mOutputFormat.mMethod == eHTMLOutput)
379 0 : return NS_OK;
380 :
381 0 : TX_ENSURE_CURRENTNODE;
382 :
383 0 : rv = nsContentUtils::CheckQName(aTarget, false);
384 0 : NS_ENSURE_SUCCESS(rv, rv);
385 :
386 : nsCOMPtr<nsIContent> pi =
387 0 : NS_NewXMLProcessingInstruction(mNodeInfoManager, aTarget, aData);
388 :
389 0 : nsCOMPtr<nsIStyleSheetLinkingElement> ssle;
390 0 : if (mCreatingNewDocument) {
391 0 : ssle = do_QueryInterface(pi);
392 0 : if (ssle) {
393 0 : ssle->InitStyleLinkElement(false);
394 0 : ssle->SetEnableUpdates(false);
395 : }
396 : }
397 :
398 0 : rv = mCurrentNode->AppendChildTo(pi, true);
399 0 : NS_ENSURE_SUCCESS(rv, rv);
400 :
401 0 : if (ssle) {
402 0 : ssle->SetEnableUpdates(true);
403 : bool willNotify;
404 : bool isAlternate;
405 0 : rv = ssle->UpdateStyleSheet(mNotifier, &willNotify, &isAlternate);
406 0 : if (mNotifier && NS_SUCCEEDED(rv) && willNotify && !isAlternate) {
407 0 : mNotifier->AddPendingStylesheet();
408 : }
409 : }
410 :
411 0 : return NS_OK;
412 : }
413 :
414 : nsresult
415 0 : txMozillaXMLOutput::startDocument()
416 : {
417 0 : if (mNotifier) {
418 0 : mNotifier->OnTransformStart();
419 : }
420 :
421 0 : if (mCreatingNewDocument) {
422 0 : ScriptLoader* loader = mDocument->ScriptLoader();
423 0 : if (loader) {
424 0 : loader->BeginDeferringScripts();
425 : }
426 : }
427 :
428 0 : return NS_OK;
429 : }
430 :
431 : nsresult
432 0 : txMozillaXMLOutput::startElement(nsIAtom* aPrefix, nsIAtom* aLocalName,
433 : nsIAtom* aLowercaseLocalName,
434 : const int32_t aNsID)
435 : {
436 0 : NS_PRECONDITION(aNsID != kNameSpaceID_None || !aPrefix,
437 : "Can't have prefix without namespace");
438 :
439 0 : if (mOutputFormat.mMethod == eHTMLOutput && aNsID == kNameSpaceID_None) {
440 0 : nsCOMPtr<nsIAtom> owner;
441 0 : if (!aLowercaseLocalName) {
442 0 : owner = TX_ToLowerCaseAtom(aLocalName);
443 0 : NS_ENSURE_TRUE(owner, NS_ERROR_OUT_OF_MEMORY);
444 :
445 0 : aLowercaseLocalName = owner;
446 : }
447 : return startElementInternal(nullptr,
448 : aLowercaseLocalName,
449 0 : kNameSpaceID_XHTML);
450 : }
451 :
452 0 : return startElementInternal(aPrefix, aLocalName, aNsID);
453 : }
454 :
455 : nsresult
456 0 : txMozillaXMLOutput::startElement(nsIAtom* aPrefix,
457 : const nsAString& aLocalName,
458 : const int32_t aNsID)
459 : {
460 0 : int32_t nsId = aNsID;
461 0 : nsCOMPtr<nsIAtom> lname;
462 :
463 0 : if (mOutputFormat.mMethod == eHTMLOutput && aNsID == kNameSpaceID_None) {
464 0 : nsId = kNameSpaceID_XHTML;
465 :
466 0 : nsAutoString lnameStr;
467 0 : nsContentUtils::ASCIIToLower(aLocalName, lnameStr);
468 0 : lname = NS_Atomize(lnameStr);
469 : }
470 : else {
471 0 : lname = NS_Atomize(aLocalName);
472 : }
473 :
474 : // No biggie if we lose the prefix due to OOM
475 0 : NS_ENSURE_TRUE(lname, NS_ERROR_OUT_OF_MEMORY);
476 :
477 : // Check that it's a valid name
478 0 : if (!nsContentUtils::IsValidNodeName(lname, aPrefix, nsId)) {
479 : // Try without prefix
480 0 : aPrefix = nullptr;
481 0 : if (!nsContentUtils::IsValidNodeName(lname, aPrefix, nsId)) {
482 0 : return NS_ERROR_XSLT_BAD_NODE_NAME;
483 : }
484 : }
485 :
486 0 : return startElementInternal(aPrefix, lname, nsId);
487 : }
488 :
489 : nsresult
490 0 : txMozillaXMLOutput::startElementInternal(nsIAtom* aPrefix,
491 : nsIAtom* aLocalName,
492 : int32_t aNsID)
493 : {
494 0 : TX_ENSURE_CURRENTNODE;
495 :
496 0 : if (mBadChildLevel) {
497 0 : ++mBadChildLevel;
498 0 : MOZ_LOG(txLog::xslt, LogLevel::Debug,
499 : ("startElement, mBadChildLevel = %d\n", mBadChildLevel));
500 0 : return NS_OK;
501 : }
502 :
503 0 : nsresult rv = closePrevious(true);
504 0 : NS_ENSURE_SUCCESS(rv, rv);
505 :
506 : // Push and init state
507 0 : if (mTreeDepth == MAX_REFLOW_DEPTH) {
508 : // eCloseElement couldn't add the parent so we fail as well or we've
509 : // reached the limit of the depth of the tree that we allow.
510 0 : ++mBadChildLevel;
511 0 : MOZ_LOG(txLog::xslt, LogLevel::Debug,
512 : ("startElement, mBadChildLevel = %d\n", mBadChildLevel));
513 0 : return NS_OK;
514 : }
515 :
516 0 : ++mTreeDepth;
517 :
518 0 : rv = mTableStateStack.push(NS_INT32_TO_PTR(mTableState));
519 0 : NS_ENSURE_SUCCESS(rv, rv);
520 :
521 0 : if (!mCurrentNodeStack.AppendObject(mCurrentNode)) {
522 0 : return NS_ERROR_OUT_OF_MEMORY;
523 : }
524 :
525 0 : mTableState = NORMAL;
526 0 : mOpenedElementIsHTML = false;
527 :
528 : // Create the element
529 : RefPtr<NodeInfo> ni =
530 0 : mNodeInfoManager->GetNodeInfo(aLocalName, aPrefix, aNsID,
531 0 : nsIDOMNode::ELEMENT_NODE);
532 :
533 0 : NS_NewElement(getter_AddRefs(mOpenedElement), ni.forget(),
534 0 : mCreatingNewDocument ?
535 0 : FROM_PARSER_XSLT : FROM_PARSER_FRAGMENT);
536 :
537 : // Set up the element and adjust state
538 0 : if (!mNoFixup) {
539 0 : if (aNsID == kNameSpaceID_XHTML) {
540 0 : mOpenedElementIsHTML = (mOutputFormat.mMethod == eHTMLOutput);
541 0 : rv = startHTMLElement(mOpenedElement, mOpenedElementIsHTML);
542 0 : NS_ENSURE_SUCCESS(rv, rv);
543 :
544 : }
545 : }
546 :
547 0 : if (mCreatingNewDocument) {
548 : // Handle all sorts of stylesheets
549 : nsCOMPtr<nsIStyleSheetLinkingElement> ssle =
550 0 : do_QueryInterface(mOpenedElement);
551 0 : if (ssle) {
552 0 : ssle->InitStyleLinkElement(false);
553 0 : ssle->SetEnableUpdates(false);
554 : }
555 : }
556 :
557 0 : return NS_OK;
558 : }
559 :
560 : nsresult
561 0 : txMozillaXMLOutput::closePrevious(bool aFlushText)
562 : {
563 0 : TX_ENSURE_CURRENTNODE;
564 :
565 : nsresult rv;
566 0 : if (mOpenedElement) {
567 0 : bool currentIsDoc = mCurrentNode == mDocument;
568 0 : if (currentIsDoc && mRootContentCreated) {
569 : // We already have a document element, but the XSLT spec allows this.
570 : // As a workaround, create a wrapper object and use that as the
571 : // document element.
572 :
573 0 : rv = createTxWrapper();
574 0 : NS_ENSURE_SUCCESS(rv, rv);
575 : }
576 :
577 0 : rv = mCurrentNode->AppendChildTo(mOpenedElement, true);
578 0 : NS_ENSURE_SUCCESS(rv, rv);
579 :
580 0 : if (currentIsDoc) {
581 0 : mRootContentCreated = true;
582 0 : nsContentSink::NotifyDocElementCreated(mDocument);
583 : }
584 :
585 0 : mCurrentNode = mOpenedElement;
586 0 : mOpenedElement = nullptr;
587 : }
588 0 : else if (aFlushText && !mText.IsEmpty()) {
589 : // Text can't appear in the root of a document
590 0 : if (mDocument == mCurrentNode) {
591 0 : if (XMLUtils::isWhitespace(mText)) {
592 0 : mText.Truncate();
593 :
594 0 : return NS_OK;
595 : }
596 :
597 0 : rv = createTxWrapper();
598 0 : NS_ENSURE_SUCCESS(rv, rv);
599 : }
600 0 : RefPtr<nsTextNode> text = new nsTextNode(mNodeInfoManager);
601 :
602 0 : rv = text->SetText(mText, false);
603 0 : NS_ENSURE_SUCCESS(rv, rv);
604 :
605 0 : rv = mCurrentNode->AppendChildTo(text, true);
606 0 : NS_ENSURE_SUCCESS(rv, rv);
607 :
608 0 : mText.Truncate();
609 : }
610 :
611 0 : return NS_OK;
612 : }
613 :
614 : nsresult
615 0 : txMozillaXMLOutput::createTxWrapper()
616 : {
617 0 : NS_ASSERTION(mDocument == mCurrentNode,
618 : "creating wrapper when document isn't parent");
619 :
620 : int32_t namespaceID;
621 : nsresult rv = nsContentUtils::NameSpaceManager()->
622 0 : RegisterNameSpace(NS_LITERAL_STRING(kTXNameSpaceURI), namespaceID);
623 0 : NS_ENSURE_SUCCESS(rv, rv);
624 :
625 : nsCOMPtr<Element> wrapper =
626 0 : mDocument->CreateElem(nsDependentAtomString(nsGkAtoms::result),
627 0 : nsGkAtoms::transformiix, namespaceID);
628 :
629 0 : uint32_t i, j, childCount = mDocument->GetChildCount();
630 : #ifdef DEBUG
631 : // Keep track of the location of the current documentElement, if there is
632 : // one, so we can verify later
633 0 : uint32_t rootLocation = 0;
634 : #endif
635 0 : for (i = 0, j = 0; i < childCount; ++i) {
636 0 : nsCOMPtr<nsIContent> childContent = mDocument->GetChildAt(j);
637 :
638 : #ifdef DEBUG
639 0 : if (childContent->IsElement()) {
640 0 : rootLocation = j;
641 : }
642 : #endif
643 :
644 0 : if (childContent->NodeInfo()->NameAtom() == nsGkAtoms::documentTypeNodeName) {
645 : #ifdef DEBUG
646 : // The new documentElement should go after the document type.
647 : // This is needed for cases when there is no existing
648 : // documentElement in the document.
649 0 : rootLocation = std::max(rootLocation, j + 1);
650 : #endif
651 0 : ++j;
652 : }
653 : else {
654 0 : mDocument->RemoveChildAt(j, true);
655 :
656 0 : rv = wrapper->AppendChildTo(childContent, true);
657 0 : NS_ENSURE_SUCCESS(rv, rv);
658 0 : break;
659 : }
660 : }
661 :
662 0 : if (!mCurrentNodeStack.AppendObject(wrapper)) {
663 0 : return NS_ERROR_OUT_OF_MEMORY;
664 : }
665 0 : mCurrentNode = wrapper;
666 0 : mRootContentCreated = true;
667 0 : NS_ASSERTION(rootLocation == mDocument->GetChildCount(),
668 : "Incorrect root location");
669 0 : return mDocument->AppendChildTo(wrapper, true);
670 : }
671 :
672 : nsresult
673 0 : txMozillaXMLOutput::startHTMLElement(nsIContent* aElement, bool aIsHTML)
674 : {
675 0 : nsresult rv = NS_OK;
676 :
677 0 : if ((!aElement->IsHTMLElement(nsGkAtoms::tr) || !aIsHTML) &&
678 0 : NS_PTR_TO_INT32(mTableStateStack.peek()) == ADDED_TBODY) {
679 0 : uint32_t last = mCurrentNodeStack.Count() - 1;
680 0 : NS_ASSERTION(last != (uint32_t)-1, "empty stack");
681 :
682 0 : mCurrentNode = mCurrentNodeStack.SafeObjectAt(last);
683 0 : mCurrentNodeStack.RemoveObjectAt(last);
684 0 : mTableStateStack.pop();
685 : }
686 :
687 0 : if (aElement->IsHTMLElement(nsGkAtoms::table) && aIsHTML) {
688 0 : mTableState = TABLE;
689 : }
690 0 : else if (aElement->IsHTMLElement(nsGkAtoms::tr) && aIsHTML &&
691 0 : NS_PTR_TO_INT32(mTableStateStack.peek()) == TABLE) {
692 0 : nsCOMPtr<nsIContent> tbody;
693 0 : rv = createHTMLElement(nsGkAtoms::tbody, getter_AddRefs(tbody));
694 0 : NS_ENSURE_SUCCESS(rv, rv);
695 :
696 0 : rv = mCurrentNode->AppendChildTo(tbody, true);
697 0 : NS_ENSURE_SUCCESS(rv, rv);
698 :
699 0 : rv = mTableStateStack.push(NS_INT32_TO_PTR(ADDED_TBODY));
700 0 : NS_ENSURE_SUCCESS(rv, rv);
701 :
702 0 : if (!mCurrentNodeStack.AppendObject(tbody)) {
703 0 : return NS_ERROR_OUT_OF_MEMORY;
704 : }
705 :
706 0 : mCurrentNode = tbody;
707 : }
708 0 : else if (aElement->IsHTMLElement(nsGkAtoms::head) &&
709 0 : mOutputFormat.mMethod == eHTMLOutput) {
710 : // Insert META tag, according to spec, 16.2, like
711 : // <META http-equiv="Content-Type" content="text/html; charset=EUC-JP">
712 0 : nsCOMPtr<nsIContent> meta;
713 0 : rv = createHTMLElement(nsGkAtoms::meta, getter_AddRefs(meta));
714 0 : NS_ENSURE_SUCCESS(rv, rv);
715 :
716 0 : rv = meta->SetAttr(kNameSpaceID_None, nsGkAtoms::httpEquiv,
717 0 : NS_LITERAL_STRING("Content-Type"), false);
718 0 : NS_ENSURE_SUCCESS(rv, rv);
719 :
720 0 : nsAutoString metacontent;
721 0 : metacontent.Append(mOutputFormat.mMediaType);
722 0 : metacontent.AppendLiteral("; charset=");
723 0 : metacontent.Append(mOutputFormat.mEncoding);
724 0 : rv = meta->SetAttr(kNameSpaceID_None, nsGkAtoms::content,
725 0 : metacontent, false);
726 0 : NS_ENSURE_SUCCESS(rv, rv);
727 :
728 : // No need to notify since aElement hasn't been inserted yet
729 0 : NS_ASSERTION(!aElement->IsInUncomposedDoc(), "should not be in doc");
730 0 : rv = aElement->AppendChildTo(meta, false);
731 0 : NS_ENSURE_SUCCESS(rv, rv);
732 : }
733 :
734 0 : return NS_OK;
735 : }
736 :
737 : nsresult
738 0 : txMozillaXMLOutput::endHTMLElement(nsIContent* aElement)
739 : {
740 0 : if (mTableState == ADDED_TBODY) {
741 0 : NS_ASSERTION(aElement->IsHTMLElement(nsGkAtoms::tbody),
742 : "Element flagged as added tbody isn't a tbody");
743 0 : uint32_t last = mCurrentNodeStack.Count() - 1;
744 0 : NS_ASSERTION(last != (uint32_t)-1, "empty stack");
745 :
746 0 : mCurrentNode = mCurrentNodeStack.SafeObjectAt(last);
747 0 : mCurrentNodeStack.RemoveObjectAt(last);
748 0 : mTableState = static_cast<TableState>
749 0 : (NS_PTR_TO_INT32(mTableStateStack.pop()));
750 :
751 0 : return NS_OK;
752 : }
753 0 : else if (mCreatingNewDocument && aElement->IsHTMLElement(nsGkAtoms::meta)) {
754 : // handle HTTP-EQUIV data
755 0 : nsAutoString httpEquiv;
756 0 : aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::httpEquiv, httpEquiv);
757 0 : if (!httpEquiv.IsEmpty()) {
758 0 : nsAutoString value;
759 0 : aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::content, value);
760 0 : if (!value.IsEmpty()) {
761 0 : nsContentUtils::ASCIIToLower(httpEquiv);
762 0 : nsCOMPtr<nsIAtom> header = NS_Atomize(httpEquiv);
763 0 : processHTTPEquiv(header, value);
764 : }
765 : }
766 : }
767 :
768 0 : return NS_OK;
769 : }
770 :
771 0 : void txMozillaXMLOutput::processHTTPEquiv(nsIAtom* aHeader, const nsString& aValue)
772 : {
773 : // For now we only handle "refresh". There's a longer list in
774 : // HTMLContentSink::ProcessHeaderData
775 0 : if (aHeader == nsGkAtoms::refresh)
776 0 : LossyCopyUTF16toASCII(aValue, mRefreshString);
777 0 : }
778 :
779 : nsresult
780 0 : txMozillaXMLOutput::createResultDocument(const nsAString& aName, int32_t aNsID,
781 : nsIDOMDocument* aSourceDocument,
782 : bool aLoadedAsData)
783 : {
784 : nsresult rv;
785 :
786 : // Create the document
787 0 : if (mOutputFormat.mMethod == eHTMLOutput) {
788 0 : rv = NS_NewHTMLDocument(getter_AddRefs(mDocument),
789 0 : aLoadedAsData);
790 0 : NS_ENSURE_SUCCESS(rv, rv);
791 : }
792 : else {
793 : // We should check the root name/namespace here and create the
794 : // appropriate document
795 0 : rv = NS_NewXMLDocument(getter_AddRefs(mDocument),
796 0 : aLoadedAsData);
797 0 : NS_ENSURE_SUCCESS(rv, rv);
798 : }
799 : // This should really be handled by nsIDocument::BeginLoad
800 0 : MOZ_ASSERT(mDocument->GetReadyStateEnum() ==
801 : nsIDocument::READYSTATE_UNINITIALIZED, "Bad readyState");
802 0 : mDocument->SetReadyStateInternal(nsIDocument::READYSTATE_LOADING);
803 0 : mDocument->SetMayStartLayout(false);
804 0 : nsCOMPtr<nsIDocument> source = do_QueryInterface(aSourceDocument);
805 0 : NS_ENSURE_STATE(source);
806 0 : bool hasHadScriptObject = false;
807 : nsIScriptGlobalObject* sgo =
808 0 : source->GetScriptHandlingObject(hasHadScriptObject);
809 0 : NS_ENSURE_STATE(sgo || !hasHadScriptObject);
810 :
811 0 : mCurrentNode = mDocument;
812 0 : mNodeInfoManager = mDocument->NodeInfoManager();
813 :
814 : // Reset and set up the document
815 0 : URIUtils::ResetWithSource(mDocument, aSourceDocument);
816 :
817 : // Make sure we set the script handling object after resetting with the
818 : // source, so that we have the right principal.
819 0 : mDocument->SetScriptHandlingObject(sgo);
820 :
821 : // Set the charset
822 0 : if (!mOutputFormat.mEncoding.IsEmpty()) {
823 0 : const Encoding* encoding = Encoding::ForLabel(mOutputFormat.mEncoding);
824 0 : if (encoding) {
825 0 : mDocument->SetDocumentCharacterSetSource(kCharsetFromOtherComponent);
826 0 : mDocument->SetDocumentCharacterSet(WrapNotNull(encoding));
827 : }
828 : }
829 :
830 : // Set the mime-type
831 0 : if (!mOutputFormat.mMediaType.IsEmpty()) {
832 0 : mDocument->SetContentType(mOutputFormat.mMediaType);
833 : }
834 0 : else if (mOutputFormat.mMethod == eHTMLOutput) {
835 0 : mDocument->SetContentType(NS_LITERAL_STRING("text/html"));
836 : }
837 : else {
838 0 : mDocument->SetContentType(NS_LITERAL_STRING("application/xml"));
839 : }
840 :
841 0 : if (mOutputFormat.mMethod == eXMLOutput &&
842 0 : mOutputFormat.mOmitXMLDeclaration != eTrue) {
843 : int32_t standalone;
844 0 : if (mOutputFormat.mStandalone == eNotSet) {
845 0 : standalone = -1;
846 : }
847 0 : else if (mOutputFormat.mStandalone == eFalse) {
848 0 : standalone = 0;
849 : }
850 : else {
851 0 : standalone = 1;
852 : }
853 :
854 : // Could use mOutputFormat.mVersion.get() when we support
855 : // versions > 1.0.
856 : static const char16_t kOneDotZero[] = { '1', '.', '0', '\0' };
857 0 : mDocument->SetXMLDeclaration(kOneDotZero, mOutputFormat.mEncoding.get(),
858 0 : standalone);
859 : }
860 :
861 : // Set up script loader of the result document.
862 0 : ScriptLoader *loader = mDocument->ScriptLoader();
863 0 : if (mNotifier) {
864 0 : loader->AddObserver(mNotifier);
865 : }
866 : else {
867 : // Don't load scripts, we can't notify the caller when they're loaded.
868 0 : loader->SetEnabled(false);
869 : }
870 :
871 0 : if (mNotifier) {
872 0 : rv = mNotifier->SetOutputDocument(mDocument);
873 0 : NS_ENSURE_SUCCESS(rv, rv);
874 : }
875 :
876 : // Do this after calling OnDocumentCreated to ensure that the
877 : // PresShell/PresContext has been hooked up and get notified.
878 0 : nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(mDocument);
879 0 : if (htmlDoc) {
880 0 : htmlDoc->SetCompatibilityMode(eCompatibility_FullStandards);
881 : }
882 :
883 : // Add a doc-type if requested
884 0 : if (!mOutputFormat.mSystemId.IsEmpty()) {
885 0 : nsAutoString qName;
886 0 : if (mOutputFormat.mMethod == eHTMLOutput) {
887 0 : qName.AssignLiteral("html");
888 : }
889 : else {
890 0 : qName.Assign(aName);
891 : }
892 :
893 0 : nsCOMPtr<nsIDOMDocumentType> documentType;
894 :
895 0 : nsresult rv = nsContentUtils::CheckQName(qName);
896 0 : if (NS_SUCCEEDED(rv)) {
897 0 : nsCOMPtr<nsIAtom> doctypeName = NS_Atomize(qName);
898 0 : if (!doctypeName) {
899 0 : return NS_ERROR_OUT_OF_MEMORY;
900 : }
901 :
902 : // Indicate that there is no internal subset (not just an empty one)
903 0 : rv = NS_NewDOMDocumentType(getter_AddRefs(documentType),
904 : mNodeInfoManager,
905 : doctypeName,
906 : mOutputFormat.mPublicId,
907 : mOutputFormat.mSystemId,
908 0 : NullString());
909 0 : NS_ENSURE_SUCCESS(rv, rv);
910 :
911 0 : nsCOMPtr<nsIContent> docType = do_QueryInterface(documentType);
912 0 : rv = mDocument->AppendChildTo(docType, true);
913 0 : NS_ENSURE_SUCCESS(rv, rv);
914 : }
915 : }
916 :
917 0 : return NS_OK;
918 : }
919 :
920 : nsresult
921 0 : txMozillaXMLOutput::createHTMLElement(nsIAtom* aName,
922 : nsIContent** aResult)
923 : {
924 0 : NS_ASSERTION(mOutputFormat.mMethod == eHTMLOutput,
925 : "need to adjust createHTMLElement");
926 :
927 0 : *aResult = nullptr;
928 :
929 0 : RefPtr<NodeInfo> ni;
930 0 : ni = mNodeInfoManager->GetNodeInfo(aName, nullptr,
931 : kNameSpaceID_XHTML,
932 0 : nsIDOMNode::ELEMENT_NODE);
933 :
934 0 : nsCOMPtr<Element> el;
935 : nsresult rv =
936 0 : NS_NewHTMLElement(getter_AddRefs(el), ni.forget(),
937 0 : mCreatingNewDocument ?
938 0 : FROM_PARSER_XSLT : FROM_PARSER_FRAGMENT);
939 0 : el.forget(aResult);
940 0 : return rv;
941 : }
942 :
943 0 : txTransformNotifier::txTransformNotifier()
944 : : mPendingStylesheetCount(0),
945 0 : mInTransform(false)
946 : {
947 0 : }
948 :
949 0 : txTransformNotifier::~txTransformNotifier()
950 : {
951 0 : }
952 :
953 0 : NS_IMPL_ISUPPORTS(txTransformNotifier,
954 : nsIScriptLoaderObserver,
955 : nsICSSLoaderObserver)
956 :
957 : NS_IMETHODIMP
958 0 : txTransformNotifier::ScriptAvailable(nsresult aResult,
959 : nsIScriptElement *aElement,
960 : bool aIsInline,
961 : nsIURI *aURI,
962 : int32_t aLineNo)
963 : {
964 0 : if (NS_FAILED(aResult) &&
965 0 : mScriptElements.RemoveObject(aElement)) {
966 0 : SignalTransformEnd();
967 : }
968 :
969 0 : return NS_OK;
970 : }
971 :
972 : NS_IMETHODIMP
973 0 : txTransformNotifier::ScriptEvaluated(nsresult aResult,
974 : nsIScriptElement *aElement,
975 : bool aIsInline)
976 : {
977 0 : if (mScriptElements.RemoveObject(aElement)) {
978 0 : SignalTransformEnd();
979 : }
980 :
981 0 : return NS_OK;
982 : }
983 :
984 : NS_IMETHODIMP
985 0 : txTransformNotifier::StyleSheetLoaded(StyleSheet* aSheet,
986 : bool aWasAlternate,
987 : nsresult aStatus)
988 : {
989 0 : if (mPendingStylesheetCount == 0) {
990 : // We weren't waiting on this stylesheet anyway. This can happen if
991 : // SignalTransformEnd got called with an error aResult. See
992 : // http://bugzilla.mozilla.org/show_bug.cgi?id=215465.
993 0 : return NS_OK;
994 : }
995 :
996 : // We're never waiting for alternate stylesheets
997 0 : if (!aWasAlternate) {
998 0 : --mPendingStylesheetCount;
999 0 : SignalTransformEnd();
1000 : }
1001 :
1002 0 : return NS_OK;
1003 : }
1004 :
1005 : void
1006 0 : txTransformNotifier::Init(nsITransformObserver* aObserver)
1007 : {
1008 0 : mObserver = aObserver;
1009 0 : }
1010 :
1011 : nsresult
1012 0 : txTransformNotifier::AddScriptElement(nsIScriptElement* aElement)
1013 : {
1014 0 : return mScriptElements.AppendObject(aElement) ? NS_OK :
1015 0 : NS_ERROR_OUT_OF_MEMORY;
1016 : }
1017 :
1018 : void
1019 0 : txTransformNotifier::AddPendingStylesheet()
1020 : {
1021 0 : ++mPendingStylesheetCount;
1022 0 : }
1023 :
1024 : void
1025 0 : txTransformNotifier::OnTransformEnd(nsresult aResult)
1026 : {
1027 0 : mInTransform = false;
1028 0 : SignalTransformEnd(aResult);
1029 0 : }
1030 :
1031 : void
1032 0 : txTransformNotifier::OnTransformStart()
1033 : {
1034 0 : mInTransform = true;
1035 0 : }
1036 :
1037 : nsresult
1038 0 : txTransformNotifier::SetOutputDocument(nsIDocument* aDocument)
1039 : {
1040 0 : mDocument = aDocument;
1041 :
1042 : // Notify the contentsink that the document is created
1043 0 : return mObserver->OnDocumentCreated(mDocument);
1044 : }
1045 :
1046 : void
1047 0 : txTransformNotifier::SignalTransformEnd(nsresult aResult)
1048 : {
1049 0 : if (mInTransform ||
1050 0 : (NS_SUCCEEDED(aResult) &&
1051 0 : (mScriptElements.Count() > 0 || mPendingStylesheetCount > 0))) {
1052 0 : return;
1053 : }
1054 :
1055 : // mPendingStylesheetCount is nonzero at this point only if aResult is an
1056 : // error. Set it to 0 so we won't reenter this code when we stop the
1057 : // CSSLoader.
1058 0 : mPendingStylesheetCount = 0;
1059 0 : mScriptElements.Clear();
1060 :
1061 : // Make sure that we don't get deleted while this function is executed and
1062 : // we remove ourselfs from the scriptloader
1063 0 : nsCOMPtr<nsIScriptLoaderObserver> kungFuDeathGrip(this);
1064 :
1065 0 : if (mDocument) {
1066 0 : mDocument->ScriptLoader()->RemoveObserver(this);
1067 : // XXX Maybe we want to cancel script loads if NS_FAILED(rv)?
1068 :
1069 0 : if (NS_FAILED(aResult)) {
1070 0 : mDocument->CSSLoader()->Stop();
1071 : }
1072 : }
1073 :
1074 0 : if (NS_SUCCEEDED(aResult)) {
1075 0 : mObserver->OnTransformDone(aResult, mDocument);
1076 : }
1077 : }
|