Back to index

lightning-sunbird  0.9+nobinonly
txMozillaStylesheetCompiler.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) 2002
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 "nsCOMArray.h"
00040 #include "nsIAuthPrompt.h"
00041 #include "nsICharsetAlias.h"
00042 #include "nsIDOMNode.h"
00043 #include "nsIDOMDocument.h"
00044 #include "nsIDocument.h"
00045 #include "nsIExpatSink.h"
00046 #include "nsIChannelEventSink.h"
00047 #include "nsIInterfaceRequestor.h"
00048 #include "nsILoadGroup.h"
00049 #include "nsINameSpaceManager.h"
00050 #include "nsINodeInfo.h"
00051 #include "nsIParser.h"
00052 #include "nsIRequestObserver.h"
00053 #include "nsIScriptSecurityManager.h"
00054 #include "nsContentPolicyUtils.h"
00055 #include "nsIStreamConverterService.h"
00056 #include "nsISyncLoadDOMService.h"
00057 #include "nsIURI.h"
00058 #include "nsIPrincipal.h"
00059 #include "nsIWindowWatcher.h"
00060 #include "nsIXMLContentSink.h"
00061 #include "nsMimeTypes.h"
00062 #include "nsNetUtil.h"
00063 #include "nsParserCIID.h"
00064 #include "txAtoms.h"
00065 #include "TxLog.h"
00066 #include "txMozillaXSLTProcessor.h"
00067 #include "txStylesheetCompiler.h"
00068 #include "XMLUtils.h"
00069 
00070 static const char kLoadAsData[] = "loadAsData";
00071 static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID);
00072 
00073 static void
00074 getSpec(nsIChannel* aChannel, nsAString& aSpec)
00075 {
00076     if (!aChannel) {
00077         return;
00078     }
00079 
00080     nsCOMPtr<nsIURI> uri;
00081     aChannel->GetOriginalURI(getter_AddRefs(uri));
00082     if (!uri) {
00083         return;
00084     }
00085 
00086     nsCAutoString spec;
00087     uri->GetSpec(spec);
00088     AppendUTF8toUTF16(spec, aSpec);
00089 }
00090 
00091 class txStylesheetSink : public nsIXMLContentSink,
00092                          public nsIExpatSink,
00093                          public nsIStreamListener,
00094                          public nsIChannelEventSink,
00095                          public nsIInterfaceRequestor
00096 {
00097 public:
00098     txStylesheetSink(txStylesheetCompiler* aCompiler, nsIParser* aParser);
00099     virtual ~txStylesheetSink();
00100 
00101     NS_DECL_ISUPPORTS
00102     NS_DECL_NSIEXPATSINK
00103     NS_DECL_NSISTREAMLISTENER
00104     NS_DECL_NSIREQUESTOBSERVER
00105     NS_DECL_NSICHANNELEVENTSINK
00106     NS_DECL_NSIINTERFACEREQUESTOR
00107 
00108     // nsIContentSink
00109     NS_IMETHOD WillBuildModel(void) { return NS_OK; }
00110     NS_IMETHOD DidBuildModel();
00111     NS_IMETHOD WillInterrupt(void) { return NS_OK; }
00112     NS_IMETHOD WillResume(void) { return NS_OK; }
00113     NS_IMETHOD SetParser(nsIParser* aParser) { return NS_OK; }
00114     virtual void FlushPendingNotifications(mozFlushType aType) { }
00115     NS_IMETHOD SetDocumentCharset(nsACString& aCharset) { return NS_OK; }
00116     virtual nsISupports *GetTarget() { return nsnull; }
00117 
00118 private:
00119     nsRefPtr<txStylesheetCompiler> mCompiler;
00120     nsCOMPtr<nsIStreamListener> mListener;
00121     PRPackedBool mCheckedForXML;
00122 
00123 protected:
00124     // This exists soly to supress a warning from nsDerivedSafe
00125     txStylesheetSink();
00126 };
00127 
00128 txStylesheetSink::txStylesheetSink(txStylesheetCompiler* aCompiler,
00129                                    nsIParser* aParser)
00130     : mCompiler(aCompiler),
00131       mCheckedForXML(PR_FALSE)
00132 {
00133     mListener = do_QueryInterface(aParser);
00134 }
00135 
00136 txStylesheetSink::~txStylesheetSink()
00137 {
00138 }
00139 
00140 NS_IMPL_ISUPPORTS7(txStylesheetSink,
00141                    nsIXMLContentSink,
00142                    nsIContentSink,
00143                    nsIExpatSink,
00144                    nsIStreamListener,
00145                    nsIRequestObserver,
00146                    nsIChannelEventSink,
00147                    nsIInterfaceRequestor)
00148 
00149 NS_IMETHODIMP
00150 txStylesheetSink::HandleStartElement(const PRUnichar *aName,
00151                                      const PRUnichar **aAtts,
00152                                      PRUint32 aAttsCount,
00153                                      PRInt32 aIndex,
00154                                      PRUint32 aLineNumber)
00155 {
00156     NS_PRECONDITION(aAttsCount % 2 == 0, "incorrect aAttsCount");
00157 
00158     nsresult rv =
00159         mCompiler->startElement(aName, aAtts, aAttsCount / 2, aIndex);
00160     if (NS_FAILED(rv)) {
00161         mCompiler->cancel(rv);
00162 
00163         return rv;
00164     }
00165     
00166     return NS_OK;
00167 }
00168 
00169 NS_IMETHODIMP
00170 txStylesheetSink::HandleEndElement(const PRUnichar *aName)
00171 {
00172     nsresult rv = mCompiler->endElement();
00173     if (NS_FAILED(rv)) {
00174         mCompiler->cancel(rv);
00175 
00176         return rv;
00177     }
00178 
00179     return NS_OK;
00180 }
00181 
00182 NS_IMETHODIMP
00183 txStylesheetSink::HandleComment(const PRUnichar *aName)
00184 {
00185     return NS_OK;
00186 }
00187 
00188 NS_IMETHODIMP
00189 txStylesheetSink::HandleCDataSection(const PRUnichar *aData,
00190                                      PRUint32 aLength)
00191 {
00192     return HandleCharacterData(aData, aLength);
00193 }
00194 
00195 NS_IMETHODIMP
00196 txStylesheetSink::HandleDoctypeDecl(const nsAString & aSubset,
00197                                     const nsAString & aName,
00198                                     const nsAString & aSystemId,
00199                                     const nsAString & aPublicId,
00200                                     nsISupports *aCatalogData)
00201 {
00202     return NS_OK;
00203 }
00204 
00205 NS_IMETHODIMP
00206 txStylesheetSink::HandleCharacterData(const PRUnichar *aData,
00207                                       PRUint32 aLength)
00208 {
00209     nsresult rv = mCompiler->characters(Substring(aData, aData + aLength));
00210     if (NS_FAILED(rv)) {
00211         mCompiler->cancel(rv);
00212         return rv;
00213     }
00214 
00215     return NS_OK;
00216 }
00217 
00218 NS_IMETHODIMP
00219 txStylesheetSink::HandleProcessingInstruction(const PRUnichar *aTarget,
00220                                               const PRUnichar *aData)
00221 {
00222     return NS_OK;
00223 }
00224 
00225 NS_IMETHODIMP
00226 txStylesheetSink::HandleXMLDeclaration(const PRUnichar *aVersion,
00227                                        const PRUnichar *aEncoding,
00228                                        PRInt32 aStandalone)
00229 {
00230     return NS_OK;
00231 }
00232 
00233 NS_IMETHODIMP
00234 txStylesheetSink::ReportError(const PRUnichar *aErrorText,
00235                               const PRUnichar *aSourceText)
00236 {
00237     mCompiler->cancel(NS_ERROR_FAILURE, aErrorText, aSourceText);
00238 
00239     return NS_OK;
00240 }
00241 
00242 NS_IMETHODIMP 
00243 txStylesheetSink::DidBuildModel()
00244 {  
00245     return mCompiler->doneLoading();
00246 }
00247 
00248 NS_IMETHODIMP
00249 txStylesheetSink::OnDataAvailable(nsIRequest *aRequest, nsISupports *aContext,
00250                                   nsIInputStream *aInputStream,
00251                                   PRUint32 aOffset, PRUint32 aCount)
00252 {
00253     if (!mCheckedForXML) {
00254         nsCOMPtr<nsIParser> parser = do_QueryInterface(aContext);
00255         nsCOMPtr<nsIDTD> dtd;
00256         parser->GetDTD(getter_AddRefs(dtd));
00257         if (dtd) {
00258             mCheckedForXML = PR_TRUE;
00259             if (!(dtd->GetType() & NS_IPARSER_FLAG_XML)) {
00260                 nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
00261                 nsAutoString spec;
00262                 getSpec(channel, spec);
00263                 mCompiler->cancel(NS_ERROR_XSLT_WRONG_MIME_TYPE, nsnull,
00264                                   spec.get());
00265 
00266                 return NS_ERROR_XSLT_WRONG_MIME_TYPE;
00267             }
00268         }
00269     }
00270 
00271     return mListener->OnDataAvailable(aRequest, aContext, aInputStream,
00272                                       aOffset, aCount);
00273 }
00274 
00275 NS_IMETHODIMP
00276 txStylesheetSink::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext)
00277 {
00278     nsCAutoString charset(NS_LITERAL_CSTRING("UTF-8"));
00279     PRInt32 charsetSource = kCharsetFromDocTypeDefault;
00280 
00281     nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
00282 
00283     // check channel's charset...
00284     nsCAutoString charsetVal;
00285     nsresult rv = channel->GetContentCharset(charsetVal);
00286     if (NS_SUCCEEDED(rv)) {
00287         nsCOMPtr<nsICharsetAlias> calias =
00288             do_GetService(NS_CHARSETALIAS_CONTRACTID);
00289 
00290         if (calias) {
00291             nsCAutoString preferred;
00292             rv = calias->GetPreferred(charsetVal,
00293                                       preferred);
00294             if (NS_SUCCEEDED(rv)) {            
00295                 charset = preferred;
00296                 charsetSource = kCharsetFromChannel;
00297              }
00298         }
00299     }
00300 
00301     nsCOMPtr<nsIParser> parser = do_QueryInterface(aContext);
00302     parser->SetDocumentCharset(charset, charsetSource);
00303 
00304     nsCAutoString contentType;
00305     channel->GetContentType(contentType);
00306 
00307     // Time to sniff! Note: this should go away once file channels do
00308     // sniffing themselves.
00309     nsCOMPtr<nsIURI> uri;
00310     channel->GetURI(getter_AddRefs(uri));
00311     PRBool sniff;
00312     if (NS_SUCCEEDED(uri->SchemeIs("file", &sniff)) && sniff &&
00313         contentType.Equals(UNKNOWN_CONTENT_TYPE)) {
00314         nsCOMPtr<nsIStreamConverterService> serv =
00315             do_GetService("@mozilla.org/streamConverters;1", &rv);
00316         if (NS_SUCCEEDED(rv)) {
00317             nsCOMPtr<nsIStreamListener> converter;
00318             rv = serv->AsyncConvertData(UNKNOWN_CONTENT_TYPE,
00319                                         "*/*",
00320                                         mListener,
00321                                         aContext,
00322                                         getter_AddRefs(converter));
00323             if (NS_SUCCEEDED(rv)) {
00324                 mListener = converter;
00325             }
00326         }
00327     }
00328 
00329     return mListener->OnStartRequest(aRequest, aContext);
00330 }
00331 
00332 NS_IMETHODIMP
00333 txStylesheetSink::OnStopRequest(nsIRequest *aRequest, nsISupports *aContext,
00334                                 nsresult aStatusCode)
00335 {
00336     PRBool success = PR_TRUE;
00337 
00338     nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
00339     if (httpChannel) {
00340         httpChannel->GetRequestSucceeded(&success);
00341     }
00342 
00343     nsresult result = aStatusCode;
00344     if (!success) {
00345         // XXX We sometimes want to use aStatusCode here, but the parser resets
00346         //     it to NS_ERROR_NOINTERFACE because we don't implement
00347         //     nsIHTMLContentSink.
00348         result = NS_ERROR_XSLT_NETWORK_ERROR;
00349     }
00350     else if (!mCheckedForXML) {
00351         nsCOMPtr<nsIParser> parser = do_QueryInterface(aContext);
00352         nsCOMPtr<nsIDTD> dtd;
00353         parser->GetDTD(getter_AddRefs(dtd));
00354         if (dtd && !(dtd->GetType() & NS_IPARSER_FLAG_XML)) {
00355             result = NS_ERROR_XSLT_WRONG_MIME_TYPE;
00356         }
00357     }
00358 
00359     if (NS_FAILED(result)) {
00360         nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
00361         nsAutoString spec;
00362         getSpec(channel, spec);
00363         mCompiler->cancel(result, nsnull, spec.get());
00364     }
00365 
00366     nsresult rv = mListener->OnStopRequest(aRequest, aContext, aStatusCode);
00367     mListener = nsnull;
00368     return rv;
00369 }
00370 
00371 NS_IMETHODIMP
00372 txStylesheetSink::OnChannelRedirect(nsIChannel *aOldChannel,
00373                                     nsIChannel *aNewChannel,
00374                                     PRUint32    aFlags)
00375 {
00376     NS_PRECONDITION(aNewChannel, "Redirect without a channel?");
00377 
00378     nsresult rv;
00379     nsCOMPtr<nsIScriptSecurityManager> secMan =
00380         do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
00381     NS_ENSURE_SUCCESS(rv, rv);
00382 
00383     nsCOMPtr<nsIURI> oldURI;
00384     rv = aOldChannel->GetURI(getter_AddRefs(oldURI)); // The original URI
00385     NS_ENSURE_SUCCESS(rv, rv);
00386 
00387     nsCOMPtr<nsIURI> newURI;
00388     rv = aNewChannel->GetURI(getter_AddRefs(newURI)); // The new URI
00389     NS_ENSURE_SUCCESS(rv, rv);
00390 
00391     return secMan->CheckSameOriginURI(oldURI, newURI);
00392 }
00393 
00394 NS_IMETHODIMP
00395 txStylesheetSink::GetInterface(const nsIID& aIID, void** aResult)
00396 {
00397     if (aIID.Equals(NS_GET_IID(nsIAuthPrompt))) {
00398         NS_ENSURE_ARG(aResult);
00399         *aResult = nsnull;
00400 
00401         nsresult rv;
00402         nsCOMPtr<nsIWindowWatcher> wwatcher =
00403             do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
00404         NS_ENSURE_SUCCESS(rv, rv);
00405 
00406         nsCOMPtr<nsIAuthPrompt> prompt;
00407         rv = wwatcher->GetNewAuthPrompter(nsnull, getter_AddRefs(prompt));
00408         NS_ENSURE_SUCCESS(rv, rv);
00409 
00410         nsIAuthPrompt* rawPtr = nsnull;
00411         prompt.swap(rawPtr);
00412         *aResult = rawPtr;
00413 
00414         return NS_OK;
00415     }
00416 
00417     return QueryInterface(aIID, aResult);
00418 }
00419 
00420 static nsresult
00421 CheckLoadURI(nsIURI *aUri, nsIURI *aReferrerUri,
00422              nsIPrincipal *aReferrerPrincipal, nsISupports *aContext)
00423 {
00424     // First do a security check.
00425     nsresult rv;
00426     nsCOMPtr<nsIScriptSecurityManager> securityManager = 
00427         do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
00428     NS_ENSURE_SUCCESS(rv, rv);
00429 
00430     if (aReferrerPrincipal) {
00431         rv = securityManager->
00432             CheckLoadURIWithPrincipal(aReferrerPrincipal, aUri,
00433                                       nsIScriptSecurityManager::STANDARD);
00434     }
00435     else {
00436         rv = securityManager->CheckLoadURI(aReferrerUri, aUri,
00437                                            nsIScriptSecurityManager::STANDARD);
00438     }
00439     NS_ENSURE_SUCCESS(rv, NS_ERROR_XSLT_LOAD_BLOCKED_ERROR);
00440 
00441     rv = securityManager->CheckSameOriginURI(aReferrerUri, aUri);
00442     NS_ENSURE_SUCCESS(rv, NS_ERROR_XSLT_LOAD_BLOCKED_ERROR);
00443 
00444     // Then do a content policy check.
00445     PRInt16 decision = nsIContentPolicy::ACCEPT;
00446     rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_STYLESHEET,
00447                                    aUri, aReferrerUri, aContext,
00448                                    NS_LITERAL_CSTRING("application/xml"), nsnull,
00449                                    &decision);
00450     NS_ENSURE_SUCCESS(rv, rv);
00451 
00452     return NS_CP_REJECTED(decision) ? NS_ERROR_XSLT_LOAD_BLOCKED_ERROR : NS_OK;
00453 }
00454 
00455 class txCompileObserver : public txACompileObserver
00456 {
00457 public:
00458     txCompileObserver(txMozillaXSLTProcessor* aProcessor,
00459                       nsILoadGroup* aLoadGroup);
00460     virtual ~txCompileObserver();
00461 
00462     TX_DECL_ACOMPILEOBSERVER;
00463 
00464     nsresult startLoad(nsIURI* aUri, txStylesheetCompiler* aCompiler,
00465                        nsIURI* aReferrerURI);
00466 
00467 protected:
00468     nsAutoRefCnt mRefCnt;
00469 
00470 private:
00471     nsRefPtr<txMozillaXSLTProcessor> mProcessor;
00472     nsCOMPtr<nsILoadGroup> mLoadGroup;
00473 
00474 protected:
00475     // This exists soly to supress a warning from nsDerivedSafe
00476     txCompileObserver();
00477 };
00478 
00479 txCompileObserver::txCompileObserver(txMozillaXSLTProcessor* aProcessor,
00480                                      nsILoadGroup* aLoadGroup)
00481     : mProcessor(aProcessor),
00482       mLoadGroup(aLoadGroup)
00483 {
00484 }
00485 
00486 txCompileObserver::~txCompileObserver()
00487 {
00488 }
00489 
00490 nsrefcnt
00491 txCompileObserver::AddRef()
00492 {
00493     return ++mRefCnt;
00494 }
00495 
00496 nsrefcnt
00497 txCompileObserver::Release()
00498 {
00499     if (--mRefCnt == 0) {
00500         mRefCnt = 1; //stabilize
00501         delete this;
00502         return 0;
00503     }
00504     return mRefCnt;
00505 }
00506 
00507 nsresult
00508 txCompileObserver::loadURI(const nsAString& aUri,
00509                            const nsAString& aReferrerUri,
00510                            txStylesheetCompiler* aCompiler)
00511 {
00512     if (mProcessor->IsLoadDisabled()) {
00513         return NS_ERROR_XSLT_LOAD_BLOCKED_ERROR;
00514     }
00515 
00516     nsCOMPtr<nsIURI> uri;
00517     nsresult rv = NS_NewURI(getter_AddRefs(uri), aUri);
00518     NS_ENSURE_SUCCESS(rv, rv);
00519 
00520     nsCOMPtr<nsIURI> referrerUri;
00521     rv = NS_NewURI(getter_AddRefs(referrerUri), aReferrerUri);
00522     NS_ENSURE_SUCCESS(rv, rv);
00523 
00524     // Do security check.
00525     rv = CheckLoadURI(uri, referrerUri, nsnull, nsnull);
00526     NS_ENSURE_SUCCESS(rv, rv);
00527 
00528     return startLoad(uri, aCompiler, referrerUri);
00529 }
00530 
00531 void
00532 txCompileObserver::onDoneCompiling(txStylesheetCompiler* aCompiler,
00533                                    nsresult aResult,
00534                                    const PRUnichar *aErrorText,
00535                                    const PRUnichar *aParam)
00536 {
00537     if (NS_SUCCEEDED(aResult)) {
00538         mProcessor->setStylesheet(aCompiler->getStylesheet());
00539     }
00540     else {
00541         mProcessor->reportError(aResult, aErrorText, aParam);
00542     }
00543 }
00544 
00545 nsresult
00546 txCompileObserver::startLoad(nsIURI* aUri, txStylesheetCompiler* aCompiler,
00547                              nsIURI* aReferrerURI)
00548 {
00549     nsCOMPtr<nsIChannel> channel;
00550     nsresult rv = NS_NewChannel(getter_AddRefs(channel), aUri);
00551     NS_ENSURE_SUCCESS(rv, rv);
00552 
00553     channel->SetLoadGroup(mLoadGroup);
00554 
00555     channel->SetContentType(NS_LITERAL_CSTRING("text/xml"));
00556 
00557     nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
00558     if (httpChannel) {
00559         httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
00560                                       NS_LITERAL_CSTRING("text/xml,application/xml,application/xhtml+xml,*/*;q=0.1"),
00561                                       PR_FALSE);
00562 
00563         if (aReferrerURI) {
00564             httpChannel->SetReferrer(aReferrerURI);
00565         }
00566     }
00567 
00568     nsCOMPtr<nsIParser> parser = do_CreateInstance(kCParserCID, &rv);
00569     NS_ENSURE_SUCCESS(rv, rv);
00570 
00571     nsRefPtr<txStylesheetSink> sink = new txStylesheetSink(aCompiler, parser);
00572     NS_ENSURE_TRUE(sink, NS_ERROR_OUT_OF_MEMORY);
00573 
00574     channel->SetNotificationCallbacks(sink);
00575 
00576     parser->SetCommand(kLoadAsData);
00577     parser->SetContentSink(sink);
00578     parser->Parse(aUri);
00579 
00580     return channel->AsyncOpen(sink, parser);
00581 }
00582 
00583 nsresult
00584 TX_LoadSheet(nsIURI* aUri, txMozillaXSLTProcessor* aProcessor,
00585              nsILoadGroup* aLoadGroup, nsIPrincipal* aCallerPrincipal)
00586 {
00587     nsCAutoString spec;
00588     aUri->GetSpec(spec);
00589     PR_LOG(txLog::xslt, PR_LOG_ALWAYS, ("TX_LoadSheet: %s\n", spec.get()));
00590 
00591     nsCOMPtr<nsIURI> referrerURI;
00592     aCallerPrincipal->GetURI(getter_AddRefs(referrerURI));
00593     NS_ASSERTION(referrerURI, "Caller principal must have a URI!");
00594 
00595     // Pass source document as the context
00596     nsresult rv = CheckLoadURI(aUri, referrerURI, aCallerPrincipal,
00597                                aProcessor->GetSourceContentModel());
00598     NS_ENSURE_SUCCESS(rv, rv);
00599 
00600     nsRefPtr<txCompileObserver> observer =
00601         new txCompileObserver(aProcessor, aLoadGroup);
00602     NS_ENSURE_TRUE(observer, NS_ERROR_OUT_OF_MEMORY);
00603 
00604     nsRefPtr<txStylesheetCompiler> compiler =
00605         new txStylesheetCompiler(NS_ConvertUTF8toUTF16(spec), observer);
00606     NS_ENSURE_TRUE(compiler, NS_ERROR_OUT_OF_MEMORY);
00607 
00608     return observer->startLoad(aUri, compiler, referrerURI);
00609 }
00610 
00615 static nsresult
00616 handleNode(nsIDOMNode* aNode, txStylesheetCompiler* aCompiler)
00617 {
00618     nsresult rv = NS_OK;
00619     PRUint16 nodetype;
00620     aNode->GetNodeType(&nodetype);
00621     switch (nodetype) {
00622         case nsIDOMNode::ELEMENT_NODE:
00623         {
00624             nsCOMPtr<nsIContent> element = do_QueryInterface(aNode);
00625 
00626             PRUint32 attsCount = element->GetAttrCount();
00627             nsAutoArrayPtr<txStylesheetAttr> atts;
00628             if (attsCount > 0) {
00629                 atts = new txStylesheetAttr[attsCount];
00630                 NS_ENSURE_TRUE(atts, NS_ERROR_OUT_OF_MEMORY);
00631 
00632                 PRUint32 counter;
00633                 for (counter = 0; counter < attsCount; ++counter) {
00634                     txStylesheetAttr& att = atts[counter];
00635                     element->GetAttrNameAt(counter, &att.mNamespaceID,
00636                                            getter_AddRefs(att.mLocalName),
00637                                            getter_AddRefs(att.mPrefix));
00638                     element->GetAttr(att.mNamespaceID, att.mLocalName, att.mValue);
00639                 }
00640             }
00641 
00642             nsINodeInfo *ni = element->GetNodeInfo();
00643 
00644             rv = aCompiler->startElement(ni->NamespaceID(),
00645                                          ni->NameAtom(),
00646                                          ni->GetPrefixAtom(), atts,
00647                                          attsCount);
00648             NS_ENSURE_SUCCESS(rv, rv);
00649 
00650             // explicitly destroy the attrs here since we no longer need it
00651             atts = nsnull;
00652 
00653             PRUint32 childCount = element->GetChildCount();
00654             if (childCount > 0) {
00655                 PRUint32 counter = 0;
00656                 nsIContent *child;
00657                 while ((child = element->GetChildAt(counter++))) {
00658                     nsCOMPtr<nsIDOMNode> childNode = do_QueryInterface(child);
00659                     rv = handleNode(childNode, aCompiler);
00660                     NS_ENSURE_SUCCESS(rv, rv);
00661                 }
00662             }
00663 
00664             rv = aCompiler->endElement();
00665             NS_ENSURE_SUCCESS(rv, rv);
00666 
00667             break;
00668         }
00669         case nsIDOMNode::CDATA_SECTION_NODE:
00670         case nsIDOMNode::TEXT_NODE:
00671         {
00672             nsAutoString chars;
00673             aNode->GetNodeValue(chars);
00674             rv = aCompiler->characters(chars);
00675             NS_ENSURE_SUCCESS(rv, rv);
00676 
00677             break;
00678         }
00679         case nsIDOMNode::DOCUMENT_NODE:
00680         {
00681             nsCOMPtr<nsIDocument> document = do_QueryInterface(aNode);
00682 
00683             PRUint32 counter = 0;
00684             nsIContent *child;
00685             while ((child = document->GetChildAt(counter++))) {
00686                 nsCOMPtr<nsIDOMNode> childNode = do_QueryInterface(child);
00687                 rv = handleNode(childNode, aCompiler);
00688                 NS_ENSURE_SUCCESS(rv, rv);
00689             }
00690             break;
00691         }
00692     }
00693     return NS_OK;
00694 }
00695 
00696 class txSyncCompileObserver : public txACompileObserver
00697 {
00698 public:
00699     txSyncCompileObserver(txMozillaXSLTProcessor* aProcessor);
00700     virtual ~txSyncCompileObserver();
00701 
00702     TX_DECL_ACOMPILEOBSERVER;
00703 
00704 protected:
00705     nsRefPtr<txMozillaXSLTProcessor> mProcessor;
00706     nsAutoRefCnt mRefCnt;
00707 
00708 private:
00709     nsCOMPtr<nsISyncLoadDOMService> mLoadService;
00710 };
00711 
00712 txSyncCompileObserver::txSyncCompileObserver(txMozillaXSLTProcessor* aProcessor)
00713   : mProcessor(aProcessor)
00714 {
00715 }
00716 
00717 txSyncCompileObserver::~txSyncCompileObserver()
00718 {
00719 }
00720 
00721 nsrefcnt
00722 txSyncCompileObserver::AddRef()
00723 {
00724     return ++mRefCnt;
00725 }
00726 
00727 nsrefcnt
00728 txSyncCompileObserver::Release()
00729 {
00730     if (--mRefCnt == 0) {
00731         mRefCnt = 1; //stabilize
00732         delete this;
00733         return 0;
00734     }
00735     return mRefCnt;
00736 }
00737 
00738 nsresult
00739 txSyncCompileObserver::loadURI(const nsAString& aUri,
00740                                const nsAString& aReferrerUri,
00741                                txStylesheetCompiler* aCompiler)
00742 {
00743     if (mProcessor->IsLoadDisabled()) {
00744         return NS_ERROR_XSLT_LOAD_BLOCKED_ERROR;
00745     }
00746 
00747     nsCOMPtr<nsIURI> uri;
00748     nsresult rv = NS_NewURI(getter_AddRefs(uri), aUri);
00749     NS_ENSURE_SUCCESS(rv, rv);
00750 
00751     nsCOMPtr<nsIURI> referrerUri;
00752     rv = NS_NewURI(getter_AddRefs(referrerUri), aReferrerUri);
00753     NS_ENSURE_SUCCESS(rv, rv);
00754 
00755     rv = CheckLoadURI(uri, referrerUri, nsnull, nsnull);
00756     NS_ENSURE_SUCCESS(rv, rv);
00757 
00758     if (!mLoadService) {
00759         mLoadService =
00760             do_GetService("@mozilla.org/content/syncload-dom-service;1");
00761         NS_ENSURE_TRUE(mLoadService, NS_ERROR_OUT_OF_MEMORY);
00762     }
00763 
00764     nsCOMPtr<nsIChannel> channel;
00765     rv = NS_NewChannel(getter_AddRefs(channel), uri);
00766     NS_ENSURE_SUCCESS(rv, rv);
00767 
00768     // This is probably called by js, a loadGroup for the channel doesn't
00769     // make sense.
00770 
00771     channel->SetContentType(NS_LITERAL_CSTRING("text/xml"));
00772 
00773     nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
00774     if (httpChannel) {
00775         httpChannel->SetReferrer(referrerUri);
00776     }
00777 
00778     nsCOMPtr<nsIDOMDocument> document;
00779     rv = mLoadService->LoadDocument(channel, referrerUri,
00780                                     getter_AddRefs(document));
00781     NS_ENSURE_SUCCESS(rv, rv);
00782     rv = handleNode(document, aCompiler);
00783     if (NS_FAILED(rv)) {
00784         nsCAutoString spec;
00785         uri->GetSpec(spec);
00786         aCompiler->cancel(rv, nsnull, NS_ConvertUTF8toUTF16(spec).get());
00787         return rv;
00788     }
00789 
00790     rv = aCompiler->doneLoading();
00791     return rv;
00792 }
00793 
00794 void txSyncCompileObserver::onDoneCompiling(txStylesheetCompiler* aCompiler,
00795                                             nsresult aResult,
00796                                             const PRUnichar *aErrorText,
00797                                             const PRUnichar *aParam)
00798 {
00799 }
00800 
00801 nsresult
00802 TX_CompileStylesheet(nsIDOMNode* aNode, txMozillaXSLTProcessor* aProcessor,
00803                      txStylesheet** aStylesheet)
00804 {
00805     // XXX This would be simpler if GetBaseURI lived on nsINode
00806     nsCOMPtr<nsIURI> uri;
00807     nsCOMPtr<nsIDocument> doc;
00808     nsCOMPtr<nsIContent> cont = do_QueryInterface(aNode);
00809     if (cont) {
00810         doc = cont->GetOwnerDoc();
00811         NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
00812 
00813         uri = cont->GetBaseURI();
00814     }
00815     else {
00816         doc = do_QueryInterface(aNode);
00817         NS_ASSERTION(doc, "aNode should be a doc or an element by now");
00818 
00819         uri = doc->GetBaseURI();
00820     }
00821 
00822     NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE);
00823     
00824     nsCAutoString spec;
00825     uri->GetSpec(spec);
00826     NS_ConvertUTF8toUTF16 baseURI(spec);
00827 
00828     uri = doc->GetDocumentURI();
00829     NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE);
00830 
00831     uri->GetSpec(spec);
00832     NS_ConvertUTF8toUTF16 stylesheetURI(spec);
00833 
00834     nsRefPtr<txSyncCompileObserver> obs =
00835         new txSyncCompileObserver(aProcessor);
00836     NS_ENSURE_TRUE(obs, NS_ERROR_OUT_OF_MEMORY);
00837 
00838     nsRefPtr<txStylesheetCompiler> compiler =
00839         new txStylesheetCompiler(stylesheetURI, obs);
00840     NS_ENSURE_TRUE(compiler, NS_ERROR_OUT_OF_MEMORY);
00841 
00842     compiler->setBaseURI(baseURI);
00843 
00844     nsresult rv = handleNode(aNode, compiler);
00845     if (NS_FAILED(rv)) {
00846         compiler->cancel(rv);
00847         return rv;
00848     }
00849 
00850     rv = compiler->doneLoading();
00851     NS_ENSURE_SUCCESS(rv, rv);
00852     
00853     *aStylesheet = compiler->getStylesheet();
00854     NS_ADDREF(*aStylesheet);
00855 
00856     return NS_OK;
00857 }