Back to index

lightning-sunbird  0.9+nobinonly
txMozillaXMLOutput.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is TransforMiiX XSLT processor code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 2001
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Peter Van der Beken <peterv@propagandism.org>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either the GNU General Public License Version 2 or later (the "GPL"), or
00027  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "txMozillaXMLOutput.h"
00040 
00041 #include "nsIDocument.h"
00042 #include "nsIDocShell.h"
00043 #include "nsIScriptLoader.h"
00044 #include "nsIDOMDocument.h"
00045 #include "nsIDOMComment.h"
00046 #include "nsIDOMDocumentType.h"
00047 #include "nsIDOMDOMImplementation.h"
00048 #include "nsIDOMNodeList.h"
00049 #include "nsIDOMProcessingInstruction.h"
00050 #include "nsIDOMText.h"
00051 #include "nsIDOMHTMLTableSectionElem.h"
00052 #include "nsIScriptElement.h"
00053 #include "nsIDOMNSDocument.h"
00054 #include "nsIParser.h"
00055 #include "nsIRefreshURI.h"
00056 #include "nsIScriptGlobalObject.h"
00057 #include "nsITextContent.h"
00058 #include "nsIXMLContent.h"
00059 #include "nsContentCID.h"
00060 #include "nsNetUtil.h"
00061 #include "nsUnicharUtils.h"
00062 #include "txAtoms.h"
00063 #include "TxLog.h"
00064 #include "nsIConsoleService.h"
00065 #include "nsIDOMDocumentFragment.h"
00066 #include "nsINameSpaceManager.h"
00067 #include "nsICSSStyleSheet.h"
00068 #include "txStringUtils.h"
00069 #include "txURIUtils.h"
00070 #include "nsIHTMLDocument.h"
00071 #include "nsIStyleSheetLinkingElement.h"
00072 #include "nsIDocumentTransformer.h"
00073 #include "nsICSSLoader.h"
00074 #include "nsICharsetAlias.h"
00075 #include "nsIHTMLContentSink.h"
00076 
00077 extern nsINameSpaceManager* gTxNameSpaceManager;
00078 
00079 static NS_DEFINE_CID(kXMLDocumentCID, NS_XMLDOCUMENT_CID);
00080 static NS_DEFINE_CID(kHTMLDocumentCID, NS_HTMLDOCUMENT_CID);
00081 
00082 #define kXHTMLNameSpaceURI "http://www.w3.org/1999/xhtml"
00083 
00084 #define TX_ENSURE_CURRENTNODE                           \
00085     NS_ASSERTION(mCurrentNode, "mCurrentNode is NULL"); \
00086     if (!mCurrentNode)                                  \
00087         return
00088 
00089 txMozillaXMLOutput::txMozillaXMLOutput(const nsAString& aRootName,
00090                                        PRInt32 aRootNsID,
00091                                        txOutputFormat* aFormat,
00092                                        nsIDOMDocument* aSourceDocument,
00093                                        nsIDOMDocument* aResultDocument,
00094                                        nsITransformObserver* aObserver)
00095     : mTreeDepth(0),
00096       mBadChildLevel(0),
00097       mTableState(NORMAL),
00098       mDontAddCurrent(PR_FALSE),
00099       mHaveTitleElement(PR_FALSE),
00100       mHaveBaseElement(PR_FALSE),
00101       mCreatingNewDocument(PR_TRUE)
00102 {
00103     if (aObserver) {
00104         mNotifier = new txTransformNotifier();
00105         if (mNotifier) {
00106             mNotifier->Init(aObserver);
00107         }
00108     }
00109 
00110     mOutputFormat.merge(*aFormat);
00111     mOutputFormat.setFromDefaults();
00112 
00113     createResultDocument(aRootName, aRootNsID, aSourceDocument, aResultDocument);
00114 }
00115 
00116 txMozillaXMLOutput::txMozillaXMLOutput(txOutputFormat* aFormat,
00117                                        nsIDOMDocumentFragment* aFragment)
00118     : mBadChildLevel(0),
00119       mTableState(NORMAL),
00120       mDontAddCurrent(PR_FALSE),
00121       mHaveTitleElement(PR_FALSE),
00122       mHaveBaseElement(PR_FALSE),
00123       mCreatingNewDocument(PR_FALSE)
00124 {
00125     mOutputFormat.merge(*aFormat);
00126     mOutputFormat.setFromDefaults();
00127 
00128     aFragment->GetOwnerDocument(getter_AddRefs(mDocument));
00129 
00130     nsCOMPtr<nsIDocument> doc = do_QueryInterface(mDocument);
00131     mDocumentIsHTML = doc && !doc->IsCaseSensitive();
00132 
00133     mCurrentNode = aFragment;
00134 }
00135 
00136 txMozillaXMLOutput::~txMozillaXMLOutput()
00137 {
00138 }
00139 
00140 void txMozillaXMLOutput::attribute(const nsAString& aName,
00141                                    const PRInt32 aNsID,
00142                                    const nsAString& aValue)
00143 {
00144     if (!mParentNode)
00145         // XXX Signal this? (can't add attributes after element closed)
00146         return;
00147 
00148     if (mBadChildLevel) {
00149         return;
00150     }
00151 
00152     nsCOMPtr<nsIDOMElement> element = do_QueryInterface(mCurrentNode);
00153     NS_ASSERTION(element, "No element to add the attribute to.");
00154     if (!element)
00155         // XXX Signal this? (no element to add attributes to)
00156         return;
00157 
00158     if ((mOutputFormat.mMethod == eHTMLOutput) && (aNsID == kNameSpaceID_None)) {
00159         // Outputting HTML as XHTML, lowercase attribute names
00160         nsAutoString lowerName;
00161         TX_ToLowerCase(aName, lowerName);
00162         element->SetAttributeNS(EmptyString(), lowerName,
00163                                 aValue);
00164     }
00165     else {
00166         nsAutoString nsURI;
00167         gTxNameSpaceManager->GetNameSpaceURI(aNsID, nsURI);
00168         element->SetAttributeNS(nsURI, aName, aValue);
00169     }
00170 }
00171 
00172 void txMozillaXMLOutput::characters(const nsAString& aData, PRBool aDOE)
00173 {
00174     closePrevious(eCloseElement);
00175 
00176     if (mBadChildLevel) {
00177         return;
00178     }
00179 
00180     mText.Append(aData);
00181 }
00182 
00183 void txMozillaXMLOutput::comment(const nsAString& aData)
00184 {
00185     closePrevious(eCloseElement | eFlushText);
00186 
00187     if (mBadChildLevel) {
00188         return;
00189     }
00190 
00191     TX_ENSURE_CURRENTNODE;
00192 
00193     nsCOMPtr<nsIDOMComment> comment;
00194     nsresult rv = mDocument->CreateComment(aData,
00195                                            getter_AddRefs(comment));
00196     NS_ASSERTION(NS_SUCCEEDED(rv), "Can't create comment");
00197     nsCOMPtr<nsIDOMNode> resultNode;
00198     rv = mCurrentNode->AppendChild(comment, getter_AddRefs(resultNode));
00199     NS_ASSERTION(NS_SUCCEEDED(rv), "Can't append comment");
00200 }
00201 
00202 void txMozillaXMLOutput::endDocument(nsresult aResult)
00203 {
00204     closePrevious(eCloseElement | eFlushText);
00205     // This should really be handled by nsIDocument::Reset
00206     if (mCreatingNewDocument && !mHaveTitleElement) {
00207         nsCOMPtr<nsIDOMNSDocument> domDoc = do_QueryInterface(mDocument);
00208         if (domDoc) {
00209             domDoc->SetTitle(EmptyString());
00210         }
00211     }
00212 
00213     if (!mRefreshString.IsEmpty()) {
00214         nsCOMPtr<nsIDocument> doc = do_QueryInterface(mDocument);
00215         nsIScriptGlobalObject *sgo = doc->GetScriptGlobalObject();
00216         if (sgo) {
00217             nsCOMPtr<nsIRefreshURI> refURI =
00218                 do_QueryInterface(sgo->GetDocShell());
00219             if (refURI) {
00220                 refURI->SetupRefreshURIFromHeader(doc->GetBaseURI(),
00221                                                   mRefreshString);
00222             }
00223         }
00224     }
00225 
00226     if (mNotifier) {
00227         mNotifier->OnTransformEnd(aResult);
00228     }
00229 }
00230 
00231 void txMozillaXMLOutput::endElement(const nsAString& aName, const PRInt32 aNsID)
00232 {
00233     TX_ENSURE_CURRENTNODE;
00234 
00235     if (mBadChildLevel) {
00236         --mBadChildLevel;
00237         PR_LOG(txLog::xslt, PR_LOG_DEBUG,
00238                ("endElement, mBadChildLevel = %d\n", mBadChildLevel));
00239         return;
00240     }
00241     
00242     --mTreeDepth;
00243 
00244 #ifdef DEBUG
00245     if (mTableState != ADDED_TBODY) {
00246         nsAutoString nodeName;
00247         mCurrentNode->GetNodeName(nodeName);
00248         NS_ASSERTION(nodeName.Equals(aName,
00249                                      nsCaseInsensitiveStringComparator()),
00250                      "Unbalanced startElement and endElement calls!");
00251     }
00252     else {
00253         nsCOMPtr<nsIDOMNode> parent;
00254         mCurrentNode->GetParentNode(getter_AddRefs(parent));
00255         nsAutoString nodeName;
00256         parent->GetNodeName(nodeName);
00257         NS_ASSERTION(nodeName.Equals(aName,
00258                                      nsCaseInsensitiveStringComparator()),
00259                      "Unbalanced startElement and endElement calls!");
00260     }
00261 #endif
00262 
00263     closePrevious(eCloseElement | eFlushText);
00264 
00265     // Handle html-elements
00266     if ((mOutputFormat.mMethod == eHTMLOutput && aNsID == kNameSpaceID_None) ||
00267         aNsID == kNameSpaceID_XHTML) {
00268         nsCOMPtr<nsIDOMElement> element = do_QueryInterface(mCurrentNode);
00269         NS_ASSERTION(element, "endElement'ing non-element");
00270         endHTMLElement(element);
00271     }
00272 
00273     // Handle svg script elements
00274     if (aNsID == kNameSpaceID_SVG && txHTMLAtoms::script->Equals(aName)) {
00275         // Add this script element to the array of loading script elements.
00276         nsCOMPtr<nsIScriptElement> scriptElement =
00277             do_QueryInterface(mCurrentNode);
00278         NS_ASSERTION(scriptElement, "Need script element");
00279         mNotifier->AddScriptElement(scriptElement);
00280     }
00281 
00282     if (mCreatingNewDocument) {
00283         // Handle all sorts of stylesheets
00284         nsCOMPtr<nsIStyleSheetLinkingElement> ssle =
00285             do_QueryInterface(mCurrentNode);
00286         if (ssle) {
00287             ssle->SetEnableUpdates(PR_TRUE);
00288             if (ssle->UpdateStyleSheet(nsnull, mNotifier) ==
00289                 NS_ERROR_HTMLPARSER_BLOCK) {
00290                 nsCOMPtr<nsIStyleSheet> stylesheet;
00291                 ssle->GetStyleSheet(*getter_AddRefs(stylesheet));
00292                 if (mNotifier) {
00293                     mNotifier->AddStyleSheet(stylesheet);
00294                 }
00295             }
00296         }
00297     }
00298 
00299     // Add the element to the tree if it wasn't added before and take one step
00300     // up the tree
00301     // we can't use GetParentNode to check if mCurrentNode is the
00302     // "non-added node" since that does strange things when we've called
00303     // BindToTree manually
00304     if (mCurrentNode == mNonAddedNode) {
00305         nsCOMPtr<nsIDocument> document = do_QueryInterface(mNonAddedParent);
00306         if (document && !mRootContent) {
00307             mRootContent = do_QueryInterface(mCurrentNode);
00308             // XXXbz what to do on failure here?
00309             document->SetRootContent(mRootContent);
00310         }
00311         else {
00312             nsCOMPtr<nsIDOMNode> resultNode;
00313             mNonAddedParent->AppendChild(mCurrentNode,
00314                                          getter_AddRefs(resultNode));
00315         }
00316         mCurrentNode = mNonAddedParent;
00317         mNonAddedParent = nsnull;
00318         mNonAddedNode = nsnull;
00319     }
00320     else {
00321         nsCOMPtr<nsIDOMNode> parent;
00322         mCurrentNode->GetParentNode(getter_AddRefs(parent));
00323         mCurrentNode = parent;
00324     }
00325 
00326     mTableState =
00327         NS_STATIC_CAST(TableState, NS_PTR_TO_INT32(mTableStateStack.pop()));
00328 }
00329 
00330 void txMozillaXMLOutput::getOutputDocument(nsIDOMDocument** aDocument)
00331 {
00332     *aDocument = mDocument;
00333     NS_IF_ADDREF(*aDocument);
00334 }
00335 
00336 void txMozillaXMLOutput::processingInstruction(const nsAString& aTarget, const nsAString& aData)
00337 {
00338     if (mOutputFormat.mMethod == eHTMLOutput)
00339         return;
00340 
00341     closePrevious(eCloseElement | eFlushText);
00342 
00343     TX_ENSURE_CURRENTNODE;
00344 
00345     nsCOMPtr<nsIDOMProcessingInstruction> pi;
00346     nsresult rv = mDocument->CreateProcessingInstruction(aTarget, aData,
00347                                                          getter_AddRefs(pi));
00348     NS_ASSERTION(NS_SUCCEEDED(rv), "Can't create processing instruction");
00349     if (NS_FAILED(rv))
00350         return;
00351 
00352     nsCOMPtr<nsIStyleSheetLinkingElement> ssle;
00353     if (mCreatingNewDocument) {
00354         ssle = do_QueryInterface(pi);
00355         if (ssle) {
00356             ssle->InitStyleLinkElement(nsnull, PR_FALSE);
00357             ssle->SetEnableUpdates(PR_FALSE);
00358         }
00359     }
00360 
00361     nsCOMPtr<nsIDOMNode> resultNode;
00362     rv = mCurrentNode->AppendChild(pi, getter_AddRefs(resultNode));
00363     NS_ASSERTION(NS_SUCCEEDED(rv), "Can't append processing instruction");
00364     if (NS_FAILED(rv))
00365         return;
00366 
00367     if (ssle) {
00368         ssle->SetEnableUpdates(PR_TRUE);
00369         rv = ssle->UpdateStyleSheet(nsnull, mNotifier);
00370         if (rv == NS_ERROR_HTMLPARSER_BLOCK) {
00371             nsCOMPtr<nsIStyleSheet> stylesheet;
00372             ssle->GetStyleSheet(*getter_AddRefs(stylesheet));
00373             if (mNotifier) {
00374                 mNotifier->AddStyleSheet(stylesheet);
00375             }
00376         }
00377     }
00378 }
00379 
00380 void txMozillaXMLOutput::startDocument()
00381 {
00382     if (mNotifier) {
00383         mNotifier->OnTransformStart();
00384     }
00385 }
00386 
00387 void txMozillaXMLOutput::startElement(const nsAString& aName,
00388                                       const PRInt32 aNsID)
00389 {
00390     TX_ENSURE_CURRENTNODE;
00391 
00392     if (mBadChildLevel) {
00393         ++mBadChildLevel;
00394         PR_LOG(txLog::xslt, PR_LOG_DEBUG,
00395                ("startElement, mBadChildLevel = %d\n", mBadChildLevel));
00396         return;
00397     }
00398 
00399     closePrevious(eCloseElement | eFlushText);
00400 
00401     if (mBadChildLevel || mTreeDepth == MAX_REFLOW_DEPTH) {
00402         // eCloseElement couldn't add the parent so we fail as well or we've
00403         // reached the limit of the depth of the tree that we allow.
00404         ++mBadChildLevel;
00405         PR_LOG(txLog::xslt, PR_LOG_DEBUG,
00406                ("startElement, mBadChildLevel = %d\n", mBadChildLevel));
00407         return;
00408     }
00409 
00410     ++mTreeDepth;
00411 
00412     nsresult rv = mTableStateStack.push(NS_INT32_TO_PTR(mTableState));
00413     if (NS_FAILED(rv)) {
00414         return;
00415     }
00416     mTableState = NORMAL;
00417 
00418     nsCOMPtr<nsIDOMElement> element;
00419     mDontAddCurrent = PR_FALSE;
00420 
00421     if ((mOutputFormat.mMethod == eHTMLOutput) && (aNsID == kNameSpaceID_None)) {
00422         if (mDocumentIsHTML) {
00423             rv = mDocument->CreateElement(aName,
00424                                           getter_AddRefs(element));
00425         }
00426         else {
00427             nsAutoString lcname;
00428             ToLowerCase(aName, lcname);
00429             rv = mDocument->CreateElementNS(NS_LITERAL_STRING(kXHTMLNameSpaceURI),
00430                                             lcname,
00431                                             getter_AddRefs(element));
00432         }
00433         if (NS_FAILED(rv)) {
00434             return;
00435         }
00436 
00437         startHTMLElement(element, PR_FALSE);
00438     }
00439     else {
00440         nsAutoString nsURI;
00441         gTxNameSpaceManager->GetNameSpaceURI(aNsID, nsURI);
00442         rv = mDocument->CreateElementNS(nsURI, aName,
00443                                         getter_AddRefs(element));
00444         NS_ASSERTION(NS_SUCCEEDED(rv), "Can't create element");
00445         if (NS_FAILED(rv)) {
00446             return;
00447         }
00448 
00449         if (aNsID == kNameSpaceID_XHTML) {
00450             startHTMLElement(element, PR_TRUE);
00451         } else if (aNsID == kNameSpaceID_SVG &&
00452                    txHTMLAtoms::script->Equals(aName)) {
00453             mDontAddCurrent = PR_TRUE;
00454         }
00455     }
00456 
00457     if (mCreatingNewDocument) {
00458         // Handle all sorts of stylesheets
00459         nsCOMPtr<nsIStyleSheetLinkingElement> ssle =
00460             do_QueryInterface(element);
00461         if (ssle) {
00462             ssle->InitStyleLinkElement(nsnull, PR_FALSE);
00463             ssle->SetEnableUpdates(PR_FALSE);
00464         }
00465     }
00466     mParentNode = mCurrentNode;
00467     mCurrentNode = do_QueryInterface(element);
00468 }
00469 
00470 void txMozillaXMLOutput::closePrevious(PRInt8 aAction)
00471 {
00472     TX_ENSURE_CURRENTNODE;
00473 
00474     nsresult rv;
00475     if ((aAction & eCloseElement) && mParentNode) {
00476         nsCOMPtr<nsIDocument> document = do_QueryInterface(mParentNode);
00477         nsCOMPtr<nsIDOMElement> currentElement = do_QueryInterface(mCurrentNode);
00478 
00479         if (document && currentElement && mRootContent) {
00480             // We already have a document element, but the XSLT spec allows this.
00481             // As a workaround, create a wrapper object and use that as the
00482             // document element.
00483             nsCOMPtr<nsIDOMElement> wrapper;
00484 
00485             rv = mDocument->CreateElementNS(NS_LITERAL_STRING(kTXNameSpaceURI),
00486                                             NS_LITERAL_STRING(kTXWrapper),
00487                                             getter_AddRefs(wrapper));
00488             NS_ASSERTION(NS_SUCCEEDED(rv), "Can't create wrapper element");
00489 
00490             nsCOMPtr<nsIDOMNode> child, resultNode;
00491             PRUint32 i, childCount = document->GetChildCount();
00492             for (i = 0; i < childCount; ++i) {
00493                 nsCOMPtr<nsIContent> childContent = document->GetChildAt(0);
00494                 if (childContent == mRootContent) {
00495                     document->SetRootContent(nsnull);
00496                 }
00497                 child = do_QueryInterface(childContent);
00498                 wrapper->AppendChild(child, getter_AddRefs(resultNode));
00499             }
00500 
00501             mParentNode = wrapper;
00502             mRootContent = do_QueryInterface(wrapper);
00503             // XXXbz what to do on failure here?
00504             document->SetRootContent(mRootContent);
00505         }
00506 
00507         if (mDontAddCurrent && !mNonAddedParent) {
00508             mNonAddedParent = mParentNode;
00509             mNonAddedNode = mCurrentNode;
00510         }
00511         else {
00512             if (document && currentElement && !mRootContent) {
00513                 mRootContent = do_QueryInterface(mCurrentNode);
00514                 // XXXbz what to do on failure here?
00515                 document->SetRootContent(mRootContent);
00516             }
00517             else {
00518                 nsCOMPtr<nsIDOMNode> resultNode;
00519 
00520                 rv = mParentNode->AppendChild(mCurrentNode, getter_AddRefs(resultNode));
00521                 if (NS_FAILED(rv)) {
00522                     mBadChildLevel = 1;
00523                     mCurrentNode = mParentNode;
00524                     PR_LOG(txLog::xslt, PR_LOG_DEBUG,
00525                            ("closePrevious, mBadChildLevel = %d\n",
00526                             mBadChildLevel));
00527                     // warning to the console
00528                     nsCOMPtr<nsIConsoleService> consoleSvc = 
00529                         do_GetService("@mozilla.org/consoleservice;1", &rv);
00530                     if (consoleSvc) {
00531                         consoleSvc->LogStringMessage(
00532                             NS_LITERAL_STRING("failed to create XSLT content").get());
00533                     }
00534                 }
00535             }
00536         }
00537         mParentNode = nsnull;
00538     }
00539     else if ((aAction & eFlushText) && !mText.IsEmpty()) {
00540         nsCOMPtr<nsIDOMText> text;
00541         rv = mDocument->CreateTextNode(mText, getter_AddRefs(text));
00542         NS_ASSERTION(NS_SUCCEEDED(rv), "Can't create text node");
00543 
00544         nsCOMPtr<nsIDOMNode> resultNode;
00545         rv = mCurrentNode->AppendChild(text, getter_AddRefs(resultNode));
00546         NS_ASSERTION(NS_SUCCEEDED(rv), "Can't append text node");
00547 
00548         mText.Truncate();
00549     }
00550 }
00551 
00552 void txMozillaXMLOutput::startHTMLElement(nsIDOMElement* aElement, PRBool aXHTML)
00553 {
00554     nsresult rv = NS_OK;
00555     nsCOMPtr<nsIContent> content = do_QueryInterface(aElement);
00556     nsIAtom *atom = content->Tag();
00557 
00558     mDontAddCurrent = (atom == txHTMLAtoms::script);
00559 
00560     if ((atom != txHTMLAtoms::tr || aXHTML) &&
00561         NS_PTR_TO_INT32(mTableStateStack.peek()) == ADDED_TBODY) {
00562         nsCOMPtr<nsIDOMNode> parent;
00563         mCurrentNode->GetParentNode(getter_AddRefs(parent));
00564         mCurrentNode.swap(parent);
00565         mTableStateStack.pop();
00566     }
00567 
00568     if (atom == txHTMLAtoms::table && !aXHTML) {
00569         mTableState = TABLE;
00570     }
00571     else if (atom == txHTMLAtoms::tr && !aXHTML &&
00572              NS_PTR_TO_INT32(mTableStateStack.peek()) == TABLE) {
00573         nsCOMPtr<nsIDOMElement> elem;
00574         rv = createHTMLElement(NS_LITERAL_STRING("tbody"),
00575                                getter_AddRefs(elem));
00576         if (NS_FAILED(rv)) {
00577             return;
00578         }
00579         nsCOMPtr<nsIDOMNode> dummy;
00580         rv = mCurrentNode->AppendChild(elem, getter_AddRefs(dummy));
00581         if (NS_FAILED(rv)) {
00582             return;
00583         }
00584         rv = mTableStateStack.push(NS_INT32_TO_PTR(ADDED_TBODY));
00585         if (NS_FAILED(rv)) {
00586             return;
00587         }
00588         mCurrentNode = elem;
00589     }
00590     else if (atom == txHTMLAtoms::head &&
00591              mOutputFormat.mMethod == eHTMLOutput) {
00592         // Insert META tag, according to spec, 16.2, like
00593         // <META http-equiv="Content-Type" content="text/html; charset=EUC-JP">
00594         nsCOMPtr<nsIDOMElement> meta;
00595         rv = createHTMLElement(NS_LITERAL_STRING("meta"),
00596                                getter_AddRefs(meta));
00597         if (NS_FAILED(rv)) {
00598             return;
00599         }
00600         rv = meta->SetAttribute(NS_LITERAL_STRING("http-equiv"),
00601                                 NS_LITERAL_STRING("Content-Type"));
00602         NS_ASSERTION(NS_SUCCEEDED(rv), "Can't set http-equiv on meta");
00603         nsAutoString metacontent;
00604         metacontent.Append(mOutputFormat.mMediaType);
00605         metacontent.AppendLiteral("; charset=");
00606         metacontent.Append(mOutputFormat.mEncoding);
00607         rv = meta->SetAttribute(NS_LITERAL_STRING("content"),
00608                                 metacontent);
00609         NS_ASSERTION(NS_SUCCEEDED(rv), "Can't set content on meta");
00610         nsCOMPtr<nsIDOMNode> dummy;
00611         rv = aElement->AppendChild(meta, getter_AddRefs(dummy));
00612         NS_ASSERTION(NS_SUCCEEDED(rv), "Can't append meta");
00613     }
00614 }
00615 
00616 void txMozillaXMLOutput::endHTMLElement(nsIDOMElement* aElement)
00617 {
00618     nsresult rv;
00619     nsCOMPtr<nsIContent> content = do_QueryInterface(aElement);
00620     NS_ASSERTION(content, "Can't QI to nsIContent");
00621 
00622     nsIAtom *atom = content->Tag();
00623 
00624     if (mTableState == ADDED_TBODY) {
00625         NS_ASSERTION(atom == txHTMLAtoms::tbody,
00626                      "Element flagged as added tbody isn't a tbody");
00627         nsCOMPtr<nsIDOMNode> parent;
00628         mCurrentNode->GetParentNode(getter_AddRefs(parent));
00629         mCurrentNode = parent;
00630         mTableState = NS_STATIC_CAST(TableState,
00631                                      NS_PTR_TO_INT32(mTableStateStack.pop()));
00632 
00633         return;
00634     }
00635 
00636     // Load scripts
00637     if (mNotifier && atom == txHTMLAtoms::script) {
00638         // Add this script element to the array of loading script elements.
00639         nsCOMPtr<nsIScriptElement> scriptElement =
00640             do_QueryInterface(mCurrentNode);
00641         NS_ASSERTION(scriptElement, "Need script element");
00642         mNotifier->AddScriptElement(scriptElement);
00643     }
00644     // Set document title
00645     else if (mCreatingNewDocument &&
00646              atom == txHTMLAtoms::title && !mHaveTitleElement) {
00647         // The first title wins
00648         mHaveTitleElement = PR_TRUE;
00649         nsCOMPtr<nsIDOMNSDocument> domDoc = do_QueryInterface(mDocument);
00650         nsCOMPtr<nsIDOMNode> textNode;
00651         aElement->GetFirstChild(getter_AddRefs(textNode));
00652         if (domDoc && textNode) {
00653             nsAutoString text;
00654             textNode->GetNodeValue(text);
00655             text.CompressWhitespace();
00656             domDoc->SetTitle(text);
00657         }
00658     }
00659     else if (mCreatingNewDocument && atom == txHTMLAtoms::base &&
00660              !mHaveBaseElement) {
00661         // The first base wins
00662         mHaveBaseElement = PR_TRUE;
00663 
00664         nsCOMPtr<nsIDocument> doc = do_QueryInterface(mDocument);
00665         NS_ASSERTION(doc, "document doesn't implement nsIDocument");
00666         nsAutoString value;
00667         content->GetAttr(kNameSpaceID_None, txHTMLAtoms::target, value);
00668         doc->SetBaseTarget(value);
00669 
00670         content->GetAttr(kNameSpaceID_None, txHTMLAtoms::href, value);
00671         nsCOMPtr<nsIURI> baseURI;
00672         rv = NS_NewURI(getter_AddRefs(baseURI), value, nsnull);
00673         if (NS_FAILED(rv))
00674             return;
00675         doc->SetBaseURI(baseURI); // The document checks if it is legal to set this base
00676     }
00677     else if (mCreatingNewDocument && atom == txHTMLAtoms::meta) {
00678         // handle HTTP-EQUIV data
00679         nsAutoString httpEquiv;
00680         content->GetAttr(kNameSpaceID_None, txHTMLAtoms::httpEquiv, httpEquiv);
00681         if (httpEquiv.IsEmpty())
00682             return;
00683 
00684         nsAutoString value;
00685         content->GetAttr(kNameSpaceID_None, txHTMLAtoms::content, value);
00686         if (value.IsEmpty())
00687             return;
00688         
00689         TX_ToLowerCase(httpEquiv);
00690         nsCOMPtr<nsIAtom> header = do_GetAtom(httpEquiv);
00691         processHTTPEquiv(header, value);
00692     }
00693 }
00694 
00695 void txMozillaXMLOutput::processHTTPEquiv(nsIAtom* aHeader, const nsAString& aValue)
00696 {
00697     // For now we only handle "refresh". There's a longer list in
00698     // HTMLContentSink::ProcessHeaderData
00699     if (aHeader == txHTMLAtoms::refresh)
00700         CopyUCS2toASCII(aValue, mRefreshString);
00701 }
00702 
00703 nsresult
00704 txMozillaXMLOutput::createResultDocument(const nsAString& aName, PRInt32 aNsID,
00705                                          nsIDOMDocument* aSourceDocument,
00706                                          nsIDOMDocument* aResultDocument)
00707 {
00708     nsresult rv;
00709 
00710     nsCOMPtr<nsIDocument> doc;
00711     if (!aResultDocument) {
00712         // Create the document
00713         if (mOutputFormat.mMethod == eHTMLOutput) {
00714             doc = do_CreateInstance(kHTMLDocumentCID, &rv);
00715             NS_ENSURE_SUCCESS(rv, rv);
00716 
00717             mDocumentIsHTML = PR_TRUE;
00718         }
00719         else {
00720             // We should check the root name/namespace here and create the
00721             // appropriate document
00722             doc = do_CreateInstance(kXMLDocumentCID, &rv);
00723             NS_ENSURE_SUCCESS(rv, rv);
00724 
00725             mDocumentIsHTML = PR_FALSE;
00726         }
00727         nsCOMPtr<nsIDocument_MOZILLA_1_8_BRANCH3> source =
00728           do_QueryInterface(aSourceDocument);
00729         NS_ENSURE_STATE(source);
00730         PRBool hasHadScriptObject = PR_FALSE;
00731         nsIScriptGlobalObject* sgo =
00732           source->GetScriptHandlingObject(hasHadScriptObject);
00733         NS_ENSURE_STATE(sgo || !hasHadScriptObject);
00734         nsCOMPtr<nsIDocument_MOZILLA_1_8_BRANCH3> doc18 =
00735           do_QueryInterface(doc);
00736         NS_ENSURE_STATE(doc18);
00737         doc18->SetScriptHandlingObject(sgo);
00738         mDocument = do_QueryInterface(doc);
00739     }
00740     else {
00741         mDocument = aResultDocument;
00742         doc = do_QueryInterface(aResultDocument);
00743         
00744         nsCOMPtr<nsIDocument> doc = do_QueryInterface(aResultDocument);
00745         mDocumentIsHTML = doc && !doc->IsCaseSensitive();
00746     }
00747 
00748     mCurrentNode = mDocument;
00749 
00750     // Reset and set up the document
00751     URIUtils::ResetWithSource(doc, aSourceDocument);
00752 
00753     // Set the charset
00754     if (!mOutputFormat.mEncoding.IsEmpty()) {
00755         NS_LossyConvertUTF16toASCII charset(mOutputFormat.mEncoding);
00756         nsCAutoString canonicalCharset;
00757         nsCOMPtr<nsICharsetAlias> calias =
00758             do_GetService("@mozilla.org/intl/charsetalias;1");
00759 
00760         if (calias &&
00761             NS_SUCCEEDED(calias->GetPreferred(charset, canonicalCharset))) {
00762             doc->SetDocumentCharacterSet(canonicalCharset);
00763             doc->SetDocumentCharacterSetSource(kCharsetFromOtherComponent);
00764         }
00765     }
00766 
00767     // Set the mime-type
00768     if (!mOutputFormat.mMediaType.IsEmpty()) {
00769         doc->SetContentType(mOutputFormat.mMediaType);
00770     }
00771     else if (mOutputFormat.mMethod == eHTMLOutput) {
00772         doc->SetContentType(NS_LITERAL_STRING("text/html"));
00773     }
00774     else {
00775         doc->SetContentType(NS_LITERAL_STRING("application/xml"));
00776     }
00777 
00778     // Set up script loader of the result document.
00779     nsIScriptLoader *loader = doc->GetScriptLoader();
00780     if (loader) {
00781         if (mNotifier) {
00782             loader->AddObserver(mNotifier);
00783         }
00784         else {
00785             // Don't load scripts, we can't notify the caller when they're loaded.
00786             loader->SetEnabled(PR_FALSE);
00787         }
00788     }
00789 
00790     if (mNotifier) {
00791         mNotifier->SetOutputDocument(mDocument);
00792     }
00793 
00794     // Do this after calling OnDocumentCreated to ensure that the
00795     // PresShell/PresContext has been hooked up and get notified.
00796     nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(doc);
00797     if (htmlDoc) {
00798         htmlDoc->SetCompatibilityMode(eCompatibility_FullStandards);
00799     }
00800 
00801     // Add a doc-type if requested
00802     if (!mOutputFormat.mSystemId.IsEmpty()) {
00803         nsCOMPtr<nsIDOMDOMImplementation> implementation;
00804         rv = aSourceDocument->GetImplementation(getter_AddRefs(implementation));
00805         NS_ENSURE_SUCCESS(rv, rv);
00806         nsAutoString qName;
00807         if (mOutputFormat.mMethod == eHTMLOutput) {
00808             qName.AssignLiteral("html");
00809         }
00810         else {
00811             qName.Assign(aName);
00812         }
00813         nsCOMPtr<nsIDOMDocumentType> documentType;
00814         rv = implementation->CreateDocumentType(qName,
00815                                                 mOutputFormat.mPublicId,
00816                                                 mOutputFormat.mSystemId,
00817                                                 getter_AddRefs(documentType));
00818         NS_ASSERTION(NS_SUCCEEDED(rv), "Can't create doctype");
00819         nsCOMPtr<nsIDOMNode> tmp;
00820         mDocument->AppendChild(documentType, getter_AddRefs(tmp));
00821     }
00822 
00823     return NS_OK;
00824 }
00825 
00826 nsresult
00827 txMozillaXMLOutput::createHTMLElement(const nsAString& aName,
00828                                       nsIDOMElement** aResult)
00829 {
00830     if (mDocumentIsHTML) {
00831         return mDocument->CreateElement(aName, aResult);
00832     }
00833 
00834     return mDocument->CreateElementNS(NS_LITERAL_STRING(kXHTMLNameSpaceURI),
00835                                       aName, aResult);
00836 }
00837 
00838 txTransformNotifier::txTransformNotifier()
00839     : mInTransform(PR_FALSE)
00840       
00841 {
00842 }
00843 
00844 txTransformNotifier::~txTransformNotifier()
00845 {
00846 }
00847 
00848 NS_IMPL_ISUPPORTS2(txTransformNotifier,
00849                    nsIScriptLoaderObserver,
00850                    nsICSSLoaderObserver)
00851 
00852 NS_IMETHODIMP
00853 txTransformNotifier::ScriptAvailable(nsresult aResult, 
00854                                      nsIScriptElement *aElement, 
00855                                      PRBool aIsInline,
00856                                      PRBool aWasPending,
00857                                      nsIURI *aURI, 
00858                                      PRInt32 aLineNo,
00859                                      const nsAString& aScript)
00860 {
00861     if (NS_FAILED(aResult) &&
00862         mScriptElements.RemoveObject(aElement)) {
00863         SignalTransformEnd();
00864     }
00865 
00866     return NS_OK;
00867 }
00868 
00869 NS_IMETHODIMP 
00870 txTransformNotifier::ScriptEvaluated(nsresult aResult, 
00871                                      nsIScriptElement *aElement,
00872                                      PRBool aIsInline,
00873                                      PRBool aWasPending)
00874 {
00875     if (mScriptElements.RemoveObject(aElement)) {
00876         SignalTransformEnd();
00877     }
00878 
00879     return NS_OK;
00880 }
00881 
00882 NS_IMETHODIMP 
00883 txTransformNotifier::StyleSheetLoaded(nsICSSStyleSheet* aSheet, PRBool aNotify)
00884 {
00885     // Check that the stylesheet was in the mStylesheets array, if not it is an
00886     // alternate and we don't want to call SignalTransformEnd since we don't
00887     // wait on alternates before calling OnTransformDone and so the load of the
00888     // alternate could finish after we called OnTransformDone already.
00889     // See http://bugzilla.mozilla.org/show_bug.cgi?id=215465.
00890     if (mStylesheets.RemoveObject(aSheet)) {
00891         SignalTransformEnd();
00892     }
00893 
00894     return NS_OK;
00895 }
00896 
00897 void
00898 txTransformNotifier::Init(nsITransformObserver* aObserver)
00899 {
00900     mObserver = aObserver;
00901 }
00902 
00903 void
00904 txTransformNotifier::AddScriptElement(nsIScriptElement* aElement)
00905 {
00906     mScriptElements.AppendObject(aElement);
00907 }
00908 
00909 void
00910 txTransformNotifier::AddStyleSheet(nsIStyleSheet* aStyleSheet)
00911 {
00912     mStylesheets.AppendObject(aStyleSheet);
00913 }
00914 
00915 void
00916 txTransformNotifier::OnTransformEnd(nsresult aResult)
00917 {
00918     mInTransform = PR_FALSE;
00919     SignalTransformEnd(aResult);
00920 }
00921 
00922 void
00923 txTransformNotifier::OnTransformStart()
00924 {
00925     mInTransform = PR_TRUE;
00926 }
00927 
00928 void
00929 txTransformNotifier::SetOutputDocument(nsIDOMDocument* aDocument)
00930 {
00931     mDocument = aDocument;
00932 
00933     // Notify the contentsink that the document is created
00934     mObserver->OnDocumentCreated(mDocument);
00935 }
00936 
00937 void
00938 txTransformNotifier::SignalTransformEnd(nsresult aResult)
00939 {
00940     if (mInTransform || (NS_SUCCEEDED(aResult) &&
00941         mScriptElements.Count() > 0 || mStylesheets.Count() > 0)) {
00942         return;
00943     }
00944 
00945     mStylesheets.Clear();
00946     mScriptElements.Clear();
00947 
00948     // Make sure that we don't get deleted while this function is executed and
00949     // we remove ourselfs from the scriptloader
00950     nsCOMPtr<nsIScriptLoaderObserver> kungFuDeathGrip(this);
00951 
00952     nsCOMPtr<nsIDocument> doc = do_QueryInterface(mDocument);
00953     if (doc) {
00954         nsIScriptLoader *scriptLoader = doc->GetScriptLoader();
00955         if (scriptLoader) {
00956             scriptLoader->RemoveObserver(this);
00957             // XXX Maybe we want to cancel script loads if NS_FAILED(rv)?
00958         }
00959 
00960         if (NS_FAILED(aResult)) {
00961             doc->CSSLoader()->Stop();
00962         }
00963     }
00964 
00965     if (NS_SUCCEEDED(aResult)) {
00966         mObserver->OnTransformDone(aResult, mDocument);
00967     }
00968 }