diff --git a/mozilla/content/xml/document/src/nsXMLContentSink.cpp b/mozilla/content/xml/document/src/nsXMLContentSink.cpp index 669cbd5b212..f7c9fdfa5a3 100644 --- a/mozilla/content/xml/document/src/nsXMLContentSink.cpp +++ b/mozilla/content/xml/document/src/nsXMLContentSink.cpp @@ -251,7 +251,12 @@ nsXMLContentSink::MaybePrettyPrint() nsresult rv = NS_NewXMLPrettyPrinter(getter_AddRefs(printer)); NS_ENSURE_SUCCESS(rv, rv); - return printer->PrettyPrint(mDocument); + PRBool isPrettyPrinting; + rv = printer->PrettyPrint(mDocument, &isPrettyPrinting); + NS_ENSURE_SUCCESS(rv, rv); + + mPrettyPrinting = isPrettyPrinting; + return NS_OK; } static void @@ -341,9 +346,25 @@ nsXMLContentSink::DidBuildModel() // Check if we want to prettyprint MaybePrettyPrint(); - StartLayout(PR_FALSE); + PRBool startLayout = PR_TRUE; + + if (mPrettyPrinting) { + NS_ASSERTION(!mPendingSheetCount, "Shouldn't have pending sheets here!"); + + // We're pretty-printing now. See whether we should wait up on + // stylesheet loads + if (mDocument->CSSLoader()->HasPendingLoads() && + NS_SUCCEEDED(mDocument->CSSLoader()->AddObserver(this))) { + // wait for those sheets to load + startLayout = PR_FALSE; + } + } + + if (startLayout) { + StartLayout(PR_FALSE); - ScrollToRef(); + ScrollToRef(); + } mDocument->RemoveObserver(this); @@ -428,6 +449,23 @@ nsXMLContentSink::OnTransformDone(nsresult aResult, return NS_OK; } +NS_IMETHODIMP +nsXMLContentSink::StyleSheetLoaded(nsICSSStyleSheet* aSheet, + PRBool aWasAlternate, + nsresult aStatus) +{ + if (!mPrettyPrinting) { + return nsContentSink::StyleSheetLoaded(aSheet, aWasAlternate, aStatus); + } + + if (!mDocument->CSSLoader()->HasPendingLoads()) { + mDocument->CSSLoader()->RemoveObserver(this); + StartLayout(PR_FALSE); + ScrollToRef(); + } + + return NS_OK; +} NS_IMETHODIMP nsXMLContentSink::WillInterrupt(void) diff --git a/mozilla/content/xml/document/src/nsXMLContentSink.h b/mozilla/content/xml/document/src/nsXMLContentSink.h index 5dbc8903c8a..48ba23c5976 100644 --- a/mozilla/content/xml/document/src/nsXMLContentSink.h +++ b/mozilla/content/xml/document/src/nsXMLContentSink.h @@ -101,6 +101,9 @@ public: NS_IMETHOD OnDocumentCreated(nsIDocument *aResultDocument); NS_IMETHOD OnTransformDone(nsresult aResult, nsIDocument *aResultDocument); + // nsICSSLoaderObserver + NS_IMETHOD StyleSheetLoaded(nsICSSStyleSheet* aSheet, PRBool aWasAlternate, + nsresult aStatus); static void ParsePIData(const nsString &aData, nsString &aHref, nsString &aTitle, nsString &aMedia, PRBool &aIsAlternate); @@ -190,7 +193,9 @@ protected: PRUint8 mPrettyPrintHasFactoredElements : 1; PRUint8 mHasProcessedBase : 1; PRUint8 mAllowAutoXLinks : 1; - PRUint8 unused : 2; // bits available if someone needs one + PRUint8 mPrettyPrinting : 1; // True if we called PrettyPrint() and it + // decided we should in fact prettyprint. + PRUint8 unused : 1; // bits available if someone needs one nsTArray mContentStack; diff --git a/mozilla/content/xml/document/src/nsXMLPrettyPrinter.cpp b/mozilla/content/xml/document/src/nsXMLPrettyPrinter.cpp index dc18f60c69e..708733db310 100644 --- a/mozilla/content/xml/document/src/nsXMLPrettyPrinter.cpp +++ b/mozilla/content/xml/document/src/nsXMLPrettyPrinter.cpp @@ -71,8 +71,11 @@ nsXMLPrettyPrinter::~nsXMLPrettyPrinter() } nsresult -nsXMLPrettyPrinter::PrettyPrint(nsIDocument* aDocument) +nsXMLPrettyPrinter::PrettyPrint(nsIDocument* aDocument, + PRBool* aDidPrettyPrint) { + *aDidPrettyPrint = PR_FALSE; + // Check for iframe with display:none. Such iframes don't have presshells if (!aDocument->GetPrimaryShell()) { return NS_OK; @@ -119,6 +122,7 @@ nsXMLPrettyPrinter::PrettyPrint(nsIDocument* aDocument) } // Ok, we should prettyprint. Let's do it! + *aDidPrettyPrint = PR_TRUE; nsresult rv = NS_OK; // Load the XSLT diff --git a/mozilla/content/xml/document/src/nsXMLPrettyPrinter.h b/mozilla/content/xml/document/src/nsXMLPrettyPrinter.h index f9b6006fbbf..be399516c2a 100644 --- a/mozilla/content/xml/document/src/nsXMLPrettyPrinter.h +++ b/mozilla/content/xml/document/src/nsXMLPrettyPrinter.h @@ -72,8 +72,10 @@ public: * displayed window. * * @param aDocument document to prettyprint + * @param [out] aDidPrettyPrint if true, and error not returned, actually + * went ahead with prettyprinting the document. */ - nsresult PrettyPrint(nsIDocument* aDocument); + nsresult PrettyPrint(nsIDocument* aDocument, PRBool* aDidPrettyPrint); private: /** diff --git a/mozilla/layout/style/nsCSSLoader.cpp b/mozilla/layout/style/nsCSSLoader.cpp index 9a66731db51..c7e6853cf31 100644 --- a/mozilla/layout/style/nsCSSLoader.cpp +++ b/mozilla/layout/style/nsCSSLoader.cpp @@ -263,7 +263,8 @@ CSSLoaderImpl::CSSLoaderImpl(void) : mDocument(nsnull), mCaseSensitive(PR_FALSE), mEnabled(PR_TRUE), - mCompatMode(eCompatibility_FullStandards) + mCompatMode(eCompatibility_FullStandards), + mDatasToNotifyOn(0) { } @@ -304,15 +305,6 @@ CSSLoaderImpl::Init(nsIDocument* aDocument) return NS_OK; } -PR_STATIC_CALLBACK(PLDHashOperator) -StartAlternateLoads(nsURIAndPrincipalHashKey *aKey, - SheetLoadData* &aData, - void* aClosure) -{ - NS_STATIC_CAST(CSSLoaderImpl*,aClosure)->LoadSheet(aData, eSheetNeedsParser); - return PL_DHASH_REMOVE; -} - NS_IMETHODIMP CSSLoaderImpl::DropDocumentReference(void) { @@ -320,8 +312,9 @@ CSSLoaderImpl::DropDocumentReference(void) // Flush out pending datas just so we don't leak by accident. These // loads should short-circuit through the mDocument check in // LoadSheet and just end up in SheetComplete immediately - if (mPendingDatas.IsInitialized()) - mPendingDatas.Enumerate(StartAlternateLoads, this); + if (mPendingDatas.IsInitialized()) { + StartAlternateLoads(); + } return NS_OK; } @@ -340,22 +333,20 @@ CSSLoaderImpl::SetCompatibilityMode(nsCompatibility aCompatMode) } PR_STATIC_CALLBACK(PLDHashOperator) -StartNonAlternates(nsURIAndPrincipalHashKey *aKey, - SheetLoadData* &aData, - void* aClosure) +CollectNonAlternates(nsURIAndPrincipalHashKey *aKey, + SheetLoadData* &aData, + void* aClosure) { NS_PRECONDITION(aData, "Must have a data"); - NS_PRECONDITION(aClosure, "Must have a loader"); + NS_PRECONDITION(aClosure, "Must have an array"); - CSSLoaderImpl* loader = NS_STATIC_CAST(CSSLoaderImpl*, aClosure); // Note that we don't want to affect what the selected style set is, // so use PR_TRUE for aHasAlternateRel. - if (loader->IsAlternate(aData->mTitle, PR_TRUE)) { + if (aData->mLoader->IsAlternate(aData->mTitle, PR_TRUE)) { return PL_DHASH_NEXT; } - // Need to start the load - loader->LoadSheet(aData, eSheetNeedsParser); + NS_STATIC_CAST(CSSLoaderImpl::LoadDataArray*,aClosure)->AppendElement(aData); return PL_DHASH_REMOVE; } @@ -378,8 +369,17 @@ CSSLoaderImpl::SetPreferredSheet(const nsAString& aTitle) mPreferredSheet = aTitle; // start any pending alternates that aren't alternates anymore - if (mPendingDatas.IsInitialized()) - mPendingDatas.Enumerate(StartNonAlternates, this); + if (mPendingDatas.IsInitialized()) { + LoadDataArray arr(mPendingDatas.Count()); + mPendingDatas.Enumerate(CollectNonAlternates, &arr); + + mDatasToNotifyOn += arr.Length(); + for (PRUint32 i = 0; i < arr.Length(); ++i) { + --mDatasToNotifyOn; + LoadSheet(arr[i], eSheetNeedsParser); + } + } + return NS_OK; } @@ -1553,19 +1553,31 @@ CSSLoaderImpl::SheetComplete(SheetLoadData* aLoadData, nsresult aStatus) // Now it's safe to go ahead and notify observers PRUint32 count = datasToNotify.Length(); + mDatasToNotifyOn += count; for (PRUint32 i = 0; i < count; ++i) { + --mDatasToNotifyOn; + SheetLoadData* data = datasToNotify[i]; - NS_ASSERTION(data && data->mMustNotify && data->mObserver, - "How did this data get here?"); - LOG((" Notifying observer 0x%x for data 0x%s. wasAlternate: %d", - data->mObserver.get(), data, data->mWasAlternate)); - data->mObserver->StyleSheetLoaded(data->mSheet, data->mWasAlternate, - aStatus); + NS_ASSERTION(data && data->mMustNotify, "How did this data get here?"); + if (data->mObserver) { + LOG((" Notifying observer 0x%x for data 0x%s. wasAlternate: %d", + data->mObserver.get(), data, data->mWasAlternate)); + data->mObserver->StyleSheetLoaded(data->mSheet, data->mWasAlternate, + aStatus); + } + + nsTObserverArray::ForwardIterator iter(mObservers); + nsCOMPtr obs; + while ((obs = iter.GetNext())) { + LOG((" Notifying global observer 0x%x for data 0x%s. wasAlternate: %d", + obs.get(), data, data->mWasAlternate)); + obs->StyleSheetLoaded(data->mSheet, data->mWasAlternate, aStatus); + } } if (mLoadingDatas.Count() == 0 && mPendingDatas.Count() > 0) { LOG((" No more loading sheets; starting alternates")); - mPendingDatas.Enumerate(StartAlternateLoads, this); + StartAlternateLoads(); } } @@ -1605,7 +1617,7 @@ CSSLoaderImpl::DoSheetComplete(SheetLoadData* aLoadData, nsresult aStatus, data->mSheet->SetModified(PR_FALSE); // it's clean data->mSheet->SetComplete(); - if (data->mMustNotify && data->mObserver) { + if (data->mMustNotify && (data->mObserver || !mObservers.IsEmpty())) { // Don't notify here so we don't trigger script. Remember the // info we need to notify, then do it later when it's safe. aDatasToNotify.AppendElement(data); @@ -2141,8 +2153,7 @@ StopLoadingSheetCallback(nsURIAndPrincipalHashKey* aKey, aData->mIsLoading = PR_FALSE; // we will handle the removal right here aData->mIsCancelled = PR_TRUE; - NS_STATIC_CAST(CSSLoaderImpl*,aClosure)->SheetComplete(aData, - NS_BINDING_ABORTED); + NS_STATIC_CAST(CSSLoaderImpl::LoadDataArray*,aClosure)->AppendElement(aData); return PL_DHASH_REMOVE; } @@ -2150,25 +2161,54 @@ StopLoadingSheetCallback(nsURIAndPrincipalHashKey* aKey, NS_IMETHODIMP CSSLoaderImpl::Stop() { - // Do the pending datas first, since finishing up all the loading - // datas will try to start pending loads. - if (mPendingDatas.IsInitialized() && mPendingDatas.Count() > 0) { - mPendingDatas.Enumerate(StopLoadingSheetCallback, this); + PRUint32 pendingCount = + mPendingDatas.IsInitialized() ? mPendingDatas.Count() : 0; + PRUint32 loadingCount = + mLoadingDatas.IsInitialized() ? mLoadingDatas.Count() : 0; + LoadDataArray arr(pendingCount + loadingCount + mPostedEvents.Length()); + + if (pendingCount) { + mPendingDatas.Enumerate(StopLoadingSheetCallback, &arr); } - if (mLoadingDatas.IsInitialized() && mLoadingDatas.Count() > 0) { - mLoadingDatas.Enumerate(StopLoadingSheetCallback, this); + if (loadingCount) { + mLoadingDatas.Enumerate(StopLoadingSheetCallback, &arr); } - for (PRUint32 i = 0; i < mPostedEvents.Length(); ++i) { + + PRUint32 i; + for (i = 0; i < mPostedEvents.Length(); ++i) { SheetLoadData* data = mPostedEvents[i]; data->mIsCancelled = PR_TRUE; - // SheetComplete() calls Release(), so give this an extra ref. - NS_ADDREF(data); - data->mLoader->SheetComplete(data, NS_BINDING_ABORTED); + if (arr.AppendElement(data)) { + // SheetComplete() calls Release(), so give this an extra ref. + NS_ADDREF(data); + } +#ifdef DEBUG + else { + NS_NOTREACHED("We preallocated this memory... shouldn't really fail, " + "except we never check that preallocation succeeds."); + } +#endif } mPostedEvents.Clear(); + + mDatasToNotifyOn += arr.Length(); + for (i = 0; i < arr.Length(); ++i) { + --mDatasToNotifyOn; + SheetComplete(arr[i], NS_BINDING_ABORTED); + } return NS_OK; } +struct StopLoadingSheetsByURIClosure { + StopLoadingSheetsByURIClosure(nsIURI* aURI, + CSSLoaderImpl::LoadDataArray& aArray) : + uri(aURI), array(aArray) + {} + + nsIURI* uri; + CSSLoaderImpl::LoadDataArray& array; +}; + PR_STATIC_CALLBACK(PLDHashOperator) StopLoadingSheetByURICallback(nsURIAndPrincipalHashKey* aKey, SheetLoadData*& aData, @@ -2177,15 +2217,16 @@ StopLoadingSheetByURICallback(nsURIAndPrincipalHashKey* aKey, NS_PRECONDITION(aData, "Must have a data!"); NS_PRECONDITION(aClosure, "Must have a loader"); + StopLoadingSheetsByURIClosure* closure = + NS_STATIC_CAST(StopLoadingSheetsByURIClosure*, aClosure); + PRBool equal; - if (NS_SUCCEEDED(aData->mURI->Equals(NS_STATIC_CAST(nsIURI*, aClosure), - &equal)) && + if (NS_SUCCEEDED(aData->mURI->Equals(closure->uri, &equal)) && equal) { aData->mIsLoading = PR_FALSE; // we will handle the removal right here aData->mIsCancelled = PR_TRUE; - aData->mLoader->SheetComplete(aData, NS_BINDING_ABORTED); - + closure->array.AppendElement(aData); return PL_DHASH_REMOVE; } @@ -2197,28 +2238,47 @@ CSSLoaderImpl::StopLoadingSheet(nsIURI* aURL) { NS_ENSURE_TRUE(aURL, NS_ERROR_NULL_POINTER); - // Do the pending datas first, since finishing up all the loading - // datas will try to start pending loads. - if (mPendingDatas.IsInitialized() && mPendingDatas.Count() > 0) { - mPendingDatas.Enumerate(StopLoadingSheetByURICallback, aURL); + PRUint32 pendingCount = + mPendingDatas.IsInitialized() ? mPendingDatas.Count() : 0; + PRUint32 loadingCount = + mLoadingDatas.IsInitialized() ? mLoadingDatas.Count() : 0; + LoadDataArray arr(pendingCount + loadingCount + mPostedEvents.Length()); + + StopLoadingSheetsByURIClosure closure(aURL, arr); + if (pendingCount) { + mPendingDatas.Enumerate(StopLoadingSheetByURICallback, &closure); } - if (mLoadingDatas.IsInitialized() && mLoadingDatas.Count() > 0) { - mLoadingDatas.Enumerate(StopLoadingSheetByURICallback, aURL); + if (loadingCount) { + mLoadingDatas.Enumerate(StopLoadingSheetByURICallback, &closure); } - for (PRUint32 i = 0; i < mPostedEvents.Length(); ++i) { - SheetLoadData* curData = mPostedEvents[i-1]; + PRUint32 i; + for (i = 0; i < mPostedEvents.Length(); ++i) { + SheetLoadData* curData = mPostedEvents[i]; PRBool equal; if (curData->mURI && NS_SUCCEEDED(curData->mURI->Equals(aURL, &equal)) && equal) { curData->mIsCancelled = PR_TRUE; - // SheetComplete() calls Release(), so give it an extra ref. - NS_ADDREF(curData); - SheetComplete(curData, NS_BINDING_ABORTED); + if (arr.AppendElement(curData)) { + // SheetComplete() calls Release(), so give this an extra ref. + NS_ADDREF(curData); + } +#ifdef DEBUG + else { + NS_NOTREACHED("We preallocated this memory... shouldn't really fail, " + "except we never check that preallocation succeeds."); + } +#endif } } mPostedEvents.Clear(); - + + mDatasToNotifyOn += arr.Length(); + for (i = 0; i < arr.Length(); ++i) { + --mDatasToNotifyOn; + SheetComplete(arr[i], NS_BINDING_ABORTED); + } + return NS_OK; } @@ -2236,3 +2296,56 @@ CSSLoaderImpl::SetEnabled(PRBool aEnabled) mEnabled = aEnabled; return NS_OK; } + +NS_IMETHODIMP_(PRBool) +CSSLoaderImpl::HasPendingLoads() +{ + return + (mLoadingDatas.IsInitialized() && mLoadingDatas.Count() != 0) || + (mPendingDatas.IsInitialized() && mPendingDatas.Count() != 0) || + mPostedEvents.Length() != 0 || + mDatasToNotifyOn != 0; +} + +NS_IMETHODIMP +CSSLoaderImpl::AddObserver(nsICSSLoaderObserver* aObserver) +{ + NS_PRECONDITION(aObserver, "Must have observer"); + if (mObservers.AppendObserver(aObserver)) { + NS_ADDREF(aObserver); + return NS_OK; + } + + return NS_ERROR_OUT_OF_MEMORY; +} + +NS_IMETHODIMP_(void) +CSSLoaderImpl::RemoveObserver(nsICSSLoaderObserver* aObserver) +{ + if (mObservers.RemoveObserver(aObserver)) { + NS_RELEASE(aObserver); + } +} + +PR_STATIC_CALLBACK(PLDHashOperator) +CollectLoadDatas(nsURIAndPrincipalHashKey *aKey, + SheetLoadData* &aData, + void* aClosure) +{ + NS_STATIC_CAST(CSSLoaderImpl::LoadDataArray*,aClosure)->AppendElement(aData); + return PL_DHASH_REMOVE; +} + +void +CSSLoaderImpl::StartAlternateLoads() +{ + NS_PRECONDITION(mPendingDatas.IsInitialized(), "Don't call me!"); + LoadDataArray arr(mPendingDatas.Count()); + mPendingDatas.Enumerate(CollectLoadDatas, &arr); + + mDatasToNotifyOn += arr.Length(); + for (PRUint32 i = 0; i < arr.Length(); ++i) { + --mDatasToNotifyOn; + LoadSheet(arr[i], eSheetNeedsParser); + } +} diff --git a/mozilla/layout/style/nsCSSLoader.h b/mozilla/layout/style/nsCSSLoader.h index 5ba23d790e2..262cd6dec68 100644 --- a/mozilla/layout/style/nsCSSLoader.h +++ b/mozilla/layout/style/nsCSSLoader.h @@ -72,6 +72,7 @@ class nsMediaList; #include "nsAutoPtr.h" #include "nsTArray.h" #include "nsIPrincipal.h" +#include "nsTObserverArray.h" /** * OVERALL ARCHITECTURE @@ -360,6 +361,10 @@ public: NS_IMETHOD GetEnabled(PRBool *aEnabled); NS_IMETHOD SetEnabled(PRBool aEnabled); + NS_IMETHOD_(PRBool) HasPendingLoads(); + NS_IMETHOD AddObserver(nsICSSLoaderObserver* aObserver); + NS_IMETHOD_(void) RemoveObserver(nsICSSLoaderObserver* aObserver); + // local helper methods (some are public for access from statics) // IsAlternate can change our currently selected style set if none @@ -417,15 +422,19 @@ private: nsICSSStyleSheet* aSheet, nsICSSLoaderObserver* aObserver, PRBool aWasAlternate); + + // Start the loads of all the sheets in mPendingDatas + void StartAlternateLoads(); + public: // Handle an event posted by PostLoadEvent void HandleLoadEvent(SheetLoadData* aEvent); +protected: // Note: LoadSheet is responsible for releasing aLoadData and setting the // sheet to complete on failure. nsresult LoadSheet(SheetLoadData* aLoadData, StyleSheetState aSheetState); -protected: friend class SheetLoadData; // Protected functions and members are ones that SheetLoadData needs @@ -439,14 +448,14 @@ protected: SheetLoadData* aLoadData, PRBool& aCompleted); -public: // The load of the sheet in aLoadData is done, one way or another. Do final // cleanup, including releasing aLoadData. void SheetComplete(SheetLoadData* aLoadData, nsresult aStatus); -private: +public: typedef nsTArray > LoadDataArray; +private: // The guts of SheetComplete. This may be called recursively on parent datas // or datas that had glommed on to a single load. The array is there so load // datas whose observers need to be notified can be added to it. @@ -481,6 +490,15 @@ private: // The array of posted stylesheet loaded events (SheetLoadDatas) we have. // Note that these are rare. LoadDataArray mPostedEvents; + + // Number of datas still waiting to be notified on if we're notifying on a + // whole bunch at once (e.g. in one of the stop methods). This is used to + // make sure that HasPendingLoads() won't return false until we're notifying + // on the last data we're working with. + PRUint32 mDatasToNotifyOn; + + // Our array of "global" observers + nsTObserverArray mObservers; }; #endif // nsCSSLoader_h__ diff --git a/mozilla/layout/style/nsICSSLoader.h b/mozilla/layout/style/nsICSSLoader.h index 3df891c59d0..5015ef74918 100644 --- a/mozilla/layout/style/nsICSSLoader.h +++ b/mozilla/layout/style/nsICSSLoader.h @@ -237,6 +237,34 @@ public: */ NS_IMETHOD GetEnabled(PRBool *aEnabled) = 0; NS_IMETHOD SetEnabled(PRBool aEnabled) = 0; + + /** + * Return true if this nsICSSLoader has pending loads (ones that would send + * notifications to an nsICSSLoaderObserver attached to this nsICSSLoader). + * If called from inside nsICSSLoaderObserver::StyleSheetLoaded, this will + * return PR_FALSE if and only if that is the last StyleSheetLoaded + * notification the CSSLoader knows it's going to send. In other words, if + * two sheets load at once (via load coalescing, e.g.), HasPendingLoads() + * will return PR_TRUE during notification for the first one, and PR_FALSE + * during notification for the second one. + */ + NS_IMETHOD_(PRBool) HasPendingLoads() = 0; + + /** + * Add an observer to this nsICSSLoader. The observer will be notified for + * all loads that would have notified their own observers (even if those + * loads don't have observers attached to them). Load-specific observers + * will be notified before generic observers. The CSSLoader holds a + * reference to the observer. + * + * aObserver must not be null. + */ + NS_IMETHOD AddObserver(nsICSSLoaderObserver* aObserver) = 0; + + /** + * Remove an observer added via AddObserver. + */ + NS_IMETHOD_(void) RemoveObserver(nsICSSLoaderObserver* aObserver) = 0; }; NS_DEFINE_STATIC_IID_ACCESSOR(nsICSSLoader, NS_ICSS_LOADER_IID)