Add an API to observe all loads in a CSSLoader. Change XML prettyprinting tonot start layout till after the prettyprinter stylesheet is loaded, so we don'treflow and reconstruct the whole thing twice.
Bug 380612, r+sr=sicking
--- a/content/xml/document/src/nsXMLContentSink.cpp
+++ b/content/xml/document/src/nsXMLContentSink.cpp
@@ -246,17 +246,22 @@ nsXMLContentSink::MaybePrettyPrint()
if (mCSSLoader) {
mCSSLoader->SetEnabled(PR_TRUE);
}
nsCOMPtr<nsXMLPrettyPrinter> printer;
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
CheckXSLTParamPI(nsIDOMProcessingInstruction* aPi,
nsIDocumentTransformer* aProcessor,
nsIDocument* aDocument)
{
nsAutoString target, data;
@@ -336,19 +341,35 @@ nsXMLContentSink::DidBuildModel()
// documentElement?
NS_ASSERTION(mDocument->IndexOf(mDocElement) != -1,
"mDocElement not in doc?");
}
// 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);
mDocument->EndLoad();
}
DropParserAndPerfHint();
@@ -423,16 +444,33 @@ nsXMLContentSink::OnTransformDone(nsresu
ScrollToRef();
originalDocument->EndLoad();
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)
{
return WillInterruptImpl();
}
NS_IMETHODIMP
--- a/content/xml/document/src/nsXMLContentSink.h
+++ b/content/xml/document/src/nsXMLContentSink.h
@@ -96,16 +96,19 @@ public:
virtual void FlushPendingNotifications(mozFlushType aType);
NS_IMETHOD SetDocumentCharset(nsACString& aCharset);
virtual nsISupports *GetTarget();
// nsITransformObserver
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);
protected:
// Start layout. If aIgnorePendingSheets is true, this will happen even if
// we still have stylesheet loads pending. Otherwise, we'll wait until the
// stylesheets are all done loading.
@@ -185,16 +188,18 @@ protected:
PRInt32 mNotifyLevel;
PRUint8 mConstrainSize : 1;
PRUint8 mPrettyPrintXML : 1;
PRUint8 mPrettyPrintHasSpecialRoot : 1;
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<StackNode> mContentStack;
nsCOMPtr<nsIDocumentTransformer> mXSLTProcessor;
};
#endif // nsXMLContentSink_h__
--- a/content/xml/document/src/nsXMLPrettyPrinter.cpp
+++ b/content/xml/document/src/nsXMLPrettyPrinter.cpp
@@ -66,18 +66,21 @@ nsXMLPrettyPrinter::nsXMLPrettyPrinter()
}
nsXMLPrettyPrinter::~nsXMLPrettyPrinter()
{
NS_ASSERTION(!mDocument, "we shouldn't be referencing the document still");
}
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;
}
// check if we're in an invisible iframe
nsPIDOMWindow *internalWin = aDocument->GetWindow();
nsCOMPtr<nsIDOMElement> frameElem;
@@ -114,16 +117,17 @@ nsXMLPrettyPrinter::PrettyPrint(nsIDocum
}
// check the pref
if (!nsContentUtils::GetBoolPref("layout.xml.prettyprint", PR_TRUE)) {
return NS_OK;
}
// Ok, we should prettyprint. Let's do it!
+ *aDidPrettyPrint = PR_TRUE;
nsresult rv = NS_OK;
// Load the XSLT
nsCOMPtr<nsIURI> xslUri;
rv = NS_NewURI(getter_AddRefs(xslUri),
NS_LITERAL_CSTRING("chrome://global/content/xml/XMLPrettyPrint.xsl"));
NS_ENSURE_SUCCESS(rv, rv);
--- a/content/xml/document/src/nsXMLPrettyPrinter.h
+++ b/content/xml/document/src/nsXMLPrettyPrinter.h
@@ -67,18 +67,20 @@ public:
nsIContent* aChild, PRInt32 aIndexInContainer);
virtual void NodeWillBeDestroyed(const nsINode* aNode);
/**
* This will prettyprint the document if the document is loaded in a
* 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:
/**
* Signals for unhooking by setting mUnhookPending if the node changed is
* non-anonymous content.
*
* @param aContent content that has changed
*/
--- a/layout/style/nsCSSLoader.cpp
+++ b/layout/style/nsCSSLoader.cpp
@@ -258,17 +258,18 @@ SheetLoadData::Run()
// static
nsCOMArray<nsICSSParser>* CSSLoaderImpl::gParsers = nsnull;
CSSLoaderImpl::CSSLoaderImpl(void)
: mDocument(nsnull),
mCaseSensitive(PR_FALSE),
mEnabled(PR_TRUE),
- mCompatMode(eCompatibility_FullStandards)
+ mCompatMode(eCompatibility_FullStandards),
+ mDatasToNotifyOn(0)
{
}
CSSLoaderImpl::~CSSLoaderImpl(void)
{
NS_ASSERTION((!mLoadingDatas.IsInitialized()) || mLoadingDatas.Count() == 0,
"How did we get destroyed when there are loading data?");
NS_ASSERTION((!mPendingDatas.IsInitialized()) || mPendingDatas.Count() == 0,
@@ -299,34 +300,26 @@ CSSLoaderImpl::Init(nsIDocument* aDocume
// and hence the selected set makes no sense at this time.
nsCOMPtr<nsIDOMNSDocumentStyle> domDoc(do_QueryInterface(mDocument));
if (domDoc) {
domDoc->GetPreferredStyleSheetSet(mPreferredSheet);
}
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)
{
mDocument = nsnull;
// 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;
}
NS_IMETHODIMP
CSSLoaderImpl::SetCaseSensitive(PRBool aCaseSensitive)
{
mCaseSensitive = aCaseSensitive;
return NS_OK;
@@ -335,32 +328,30 @@ CSSLoaderImpl::SetCaseSensitive(PRBool a
NS_IMETHODIMP
CSSLoaderImpl::SetCompatibilityMode(nsCompatibility aCompatMode)
{
mCompatMode = aCompatMode;
return NS_OK;
}
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;
}
NS_IMETHODIMP
CSSLoaderImpl::SetPreferredSheet(const nsAString& aTitle)
{
#ifdef DEBUG
nsCOMPtr<nsIDOMNSDocumentStyle> doc(do_QueryInterface(mDocument));
@@ -373,18 +364,27 @@ CSSLoaderImpl::SetPreferredSheet(const n
NS_ASSERTION(currentPreferred.Equals(aTitle),
"Unexpected argument to SetPreferredSheet");
}
#endif
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;
}
NS_IMETHODIMP
CSSLoaderImpl::GetPreferredSheet(nsAString& aTitle)
{
aTitle.Assign(mPreferredSheet);
return NS_OK;
@@ -1548,29 +1548,41 @@ CSSLoaderImpl::SheetComplete(SheetLoadDa
// 8 is probably big enough for all our common cases. It's not likely that
// imports will nest more than 8 deep, and multiple sheets with the same URI
// are rare.
nsAutoTArray<nsRefPtr<SheetLoadData>, 8> datasToNotify;
DoSheetComplete(aLoadData, aStatus, datasToNotify);
// 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<nsICSSLoaderObserver>::ForwardIterator iter(mObservers);
+ nsCOMPtr<nsICSSLoaderObserver> 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();
}
}
void
CSSLoaderImpl::DoSheetComplete(SheetLoadData* aLoadData, nsresult aStatus,
LoadDataArray& aDatasToNotify)
{
LOG(("CSSLoaderImpl::DoSheetComplete"));
@@ -1600,17 +1612,17 @@ CSSLoaderImpl::DoSheetComplete(SheetLoad
}
// Go through and deal with the whole linked list.
SheetLoadData* data = aLoadData;
while (data) {
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);
// On append failure, just press on. We'll fail to notify the observer,
// but not much we can do about that....
}
@@ -2136,94 +2148,142 @@ StopLoadingSheetCallback(nsURIAndPrincip
void* aClosure)
{
NS_PRECONDITION(aData, "Must have a data!");
NS_PRECONDITION(aClosure, "Must have a loader");
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;
}
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,
void* aClosure)
{
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;
}
return PL_DHASH_NEXT;
}
NS_IMETHODIMP
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;
}
NS_IMETHODIMP
CSSLoaderImpl::GetEnabled(PRBool *aEnabled)
{
NS_ENSURE_ARG_POINTER(aEnabled);
*aEnabled = mEnabled;
@@ -2231,8 +2291,61 @@ CSSLoaderImpl::GetEnabled(PRBool *aEnabl
}
NS_IMETHODIMP
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);
+ }
+}
--- a/layout/style/nsCSSLoader.h
+++ b/layout/style/nsCSSLoader.h
@@ -67,16 +67,17 @@ class nsMediaList;
#include "nsCOMArray.h"
#include "nsString.h"
#include "nsURIHashKey.h"
#include "nsInterfaceHashtable.h"
#include "nsDataHashtable.h"
#include "nsAutoPtr.h"
#include "nsTArray.h"
#include "nsIPrincipal.h"
+#include "nsTObserverArray.h"
/**
* OVERALL ARCHITECTURE
*
* The CSS Loader gets requests to load various sorts of style sheets:
* inline style from <style> elements, linked style, @import-ed child
* sheets, non-document sheets. The loader handles the following tasks:
*
@@ -355,16 +356,20 @@ public:
* When disabled, processing of new styles is disabled and an attempt
* to do so will fail with a return code of
* NS_ERROR_NOT_AVAILABLE. Note that this DOES NOT disable
* currently loading styles or already processed styles.
*/
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
// is selected and aHasAlternateRel is false.
PRBool IsAlternate(const nsAString& aTitle, PRBool aHasAlternateRel);
private:
nsresult CheckLoadAllowed(nsIURI* aSourceURI,
@@ -412,46 +417,50 @@ private:
// canceled at some point (in which case it will be sent with
// NS_BINDING_ABORTED). aWasAlternate indicates the state when the load was
// initiated, not the state at some later time. aURI should be the URI the
// sheet was loaded from (may be null for inline sheets).
nsresult PostLoadEvent(nsIURI* aURI,
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
// access to.
// Parse the stylesheet in aLoadData. The sheet data comes from aStream.
// Set aCompleted to true if the parse finished, false otherwise (e.g. if the
// sheet had an @import). If aCompleted is true when this returns, then
// ParseSheet also called SheetComplete on aLoadData
nsresult ParseSheet(nsIUnicharInputStream* aStream,
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<nsRefPtr<SheetLoadData> > 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.
void DoSheetComplete(SheetLoadData* aLoadData, nsresult aStatus,
LoadDataArray& aDatasToNotify);
static nsCOMArray<nsICSSParser>* gParsers; // array of idle CSS parsers
@@ -476,11 +485,20 @@ private:
// We're not likely to have many levels of @import... But likely to have
// some. Allocate some storage, what the hell.
nsAutoVoidArray mParsingDatas;
// 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<nsICSSLoaderObserver> mObservers;
};
#endif // nsCSSLoader_h__
--- a/layout/style/nsICSSLoader.h
+++ b/layout/style/nsICSSLoader.h
@@ -232,16 +232,44 @@ public:
* Whether the loader is enabled or not.
* When disabled, processing of new styles is disabled and an attempt
* to do so will fail with a return code of
* NS_ERROR_NOT_AVAILABLE. Note that this DOES NOT disable
* currently loading styles or already processed styles.
*/
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)
nsresult
NS_NewCSSLoader(nsIDocument* aDocument, nsICSSLoader** aLoader);
nsresult