Add an API to observe all loads in a CSSLoader. Change XML prettyprinting to

not start layout till after the prettyprinter stylesheet is loaded, so we don't
reflow and reconstruct the whole thing twice.  Bug 380612, r+sr=sicking


git-svn-id: svn://10.0.0.236/trunk@227308 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
bzbarsky%mit.edu
2007-05-31 01:42:48 +00:00
parent 9a4bd167ad
commit 829c92df27
7 changed files with 275 additions and 67 deletions

View File

@@ -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<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();
}
}
@@ -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);
}
}