Back to index

lightning-sunbird  0.9+nobinonly
nsCSSLoader.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
00002  * vim: ft=cpp tw=78 sw=2 et ts=2
00003  *
00004  * ***** BEGIN LICENSE BLOCK *****
00005  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00006  *
00007  * The contents of this file are subject to the Mozilla Public License Version
00008  * 1.1 (the "License"); you may not use this file except in compliance with
00009  * the License. You may obtain a copy of the License at
00010  * http://www.mozilla.org/MPL/
00011  *
00012  * Software distributed under the License is distributed on an "AS IS" basis,
00013  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00014  * for the specific language governing rights and limitations under the
00015  * License.
00016  *
00017  * The Original Code is mozilla.org code.
00018  *
00019  * The Initial Developer of the Original Code is
00020  * Netscape Communications Corporation.
00021  * Portions created by the Initial Developer are Copyright (C) 1999
00022  * the Initial Developer. All Rights Reserved.
00023  *
00024  * Contributor(s):
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either of the GNU General Public License Version 2 or later (the "GPL"),
00028  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK *****
00039  *
00040  * This Original Code has been modified by IBM Corporation. Modifications made by IBM 
00041  * described herein are Copyright (c) International Business Machines Corporation, 2000.
00042  * Modifications to Mozilla code or documentation identified per MPL Section 3.3
00043  *
00044  * Date             Modified by     Description of modification
00045  * 04/20/2000       IBM Corp.      OS/2 VisualAge build.
00046  */
00047 #include "nsCSSLoader.h"
00048 #include "nsIContent.h"
00049 #include "nsIDOMNode.h"
00050 #include "nsIDOMWindow.h"
00051 #include "nsIDocument.h"
00052 #include "nsIUnicharInputStream.h"
00053 #include "nsIConverterInputStream.h"
00054 #include "nsICharsetAlias.h"
00055 #include "nsUnicharUtils.h"
00056 #include "nsHashtable.h"
00057 #include "nsIURI.h"
00058 #include "nsIURL.h"
00059 #include "nsIParser.h"
00060 #include "nsIServiceManager.h"
00061 #include "nsNetUtil.h"
00062 #include "nsContentUtils.h"
00063 #include "nsCRT.h"
00064 #include "nsIScriptSecurityManager.h"
00065 #include "nsContentPolicyUtils.h"
00066 #include "nsIScriptGlobalObject.h"
00067 #include "nsITimelineService.h"
00068 #include "nsIHttpChannel.h"
00069 #include "nsIScriptError.h"
00070 #include "nsMimeTypes.h"
00071 #include "nsIAtom.h"
00072 #include "nsIDOM3Node.h"
00073 #include "nsICSSStyleSheet.h"
00074 #include "nsIStyleSheetLinkingElement.h"
00075 #include "nsICSSLoaderObserver.h"
00076 #include "nsICSSLoader.h"
00077 #include "nsICSSParser.h"
00078 #include "nsICSSImportRule.h"
00079 
00080 #ifdef MOZ_XUL
00081 #include "nsIXULPrototypeCache.h"
00082 #endif
00083 
00084 #include "nsIMediaList.h"
00085 #include "nsIDOMStyleSheet.h"
00086 #include "nsIDOMCSSStyleSheet.h"
00087 #include "nsIDOMCSSImportRule.h"
00088 
00089 #ifdef MOZ_LOGGING
00090 // #define FORCE_PR_LOG /* Allow logging in the release build */
00091 #endif /* MOZ_LOGGING */
00092 #include "prlog.h"
00093 
00094 #ifdef PR_LOGGING
00095 static PRLogModuleInfo *gLoaderLog = PR_NewLogModule("nsCSSLoader");
00096 #endif /* PR_LOGGING */
00097 
00098 #define LOG_FORCE(args) PR_LOG(gLoaderLog, PR_LOG_ALWAYS, args)
00099 #define LOG_ERROR(args) PR_LOG(gLoaderLog, PR_LOG_ERROR, args)
00100 #define LOG_WARN(args) PR_LOG(gLoaderLog, PR_LOG_WARNING, args)
00101 #define LOG_DEBUG(args) PR_LOG(gLoaderLog, PR_LOG_DEBUG, args)
00102 #define LOG(args) LOG_DEBUG(args)
00103 
00104 #define LOG_FORCE_ENABLED() PR_LOG_TEST(gLoaderLog, PR_LOG_ALWAYS)
00105 #define LOG_ERROR_ENABLED() PR_LOG_TEST(gLoaderLog, PR_LOG_ERROR)
00106 #define LOG_WARN_ENABLED() PR_LOG_TEST(gLoaderLog, PR_LOG_WARNING)
00107 #define LOG_DEBUG_ENABLED() PR_LOG_TEST(gLoaderLog, PR_LOG_DEBUG)
00108 #define LOG_ENABLED() LOG_DEBUG_ENABLED()
00109 
00110 #ifdef PR_LOGGING
00111 #define LOG_URI(format, uri)                        \
00112   PR_BEGIN_MACRO                                    \
00113     NS_ASSERTION(uri, "Logging null uri");          \
00114     if (LOG_ENABLED()) {                            \
00115       nsCAutoString _logURISpec;                    \
00116       uri->GetSpec(_logURISpec);                    \
00117       LOG((format, _logURISpec.get()));             \
00118     }                                               \
00119   PR_END_MACRO
00120 #else // PR_LOGGING
00121 #define LOG_URI(format, uri)
00122 #endif // PR_LOGGING
00123 
00124 // And some convenience strings...
00125 #ifdef PR_LOGGING
00126 static const char* const gStateStrings[] = {
00127   "eSheetStateUnknown",
00128   "eSheetNeedsParser",
00129   "eSheetPending",
00130   "eSheetLoading",
00131   "eSheetComplete"
00132 };
00133 #endif
00134 
00135 /********************************
00136  * SheetLoadData implementation *
00137  ********************************/
00138 NS_IMPL_ISUPPORTS1(SheetLoadData, nsIUnicharStreamLoaderObserver)
00139 
00140 SheetLoadData::SheetLoadData(CSSLoaderImpl* aLoader,
00141                              const nsSubstring& aTitle,
00142                              nsIParser* aParserToUnblock,
00143                              nsIURI* aURI,
00144                              nsICSSStyleSheet* aSheet,
00145                              nsIStyleSheetLinkingElement* aOwningElement,
00146                              nsICSSLoaderObserver* aObserver)
00147   : mLoader(aLoader),
00148     mTitle(aTitle),
00149     mParserToUnblock(aParserToUnblock),
00150     mURI(aURI),
00151     mLineNumber(1),
00152     mSheet(aSheet),
00153     mNext(nsnull),
00154     mParentData(nsnull),
00155     mPendingChildren(0),
00156     mSyncLoad(PR_FALSE),
00157     mIsAgent(PR_FALSE),
00158     mIsLoading(PR_FALSE),
00159     mIsCancelled(PR_FALSE),
00160     mAllowUnsafeRules(PR_FALSE),
00161     mOwningElement(aOwningElement),
00162     mObserver(aObserver)
00163 {
00164 
00165   NS_PRECONDITION(mLoader, "Must have a loader!");
00166   NS_ADDREF(mLoader);
00167 }
00168 
00169 SheetLoadData::SheetLoadData(CSSLoaderImpl* aLoader,
00170                              nsIURI* aURI,
00171                              nsICSSStyleSheet* aSheet,
00172                              SheetLoadData* aParentData,
00173                              nsICSSLoaderObserver* aObserver)
00174   : mLoader(aLoader),
00175     mParserToUnblock(nsnull),
00176     mURI(aURI),
00177     mLineNumber(1),
00178     mSheet(aSheet),
00179     mNext(nsnull),
00180     mParentData(aParentData),
00181     mPendingChildren(0),
00182     mSyncLoad(PR_FALSE),
00183     mIsAgent(PR_FALSE),
00184     mIsLoading(PR_FALSE),
00185     mIsCancelled(PR_FALSE),
00186     mAllowUnsafeRules(PR_FALSE),
00187     mOwningElement(nsnull),
00188     mObserver(aObserver)
00189 {
00190 
00191   NS_PRECONDITION(mLoader, "Must have a loader!");
00192   NS_ADDREF(mLoader);
00193   if (mParentData) {
00194     NS_ADDREF(mParentData);
00195     mSyncLoad = mParentData->mSyncLoad;
00196     mIsAgent = mParentData->mIsAgent;
00197     mAllowUnsafeRules = mParentData->mAllowUnsafeRules;
00198     ++(mParentData->mPendingChildren);
00199   }
00200 }
00201 
00202 SheetLoadData::SheetLoadData(CSSLoaderImpl* aLoader,
00203                              nsIURI* aURI,
00204                              nsICSSStyleSheet* aSheet,
00205                              PRBool aSyncLoad,
00206                              PRBool aAllowUnsafeRules,
00207                              nsICSSLoaderObserver* aObserver)
00208   : mLoader(aLoader),
00209     mParserToUnblock(nsnull),
00210     mURI(aURI),
00211     mLineNumber(1),
00212     mSheet(aSheet),
00213     mNext(nsnull),
00214     mParentData(nsnull),
00215     mPendingChildren(0),
00216     mSyncLoad(aSyncLoad),
00217     mIsAgent(PR_TRUE),
00218     mIsLoading(PR_FALSE),
00219     mIsCancelled(PR_FALSE),
00220     mAllowUnsafeRules(aAllowUnsafeRules),
00221     mOwningElement(nsnull),
00222     mObserver(aObserver)
00223 {
00224 
00225   NS_PRECONDITION(mLoader, "Must have a loader!");
00226   NS_ADDREF(mLoader);
00227 }
00228 
00229 SheetLoadData::~SheetLoadData()
00230 {
00231   NS_RELEASE(mLoader);
00232   NS_IF_RELEASE(mParentData);
00233   NS_IF_RELEASE(mNext);
00234 }
00235 
00236 /*************************
00237  * Loader Implementation *
00238  *************************/
00239 
00240 // static
00241 nsCOMArray<nsICSSParser>* CSSLoaderImpl::gParsers = nsnull;
00242 
00243 CSSLoaderImpl::CSSLoaderImpl(void)
00244   : mDocument(nsnull), 
00245     mCaseSensitive(PR_FALSE),
00246     mEnabled(PR_TRUE), 
00247     mCompatMode(eCompatibility_FullStandards)
00248 {
00249 }
00250 
00251 CSSLoaderImpl::~CSSLoaderImpl(void)
00252 {
00253   NS_ASSERTION((!mLoadingDatas.IsInitialized()) || mLoadingDatas.Count() == 0,
00254                "How did we get destroyed when there are loading data?");
00255   NS_ASSERTION((!mPendingDatas.IsInitialized()) || mPendingDatas.Count() == 0,
00256                "How did we get destroyed when there are pending data?");
00257 }
00258 
00259 NS_IMPL_ISUPPORTS2(CSSLoaderImpl, nsICSSLoader, nsICSSLoader_MOZILLA_1_8_BRANCH)
00260 
00261 void
00262 CSSLoaderImpl::Shutdown()
00263 {
00264   delete gParsers;
00265   gParsers = nsnull;
00266 }
00267 
00268 NS_IMETHODIMP
00269 CSSLoaderImpl::Init(nsIDocument* aDocument)
00270 {
00271   NS_ASSERTION(! mDocument, "already initialized");
00272 
00273   if (! mDocument) {
00274     mDocument = aDocument;
00275     return NS_OK;
00276   }
00277   return NS_ERROR_ALREADY_INITIALIZED;
00278 }
00279 
00280 PR_STATIC_CALLBACK(PLDHashOperator)
00281 StartAlternateLoads(nsIURI *aKey, SheetLoadData* &aData, void* aClosure)
00282 {
00283   NS_STATIC_CAST(CSSLoaderImpl*,aClosure)->LoadSheet(aData, eSheetNeedsParser);
00284   return PL_DHASH_REMOVE;
00285 }
00286 
00287 NS_IMETHODIMP
00288 CSSLoaderImpl::DropDocumentReference(void)
00289 {
00290   mDocument = nsnull;
00291   // Flush out pending datas just so we don't leak by accident.  These
00292   // loads should short-circuit through the mDocument check in
00293   // LoadSheet and just end up in SheetComplete immediately
00294   if (mPendingDatas.IsInitialized())
00295     mPendingDatas.Enumerate(StartAlternateLoads, this);
00296   return NS_OK;
00297 }
00298 
00299 NS_IMETHODIMP
00300 CSSLoaderImpl::SetCaseSensitive(PRBool aCaseSensitive)
00301 {
00302   mCaseSensitive = aCaseSensitive;
00303   return NS_OK;
00304 }
00305 
00306 NS_IMETHODIMP
00307 CSSLoaderImpl::SetCompatibilityMode(nsCompatibility aCompatMode)
00308 {
00309   mCompatMode = aCompatMode;
00310   return NS_OK;
00311 }
00312 
00313 PR_STATIC_CALLBACK(PLDHashOperator)
00314 StartNonAlternates(nsIURI *aKey, SheetLoadData* &aData, void* aClosure)
00315 {
00316   NS_PRECONDITION(aData, "Must have a data");
00317   NS_PRECONDITION(aClosure, "Must have a loader");
00318   
00319   CSSLoaderImpl* loader = NS_STATIC_CAST(CSSLoaderImpl*, aClosure);
00320   if (loader->IsAlternate(aData->mTitle)) {
00321     return PL_DHASH_NEXT;
00322   }
00323 
00324   // Need to start the load
00325   loader->LoadSheet(aData, eSheetNeedsParser);
00326   return PL_DHASH_REMOVE;
00327 }
00328 
00329 NS_IMETHODIMP
00330 CSSLoaderImpl::SetPreferredSheet(const nsAString& aTitle)
00331 {
00332   mPreferredSheet = aTitle;
00333 
00334   // start any pending alternates that aren't alternates anymore
00335   if (mPendingDatas.IsInitialized())
00336     mPendingDatas.Enumerate(StartNonAlternates, this);
00337   return NS_OK;
00338 }
00339 
00340 NS_IMETHODIMP
00341 CSSLoaderImpl::GetPreferredSheet(nsAString& aTitle)
00342 {
00343   aTitle.Assign(mPreferredSheet);
00344   return NS_OK;
00345 }
00346 
00347 NS_IMETHODIMP
00348 CSSLoaderImpl::GetParserFor(nsICSSStyleSheet* aSheet, 
00349                             nsICSSParser** aParser)
00350 {
00351   NS_PRECONDITION(aParser, "Null out param");
00352 
00353   *aParser = nsnull;
00354 
00355   if (!gParsers) {
00356     gParsers = new nsCOMArray<nsICSSParser>;
00357     if (!gParsers) {
00358       return NS_ERROR_OUT_OF_MEMORY;
00359     }
00360   }
00361 
00362   PRInt32 count = gParsers->Count();
00363   if (0 < count--) {
00364     *aParser = gParsers->ObjectAt(count);
00365     NS_ADDREF(*aParser);
00366     gParsers->RemoveObjectAt(count);
00367   }
00368 
00369   nsresult result = NS_OK;
00370   if (! *aParser) {
00371     result = NS_NewCSSParser(aParser);
00372   }
00373   
00374   if (*aParser) {
00375     (*aParser)->SetCaseSensitive(mCaseSensitive);
00376     (*aParser)->SetQuirkMode(mCompatMode == eCompatibility_NavQuirks);
00377     if (aSheet) {
00378       (*aParser)->SetStyleSheet(aSheet);
00379     }
00380     (*aParser)->SetChildLoader(this);
00381   }
00382   return result;
00383 }
00384 
00385 NS_IMETHODIMP
00386 CSSLoaderImpl::RecycleParser(nsICSSParser* aParser)
00387 {
00388   NS_PRECONDITION(aParser, "Recycle only good parsers, please");
00389   NS_ENSURE_TRUE(gParsers, NS_ERROR_UNEXPECTED);
00390 
00391   if (!gParsers->AppendObject(aParser)) {
00392     return NS_ERROR_FAILURE;
00393   }
00394   
00395   return NS_OK;
00396 }
00397 
00398 static const char kCharsetSym[] = "@charset";
00399 
00400 static nsresult GetCharsetFromData(const unsigned char* aStyleSheetData,
00401                                    PRUint32 aDataLength,
00402                                    nsACString& aCharset)
00403 {
00404   aCharset.Truncate();
00405   if (aDataLength <= sizeof(kCharsetSym) - 1)
00406     return NS_ERROR_NOT_AVAILABLE;
00407   PRUint32 step = 1;
00408   PRUint32 pos = 0;
00409   // Determine the encoding type.  If we have a BOM, set aCharset to the
00410   // charset listed for that BOM in http://www.w3.org/TR/REC-xml#sec-guessing;
00411   // that way even if we don't have a valid @charset rule we can use the BOM to
00412   // get a reasonable charset.  If we do have an @charset rule, the string from
00413   // that will override this fallback setting of aCharset.
00414   if (*aStyleSheetData == 0x40 && *(aStyleSheetData+1) == 0x63 /* '@c' */ ) {
00415     // 1-byte ASCII-based encoding (ISO-8859-*, UTF-8, etc), no BOM
00416     step = 1;
00417     pos = 0;
00418   }
00419   else if (aStyleSheetData[0] == 0xEF &&
00420            aStyleSheetData[1] == 0xBB &&
00421            aStyleSheetData[2] == 0xBF) {
00422     // UTF-8 BOM
00423     step = 1;
00424     pos = 3;
00425     aCharset = "UTF-8";
00426   }
00427   // Check for a 4-byte encoding BOM before checking for a 2-byte one,
00428   // since the latter can be a proper subset of the former.
00429   else if (aStyleSheetData[0] == 0x00 &&
00430            aStyleSheetData[1] == 0x00 &&
00431            aStyleSheetData[2] == 0xFE &&
00432            aStyleSheetData[3] == 0xFF) {
00433     // big-endian 4-byte encoding BOM
00434     step = 4;
00435     pos = 7;
00436     aCharset = "UTF-32BE";
00437   }
00438   else if (aStyleSheetData[0] == 0xFF &&
00439            aStyleSheetData[1] == 0xFE &&
00440            aStyleSheetData[2] == 0x00 &&
00441            aStyleSheetData[3] == 0x00) {
00442     // little-endian 4-byte encoding BOM
00443     step = 4;
00444     pos = 4;
00445     aCharset = "UTF-32LE";
00446   }
00447   else if (aStyleSheetData[0] == 0x00 &&
00448            aStyleSheetData[1] == 0x00 &&
00449            aStyleSheetData[2] == 0xFF &&
00450            aStyleSheetData[3] == 0xFE) {
00451     // 4-byte encoding BOM in 2143 order
00452     NS_WARNING("Our unicode decoders aren't likely  to deal with this one");
00453     step = 4;
00454     pos = 6;
00455     aCharset = "UTF-32";
00456   }
00457   else if (aStyleSheetData[0] == 0xFE &&
00458            aStyleSheetData[1] == 0xFF &&
00459            aStyleSheetData[2] == 0x00 &&
00460            aStyleSheetData[3] == 0x00) {
00461     // 4-byte encoding BOM in 3412 order
00462     NS_WARNING("Our unicode decoders aren't likely  to deal with this one");
00463     step = 4;
00464     pos = 5;
00465     aCharset = "UTF-32";
00466   }
00467   else if (aStyleSheetData[0] == 0xFE && aStyleSheetData[1] == 0xFF) {
00468     // big-endian 2-byte encoding BOM
00469     step = 2;
00470     pos = 3;
00471     aCharset = "UTF-16BE";
00472   }
00473   else if (aStyleSheetData[0] == 0xFF && aStyleSheetData[1] == 0xFE) {
00474     // little-endian 2-byte encoding BOM
00475     step = 2;
00476     pos = 2;
00477     aCharset = "UTF-16LE";
00478   }
00479   else if (aStyleSheetData[0] == 0x00 &&
00480            aStyleSheetData[1] == 0x00 &&
00481            aStyleSheetData[2] == 0x00 &&
00482            aStyleSheetData[3] == 0x40) {
00483     // big-endian 4-byte encoding, no BOM
00484     step = 4;
00485     pos = 3;
00486   }
00487   else if (aStyleSheetData[0] == 0x40 &&
00488            aStyleSheetData[1] == 0x00 &&
00489            aStyleSheetData[2] == 0x00 &&
00490            aStyleSheetData[3] == 0x00) {
00491     // little-endian 4-byte encoding, no BOM
00492     step = 4;
00493     pos = 0;
00494   }
00495   else if (aStyleSheetData[0] == 0x00 &&
00496            aStyleSheetData[1] == 0x00 &&
00497            aStyleSheetData[2] == 0x40 &&
00498            aStyleSheetData[3] == 0x00) {
00499     // 4-byte encoding in 2143 order, no BOM
00500     step = 4;
00501     pos = 2;
00502   }
00503   else if (aStyleSheetData[0] == 0x00 &&
00504            aStyleSheetData[1] == 0x40 &&
00505            aStyleSheetData[2] == 0x00 &&
00506            aStyleSheetData[3] == 0x00) {
00507     // 4-byte encoding in 3412 order, no BOM
00508     step = 4;
00509     pos = 1;
00510   }
00511   else if (aStyleSheetData[0] == 0x00 &&
00512            aStyleSheetData[1] == 0x40 &&
00513            aStyleSheetData[2] == 0x00 &&
00514            aStyleSheetData[3] == 0x63) {
00515     // 2-byte big-endian encoding, no BOM
00516     step = 2;
00517     pos = 1;
00518   }
00519   else if (aStyleSheetData[0] == 0x40 &&
00520            aStyleSheetData[1] == 0x00 &&
00521            aStyleSheetData[2] == 0x63 &&
00522            aStyleSheetData[3] == 0x00) {
00523     // 2-byte little-endian encoding, no BOM
00524     step = 2;
00525     pos = 0;
00526   }
00527   else {
00528     // no clue what this is
00529     return NS_ERROR_UNEXPECTED;
00530   }
00531 
00532   PRUint32 index = 0;
00533   while (pos < aDataLength && index < sizeof(kCharsetSym) - 1) {
00534     if (aStyleSheetData[pos] != kCharsetSym[index]) {
00535       // If we have a guess as to the charset based on the BOM, then
00536       // we can just return NS_OK even if there is no valid @charset
00537       // rule.
00538       return aCharset.IsEmpty() ? NS_ERROR_NOT_AVAILABLE : NS_OK;
00539     }
00540     ++index;
00541     pos += step;
00542   }
00543 
00544   while (pos < aDataLength && nsCRT::IsAsciiSpace(aStyleSheetData[pos])) {
00545     pos += step;
00546   }
00547 
00548   if (pos >= aDataLength ||
00549       (aStyleSheetData[pos] != '"' && aStyleSheetData[pos] != '\'')) {
00550     return aCharset.IsEmpty() ? NS_ERROR_NOT_AVAILABLE : NS_OK;
00551   }
00552 
00553   char quote = aStyleSheetData[pos];
00554   pos += step;
00555   nsCAutoString charset;
00556   while (pos < aDataLength) {
00557     if (aStyleSheetData[pos] == '\\') {
00558       pos += step;
00559       if (pos >= aDataLength) {
00560         break;
00561       }          
00562     } else if (aStyleSheetData[pos] == quote) {
00563       break;
00564     }
00565     
00566     // casting to avoid ambiguities
00567     charset.Append(char(aStyleSheetData[pos]));
00568     pos += step;
00569   }
00570 
00571   // Check for the ending ';'
00572   pos += step;
00573   while (pos < aDataLength && nsCRT::IsAsciiSpace(aStyleSheetData[pos])) {
00574     pos += step;    
00575   }
00576 
00577   if (pos >= aDataLength || aStyleSheetData[pos] != ';') {
00578     return aCharset.IsEmpty() ? NS_ERROR_NOT_AVAILABLE : NS_OK;
00579   }
00580 
00581   aCharset = charset;
00582   return NS_OK;
00583 }
00584 
00585 NS_IMETHODIMP
00586 SheetLoadData::OnDetermineCharset(nsIUnicharStreamLoader* aLoader,
00587                                   nsISupports* aContext,
00588                                   const char* aData,
00589                                   PRUint32 aDataLength,
00590                                   nsACString& aCharset)
00591 {
00592   LOG_URI("SheetLoadData::OnDetermineCharset for '%s'", mURI);
00593   nsCOMPtr<nsIChannel> channel;
00594   nsresult result = aLoader->GetChannel(getter_AddRefs(channel));
00595   if (NS_FAILED(result))
00596     channel = nsnull;
00597 
00598   aCharset.Truncate();
00599 
00600   /*
00601    * First determine the charset (if one is indicated)
00602    * 1)  Check nsIChannel::contentCharset
00603    * 2)  Check @charset rules in the data
00604    * 3)  Check "charset" attribute of the <LINK> or <?xml-stylesheet?>
00605    *
00606    * If all these fail to give us a charset, fall back on our default
00607    * (parent sheet charset, document charset or ISO-8859-1 in that order)
00608    */
00609   if (channel) {
00610     channel->GetContentCharset(aCharset);
00611   }
00612 
00613   result = NS_ERROR_NOT_AVAILABLE;
00614 
00615 #ifdef PR_LOGGING
00616   if (! aCharset.IsEmpty()) {
00617     LOG(("  Setting from HTTP to: %s", PromiseFlatCString(aCharset).get()));
00618   }
00619 #endif
00620 
00621   if (aCharset.IsEmpty()) {
00622     //  We have no charset
00623     //  Try @charset rule and BOM
00624     result = GetCharsetFromData((const unsigned char*)aData,
00625                                 aDataLength, aCharset);
00626 #ifdef PR_LOGGING
00627     if (NS_SUCCEEDED(result)) {
00628       LOG(("  Setting from @charset rule or BOM: %s",
00629            PromiseFlatCString(aCharset).get()));
00630     }
00631 #endif
00632   }
00633 
00634   if (aCharset.IsEmpty()) {
00635     // Now try the charset on the <link> or processing instruction
00636     // that loaded us
00637     if (mOwningElement) {
00638       nsAutoString elementCharset;
00639       mOwningElement->GetCharset(elementCharset);
00640       CopyUCS2toASCII(elementCharset, aCharset);
00641 #ifdef PR_LOGGING
00642       if (! aCharset.IsEmpty()) {
00643         LOG(("  Setting from property on element: %s",
00644              PromiseFlatCString(aCharset).get()));
00645       }
00646 #endif
00647     }
00648   }
00649 
00650   if (aCharset.IsEmpty() && mParentData) {
00651     aCharset = mParentData->mCharset;
00652 #ifdef PR_LOGGING
00653     if (! aCharset.IsEmpty()) {
00654       LOG(("  Setting from parent sheet: %s",
00655            PromiseFlatCString(aCharset).get()));
00656     }
00657 #endif
00658   }
00659   
00660   if (aCharset.IsEmpty() && mLoader->mDocument) {
00661     // no useful data on charset.  Try the document charset.
00662     aCharset = mLoader->mDocument->GetDocumentCharacterSet();
00663 #ifdef PR_LOGGING
00664     LOG(("  Set from document: %s", PromiseFlatCString(aCharset).get()));
00665 #endif
00666   }      
00667 
00668   if (aCharset.IsEmpty()) {
00669     NS_WARNING("Unable to determine charset for sheet, using ISO-8859-1!");
00670 #ifdef PR_LOGGING
00671     LOG_WARN(("  Falling back to ISO-8859-1"));
00672 #endif
00673     aCharset.AssignLiteral("ISO-8859-1");
00674   }
00675 
00676   mCharset = aCharset;
00677   return NS_OK;
00678 }
00679 
00680 already_AddRefed<nsIURI>
00681 SheetLoadData::GetReferrerURI()
00682 {
00683   nsIURI* uri = nsnull;
00684   if (mParentData)
00685     mParentData->mSheet->GetSheetURI(&uri);
00686   if (!uri && mLoader->mDocument)
00687     NS_IF_ADDREF(uri = mLoader->mDocument->GetDocumentURI());
00688   return uri;
00689 }
00690 
00691 /*
00692  * Here we need to check that the load did not give us an http error
00693  * page and check the mimetype on the channel to make sure we're not
00694  * loading non-text/css data in standards mode.
00695  */
00696 NS_IMETHODIMP
00697 SheetLoadData::OnStreamComplete(nsIUnicharStreamLoader* aLoader,
00698                                 nsISupports* aContext,
00699                                 nsresult aStatus,
00700                                 nsIUnicharInputStream* aDataStream)
00701 {
00702   LOG(("SheetLoadData::OnStreamComplete"));
00703   NS_ASSERTION(!mLoader->mSyncCallback, "Synchronous callback from necko");
00704 
00705   if (mIsCancelled) {
00706     // Just return.  Don't call SheetComplete -- it's already been
00707     // called and calling it again will lead to an extra NS_RELEASE on
00708     // this data and a likely crash.
00709     return NS_OK;
00710   }
00711   
00712   if (!mLoader->mDocument && !mIsAgent) {
00713     // Sorry, we don't care about this load anymore
00714     LOG_WARN(("  No document and not agent sheet; dropping load"));
00715     mLoader->SheetComplete(this, PR_FALSE);
00716     return NS_OK;
00717   }
00718   
00719   nsCOMPtr<nsIChannel> channel;
00720   nsresult result = aLoader->GetChannel(getter_AddRefs(channel));
00721   if (NS_FAILED(result))
00722     channel = nsnull;
00723   
00724   nsCOMPtr<nsIURI> originalURI;
00725   nsCOMPtr<nsIURI> channelURI;
00726   if (channel) {
00727     channel->GetOriginalURI(getter_AddRefs(originalURI));
00728   
00729     // If the channel's original URI is "chrome:", we want that, since
00730     // the observer code in nsXULPrototypeCache depends on chrome stylesheets
00731     // having a chrome URI.  (Whether or not chrome stylesheets come through
00732     // this codepath seems nondeterministic.)
00733     // Otherwise we want the potentially-HTTP-redirected URI.
00734     PRBool isChrome;
00735     if (NS_FAILED(originalURI->SchemeIs("chrome", &isChrome)) || !isChrome) {
00736       channel->GetURI(getter_AddRefs(channelURI));
00737     } else {
00738       channelURI = originalURI;
00739     }
00740   }
00741   
00742 #ifdef MOZ_TIMELINE
00743   NS_TIMELINE_OUTDENT();
00744   NS_TIMELINE_MARK_CHANNEL("SheetLoadData::OnStreamComplete(%s)", channel);
00745 #endif // MOZ_TIMELINE
00746   
00747   // If it's an HTTP channel, we want to make sure this is not an
00748   // error document we got.
00749   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
00750   if (httpChannel) {
00751     PRBool requestSucceeded;
00752     result = httpChannel->GetRequestSucceeded(&requestSucceeded);
00753     if (NS_SUCCEEDED(result) && !requestSucceeded) {
00754       LOG(("  Load returned an error page"));
00755       mLoader->SheetComplete(this, PR_FALSE);
00756       return NS_OK;
00757     }
00758   }
00759 
00760   if (aDataStream) {
00761     nsCAutoString contentType;
00762     if (channel) {
00763       channel->GetContentType(contentType);
00764     }
00765     
00766     PRBool validType = contentType.EqualsLiteral("text/css") ||
00767       contentType.EqualsLiteral(UNKNOWN_CONTENT_TYPE) ||
00768       contentType.IsEmpty();
00769                                           
00770     if (!validType) {
00771       nsCAutoString spec;
00772       if (channelURI) {
00773         channelURI->GetSpec(spec);
00774       }
00775 
00776       const nsAFlatString& specUTF16 = NS_ConvertUTF8toUTF16(spec);
00777       const nsAFlatString& ctypeUTF16 = NS_ConvertASCIItoUTF16(contentType);
00778       const PRUnichar *strings[] = { specUTF16.get(), ctypeUTF16.get() };
00779 
00780       const char *errorMessage;
00781       PRUint32 errorFlag;
00782 
00783       if (mLoader->mCompatMode == eCompatibility_NavQuirks) {
00784         errorMessage = "MimeNotCssWarn";
00785         errorFlag = nsIScriptError::warningFlag;
00786       } else {
00787         // Drop the data stream so that we do not load it
00788         aDataStream = nsnull;
00789 
00790         errorMessage = "MimeNotCss";
00791         errorFlag = nsIScriptError::errorFlag;
00792       }
00793       nsCOMPtr<nsIURI> referrer = GetReferrerURI();
00794       nsContentUtils::ReportToConsole(nsContentUtils::eCSS_PROPERTIES,
00795                                       errorMessage,
00796                                       strings, NS_ARRAY_LENGTH(strings),
00797                                       referrer, EmptyString(), 0, 0, errorFlag,
00798                                       "CSS Loader");
00799     }
00800   }
00801   
00802   if (NS_FAILED(aStatus) || !aDataStream) {
00803     LOG_WARN(("  Load failed: status %u, data stream %p",
00804               aStatus, aDataStream));
00805     mLoader->SheetComplete(this, PR_FALSE);
00806     return NS_OK;
00807   }
00808 
00809   if (channelURI) {
00810     // Enough to set the URIs on mSheet, since any sibling datas we have share
00811     // the same mInner as mSheet and will thus get the same URI.
00812     nsCOMPtr<nsICSSStyleSheet_MOZILLA_1_8_BRANCH> sheet =
00813       do_QueryInterface(mSheet);
00814     sheet->SetURIs18(channelURI, originalURI, channelURI);
00815   }
00816   
00817   PRBool completed;
00818   return mLoader->ParseSheet(aDataStream, this, completed);
00819 }
00820 
00821 #ifdef MOZ_XUL
00822 static PRBool IsChromeURI(nsIURI* aURI)
00823 {
00824   NS_ASSERTION(aURI, "Have to pass in a URI");
00825   PRBool isChrome = PR_FALSE;
00826   aURI->SchemeIs("chrome", &isChrome);
00827   return isChrome;
00828 }
00829 #endif
00830 
00831 PRBool
00832 CSSLoaderImpl::IsAlternate(const nsAString& aTitle)
00833 {
00834   if (!aTitle.IsEmpty()) {
00835     return PRBool(! aTitle.Equals(mPreferredSheet, nsCaseInsensitiveStringComparator()));
00836   }
00837   return PR_FALSE;
00838 }
00839 
00849 nsresult
00850 CSSLoaderImpl::CheckLoadAllowed(nsIURI* aSourceURI,
00851                                 nsIURI* aTargetURI,
00852                                 nsISupports* aContext)
00853 {
00854   LOG(("CSSLoaderImpl::CheckLoadAllowed"));
00855   
00856   // Check with the security manager
00857   nsIScriptSecurityManager *secMan = nsContentUtils::GetSecurityManager();
00858   nsresult rv = secMan->CheckLoadURI(aSourceURI, aTargetURI,
00859                                      nsIScriptSecurityManager::ALLOW_CHROME);
00860   if (NS_FAILED(rv)) { // failure is normal here; don't warn
00861     return rv;
00862   }
00863 
00864   LOG(("  Passed security check"));
00865 
00866   // Check with content policy
00867 
00868   PRInt16 shouldLoad = nsIContentPolicy::ACCEPT;
00869   rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_STYLESHEET,
00870                                  aTargetURI,
00871                                  aSourceURI,
00872                                  aContext,
00873                                  NS_LITERAL_CSTRING("text/css"),
00874                                  nsnull,                        //extra param
00875                                  &shouldLoad);
00876 
00877   if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
00878     LOG(("  Load blocked by content policy"));
00879     return NS_ERROR_CONTENT_BLOCKED;
00880   }
00881 
00882   return rv;
00883 }
00884 
00894 nsresult
00895 CSSLoaderImpl::CreateSheet(nsIURI* aURI,
00896                            nsIContent* aLinkingContent,
00897                            PRBool aSyncLoad,
00898                            StyleSheetState& aSheetState,
00899                            nsICSSStyleSheet** aSheet)
00900 {
00901   LOG(("CSSLoaderImpl::CreateSheet"));
00902   NS_PRECONDITION(aSheet, "Null out param!");
00903 
00904   NS_ENSURE_TRUE((mCompleteSheets.IsInitialized() || mCompleteSheets.Init()) &&
00905                    (mLoadingDatas.IsInitialized() || mLoadingDatas.Init()) &&
00906                    (mPendingDatas.IsInitialized() || mPendingDatas.Init()),
00907                  NS_ERROR_OUT_OF_MEMORY);
00908   
00909   nsresult rv = NS_OK;
00910   *aSheet = nsnull;
00911   aSheetState = eSheetStateUnknown;
00912   
00913   if (aURI) {
00914     aSheetState = eSheetComplete;
00915     nsCOMPtr<nsICSSStyleSheet> sheet;
00916 
00917     // First, the XUL cache
00918 #ifdef MOZ_XUL
00919     if (IsChromeURI(aURI)) {
00920       nsCOMPtr<nsIXULPrototypeCache> cache(do_GetService("@mozilla.org/xul/xul-prototype-cache;1"));
00921       if (cache) {
00922         PRBool enabled;
00923         cache->GetEnabled(&enabled);
00924         if (enabled) {
00925           cache->GetStyleSheet(aURI, getter_AddRefs(sheet));
00926           LOG(("  From XUL cache: %p", sheet.get()));
00927         }
00928       }
00929     }
00930 #endif
00931 
00932     if (!sheet) {
00933       // Then complete sheets
00934       mCompleteSheets.Get(aURI, getter_AddRefs(sheet));
00935       LOG(("  From completed: %p", sheet.get()));
00936     
00937       // Then loading sheets
00938       if (!sheet && !aSyncLoad) {
00939         aSheetState = eSheetLoading;
00940         SheetLoadData* loadData = nsnull;
00941         mLoadingDatas.Get(aURI, &loadData);
00942         if (loadData) {
00943           sheet = loadData->mSheet;
00944           LOG(("  From loading: %p", sheet.get()));
00945         }
00946 
00947         // Then alternate sheets
00948         if (!sheet) {
00949           aSheetState = eSheetPending;
00950           SheetLoadData* loadData = nsnull;
00951           mPendingDatas.Get(aURI, &loadData);
00952           if (loadData) {
00953             sheet = loadData->mSheet;
00954             LOG(("  From pending: %p", sheet.get()));
00955           }
00956         }
00957       }
00958     }
00959 
00960     if (sheet) {
00961       // We can use this cached sheet if it's either incomplete or unmodified
00962       PRBool modified = PR_TRUE;
00963       sheet->IsModified(&modified);
00964       PRBool complete = PR_FALSE;
00965       sheet->GetComplete(complete);
00966       if (!modified || !complete) {
00967         // Proceed on failures; at worst we'll try to create one below
00968         sheet->Clone(nsnull, nsnull, nsnull, nsnull, aSheet);
00969         NS_ASSERTION(complete || aSheetState != eSheetComplete,
00970                      "Sheet thinks it's not complete while we think it is");
00971       }
00972     }
00973   }
00974 
00975   if (!*aSheet) {
00976     aSheetState = eSheetNeedsParser;
00977     nsIURI *sheetURI;
00978     nsCOMPtr<nsIURI> baseURI;
00979     nsIURI* originalURI;
00980     if (!aURI) {
00981       // Inline style.  Use the document's base URL so that @import in
00982       // the inline sheet picks up the right base.
00983       NS_ASSERTION(aLinkingContent, "Inline stylesheet without linking content?");
00984       baseURI = aLinkingContent->GetBaseURI();
00985       sheetURI = aLinkingContent->GetDocument()->GetDocumentURI();
00986       originalURI = sheetURI;
00987     } else {
00988       baseURI = aURI;
00989       sheetURI = aURI;
00990       originalURI = aURI;
00991     }      
00992 
00993     rv = NS_NewCSSStyleSheet(aSheet);
00994     NS_ENSURE_SUCCESS(rv, rv);
00995 
00996     nsCOMPtr<nsICSSStyleSheet_MOZILLA_1_8_BRANCH> sheet =
00997       do_QueryInterface(*aSheet);
00998     sheet->SetURIs18(sheetURI, originalURI, baseURI);
00999   }
01000 
01001   NS_ASSERTION(*aSheet, "We should have a sheet by now!");
01002   NS_ASSERTION(aSheetState != eSheetStateUnknown, "Have to set a state!");
01003   LOG(("  State: %s", gStateStrings[aSheetState]));
01004   
01005   return NS_OK;
01006 }
01007 
01012 nsresult
01013 CSSLoaderImpl::PrepareSheet(nsICSSStyleSheet* aSheet,
01014                             const nsSubstring& aTitle,
01015                             const nsSubstring& aMediaString,
01016                             nsMediaList* aMediaList)
01017 {
01018   NS_PRECONDITION(aSheet, "Must have a sheet!");
01019 
01020   nsresult rv;
01021   nsCOMPtr<nsMediaList> mediaList(aMediaList);
01022 
01023   if (!aMediaString.IsEmpty()) {
01024     NS_ASSERTION(!aMediaList,
01025                  "must not provide both aMediaString and aMediaList");
01026     mediaList = new nsMediaList();
01027     NS_ENSURE_TRUE(mediaList, NS_ERROR_OUT_OF_MEMORY);
01028     nsCOMPtr<nsICSSParser> mediumParser;
01029     nsresult rv = GetParserFor(nsnull, getter_AddRefs(mediumParser));
01030     NS_ENSURE_SUCCESS(rv, rv);
01031     // We have aMediaString only when linked from link elements, style
01032     // elements, or PIs, so pass PR_TRUE.
01033     rv = mediumParser->ParseMediaList(aMediaString, nsnull, 0, mediaList,
01034                                       PR_TRUE);
01035     NS_ENSURE_SUCCESS(rv, rv);
01036     RecycleParser(mediumParser);
01037   }
01038 
01039   rv = aSheet->SetMedia(mediaList);
01040   NS_ENSURE_SUCCESS(rv, rv);
01041 
01042   aSheet->SetTitle(aTitle);
01043   aSheet->SetEnabled(! IsAlternate(aTitle));
01044   return NS_OK;    
01045 }
01046 
01060 nsresult
01061 CSSLoaderImpl::InsertSheetInDoc(nsICSSStyleSheet* aSheet,
01062                                 nsIContent* aLinkingContent,
01063                                 nsIDocument* aDocument)
01064 {
01065   LOG(("CSSLoaderImpl::InsertSheetInDoc"));
01066   NS_PRECONDITION(aSheet, "Nothing to insert");
01067   NS_PRECONDITION(aDocument, "Must have a document to insert into");
01068 
01069   // all nodes that link in sheets should be implementing nsIDOM3Node
01070   nsresult rv = NS_OK;
01071   nsCOMPtr<nsIDOM3Node> linkingNode = do_QueryInterface(aLinkingContent);
01072   NS_ASSERTION(linkingNode || !aLinkingContent,
01073                "Need to implement nsIDOM3Node to get insertion order right");
01074 
01075   // XXX Need to cancel pending sheet loads for this element, if any
01076 
01077   PRInt32 sheetCount = aDocument->GetNumberOfStyleSheets();
01078 
01079   /*
01080    * Start the walk at the _end_ of the list, since in the typical
01081    * case we'll just want to append anyway.  We want to break out of
01082    * the loop when insertionPoint points to just before the index we
01083    * want to insert at.  In other words, when we leave the loop
01084    * insertionPoint is the index of the stylesheet that immediately
01085    * precedes the one we're inserting.
01086    */
01087   PRInt32 insertionPoint;
01088   for (insertionPoint = sheetCount - 1; insertionPoint >= 0; --insertionPoint) {
01089     nsIStyleSheet *curSheet = aDocument->GetStyleSheetAt(insertionPoint);
01090     NS_ASSERTION(curSheet, "There must be a sheet here!");
01091     nsCOMPtr<nsIDOMStyleSheet> domSheet = do_QueryInterface(curSheet);
01092     NS_ASSERTION(domSheet, "All the \"normal\" sheets implement nsIDOMStyleSheet");
01093     nsCOMPtr<nsIDOMNode> sheetOwner;
01094     domSheet->GetOwnerNode(getter_AddRefs(sheetOwner));
01095     if (sheetOwner && !linkingNode) {
01096       // Keep moving; all sheets with a sheetOwner come after all
01097       // sheets without a linkingNode 
01098       continue;
01099     }
01100 
01101     if (!sheetOwner) {
01102       // Aha!  The current sheet has no sheet owner, so we want to
01103       // insert after it no matter whether we have a linkingNode
01104       break;
01105     }
01106 
01107     // Have to compare
01108     PRUint16 comparisonFlags = 0;
01109     rv = linkingNode->CompareDocumentPosition(sheetOwner, &comparisonFlags);
01110     // If we can't get the order right, just bail...
01111     NS_ENSURE_SUCCESS(rv, rv);
01112     NS_ASSERTION(!(comparisonFlags & nsIDOM3Node::DOCUMENT_POSITION_DISCONNECTED),
01113                  "Why are these elements in different documents?");
01114 #ifdef DEBUG
01115     {
01116       PRBool sameNode = PR_FALSE;
01117       linkingNode->IsSameNode(sheetOwner, &sameNode);
01118       NS_ASSERTION(!sameNode, "Why do we still have our old sheet?");
01119     }
01120 #endif // DEBUG
01121     if (comparisonFlags & nsIDOM3Node::DOCUMENT_POSITION_PRECEDING) {
01122       // The current sheet comes before us, and it better be the first
01123       // such, because now we break
01124       break;
01125     }
01126   }
01127 
01128   ++insertionPoint; // adjust the index to the spot we want to insert in
01129   
01130   // XXX <meta> elements do not implement nsIStyleSheetLinkingElement;
01131   // need to fix this for them to be ordered correctly.
01132   nsCOMPtr<nsIStyleSheetLinkingElement>
01133     linkingElement = do_QueryInterface(aLinkingContent);
01134   if (linkingElement) {
01135     linkingElement->SetStyleSheet(aSheet); // This sets the ownerNode on the sheet
01136   }
01137 
01138   aDocument->BeginUpdate(UPDATE_STYLE);
01139   aDocument->InsertStyleSheetAt(aSheet, insertionPoint);
01140   aDocument->EndUpdate(UPDATE_STYLE);
01141   LOG(("  Inserting into document at position %d", insertionPoint));
01142 
01143   return NS_OK;
01144 }
01145 
01155 nsresult
01156 CSSLoaderImpl::InsertChildSheet(nsICSSStyleSheet* aSheet,
01157                                 nsICSSStyleSheet* aParentSheet,
01158                                 nsICSSImportRule* aParentRule)
01159 {
01160   LOG(("CSSLoaderImpl::InsertChildSheet"));
01161   NS_PRECONDITION(aSheet, "Nothing to insert");
01162   NS_PRECONDITION(aParentSheet, "Need a parent to insert into");
01163   NS_PRECONDITION(aParentSheet, "How did we get imported?");
01164 
01165   // child sheets should always start out enabled, even if they got
01166   // cloned off of top-level sheets which were disabled
01167   aSheet->SetEnabled(PR_TRUE);
01168   
01169   aParentSheet->AppendStyleSheet(aSheet);
01170   aParentRule->SetSheet(aSheet); // This sets the ownerRule on the sheet
01171 
01172   LOG(("  Inserting into parent sheet"));
01173   //  LOG(("  Inserting into parent sheet at position %d", insertionPoint));
01174 
01175   return NS_OK;
01176 }
01177 
01186 nsresult
01187 CSSLoaderImpl::LoadSheet(SheetLoadData* aLoadData, StyleSheetState aSheetState)
01188 {
01189   LOG(("CSSLoaderImpl::LoadSheet"));
01190   NS_PRECONDITION(aLoadData, "Need a load data");
01191   NS_PRECONDITION(aLoadData->mURI, "Need a URI to load");
01192   NS_PRECONDITION(aLoadData->mSheet, "Need a sheet to load into");
01193   NS_PRECONDITION(aSheetState != eSheetComplete, "Why bother?");
01194   NS_ASSERTION(mLoadingDatas.IsInitialized(), "mLoadingDatas should be initialized by now.");
01195 
01196   LOG_URI("  Load from: '%s'", aLoadData->mURI);
01197   
01198   nsresult rv = NS_OK;  
01199 
01200   if (!mDocument && !aLoadData->mIsAgent) {
01201     // No point starting the load; just release all the data and such.
01202     LOG_WARN(("  No document and not agent sheet; pre-dropping load"));
01203     SheetComplete(aLoadData, PR_FALSE);
01204     return NS_OK;
01205   }
01206 
01207   if (aLoadData->mSyncLoad) {
01208     LOG(("  Synchronous load"));
01209     NS_ASSERTION(aSheetState == eSheetNeedsParser,
01210                  "Sync loads can't reuse existing async loads");
01211 
01212     // Just load it
01213     nsCOMPtr<nsIInputStream> stream;
01214     rv = NS_OpenURI(getter_AddRefs(stream), aLoadData->mURI);
01215     if (NS_FAILED(rv)) {
01216       LOG_ERROR(("  Failed to open URI synchronously"));
01217       SheetComplete(aLoadData, PR_FALSE);
01218       return rv;
01219     }
01220 
01221     nsCOMPtr<nsIConverterInputStream> converterStream = 
01222       do_CreateInstance("@mozilla.org/intl/converter-input-stream;1", &rv);
01223     
01224     if (NS_FAILED(rv)) {
01225       LOG_ERROR(("  Failed to create converter stream"));
01226       SheetComplete(aLoadData, PR_FALSE);
01227       return rv;
01228     }
01229 
01230     // This forces UA sheets to be UTF-8.  We should really look for
01231     // @charset rules here via ReadSegments on the raw stream...
01232 
01233     // 8192 is a nice magic number that happens to be what a lot of
01234     // other things use for buffer sizes.
01235     rv = converterStream->Init(stream, "UTF-8",
01236                                8192,
01237                                nsIConverterInputStream::
01238                                     DEFAULT_REPLACEMENT_CHARACTER);
01239     
01240     if (NS_FAILED(rv)) {
01241       LOG_ERROR(("  Failed to initialize converter stream"));
01242       SheetComplete(aLoadData, PR_FALSE);
01243       return rv;
01244     }
01245 
01246     PRBool completed;
01247     rv = ParseSheet(converterStream, aLoadData, completed);
01248     NS_ASSERTION(completed, "sync load did not complete");
01249     return rv;
01250   }
01251 
01252   SheetLoadData* existingData = nsnull;
01253 
01254   if (aSheetState == eSheetLoading) {
01255     mLoadingDatas.Get(aLoadData->mURI, &existingData);
01256     NS_ASSERTION(existingData, "CreateSheet lied about the state");
01257   }
01258   else if (aSheetState == eSheetPending){
01259     mPendingDatas.Get(aLoadData->mURI, &existingData);
01260     NS_ASSERTION(existingData, "CreateSheet lied about the state");
01261   }
01262   
01263   if (existingData) {
01264     LOG(("  Glomming on to existing load"));
01265     SheetLoadData* data = existingData;
01266     while (data->mNext) {
01267       data = data->mNext;
01268     }
01269     data->mNext = aLoadData; // transfer ownership
01270     if (aSheetState == eSheetPending && !IsAlternate(aLoadData->mTitle)) {
01271       // Kick the load off; someone cares about it right away
01272 
01273 #ifdef DEBUG
01274       SheetLoadData* removedData;
01275       NS_ASSERTION(mPendingDatas.Get(aLoadData->mURI, &removedData) &&
01276                    removedData == existingData,
01277                    "Bad pending table.");
01278 #endif
01279 
01280       mPendingDatas.Remove(aLoadData->mURI);
01281 
01282       LOG(("  Forcing load of pending data"));
01283       LoadSheet(existingData, eSheetNeedsParser);
01284     }
01285     // All done here; once the load completes we'll be marked complete
01286     // automatically
01287     return NS_OK;
01288   }
01289 
01290 #ifdef DEBUG
01291   mSyncCallback = PR_TRUE;
01292 #endif
01293   nsCOMPtr<nsILoadGroup> loadGroup;
01294   if (mDocument) {
01295     loadGroup = mDocument->GetDocumentLoadGroup();
01296     NS_ASSERTION(loadGroup,
01297                  "No loadgroup for stylesheet; onload will fire early");
01298   }
01299 
01300 #ifdef MOZ_TIMELINE
01301   NS_TIMELINE_MARK_URI("Loading style sheet: %s", aLoadData->mURI);
01302   NS_TIMELINE_INDENT();
01303 #endif
01304   
01305   nsCOMPtr<nsIChannel> channel;
01306   rv = NS_NewChannel(getter_AddRefs(channel),
01307                      aLoadData->mURI, nsnull, loadGroup,
01308                      nsnull, nsIChannel::LOAD_NORMAL);
01309   
01310   if (NS_FAILED(rv)) {
01311     LOG_ERROR(("  Failed to create channel"));
01312     SheetComplete(aLoadData, PR_FALSE);
01313     return rv;
01314   }
01315 
01316   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
01317   if (httpChannel) {
01318     // send a minimal Accept header for text/css
01319     httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
01320                                   NS_LITERAL_CSTRING("text/css,*/*;q=0.1"),
01321                                   PR_FALSE);
01322     nsCOMPtr<nsIURI> referrerURI = aLoadData->GetReferrerURI();
01323     if (referrerURI)
01324       httpChannel->SetReferrer(referrerURI);
01325   }
01326 
01327   // Now tell the channel we expect text/css data back....  We do
01328   // this before opening it, so it's only treated as a hint.
01329   channel->SetContentType(NS_LITERAL_CSTRING("text/css"));
01330 
01331   // We don't have to hold on to the stream loader.  The ownership
01332   // model is: Necko owns the stream loader, which owns the load data,
01333   // which owns us
01334   nsCOMPtr<nsIUnicharStreamLoader> streamLoader;
01335   rv = NS_NewUnicharStreamLoader(getter_AddRefs(streamLoader),
01336                                  channel, aLoadData);
01337 
01338 #ifdef DEBUG
01339   mSyncCallback = PR_FALSE;
01340 #endif
01341 
01342   if (NS_FAILED(rv)) {
01343     LOG_ERROR(("  Failed to create stream loader"));
01344     SheetComplete(aLoadData, PR_FALSE);
01345     return rv;
01346   }
01347 
01348   mLoadingDatas.Put(aLoadData->mURI, aLoadData);
01349   aLoadData->mIsLoading = PR_TRUE;
01350   
01351   return NS_OK;
01352 }
01353 
01360 nsresult
01361 CSSLoaderImpl::ParseSheet(nsIUnicharInputStream* aStream,
01362                           SheetLoadData* aLoadData,
01363                           PRBool& aCompleted)
01364 {
01365   LOG(("CSSLoaderImpl::ParseSheet"));
01366   NS_PRECONDITION(aStream, "Must have data to parse");
01367   NS_PRECONDITION(aLoadData, "Must have load data");
01368   NS_PRECONDITION(aLoadData->mSheet, "Must have sheet to parse into");
01369 
01370   aCompleted = PR_FALSE;
01371 
01372   nsCOMPtr<nsICSSParser> parser;
01373   nsresult rv = GetParserFor(aLoadData->mSheet, getter_AddRefs(parser));
01374   if (NS_FAILED(rv)) {
01375     LOG_ERROR(("  Failed to get CSS parser"));
01376     SheetComplete(aLoadData, PR_FALSE);
01377     return rv;
01378   }
01379 
01380   // The parser insists on passing out a strong ref to the sheet it
01381   // parsed.  We don't care.
01382   nsCOMPtr<nsICSSStyleSheet> dummySheet;
01383   // Push our load data on the stack so any kids can pick it up
01384   mParsingDatas.AppendElement(aLoadData);
01385   nsCOMPtr<nsIURI> sheetURI, baseURI;
01386   aLoadData->mSheet->GetSheetURI(getter_AddRefs(sheetURI));
01387   aLoadData->mSheet->GetBaseURI(getter_AddRefs(baseURI));
01388   nsCOMPtr<nsICSSParser_MOZILLA_1_8_BRANCH> parser18 = do_QueryInterface(parser);
01389   rv = parser18->Parse(aStream, sheetURI, baseURI, aLoadData->mLineNumber,
01390                        aLoadData->mAllowUnsafeRules,
01391                        *getter_AddRefs(dummySheet));
01392   mParsingDatas.RemoveElementAt(mParsingDatas.Count() - 1);
01393   RecycleParser(parser);
01394 
01395   NS_ASSERTION(aLoadData->mPendingChildren == 0 || !aLoadData->mSyncLoad,
01396                "Sync load has leftover pending children!");
01397   
01398   if (aLoadData->mPendingChildren == 0) {
01399     LOG(("  No pending kids from parse"));
01400     aCompleted = PR_TRUE;
01401     if (!aLoadData->mURI) {
01402       // inline sheet and we're all done with no kids, so we will not
01403       // be blocking the parser
01404       LOG(("  Inline sheet with no pending kids; nulling out parser"));
01405       aLoadData->mParserToUnblock = nsnull;
01406     }
01407     SheetComplete(aLoadData, PR_TRUE);
01408   }
01409   // Otherwise, the children are holding strong refs to the data and
01410   // will call SheetComplete() on it when they complete.
01411   
01412   return NS_OK;
01413 }
01414 
01423 void
01424 CSSLoaderImpl::SheetComplete(SheetLoadData* aLoadData, PRBool aSucceeded)
01425 {
01426   LOG(("CSSLoaderImpl::SheetComplete"));
01427   NS_PRECONDITION(aLoadData, "Must have a load data!");
01428   NS_PRECONDITION(aLoadData->mSheet, "Must have a sheet");
01429   NS_ASSERTION(mLoadingDatas.IsInitialized(),"mLoadingDatas should be initialized by now.");
01430 
01431   LOG(("Load completed: %d", aSucceeded));
01432 
01433   // Twiddle the hashtables
01434   if (aLoadData->mURI) {
01435     LOG_URI("  Finished loading: '%s'", aLoadData->mURI);
01436     // Remove the data from the list of loading datas
01437     if (aLoadData->mIsLoading) {
01438 #ifdef DEBUG
01439       SheetLoadData *loadingData;
01440       NS_ASSERTION(mLoadingDatas.Get(aLoadData->mURI, &loadingData) &&
01441                    loadingData == aLoadData,
01442                    "Bad loading table");
01443 #endif
01444 
01445       mLoadingDatas.Remove(aLoadData->mURI);
01446       aLoadData->mIsLoading = PR_FALSE;
01447     }
01448   }
01449   
01450 
01451   // This is a mess.  If we have a document.write() that writes out
01452   // two <link> elements pointing to the same url, we will actually
01453   // end up blocking the same parser twice.  This seems very wrong --
01454   // if we blocked it the first time, why is more stuff getting
01455   // written??  In any case, we only want to unblock it once.
01456   // Otherwise we get icky things like crashes in layout...  We need
01457   // to stop blocking the parser.  We really do.
01458   PRBool seenParser = PR_FALSE;
01459 
01460   // Go through and deal with the whole linked list.
01461   SheetLoadData* data = aLoadData;
01462   while (data) {
01463 
01464     data->mSheet->SetModified(PR_FALSE); // it's clean
01465     data->mSheet->SetComplete();
01466     if (data->mObserver) {
01467       data->mObserver->StyleSheetLoaded(data->mSheet, PR_TRUE);
01468     }
01469                            
01470     if (data->mParserToUnblock) {
01471       LOG(("Parser to unblock: %p", data->mParserToUnblock.get()));
01472       if (!seenParser) {
01473         LOG(("Unblocking parser: %p", data->mParserToUnblock.get()));
01474         seenParser = PR_TRUE;
01475         data->mParserToUnblock->ContinueParsing();
01476       }
01477       data->mParserToUnblock = nsnull; // drop the ref, just in case
01478     }
01479 
01480     NS_ASSERTION(!data->mParentData ||
01481                  data->mParentData->mPendingChildren != 0,
01482                  "Broken pending child count on our parent");
01483 
01484     // If we have a parent, our parent is no longer being parsed, and
01485     // we are the last pending child, then our load completion
01486     // completes the parent too.  Note that the parent _can_ still be
01487     // being parsed (eg if the child (us) failed to open the channel
01488     // or some such).
01489     if (data->mParentData &&
01490         --(data->mParentData->mPendingChildren) == 0 &&
01491         mParsingDatas.IndexOf(data->mParentData) == -1) {
01492       SheetComplete(data->mParentData, aSucceeded);
01493     }
01494     
01495     data = data->mNext;
01496   }
01497 
01498   // Now that it's marked complete, put the sheet in our cache
01499   if (aSucceeded && aLoadData->mURI) {
01500 #ifdef MOZ_XUL
01501     if (IsChromeURI(aLoadData->mURI)) {
01502       nsCOMPtr<nsIXULPrototypeCache> cache(do_GetService("@mozilla.org/xul/xul-prototype-cache;1"));
01503       if (cache) {
01504         PRBool enabled;
01505         cache->GetEnabled(&enabled);
01506         if (enabled) {
01507           nsCOMPtr<nsICSSStyleSheet> sheet;
01508           cache->GetStyleSheet(aLoadData->mURI, getter_AddRefs(sheet));
01509           if (!sheet) {
01510             LOG(("  Putting sheet in XUL prototype cache"));
01511             cache->PutStyleSheet(aLoadData->mSheet);
01512           }
01513         }
01514       }
01515     }
01516     else {
01517 #endif
01518       mCompleteSheets.Put(aLoadData->mURI, aLoadData->mSheet);
01519 #ifdef MOZ_XUL
01520     }
01521 #endif
01522   }
01523 
01524   NS_RELEASE(aLoadData);  // this will release parents and siblings and all that
01525   if (mLoadingDatas.Count() == 0 && mPendingDatas.Count() > 0) {
01526     LOG(("  No more loading sheets; starting alternates"));
01527     mPendingDatas.Enumerate(StartAlternateLoads, this);
01528   }
01529 }
01530 
01531 NS_IMETHODIMP
01532 CSSLoaderImpl::LoadInlineStyle(nsIContent* aElement,
01533                                nsIUnicharInputStream* aStream, 
01534                                PRUint32 aLineNumber,
01535                                const nsSubstring& aTitle, 
01536                                const nsSubstring& aMedia, 
01537                                nsIParser* aParserToUnblock,
01538                                PRBool& aCompleted,
01539                                nsICSSLoaderObserver* aObserver)
01540 {
01541   LOG(("CSSLoaderImpl::LoadInlineStyle"));
01542   NS_PRECONDITION(aStream, "Must have a stream to parse!");
01543   NS_ASSERTION(mParsingDatas.Count() == 0, "We're in the middle of a parse?");
01544 
01545   aCompleted = PR_TRUE;
01546 
01547   if (!mEnabled) {
01548     LOG_WARN(("  Not enabled"));
01549     return NS_ERROR_NOT_AVAILABLE;
01550   }
01551 
01552   NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_INITIALIZED);
01553 
01554   nsCOMPtr<nsIStyleSheetLinkingElement> owningElement(do_QueryInterface(aElement));
01555   NS_ASSERTION(owningElement, "Element is not a style linking element!");
01556   
01557   StyleSheetState state;
01558   nsCOMPtr<nsICSSStyleSheet> sheet;
01559   nsresult rv = CreateSheet(nsnull, aElement, PR_FALSE, state,
01560                             getter_AddRefs(sheet));
01561   NS_ENSURE_SUCCESS(rv, rv);
01562   NS_ASSERTION(state == eSheetNeedsParser,
01563                "Inline sheets should not be cached");
01564 
01565   rv = PrepareSheet(sheet, aTitle, aMedia, nsnull);
01566   NS_ENSURE_SUCCESS(rv, rv);
01567   
01568   rv = InsertSheetInDoc(sheet, aElement, mDocument);
01569   NS_ENSURE_SUCCESS(rv, rv);
01570   
01571   SheetLoadData* data = new SheetLoadData(this, aTitle, aParserToUnblock,
01572                                           nsnull, sheet, owningElement,
01573                                           aObserver);
01574 
01575   if (!data) {
01576     sheet->SetComplete();
01577     return NS_ERROR_OUT_OF_MEMORY;
01578   }
01579 
01580   NS_ADDREF(data);
01581   data->mLineNumber = aLineNumber;
01582   // Parse completion releases the load data
01583   return ParseSheet(aStream, data, aCompleted);
01584 }        
01585 
01586 NS_IMETHODIMP
01587 CSSLoaderImpl::LoadStyleLink(nsIContent* aElement,
01588                              nsIURI* aURL, 
01589                              const nsSubstring& aTitle, 
01590                              const nsSubstring& aMedia, 
01591                              nsIParser* aParserToUnblock,
01592                              PRBool& aCompleted,
01593                              nsICSSLoaderObserver* aObserver)
01594 {
01595   LOG(("CSSLoaderImpl::LoadStyleLink"));
01596   NS_PRECONDITION(aURL, "Must have URL to load");
01597   NS_ASSERTION(mParsingDatas.Count() == 0, "We're in the middle of a parse?");
01598 
01599   LOG_URI("  Link uri: '%s'", aURL);
01600   
01601   aCompleted = PR_TRUE;
01602 
01603   if (!mEnabled) {
01604     LOG_WARN(("  Not enabled"));
01605     return NS_ERROR_NOT_AVAILABLE;
01606   }
01607   
01608   NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_INITIALIZED);
01609 
01610   // Check whether we should even load
01611   nsIURI *docURI = mDocument->GetDocumentURI();
01612   if (!docURI) return NS_ERROR_FAILURE;
01613 
01614   nsISupports* context = aElement;
01615   if (!context) {
01616     context = mDocument;
01617   }
01618   nsresult rv = CheckLoadAllowed(docURI, aURL, context);
01619   if (NS_FAILED(rv)) return rv;
01620 
01621   LOG(("  Passed load check"));
01622   
01623   StyleSheetState state;
01624   nsCOMPtr<nsICSSStyleSheet> sheet;
01625   rv = CreateSheet(aURL, aElement, PR_FALSE, state,
01626                    getter_AddRefs(sheet));
01627   NS_ENSURE_SUCCESS(rv, rv);
01628 
01629   rv = PrepareSheet(sheet, aTitle, aMedia, nsnull);
01630   NS_ENSURE_SUCCESS(rv, rv);
01631   
01632   rv = InsertSheetInDoc(sheet, aElement, mDocument);
01633   NS_ENSURE_SUCCESS(rv, rv);
01634 
01635   if (state == eSheetComplete) {
01636     LOG(("  Sheet already complete"));
01637     // We're completely done; this sheet is fully loaded, clean, and so forth
01638     if (aObserver) {
01639       aObserver->StyleSheetLoaded(sheet, PR_TRUE);
01640     }
01641     return NS_OK;
01642   }
01643 
01644   nsCOMPtr<nsIStyleSheetLinkingElement> owningElement(do_QueryInterface(aElement));
01645 
01646   // Now we need to actually load it
01647   SheetLoadData* data = new SheetLoadData(this, aTitle, aParserToUnblock, aURL,
01648                                           sheet, owningElement, aObserver);
01649   if (!data) {
01650     sheet->SetComplete();
01651     if (aObserver) {
01652       aObserver->StyleSheetLoaded(sheet, PR_TRUE);
01653     }
01654     return NS_ERROR_OUT_OF_MEMORY;
01655   }
01656 
01657   NS_ADDREF(data);
01658 
01659   // Caller gets to wait for the sheet to load.
01660   aCompleted = PR_FALSE;
01661 
01662   // If we have to parse and it's an alternate non-inline, defer it
01663   if (aURL && state == eSheetNeedsParser && mLoadingDatas.Count() != 0 &&
01664       IsAlternate(aTitle)) {
01665     LOG(("  Deferring alternate sheet load"));
01666     mPendingDatas.Put(aURL, data);
01667     return NS_OK;
01668   }
01669 
01670   // Load completion will free the data
01671   return LoadSheet(data, state);
01672 }
01673 
01674 NS_IMETHODIMP
01675 CSSLoaderImpl::LoadChildSheet(nsICSSStyleSheet* aParentSheet,
01676                               nsIURI* aURL, 
01677                               nsMediaList* aMedia,
01678                               nsICSSImportRule* aParentRule)
01679 {
01680   LOG(("CSSLoaderImpl::LoadChildSheet"));
01681   NS_PRECONDITION(aURL, "Must have a URI to load");
01682 
01683   if (!mEnabled) {
01684     LOG_WARN(("  Not enabled"));
01685     return NS_ERROR_NOT_AVAILABLE;
01686   }
01687   
01688   LOG_URI("  Child uri: '%s'", aURL);
01689 
01690   // Check whether we should even load
01691   nsCOMPtr<nsIURI> sheetURI;
01692   nsresult rv = aParentSheet->GetSheetURI(getter_AddRefs(sheetURI));
01693   if (NS_FAILED(rv) || !sheetURI) return NS_ERROR_FAILURE;
01694 
01695   nsCOMPtr<nsIDOMNode> owningNode;
01696 
01697   // check for an owning document: if none, don't bother walking up the parent
01698   // sheets
01699   nsCOMPtr<nsIDocument> owningDoc;
01700   rv = aParentSheet->GetOwningDocument(*getter_AddRefs(owningDoc));
01701   if (NS_SUCCEEDED(rv) && owningDoc) {
01702     nsCOMPtr<nsIDOMStyleSheet> nextParentSheet(do_QueryInterface(aParentSheet));
01703     NS_ENSURE_TRUE(nextParentSheet, NS_ERROR_FAILURE); //Not a stylesheet!?
01704 
01705     nsCOMPtr<nsIDOMStyleSheet> topSheet;
01706     //traverse our way to the top-most sheet
01707     do {
01708       topSheet.swap(nextParentSheet);
01709       topSheet->GetParentStyleSheet(getter_AddRefs(nextParentSheet));
01710     } while (nextParentSheet);
01711 
01712     topSheet->GetOwnerNode(getter_AddRefs(owningNode));
01713   }
01714 
01715   nsISupports* context = owningNode;
01716   if (!context) {
01717     context = mDocument;
01718   }
01719   
01720   rv = CheckLoadAllowed(sheetURI, aURL, context);
01721   if (NS_FAILED(rv)) return rv;
01722 
01723   LOG(("  Passed load check"));
01724   
01725   SheetLoadData* parentData = nsnull;
01726   nsCOMPtr<nsICSSLoaderObserver> observer;
01727 
01728   PRInt32 count = mParsingDatas.Count();
01729   if (count > 0) {
01730     LOG(("  Have a parent load"));
01731     parentData = NS_STATIC_CAST(SheetLoadData*,
01732                                 mParsingDatas.ElementAt(count - 1));
01733     // Check for cycles
01734     SheetLoadData* data = parentData;
01735     while (data && data->mURI) {
01736       PRBool equal;
01737       if (NS_SUCCEEDED(data->mURI->Equals(aURL, &equal)) && equal) {
01738         // Houston, we have a loop, blow off this child and pretend this never
01739         // happened
01740         LOG_ERROR(("  @import cycle detected, dropping load"));
01741         return NS_OK;
01742       }
01743       data = data->mParentData;
01744     }
01745 
01746     NS_ASSERTION(parentData->mSheet == aParentSheet,
01747                  "Unexpected call to LoadChildSheet");
01748   } else {
01749     LOG(("  No parent load; must be CSSOM"));
01750     // No parent load data, so the sheet will need to be notified when
01751     // we finish, if it can be, if we do the load asynchronously.
01752     observer = do_QueryInterface(aParentSheet);
01753   }
01754 
01755   // Now that we know it's safe to load this (passes security check and not a
01756   // loop) do so
01757   nsCOMPtr<nsICSSStyleSheet> sheet;
01758   StyleSheetState state;
01759   rv = CreateSheet(aURL, nsnull,
01760                    parentData ? parentData->mSyncLoad : PR_FALSE,
01761                    state, getter_AddRefs(sheet));
01762   NS_ENSURE_SUCCESS(rv, rv);
01763 
01764   const nsSubstring& empty = EmptyString();
01765   rv = PrepareSheet(sheet, empty, empty, aMedia);
01766   NS_ENSURE_SUCCESS(rv, rv);
01767   
01768   rv = InsertChildSheet(sheet, aParentSheet, aParentRule);
01769   NS_ENSURE_SUCCESS(rv, rv);
01770   
01771   if (state == eSheetComplete) {
01772     LOG(("  Sheet already complete"));
01773     // We're completely done.  No need to notify, even, since the
01774     // @import rule addition/modification will trigger the right style
01775     // changes automatically.
01776     return NS_OK;
01777   }
01778 
01779   
01780   SheetLoadData* data = new SheetLoadData(this, aURL, sheet, parentData,
01781                                           observer);
01782 
01783   if (!data) {
01784     sheet->SetComplete();
01785     return NS_ERROR_OUT_OF_MEMORY;
01786   }
01787 
01788   NS_ADDREF(data);
01789 
01790   // Load completion will release the data
01791   return LoadSheet(data, state);
01792   
01793 }
01794 
01795 NS_IMETHODIMP
01796 CSSLoaderImpl::LoadSheetSync(nsIURI* aURL, PRBool aAllowUnsafeRules,
01797                              nsICSSStyleSheet** aSheet)
01798 {
01799   LOG(("CSSLoaderImpl::LoadAgentSheet synchronous"));
01800   return InternalLoadAgentSheet(aURL, aSheet, aAllowUnsafeRules, nsnull);
01801 }
01802 
01803 NS_IMETHODIMP
01804 CSSLoaderImpl::LoadAgentSheet(nsIURI* aURL, 
01805                               nsICSSStyleSheet** aSheet)
01806 {
01807   LOG(("CSSLoaderImpl::LoadAgentSheet synchronous"));
01808   return InternalLoadAgentSheet(aURL, aSheet, PR_FALSE, nsnull);
01809 }
01810 
01811 NS_IMETHODIMP
01812 CSSLoaderImpl::LoadAgentSheet(nsIURI* aURL, 
01813                               nsICSSLoaderObserver* aObserver)
01814 {
01815   LOG(("CSSLoaderImpl::LoadAgentSheet asynchronous"));
01816   return InternalLoadAgentSheet(aURL, nsnull, PR_FALSE, aObserver);
01817 }
01818 
01819 nsresult
01820 CSSLoaderImpl::InternalLoadAgentSheet(nsIURI* aURL, 
01821                                       nsICSSStyleSheet** aSheet,
01822                                       PRBool aAllowUnsafeRules,
01823                                       nsICSSLoaderObserver* aObserver)
01824 {
01825   NS_PRECONDITION(aURL, "Must have a URI to load");
01826   NS_PRECONDITION((!aSheet || !aObserver) && (aSheet || aObserver),
01827                   "One or the other please, at most one");
01828   NS_ASSERTION(mParsingDatas.Count() == 0, "We're in the middle of a parse?");
01829 
01830   LOG_URI("  Agent uri: '%s'", aURL);
01831   
01832   if (!mEnabled) {
01833     LOG_WARN(("  Not enabled"));
01834     return NS_ERROR_NOT_AVAILABLE;
01835   }
01836 
01837   StyleSheetState state;
01838   nsCOMPtr<nsICSSStyleSheet> sheet;
01839   PRBool syncLoad = (aObserver == nsnull);
01840   
01841   nsresult rv = CreateSheet(aURL, nsnull, syncLoad, state,
01842                             getter_AddRefs(sheet));
01843   NS_ENSURE_SUCCESS(rv, rv);
01844 
01845   const nsSubstring& empty = EmptyString();
01846   rv = PrepareSheet(sheet, empty, empty, nsnull);
01847   NS_ENSURE_SUCCESS(rv, rv);
01848   
01849   if (aSheet) {
01850     *aSheet = nsnull;
01851   }
01852   
01853   if (state == eSheetComplete) {
01854     LOG(("  Sheet already complete"));
01855     if (aSheet) {
01856       *aSheet = sheet;
01857       NS_ADDREF(*aSheet);
01858     } else {
01859       aObserver->StyleSheetLoaded(sheet, PR_TRUE);
01860     }
01861     return NS_OK;
01862   }
01863 
01864   SheetLoadData* data =
01865     new SheetLoadData(this, aURL, sheet, syncLoad, aAllowUnsafeRules, aObserver);
01866 
01867   if (!data) {
01868     sheet->SetComplete();
01869     return NS_ERROR_OUT_OF_MEMORY;
01870   }
01871   
01872   NS_ADDREF(data);
01873   rv = LoadSheet(data, state);
01874 
01875   if (NS_SUCCEEDED(rv) && aSheet) {
01876     *aSheet = sheet;
01877     NS_ADDREF(*aSheet);
01878   }
01879 
01880   return rv;
01881 }
01882 
01883 nsresult NS_NewCSSLoader(nsIDocument* aDocument, nsICSSLoader** aLoader)
01884 {
01885   CSSLoaderImpl* it = new CSSLoaderImpl();
01886 
01887   NS_ENSURE_TRUE(it, NS_ERROR_OUT_OF_MEMORY);
01888 
01889   it->Init(aDocument);
01890   return CallQueryInterface(it, aLoader);
01891 }
01892 
01893 nsresult NS_NewCSSLoader(nsICSSLoader** aLoader)
01894 {
01895   CSSLoaderImpl* it = new CSSLoaderImpl();
01896 
01897   NS_ENSURE_TRUE(it, NS_ERROR_OUT_OF_MEMORY);
01898 
01899   return CallQueryInterface(it, aLoader);
01900 }
01901 
01902 PR_STATIC_CALLBACK(PLDHashOperator)
01903 StopLoadingSheetCallback(nsIURI* aKey, SheetLoadData*& aData, void* aClosure)
01904 {
01905   NS_PRECONDITION(aData, "Must have a data!");
01906   NS_PRECONDITION(aClosure, "Must have a loader");
01907 
01908   aData->mIsLoading = PR_FALSE; // we will handle the removal right here
01909   aData->mIsCancelled = PR_TRUE;
01910   
01911   NS_STATIC_CAST(CSSLoaderImpl*,aClosure)->SheetComplete(aData, PR_FALSE);
01912 
01913   return PL_DHASH_REMOVE;
01914 }
01915 
01916 NS_IMETHODIMP
01917 CSSLoaderImpl::Stop()
01918 {
01919   if (mLoadingDatas.IsInitialized() && mLoadingDatas.Count() > 0) {
01920     mLoadingDatas.Enumerate(StopLoadingSheetCallback, this);
01921   }
01922   if (mPendingDatas.IsInitialized() && mPendingDatas.Count() > 0) {
01923     mPendingDatas.Enumerate(StopLoadingSheetCallback, this);
01924   }
01925   return NS_OK;
01926 }
01927 
01928 NS_IMETHODIMP
01929 CSSLoaderImpl::StopLoadingSheet(nsIURI* aURL)
01930 {
01931   NS_ENSURE_TRUE(aURL, NS_ERROR_NULL_POINTER);
01932   if (mLoadingDatas.Count() > 0 || mPendingDatas.Count() > 0) {
01933     
01934     SheetLoadData* loadData = nsnull;
01935     mLoadingDatas.Get(aURL, &loadData);
01936     if (!loadData) {
01937       mPendingDatas.Get(aURL, &loadData);
01938       if (loadData) {
01939         // have to remove from mPendingDatas ourselves, since
01940         // SheetComplete won't do that.
01941         mPendingDatas.Remove(aURL);
01942       }
01943     }
01944     
01945     if (loadData) {
01946       loadData->mIsCancelled = PR_TRUE;
01947       SheetComplete(loadData, PR_FALSE);
01948     }
01949   }
01950   return NS_OK;
01951 }
01952 
01953 NS_IMETHODIMP
01954 CSSLoaderImpl::GetEnabled(PRBool *aEnabled)
01955 {
01956   NS_ENSURE_ARG_POINTER(aEnabled);
01957   *aEnabled = mEnabled;
01958   return NS_OK;
01959 }
01960 
01961 NS_IMETHODIMP
01962 CSSLoaderImpl::SetEnabled(PRBool aEnabled)
01963 {
01964   mEnabled = aEnabled;
01965   return NS_OK;
01966 }