Improve the CSSLoader API: make it clear which methods return results

sync and which return async, move the handling of alternates more completely
into the CSSLoader, make it possible for observers to tell whether the load
actually succeeded.  Bug 293825, r+sr=peterv
This commit is contained in:
bzbarsky%mit.edu 2005-09-12 18:41:15 +00:00
parent 853c2b4645
commit 818aafcc15
27 changed files with 608 additions and 309 deletions

View File

@ -1057,7 +1057,7 @@ nsChromeRegistry::LoadStyleSheetWithURL(nsIURI* aURL, nsICSSStyleSheet** aSheet)
nsCOMPtr<nsICSSLoader> cssLoader = do_GetService(kCSSLoaderCID);
if (!cssLoader) return NS_ERROR_FAILURE;
return cssLoader->LoadAgentSheet(aURL, aSheet);
return cssLoader->LoadSheetSync(aURL, aSheet);
}
NS_IMETHODIMP

View File

@ -191,8 +191,9 @@ nsContentSink::Init(nsIDocument* aDoc,
}
NS_IMETHODIMP
nsContentSink::StyleSheetLoaded(nsICSSStyleSheet* aSheet,
PRBool aDidNotify)
nsContentSink::StyleSheetLoaded(nsICSSStyleSheet* aSheet,
PRBool aWasAlternate,
nsresult aStatus)
{
return NS_OK;
}
@ -646,42 +647,15 @@ nsContentSink::ProcessStyleLink(nsIContent* aElement,
return NS_OK;
}
if (!aAlternate) {
// possibly preferred sheet
if (!aTitle.IsEmpty()) {
nsAutoString preferredStyle;
mDocument->GetHeaderData(nsHTMLAtoms::headerDefaultStyle,
preferredStyle);
if (preferredStyle.IsEmpty()) {
mDocument->SetHeaderData(nsHTMLAtoms::headerDefaultStyle, aTitle);
}
}
}
PRBool blockParser = kBlockByDefault;
if (aAlternate) {
blockParser = PR_FALSE;
}
// NOTE: no longer honoring the important keyword to indicate
// blocking as it is proprietary and unnecessary since all
// non-alternate will block the parser now -mja
#if 0
if (linkTypes.IndexOf("important") != -1) {
blockParser = PR_TRUE;
}
#endif
PRBool doneLoading;
nsIParser* parser = nsnull;
if (blockParser) {
if (kBlockByDefault) {
parser = mParser;
}
rv = mCSSLoader->LoadStyleLink(aElement, url, aTitle, aMedia,
parser, doneLoading, this);
if (NS_SUCCEEDED(rv) && blockParser && !doneLoading) {
PRBool isAlternate;
rv = mCSSLoader->LoadStyleLink(aElement, url, aTitle, aMedia, aAlternate,
parser, this, &isAlternate);
if (NS_SUCCEEDED(rv) && parser && !isAlternate) {
rv = NS_ERROR_HTMLPARSER_BLOCK;
}

View File

@ -68,7 +68,8 @@ class nsContentSink : public nsICSSLoaderObserver,
NS_DECL_NSISCRIPTLOADEROBSERVER
// nsICSSLoaderObserver
NS_IMETHOD StyleSheetLoaded(nsICSSStyleSheet* aSheet, PRBool aNotify);
NS_IMETHOD StyleSheetLoaded(nsICSSStyleSheet* aSheet, PRBool aWasAlternate,
nsresult aStatus);
protected:
nsContentSink();

View File

@ -1985,7 +1985,7 @@ nsDocument::EnsureCatalogStyleSheet(const char *aStyleSheetURI)
NS_NewURI(getter_AddRefs(uri), aStyleSheetURI);
if (uri) {
nsCOMPtr<nsICSSStyleSheet> sheet;
cssLoader->LoadAgentSheet(uri, getter_AddRefs(sheet));
cssLoader->LoadSheetSync(uri, getter_AddRefs(sheet));
if (sheet) {
BeginUpdate(UPDATE_STYLE);
AddCatalogStyleSheet(sheet);

View File

@ -261,26 +261,8 @@ nsStyleLinkElement::UpdateStyleSheet(nsIDocument *aOldDocument,
return NS_OK;
}
PRBool blockParser = kBlockByDefault;
if (isAlternate) {
blockParser = PR_FALSE;
}
/* NOTE: no longer honoring the important keyword to indicate blocking
as it is proprietary and unnecessary since all non-alternate
will block the parser now -mja
if (-1 != linkTypes.IndexOf("important")) {
blockParser = PR_TRUE;
}
*/
if (!isAlternate && !title.IsEmpty()) { // possibly preferred sheet
nsAutoString prefStyle;
doc->GetHeaderData(nsHTMLAtoms::headerDefaultStyle, prefStyle);
if (prefStyle.IsEmpty()) {
doc->SetHeaderData(nsHTMLAtoms::headerDefaultStyle, title);
}
if (!kBlockByDefault) {
parser = nsnull;
}
PRBool doneLoading;
@ -321,17 +303,17 @@ nsStyleLinkElement::UpdateStyleSheet(nsIDocument *aOldDocument,
// style sheet.
rv = doc->CSSLoader()->
LoadInlineStyle(thisContent, uin, mLineNumber, title, media,
((blockParser) ? parser.get() : nsnull),
doneLoading, aObserver);
parser, aObserver, &doneLoading, &isAlternate);
}
else {
doneLoading = PR_FALSE; // If rv is success, we won't be done loading; if
// it's not, this value doesn't matter.
rv = doc->CSSLoader()->
LoadStyleLink(thisContent, uri, title, media,
((blockParser) ? parser.get() : nsnull),
doneLoading, aObserver);
LoadStyleLink(thisContent, uri, title, media, isAlternate,
parser, aObserver, &isAlternate);
}
if (NS_SUCCEEDED(rv) && blockParser && !doneLoading) {
if (NS_SUCCEEDED(rv) && parser && !doneLoading && !isAlternate) {
rv = NS_ERROR_HTMLPARSER_BLOCK;
}

View File

@ -132,7 +132,7 @@ nsXBLPrototypeResources::FlushSkinSheets()
nsCOMPtr<nsICSSStyleSheet> newSheet;
if (IsChromeURI(uri)) {
if (NS_FAILED(loader->LoadAgentSheet(uri, getter_AddRefs(newSheet))))
if (NS_FAILED(loader->LoadSheetSync(uri, getter_AddRefs(newSheet))))
continue;
}
else {

View File

@ -132,24 +132,19 @@ nsXBLResourceLoader::LoadResources(PRBool* aResult)
if (NS_SUCCEEDED(url->SchemeIs("chrome", &chrome)) && chrome)
{
nsCOMPtr<nsICSSStyleSheet> sheet;
rv = cssLoader->LoadAgentSheet(url, getter_AddRefs(sheet));
rv = cssLoader->LoadSheetSync(url, getter_AddRefs(sheet));
NS_ASSERTION(NS_SUCCEEDED(rv), "Load failed!!!");
if (NS_SUCCEEDED(rv))
{
rv = StyleSheetLoaded(sheet, PR_TRUE);
rv = StyleSheetLoaded(sheet, PR_FALSE, NS_OK);
NS_ASSERTION(NS_SUCCEEDED(rv), "Processing the style sheet failed!!!");
}
}
else
{
PRBool doneLoading;
NS_NAMED_LITERAL_STRING(empty, "");
rv = cssLoader->LoadStyleLink(nsnull, url, empty, empty,
nsnull, doneLoading, this);
NS_ASSERTION(NS_SUCCEEDED(rv), "Load failed!!!");
if (!doneLoading)
mPendingSheets++;
rv = cssLoader->LoadSheet(url, this);
if (NS_SUCCEEDED(rv))
++mPendingSheets;
}
}
}
@ -164,7 +159,9 @@ nsXBLResourceLoader::LoadResources(PRBool* aResult)
// nsICSSLoaderObserver
NS_IMETHODIMP
nsXBLResourceLoader::StyleSheetLoaded(nsICSSStyleSheet* aSheet, PRBool aNotify)
nsXBLResourceLoader::StyleSheetLoaded(nsICSSStyleSheet* aSheet,
PRBool aWasAlternate,
nsresult aStatus)
{
if (!mResources) {
// Our resources got destroyed -- just bail out

View File

@ -77,7 +77,8 @@ public:
NS_DECL_ISUPPORTS
// nsICSSLoaderObserver
NS_IMETHOD StyleSheetLoaded(nsICSSStyleSheet* aSheet, PRBool aNotify);
NS_IMETHOD StyleSheetLoaded(nsICSSStyleSheet* aSheet, PRBool aWasAlternate,
nsresult aStatus);
void LoadResources(PRBool* aResult);
void AddResource(nsIAtom* aResourceType, const nsAString& aSrc);
@ -100,6 +101,8 @@ public:
nsXBLResource* mLastResource;
PRPackedBool mLoadingResources;
// We need mInLoadResourcesFunc because we do a mixture of sync and
// async loads.
PRPackedBool mInLoadResourcesFunc;
PRInt16 mPendingSheets; // The number of stylesheets that have yet to load.

View File

@ -1105,7 +1105,7 @@ nsXMLContentSink::HandleDoctypeDecl(const nsAString & aSubset,
nsCOMPtr<nsIURI> uri(do_QueryInterface(aCatalogData));
if (uri) {
nsCOMPtr<nsICSSStyleSheet> sheet;
mCSSLoader->LoadAgentSheet(uri, getter_AddRefs(sheet));
mCSSLoader->LoadSheetSync(uri, getter_AddRefs(sheet));
#ifdef NS_DEBUG
nsCAutoString uriStr;

View File

@ -232,7 +232,6 @@ protected:
nsCOMPtr<nsIXULPrototypeDocument> mPrototype; // [OWNER]
nsIParser* mParser; // [OWNER] We use regular pointer b/c of funky exports on nsIParser
nsString mPreferredStyle;
nsCOMPtr<nsICSSLoader> mCSSLoader; // [OWNER]
nsCOMPtr<nsICSSParser> mCSSParser; // [OWNER]
nsCOMPtr<nsIScriptSecurityManager> mSecMan;
@ -469,33 +468,15 @@ XULContentSinkImpl::ProcessStyleLink(nsIContent* aElement,
// Add the style sheet reference to the prototype
mPrototype->AddStyleSheetReference(url);
// Nope, we need to load it asynchronously
PRBool blockParser = PR_FALSE;
if (! aAlternate) {
if (!aTitle.IsEmpty()) { // possibly preferred sheet
if (mPreferredStyle.IsEmpty()) {
mPreferredStyle = aTitle;
mCSSLoader->SetPreferredSheet(aTitle);
nsCOMPtr<nsIAtom> defaultStyle = do_GetAtom("default-style");
if (defaultStyle) {
mPrototype->SetHeaderData(defaultStyle, aTitle);
}
}
}
else { // persistent sheet, block
blockParser = PR_TRUE;
}
}
nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocument);
if (! doc)
return NS_ERROR_FAILURE; // doc went away!
PRBool doneLoading;
PRBool isAlternate;
rv = mCSSLoader->LoadStyleLink(aElement, url, aTitle, aMedia,
((blockParser) ? mParser : nsnull),
doneLoading, nsnull);
if (NS_SUCCEEDED(rv) && blockParser && (! doneLoading)) {
aAlternate, mParser, nsnull,
&isAlternate);
if (NS_SUCCEEDED(rv) && !isAlternate) {
rv = NS_ERROR_HTMLPARSER_BLOCK;
}
}
@ -543,16 +524,21 @@ XULContentSinkImpl::Init(nsIDocument* aDocument, nsIXULPrototypeDocument* aProto
// XXX this presumes HTTP header info is already set in document
// XXX if it isn't we need to set it here...
nsCOMPtr<nsIAtom> defaultStyle = do_GetAtom("default-style");
if (! defaultStyle)
return NS_ERROR_OUT_OF_MEMORY;
rv = mPrototype->GetHeaderData(defaultStyle, mPreferredStyle);
// XXXbz not like GetHeaderData on the proto doc _does_ anything....
nsAutoString preferredStyle;
rv = mPrototype->GetHeaderData(nsHTMLAtoms::headerDefaultStyle,
preferredStyle);
if (NS_FAILED(rv)) return rv;
if (!preferredStyle.IsEmpty()) {
aDocument->SetHeaderData(nsHTMLAtoms::headerDefaultStyle,
preferredStyle);
}
// Get the CSS loader from the document so we can load
// stylesheets
mCSSLoader = aDocument->CSSLoader();
mCSSLoader->SetPreferredSheet(preferredStyle);
mNodeInfoManager = aPrototype->GetNodeInfoManager();
if (! mNodeInfoManager)

View File

@ -717,7 +717,7 @@ nsXULDocument::EndLoad()
mCurrentPrototype->AddStyleSheetReference(sheetURI);
}
cssLoader->LoadAgentSheet(sheetURI, getter_AddRefs(sheet));
cssLoader->LoadSheetSync(sheetURI, getter_AddRefs(sheet));
if (!sheet) {
NS_WARNING("Couldn't load chrome style overlay.");
continue;
@ -3786,10 +3786,10 @@ nsXULDocument::AddPrototypeSheets()
// only system that partially invalidates the XUL cache).
// - dwh
//XXXbz we hit this code from fastload all the time. Bug 183505.
rv = CSSLoader()->LoadAgentSheet(uri, getter_AddRefs(sheet));
rv = CSSLoader()->LoadSheetSync(uri, getter_AddRefs(sheet));
// XXXldb We need to prevent bogus sheets from being held in the
// prototype's list, but until then, don't propagate the failure
// from LoadAgentSheet (and thus exit the loop).
// from LoadSheetSync (and thus exit the loop).
if (NS_SUCCEEDED(rv)) {
AddStyleSheet(sheet);
}

View File

@ -3534,8 +3534,7 @@ nsHTMLEditor::ReplaceStyleSheet(const nsAString& aURL)
rv = NS_NewURI(getter_AddRefs(uaURI), aURL);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsICSSStyleSheet> sheet;
rv = cssLoader->LoadAgentSheet(uaURI, this);
rv = cssLoader->LoadSheet(uaURI, this);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
@ -3585,8 +3584,10 @@ nsHTMLEditor::AddOverrideStyleSheet(const nsAString& aURL)
NS_ENSURE_SUCCESS(rv, rv);
// We MUST ONLY load synchronous local files (no @import)
// XXXbz Except this will actually try to load remote files
// synchronously, of course..
nsCOMPtr<nsICSSStyleSheet> sheet;
rv = cssLoader->LoadAgentSheet(uaURI, getter_AddRefs(sheet));
rv = cssLoader->LoadSheetSync(uaURI, getter_AddRefs(sheet));
// Synchronous loads should ALWAYS return completed
if (!sheet)
@ -4179,7 +4180,8 @@ nsHTMLEditor::GetReconversionString(nsReconversionEventReply* aReply)
NS_IMETHODIMP
nsHTMLEditor::StyleSheetLoaded(nsICSSStyleSheet* aSheet, PRBool aNotify)
nsHTMLEditor::StyleSheetLoaded(nsICSSStyleSheet* aSheet, PRBool aWasAlternate,
nsresult aStatus)
{
nsresult rv = NS_OK;
nsAutoEditBatch batchIt(this);

View File

@ -371,7 +371,8 @@ public:
PRUint32 aLength);
/* ------------ nsICSSLoaderObserver -------------- */
NS_IMETHOD StyleSheetLoaded(nsICSSStyleSheet*aSheet, PRBool aNotify);
NS_IMETHOD StyleSheetLoaded(nsICSSStyleSheet*aSheet, PRBool aWasAlternate,
nsresult aStatus);
/* ------------ Utility Routines, not part of public API -------------- */
NS_IMETHOD TypedText(const nsAString& aString, PRInt32 aAction);

View File

@ -869,7 +869,9 @@ txTransformNotifier::ScriptEvaluated(nsresult aResult,
}
NS_IMETHODIMP
txTransformNotifier::StyleSheetLoaded(nsICSSStyleSheet* aSheet, PRBool aNotify)
txTransformNotifier::StyleSheetLoaded(nsICSSStyleSheet* aSheet,
PRBool aWasAlternate,
nsresult aStatus)
{
// Check that the stylesheet was in the mStylesheets array, if not it is an
// alternate and we don't want to call SignalTransformEnd since we don't

View File

@ -67,7 +67,9 @@ public:
NS_DECL_NSISCRIPTLOADEROBSERVER
// nsICSSLoaderObserver
NS_IMETHOD StyleSheetLoaded(nsICSSStyleSheet* aSheet, PRBool aNotify);
NS_IMETHOD StyleSheetLoaded(nsICSSStyleSheet* aSheet,
PRBool aWasAlternate,
nsresult aStatus);
void Init(nsITransformObserver* aObserver);
void AddScriptElement(nsIScriptElement* aElement);

View File

@ -2042,7 +2042,7 @@ DocumentViewerImpl::CreateStyleSet(nsIDocument* aDocument,
baseURI);
if (!uri) continue;
cssLoader->LoadAgentSheet(uri, getter_AddRefs(csssheet));
cssLoader->LoadSheetSync(uri, getter_AddRefs(csssheet));
if (!sheet) continue;
styleSet->PrependStyleSheet(nsStyleSet::eAgentSheet, csssheet);

View File

@ -145,7 +145,7 @@ nsStyleSheetService::LoadAndRegisterSheet(nsIURI *aSheetURI,
nsCOMPtr<nsICSSLoader> loader = do_CreateInstance(kCSSLoaderCID);
nsCOMPtr<nsICSSStyleSheet> sheet;
nsresult rv = loader->LoadAgentSheet(aSheetURI, getter_AddRefs(sheet));
nsresult rv = loader->LoadSheetSync(aSheetURI, getter_AddRefs(sheet));
NS_ENSURE_SUCCESS(rv, rv);
mSheets[aSheetType].AppendObject(sheet);

View File

@ -642,7 +642,7 @@ nsContentDLF::EnsureUAStyleSheet()
NS_NewCSSLoader(getter_AddRefs(cssLoader));
if (!cssLoader)
return NS_ERROR_OUT_OF_MEMORY;
rv = cssLoader->LoadAgentSheet(uri, &gUAStyleSheet);
rv = cssLoader->LoadSheetSync(uri, &gUAStyleSheet);
#ifdef DEBUG
if (NS_FAILED(rv))
printf("*** open of %s failed: error=%x\n", UA_CSS_URL, rv);

View File

@ -76,7 +76,7 @@ ParseCSSFile(nsIURI *aSheetURI)
{
nsCOMPtr<nsICSSLoader> loader(do_CreateInstance(kCSSLoaderCID));
nsCOMPtr<nsICSSStyleSheet> sheet;
loader->LoadAgentSheet(aSheetURI, getter_AddRefs(sheet));
loader->LoadSheetSync(aSheetURI, getter_AddRefs(sheet));
NS_ASSERTION(sheet, "sheet load failed");
/* This can happen if the file can't be found (e.g. you
* ask for a relative path and xpcom/io rejects it)

View File

@ -55,7 +55,6 @@
#include "nsUnicharUtils.h"
#include "nsHashtable.h"
#include "nsIURI.h"
#include "nsIURL.h"
#include "nsIParser.h"
#include "nsIServiceManager.h"
#include "nsNetUtil.h"
@ -76,6 +75,10 @@
#include "nsICSSLoader.h"
#include "nsICSSParser.h"
#include "nsICSSImportRule.h"
#include "plevent.h"
#include "nsIEventQueue.h"
#include "nsIEventQueueService.h"
#include "nsHTMLAtoms.h"
#ifdef MOZ_XUL
#include "nsIXULPrototypeCache.h"
@ -143,6 +146,7 @@ SheetLoadData::SheetLoadData(CSSLoaderImpl* aLoader,
nsIURI* aURI,
nsICSSStyleSheet* aSheet,
nsIStyleSheetLinkingElement* aOwningElement,
PRBool aIsAlternate,
nsICSSLoaderObserver* aObserver)
: mLoader(aLoader),
mTitle(aTitle),
@ -154,9 +158,11 @@ SheetLoadData::SheetLoadData(CSSLoaderImpl* aLoader,
mParentData(nsnull),
mPendingChildren(0),
mSyncLoad(PR_FALSE),
mIsAgent(PR_FALSE),
mIsNonDocumentSheet(PR_FALSE),
mIsLoading(PR_FALSE),
mIsCancelled(PR_FALSE),
mMustNotify(PR_FALSE),
mWasAlternate(aIsAlternate),
mOwningElement(aOwningElement),
mObserver(aObserver)
{
@ -179,9 +185,11 @@ SheetLoadData::SheetLoadData(CSSLoaderImpl* aLoader,
mParentData(aParentData),
mPendingChildren(0),
mSyncLoad(PR_FALSE),
mIsAgent(PR_FALSE),
mIsNonDocumentSheet(PR_FALSE),
mIsLoading(PR_FALSE),
mIsCancelled(PR_FALSE),
mMustNotify(PR_FALSE),
mWasAlternate(PR_FALSE),
mOwningElement(nsnull),
mObserver(aObserver)
{
@ -191,7 +199,7 @@ SheetLoadData::SheetLoadData(CSSLoaderImpl* aLoader,
if (mParentData) {
NS_ADDREF(mParentData);
mSyncLoad = mParentData->mSyncLoad;
mIsAgent = mParentData->mIsAgent;
mIsNonDocumentSheet = mParentData->mIsNonDocumentSheet;
++(mParentData->mPendingChildren);
}
}
@ -210,9 +218,11 @@ SheetLoadData::SheetLoadData(CSSLoaderImpl* aLoader,
mParentData(nsnull),
mPendingChildren(0),
mSyncLoad(aSyncLoad),
mIsAgent(PR_TRUE),
mIsNonDocumentSheet(PR_TRUE),
mIsLoading(PR_FALSE),
mIsCancelled(PR_FALSE),
mMustNotify(PR_FALSE),
mWasAlternate(PR_FALSE),
mOwningElement(nsnull),
mObserver(aObserver)
{
@ -249,6 +259,9 @@ CSSLoaderImpl::~CSSLoaderImpl(void)
"How did we get destroyed when there are loading data?");
NS_ASSERTION((!mPendingDatas.IsInitialized()) || mPendingDatas.Count() == 0,
"How did we get destroyed when there are pending data?");
// Note: no real need to revoke our stylesheet loaded events -- they
// hold strong references to us, so if we're going away that means
// they're all done.
}
NS_IMPL_ISUPPORTS1(CSSLoaderImpl, nsICSSLoader)
@ -312,7 +325,9 @@ StartNonAlternates(nsIURI *aKey, SheetLoadData* &aData, void* aClosure)
NS_PRECONDITION(aClosure, "Must have a loader");
CSSLoaderImpl* loader = NS_STATIC_CAST(CSSLoaderImpl*, aClosure);
if (loader->IsAlternate(aData->mTitle)) {
// 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)) {
return PL_DHASH_NEXT;
}
@ -704,10 +719,10 @@ SheetLoadData::OnStreamComplete(nsIUnicharStreamLoader* aLoader,
return NS_OK;
}
if (!mLoader->mDocument && !mIsAgent) {
if (!mLoader->mDocument && !mIsNonDocumentSheet) {
// Sorry, we don't care about this load anymore
LOG_WARN((" No document and not agent sheet; dropping load"));
mLoader->SheetComplete(this, PR_FALSE);
LOG_WARN((" No document and not non-document sheet; dropping load"));
mLoader->SheetComplete(this, NS_BINDING_ABORTED);
return NS_OK;
}
@ -742,7 +757,7 @@ SheetLoadData::OnStreamComplete(nsIUnicharStreamLoader* aLoader,
result = httpChannel->GetRequestSucceeded(&requestSucceeded);
if (NS_SUCCEEDED(result) && !requestSucceeded) {
LOG((" Load returned an error page"));
mLoader->SheetComplete(this, PR_FALSE);
mLoader->SheetComplete(this, NS_ERROR_NOT_AVAILABLE);
return NS_OK;
}
}
@ -789,13 +804,18 @@ SheetLoadData::OnStreamComplete(nsIUnicharStreamLoader* aLoader,
}
}
if (NS_FAILED(aStatus) || !aDataStream) {
LOG_WARN((" Load failed: status %u, data stream %p",
aStatus, aDataStream));
mLoader->SheetComplete(this, PR_FALSE);
if (NS_FAILED(aStatus)) {
LOG_WARN((" Load failed: status 0x%x", aStatus));
mLoader->SheetComplete(this, aStatus);
return NS_OK;
}
if (!aDataStream) {
LOG_WARN((" No data stream; bailing"));
mLoader->SheetComplete(this, NS_ERROR_NOT_AVAILABLE);
return NS_OK;
}
if (channelURI) {
// Enough to set the URI on mSheet, since any sibling datas we have share
// the same mInner as mSheet and will thus get the same URI.
@ -817,12 +837,29 @@ static PRBool IsChromeURI(nsIURI* aURI)
#endif
PRBool
CSSLoaderImpl::IsAlternate(const nsAString& aTitle)
CSSLoaderImpl::IsAlternate(const nsAString& aTitle, PRBool aHasAlternateRel)
{
if (!aTitle.IsEmpty()) {
return PRBool(! aTitle.Equals(mPreferredSheet, nsCaseInsensitiveStringComparator()));
// A sheet is alternate if it has a nonempty title that doesn't match the
// currently selected style set. But if there _is_ no currently selected
// style set, the sheet wasn't marked as an alternate explicitly, and aTitle
// is nonempty, we should select the style set corresponding to aTitle, since
// that's a preferred sheet.
if (aTitle.IsEmpty()) {
return PR_FALSE;
}
return PR_FALSE;
if (!aHasAlternateRel && mDocument && mPreferredSheet.IsEmpty()) {
// There's no preferred set yet, and we now have a sheet with a title.
// Make that be the preferred set.
// XXXbz maybe this should be checking IsVoid(), actually, since the
// preferred set can be explicitly set to the empty string. Look into
// this.
mDocument->SetHeaderData(nsHTMLAtoms::headerDefaultStyle, aTitle);
// We're definitely not an alternate
return PR_FALSE;
}
return !aTitle.Equals(mPreferredSheet, nsCaseInsensitiveStringComparator());
}
/**
@ -987,13 +1024,16 @@ CSSLoaderImpl::CreateSheet(nsIURI* aURI,
/**
* PrepareSheet() handles setting the media and title on the sheet, as
* well as setting the enabled state based on the title
* well as setting the enabled state based on the title and whether
* the sheet had "alternate" in its rel.
*/
nsresult
CSSLoaderImpl::PrepareSheet(nsICSSStyleSheet* aSheet,
const nsSubstring& aTitle,
const nsSubstring& aMediaString,
nsMediaList* aMediaList)
nsMediaList* aMediaList,
PRBool aHasAlternateRel,
PRBool *aIsAlternate)
{
NS_PRECONDITION(aSheet, "Must have a sheet!");
@ -1020,7 +1060,11 @@ CSSLoaderImpl::PrepareSheet(nsICSSStyleSheet* aSheet,
NS_ENSURE_SUCCESS(rv, rv);
aSheet->SetTitle(aTitle);
aSheet->SetEnabled(! IsAlternate(aTitle));
PRBool alternate = IsAlternate(aTitle, aHasAlternateRel);
aSheet->SetEnabled(! alternate);
if (aIsAlternate) {
*aIsAlternate = alternate;
}
return NS_OK;
}
@ -1177,15 +1221,16 @@ CSSLoaderImpl::LoadSheet(SheetLoadData* aLoadData, StyleSheetState aSheetState)
nsresult rv = NS_OK;
if (!mDocument && !aLoadData->mIsAgent) {
if (!mDocument && !aLoadData->mIsNonDocumentSheet) {
// No point starting the load; just release all the data and such.
LOG_WARN((" No document and not agent sheet; pre-dropping load"));
SheetComplete(aLoadData, PR_FALSE);
return NS_OK;
LOG_WARN((" No document and not non-document sheet; pre-dropping load"));
SheetComplete(aLoadData, NS_BINDING_ABORTED);
return NS_BINDING_ABORTED;
}
if (aLoadData->mSyncLoad) {
LOG((" Synchronous load"));
NS_ASSERTION(!aLoadData->mObserver, "Observer for a sync load?");
NS_ASSERTION(aSheetState == eSheetNeedsParser,
"Sync loads can't reuse existing async loads");
@ -1194,7 +1239,7 @@ CSSLoaderImpl::LoadSheet(SheetLoadData* aLoadData, StyleSheetState aSheetState)
rv = NS_OpenURI(getter_AddRefs(stream), aLoadData->mURI);
if (NS_FAILED(rv)) {
LOG_ERROR((" Failed to open URI synchronously"));
SheetComplete(aLoadData, PR_FALSE);
SheetComplete(aLoadData, rv);
return rv;
}
@ -1203,7 +1248,7 @@ CSSLoaderImpl::LoadSheet(SheetLoadData* aLoadData, StyleSheetState aSheetState)
if (NS_FAILED(rv)) {
LOG_ERROR((" Failed to create converter stream"));
SheetComplete(aLoadData, PR_FALSE);
SheetComplete(aLoadData, rv);
return rv;
}
@ -1219,7 +1264,7 @@ CSSLoaderImpl::LoadSheet(SheetLoadData* aLoadData, StyleSheetState aSheetState)
if (NS_FAILED(rv)) {
LOG_ERROR((" Failed to initialize converter stream"));
SheetComplete(aLoadData, PR_FALSE);
SheetComplete(aLoadData, rv);
return rv;
}
@ -1247,7 +1292,7 @@ CSSLoaderImpl::LoadSheet(SheetLoadData* aLoadData, StyleSheetState aSheetState)
data = data->mNext;
}
data->mNext = aLoadData; // transfer ownership
if (aSheetState == eSheetPending && !IsAlternate(aLoadData->mTitle)) {
if (aSheetState == eSheetPending && !aLoadData->mWasAlternate) {
// Kick the load off; someone cares about it right away
#ifdef DEBUG
@ -1260,7 +1305,7 @@ CSSLoaderImpl::LoadSheet(SheetLoadData* aLoadData, StyleSheetState aSheetState)
mPendingDatas.Remove(aLoadData->mURI);
LOG((" Forcing load of pending data"));
LoadSheet(existingData, eSheetNeedsParser);
return LoadSheet(existingData, eSheetNeedsParser);
}
// All done here; once the load completes we'll be marked complete
// automatically
@ -1289,7 +1334,7 @@ CSSLoaderImpl::LoadSheet(SheetLoadData* aLoadData, StyleSheetState aSheetState)
if (NS_FAILED(rv)) {
LOG_ERROR((" Failed to create channel"));
SheetComplete(aLoadData, PR_FALSE);
SheetComplete(aLoadData, rv);
return rv;
}
@ -1321,11 +1366,18 @@ CSSLoaderImpl::LoadSheet(SheetLoadData* aLoadData, StyleSheetState aSheetState)
if (NS_FAILED(rv)) {
LOG_ERROR((" Failed to create stream loader"));
SheetComplete(aLoadData, PR_FALSE);
SheetComplete(aLoadData, rv);
return rv;
}
mLoadingDatas.Put(aLoadData->mURI, aLoadData);
if (!mLoadingDatas.Put(aLoadData->mURI, aLoadData)) {
LOG_ERROR((" Failed to put data in loading table"));
aLoadData->mIsCancelled = PR_TRUE;
channel->Cancel(NS_ERROR_OUT_OF_MEMORY);
SheetComplete(aLoadData, NS_ERROR_OUT_OF_MEMORY);
return NS_ERROR_OUT_OF_MEMORY;
}
aLoadData->mIsLoading = PR_TRUE;
return NS_OK;
@ -1353,7 +1405,7 @@ CSSLoaderImpl::ParseSheet(nsIUnicharInputStream* aStream,
nsresult rv = GetParserFor(aLoadData->mSheet, getter_AddRefs(parser));
if (NS_FAILED(rv)) {
LOG_ERROR((" Failed to get CSS parser"));
SheetComplete(aLoadData, PR_FALSE);
SheetComplete(aLoadData, rv);
return rv;
}
@ -1376,13 +1428,7 @@ CSSLoaderImpl::ParseSheet(nsIUnicharInputStream* aStream,
if (aLoadData->mPendingChildren == 0) {
LOG((" No pending kids from parse"));
aCompleted = PR_TRUE;
if (!aLoadData->mURI) {
// inline sheet and we're all done with no kids, so we will not
// be blocking the parser
LOG((" Inline sheet with no pending kids; nulling out parser"));
aLoadData->mParserToUnblock = nsnull;
}
SheetComplete(aLoadData, PR_TRUE);
SheetComplete(aLoadData, NS_OK);
}
// Otherwise, the children are holding strong refs to the data and
// will call SheetComplete() on it when they complete.
@ -1394,19 +1440,19 @@ CSSLoaderImpl::ParseSheet(nsIUnicharInputStream* aStream,
* SheetComplete is the do-it-all cleanup function. It removes the
* load data from the "loading" hashtable, adds the sheet to the
* "completed" hashtable, massages the XUL cache, handles siblings of
* the load data (other loads for the same URI, handles unblocking
* the load data (other loads for the same URI), handles unblocking
* blocked parent loads as needed, and most importantly calls
* NS_RELEASE on the load data to destroy the whole mess.
*/
void
CSSLoaderImpl::SheetComplete(SheetLoadData* aLoadData, PRBool aSucceeded)
CSSLoaderImpl::SheetComplete(SheetLoadData* aLoadData, nsresult aStatus)
{
LOG(("CSSLoaderImpl::SheetComplete"));
NS_PRECONDITION(aLoadData, "Must have a load data!");
NS_PRECONDITION(aLoadData->mSheet, "Must have a sheet");
NS_ASSERTION(mLoadingDatas.IsInitialized(),"mLoadingDatas should be initialized by now.");
LOG(("Load completed: %d", aSucceeded));
LOG(("Load completed, status: 0x%x", aStatus));
// Twiddle the hashtables
if (aLoadData->mURI) {
@ -1441,13 +1487,16 @@ CSSLoaderImpl::SheetComplete(SheetLoadData* aLoadData, PRBool aSucceeded)
data->mSheet->SetModified(PR_FALSE); // it's clean
data->mSheet->SetComplete();
if (data->mObserver) {
data->mObserver->StyleSheetLoaded(data->mSheet, PR_TRUE);
if (data->mMustNotify && data->mObserver) {
data->mObserver->StyleSheetLoaded(data->mSheet, data->mWasAlternate,
aStatus);
}
// Only unblock the parser if mMustNotify is true (so we're not being
// called synchronously from LoadSheet) and mWasAlternate is false.
if (data->mParserToUnblock) {
LOG(("Parser to unblock: %p", data->mParserToUnblock.get()));
if (!seenParser) {
if (!seenParser && data->mMustNotify && !data->mWasAlternate) {
LOG(("Unblocking parser: %p", data->mParserToUnblock.get()));
seenParser = PR_TRUE;
data->mParserToUnblock->ContinueParsing();
@ -1467,14 +1516,14 @@ CSSLoaderImpl::SheetComplete(SheetLoadData* aLoadData, PRBool aSucceeded)
if (data->mParentData &&
--(data->mParentData->mPendingChildren) == 0 &&
mParsingDatas.IndexOf(data->mParentData) == -1) {
SheetComplete(data->mParentData, aSucceeded);
SheetComplete(data->mParentData, aStatus);
}
data = data->mNext;
}
// Now that it's marked complete, put the sheet in our cache
if (aSucceeded && aLoadData->mURI) {
if (NS_SUCCEEDED(aStatus) && aLoadData->mURI) {
#ifdef MOZ_XUL
if (IsChromeURI(aLoadData->mURI)) {
nsCOMPtr<nsIXULPrototypeCache> cache(do_GetService("@mozilla.org/xul/xul-prototype-cache;1"));
@ -1510,17 +1559,19 @@ NS_IMETHODIMP
CSSLoaderImpl::LoadInlineStyle(nsIContent* aElement,
nsIUnicharInputStream* aStream,
PRUint32 aLineNumber,
const nsSubstring& aTitle,
const nsSubstring& aMedia,
const nsSubstring& aTitle,
const nsSubstring& aMedia,
nsIParser* aParserToUnblock,
PRBool& aCompleted,
nsICSSLoaderObserver* aObserver)
nsICSSLoaderObserver* aObserver,
PRBool* aCompleted,
PRBool* aIsAlternate)
{
LOG(("CSSLoaderImpl::LoadInlineStyle"));
NS_PRECONDITION(aStream, "Must have a stream to parse!");
NS_ASSERTION(mParsingDatas.Count() == 0, "We're in the middle of a parse?");
aCompleted = PR_TRUE;
*aCompleted = PR_TRUE;
if (!mEnabled) {
LOG_WARN((" Not enabled"));
@ -1540,15 +1591,18 @@ CSSLoaderImpl::LoadInlineStyle(nsIContent* aElement,
NS_ASSERTION(state == eSheetNeedsParser,
"Inline sheets should not be cached");
rv = PrepareSheet(sheet, aTitle, aMedia, nsnull);
rv = PrepareSheet(sheet, aTitle, aMedia, nsnull, PR_FALSE,
aIsAlternate);
NS_ENSURE_SUCCESS(rv, rv);
LOG((" Sheet is alternate: %d", *aIsAlternate));
rv = InsertSheetInDoc(sheet, aElement, mDocument);
NS_ENSURE_SUCCESS(rv, rv);
SheetLoadData* data = new SheetLoadData(this, aTitle, aParserToUnblock,
nsnull, sheet, owningElement,
aObserver);
*aIsAlternate, aObserver);
if (!data) {
sheet->SetComplete();
@ -1558,25 +1612,34 @@ CSSLoaderImpl::LoadInlineStyle(nsIContent* aElement,
NS_ADDREF(data);
data->mLineNumber = aLineNumber;
// Parse completion releases the load data
return ParseSheet(aStream, data, aCompleted);
rv = ParseSheet(aStream, data, *aCompleted);
NS_ENSURE_SUCCESS(rv, rv);
// If aCompleted is true, |data| may well be deleted by now.
if (!*aCompleted) {
data->mMustNotify = PR_TRUE;
}
return rv;
}
NS_IMETHODIMP
CSSLoaderImpl::LoadStyleLink(nsIContent* aElement,
nsIURI* aURL,
const nsSubstring& aTitle,
const nsSubstring& aMedia,
const nsSubstring& aTitle,
const nsSubstring& aMedia,
PRBool aHasAlternateRel,
nsIParser* aParserToUnblock,
PRBool& aCompleted,
nsICSSLoaderObserver* aObserver)
nsICSSLoaderObserver* aObserver,
PRBool* aIsAlternate)
{
LOG(("CSSLoaderImpl::LoadStyleLink"));
NS_PRECONDITION(aURL, "Must have URL to load");
NS_ASSERTION(mParsingDatas.Count() == 0, "We're in the middle of a parse?");
LOG_URI(" Link uri: '%s'", aURL);
aCompleted = PR_TRUE;
LOG((" Link title: '%s'", NS_ConvertUTF16toUTF8(aTitle).get()));
LOG((" Link media: '%s'", NS_ConvertUTF16toUTF8(aMedia).get()));
LOG((" Link alternate rel: %d", aHasAlternateRel));
if (!mEnabled) {
LOG_WARN((" Not enabled"));
@ -1604,49 +1667,53 @@ CSSLoaderImpl::LoadStyleLink(nsIContent* aElement,
getter_AddRefs(sheet));
NS_ENSURE_SUCCESS(rv, rv);
rv = PrepareSheet(sheet, aTitle, aMedia, nsnull);
rv = PrepareSheet(sheet, aTitle, aMedia, nsnull, aHasAlternateRel,
aIsAlternate);
NS_ENSURE_SUCCESS(rv, rv);
LOG((" Sheet is alternate: %d", *aIsAlternate));
rv = InsertSheetInDoc(sheet, aElement, mDocument);
NS_ENSURE_SUCCESS(rv, rv);
if (state == eSheetComplete) {
LOG((" Sheet already complete"));
// We're completely done; this sheet is fully loaded, clean, and so forth
if (aObserver) {
aObserver->StyleSheetLoaded(sheet, PR_TRUE);
}
return NS_OK;
LOG((" Sheet already complete: 0x%p",
NS_STATIC_CAST(void*, sheet.get())));
return PostLoadEvent(aURL, sheet, aObserver, aParserToUnblock,
*aIsAlternate);
}
nsCOMPtr<nsIStyleSheetLinkingElement> owningElement(do_QueryInterface(aElement));
// Now we need to actually load it
SheetLoadData* data = new SheetLoadData(this, aTitle, aParserToUnblock, aURL,
sheet, owningElement, aObserver);
sheet, owningElement, *aIsAlternate,
aObserver);
if (!data) {
sheet->SetComplete();
if (aObserver) {
aObserver->StyleSheetLoaded(sheet, PR_TRUE);
}
return NS_ERROR_OUT_OF_MEMORY;
}
NS_ADDREF(data);
// Caller gets to wait for the sheet to load.
aCompleted = PR_FALSE;
// If we have to parse and it's an alternate non-inline, defer it
if (aURL && state == eSheetNeedsParser && mLoadingDatas.Count() != 0 &&
IsAlternate(aTitle)) {
*aIsAlternate) {
LOG((" Deferring alternate sheet load"));
mPendingDatas.Put(aURL, data);
if (!mPendingDatas.Put(aURL, data)) {
return NS_ERROR_OUT_OF_MEMORY;
}
data->mMustNotify = PR_TRUE;
return NS_OK;
}
// Load completion will free the data
return LoadSheet(data, state);
rv = LoadSheet(data, state);
NS_ENSURE_SUCCESS(rv, rv);
data->mMustNotify = PR_TRUE;
return rv;
}
NS_IMETHODIMP
@ -1766,37 +1833,42 @@ CSSLoaderImpl::LoadChildSheet(nsICSSStyleSheet* aParentSheet,
NS_ADDREF(data);
// Load completion will release the data
return LoadSheet(data, state);
rv = LoadSheet(data, state);
NS_ENSURE_SUCCESS(rv, rv);
data->mMustNotify = PR_TRUE;
return rv;
}
NS_IMETHODIMP
CSSLoaderImpl::LoadAgentSheet(nsIURI* aURL,
nsICSSStyleSheet** aSheet)
CSSLoaderImpl::LoadSheetSync(nsIURI* aURL, nsICSSStyleSheet** aSheet)
{
LOG(("CSSLoaderImpl::LoadAgentSheet synchronous"));
return InternalLoadAgentSheet(aURL, aSheet, nsnull);
LOG(("CSSLoaderImpl::LoadSheetSync"));
return InternalLoadNonDocumentSheet(aURL, aSheet, nsnull);
}
NS_IMETHODIMP
CSSLoaderImpl::LoadAgentSheet(nsIURI* aURL,
nsICSSLoaderObserver* aObserver)
CSSLoaderImpl::LoadSheet(nsIURI* aURL, nsICSSLoaderObserver* aObserver)
{
LOG(("CSSLoaderImpl::LoadAgentSheet asynchronous"));
return InternalLoadAgentSheet(aURL, nsnull, aObserver);
LOG(("CSSLoaderImpl::LoadSheet api call"));
return InternalLoadNonDocumentSheet(aURL, nsnull, aObserver);
}
nsresult
CSSLoaderImpl::InternalLoadAgentSheet(nsIURI* aURL,
nsICSSStyleSheet** aSheet,
nsICSSLoaderObserver* aObserver)
CSSLoaderImpl::InternalLoadNonDocumentSheet(nsIURI* aURL,
nsICSSStyleSheet** aSheet,
nsICSSLoaderObserver* aObserver)
{
NS_PRECONDITION(aURL, "Must have a URI to load");
NS_PRECONDITION((!aSheet || !aObserver) && (aSheet || aObserver),
"One or the other please, at most one");
NS_ASSERTION(mParsingDatas.Count() == 0, "We're in the middle of a parse?");
LOG_URI(" Agent uri: '%s'", aURL);
LOG_URI(" Non-document sheet uri: '%s'", aURL);
if (aSheet) {
*aSheet = nsnull;
}
if (!mEnabled) {
LOG_WARN((" Not enabled"));
@ -1815,19 +1887,14 @@ CSSLoaderImpl::InternalLoadAgentSheet(nsIURI* aURL,
rv = PrepareSheet(sheet, empty, empty, nsnull);
NS_ENSURE_SUCCESS(rv, rv);
if (aSheet) {
*aSheet = nsnull;
}
if (state == eSheetComplete) {
LOG((" Sheet already complete"));
if (aSheet) {
*aSheet = sheet;
NS_ADDREF(*aSheet);
sheet.swap(*aSheet);
} else {
aObserver->StyleSheetLoaded(sheet, PR_TRUE);
rv = PostLoadEvent(aURL, sheet, aObserver, nsnull, PR_FALSE);
}
return NS_OK;
return rv;
}
SheetLoadData* data = new SheetLoadData(this, aURL, sheet, syncLoad, aObserver);
@ -1839,15 +1906,120 @@ CSSLoaderImpl::InternalLoadAgentSheet(nsIURI* aURL,
NS_ADDREF(data);
rv = LoadSheet(data, state);
NS_ENSURE_SUCCESS(rv, rv);
if (NS_SUCCEEDED(rv) && aSheet) {
*aSheet = sheet;
NS_ADDREF(*aSheet);
if (aSheet) {
sheet.swap(*aSheet);
} else {
data->mMustNotify = PR_TRUE;
}
return rv;
}
PR_BEGIN_EXTERN_C
PR_STATIC_CALLBACK(void*) HandleStyleSheetLoadedEvent(PLEvent* aEvent);
PR_STATIC_CALLBACK(void) DestroyStyleSheetLoadedEvent(PLEvent* aEvent);
PR_END_EXTERN_C
PR_STATIC_CALLBACK(void*)
HandleStyleSheetLoadedEvent(PLEvent* aEvent)
{
SheetLoadData* data = NS_STATIC_CAST(SheetLoadData*, aEvent);
data->mLoader->HandleLoadEvent(data);
return nsnull;
}
PR_STATIC_CALLBACK(void)
DestroyStyleSheetLoadedEvent(PLEvent* aEvent)
{
SheetLoadData* data = NS_STATIC_CAST(SheetLoadData*, aEvent);
data->mLoader->DestroyLoadEvent(data);
}
nsresult
CSSLoaderImpl::PostLoadEvent(nsIURI* aURI,
nsICSSStyleSheet* aSheet,
nsICSSLoaderObserver* aObserver,
nsIParser* aParserToUnblock,
PRBool aWasAlternate)
{
LOG(("nsCSSLoader::PostLoadEvent"));
NS_PRECONDITION(aSheet, "Must have sheet");
// XXXbz can't assert this yet; have to post even with a null
// observer, since we may need to unblock the parser
// NS_PRECONDITION(aObserver, "Must have observer");
nsCOMPtr<nsIEventQueue> eventQ;
nsresult rv = nsContentUtils::EventQueueService()->
GetSpecialEventQueue(nsIEventQueueService::UI_THREAD_EVENT_QUEUE,
getter_AddRefs(eventQ));
NS_ENSURE_TRUE(eventQ, rv);
SheetLoadData* evt =
new SheetLoadData(this, EmptyString(), // title doesn't matter here
aParserToUnblock,
aURI,
aSheet,
nsnull, // owning element doesn't matter here
aWasAlternate,
aObserver);
NS_ENSURE_TRUE(evt, NS_ERROR_OUT_OF_MEMORY);
NS_ADDREF(evt);
PL_InitEvent(evt, this, ::HandleStyleSheetLoadedEvent,
::DestroyStyleSheetLoadedEvent);
// We'll unblock onload when we destroy the event, so make sure to block it
// now. We unblock from destruction, not firing, just in case events get
// revoked for some reason.
if (mDocument) {
mDocument->BlockOnload();
}
if (!mPostedEvents.AppendElement(evt)) {
PL_DestroyEvent(evt);
return NS_ERROR_OUT_OF_MEMORY;
}
rv = eventQ->PostEvent(evt);
if (NS_FAILED(rv)) {
PL_DestroyEvent(evt);
} else {
// We want to notify the observer for this data. We didn't want to set
// this earlier, since we would have returned an error _and_ notified.
evt->mMustNotify = PR_TRUE;
}
return rv;
}
void
CSSLoaderImpl::HandleLoadEvent(SheetLoadData* aEvent)
{
// XXXbz can't assert this yet.... May not have an observer because
// we're unblocking the parser
// NS_ASSERTION(aEvent->mObserver, "Must have observer");
NS_ASSERTION(aEvent->mSheet, "Must have sheet");
if (!aEvent->mIsCancelled) {
// SheetComplete will call Release(), but we want aEvent to survive to have
// DestroyStyleSheetLoadedEvent called on it!
NS_ADDREF(aEvent);
SheetComplete(aEvent, NS_OK);
}
}
void
CSSLoaderImpl::DestroyLoadEvent(SheetLoadData* aEvent)
{
mPostedEvents.RemoveElement(aEvent);
NS_RELEASE(aEvent);
if (mDocument) {
mDocument->UnblockOnload(PR_TRUE);
}
}
nsresult NS_NewCSSLoader(nsIDocument* aDocument, nsICSSLoader** aLoader)
{
CSSLoaderImpl* it = new CSSLoaderImpl();
@ -1876,20 +2048,37 @@ StopLoadingSheetCallback(nsIURI* aKey, SheetLoadData*& aData, void* aClosure)
aData->mIsLoading = PR_FALSE; // we will handle the removal right here
aData->mIsCancelled = PR_TRUE;
NS_STATIC_CAST(CSSLoaderImpl*,aClosure)->SheetComplete(aData, PR_FALSE);
NS_STATIC_CAST(CSSLoaderImpl*,aClosure)->SheetComplete(aData,
NS_BINDING_ABORTED);
return PL_DHASH_REMOVE;
}
PR_STATIC_CALLBACK(PRBool)
CancelSheetEventCallback(void* aData, void* aClosure)
{
NS_PRECONDITION(aData, "Must have data");
SheetLoadData* data = NS_STATIC_CAST(SheetLoadData*, aData);
data->mIsCancelled = PR_TRUE;
// Addref to keep |data| from dying, since we're not removing it from the
// event queue but plan to call SheetComplete on it.
NS_ADDREF(data);
data->mLoader->SheetComplete(data, NS_BINDING_ABORTED);
return PR_TRUE;
}
NS_IMETHODIMP
CSSLoaderImpl::Stop()
{
if (mLoadingDatas.Count() > 0) {
if (mLoadingDatas.IsInitialized() && mLoadingDatas.Count() > 0) {
mLoadingDatas.Enumerate(StopLoadingSheetCallback, this);
}
if (mPendingDatas.Count() > 0) {
if (mPendingDatas.IsInitialized() && mPendingDatas.Count() > 0) {
mPendingDatas.Enumerate(StopLoadingSheetCallback, this);
}
mPostedEvents.EnumerateForwards(CancelSheetEventCallback, nsnull);
mPostedEvents.Clear();
return NS_OK;
}
@ -1897,24 +2086,41 @@ NS_IMETHODIMP
CSSLoaderImpl::StopLoadingSheet(nsIURI* aURL)
{
NS_ENSURE_TRUE(aURL, NS_ERROR_NULL_POINTER);
if (mLoadingDatas.Count() > 0 || mPendingDatas.Count() > 0) {
SheetLoadData* loadData = nsnull;
SheetLoadData* loadData = nsnull;
if (mLoadingDatas.IsInitialized()) {
mLoadingDatas.Get(aURL, &loadData);
if (!loadData) {
mPendingDatas.Get(aURL, &loadData);
if (loadData) {
// have to remove from mPendingDatas ourselves, since
// SheetComplete won't do that.
mPendingDatas.Remove(aURL);
}
}
}
if (!loadData && mPendingDatas.IsInitialized()) {
mPendingDatas.Get(aURL, &loadData);
if (loadData) {
loadData->mIsCancelled = PR_TRUE;
SheetComplete(loadData, PR_FALSE);
// have to remove from mPendingDatas ourselves, since
// SheetComplete won't do that.
mPendingDatas.Remove(aURL);
}
}
if (!loadData) {
for (PRInt32 i = 0; i < mPostedEvents.Count(); ++i) {
SheetLoadData* curData = NS_STATIC_CAST(SheetLoadData*, mPostedEvents[i]);
PRBool equal;
if (curData->mURI && NS_SUCCEEDED(curData->mURI->Equals(aURL, &equal)) &&
equal) {
loadData = curData;
// Addref to keep |curData| from dying, since we're not removing it
// from the event queue but plan to call SheetComplete on it.
NS_ADDREF(curData);
mPostedEvents.RemoveElementAt(i);
break;
}
}
}
if (loadData) {
loadData->mIsCancelled = PR_TRUE;
SheetComplete(loadData, NS_BINDING_ABORTED);
}
return NS_OK;
}

View File

@ -67,13 +67,14 @@ class nsMediaList;
#include "nsURIHashKey.h"
#include "nsInterfaceHashtable.h"
#include "nsDataHashtable.h"
#include "plevent.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, agent sheets. The loader handles the following tasks:
* sheets, non-document sheets. The loader handles the following tasks:
*
* 1) Checking whether the load is allowed: CheckLoadAllowed()
* 2) Creation of the actual style sheet objects: CreateSheet()
@ -100,7 +101,8 @@ class nsMediaList;
* Data needed to properly load a stylesheet *
*********************************************/
class SheetLoadData : public nsIUnicharStreamLoaderObserver
class SheetLoadData : public PLEvent,
public nsIUnicharStreamLoaderObserver
{
public:
virtual ~SheetLoadData(void);
@ -111,6 +113,7 @@ public:
nsIURI* aURI,
nsICSSStyleSheet* aSheet,
nsIStyleSheetLinkingElement* aOwningElement,
PRBool aIsAlternate,
nsICSSLoaderObserver* aObserver);
// Data for loading a sheet linked from an @import rule
@ -120,7 +123,7 @@ public:
SheetLoadData* aParentData,
nsICSSLoaderObserver* aObserver);
// Data for loading an agent sheet
// Data for loading a non-document sheet
SheetLoadData(CSSLoaderImpl* aLoader,
nsIURI* aURI,
nsICSSStyleSheet* aSheet,
@ -166,25 +169,34 @@ public:
PRUint32 mPendingChildren;
// mSyncLoad is true when the load needs to be synchronous -- right
// now only for LoadAgentSheet with no observer
PRPackedBool mSyncLoad;
// now only for LoadSheetSync and children of sync loads.
PRPackedBool mSyncLoad : 1;
// mIsAgent is true if the load was triggered by LoadAgentSheet or
// an @import from an agent sheet. Agent loads can proceed even if
// we have no document.
PRPackedBool mIsAgent;
// mIsNonDocumentSheet is true if the load was triggered by LoadSheetSync or
// LoadSheet or an @import from such a sheet. Non-document sheet loads can
// proceed even if we have no document.
PRPackedBool mIsNonDocumentSheet : 1;
// mIsLoading is true from the moment we are placed in the loader's
// "loading datas" table (right after the async channel is opened)
// to the moment we are removed from said table (due to the load
// completing or being cancelled).
PRPackedBool mIsLoading; // Set once the data is in the "loading" table
PRPackedBool mIsLoading : 1;
// mIsCancelled is set to true when a sheet load is stopped by
// Stop() or StopLoadingSheet(). SheetLoadData::OnStreamComplete()
// checks this to avoid parsing sheets that have been cancelled and
// such.
PRPackedBool mIsCancelled;
PRPackedBool mIsCancelled : 1;
// mMustNotify is true if the load data is being loaded async and
// the original function call that started the load has returned.
// XXXbz sort our relationship with load/error events!
PRPackedBool mMustNotify : 1;
// mWasAlternate is true if the sheet was an alternate when the load data was
// created.
PRPackedBool mWasAlternate : 1;
// This is the element that imported the sheet. Needed to get the
// charset set on it.
@ -237,28 +249,30 @@ public:
NS_IMETHOD LoadInlineStyle(nsIContent* aElement,
nsIUnicharInputStream* aStream,
PRUint32 aLineNumber,
const nsSubstring& aTitle,
const nsSubstring& aMedia,
const nsSubstring& aTitle,
const nsSubstring& aMedia,
nsIParser* aParserToUnblock,
PRBool& aCompleted,
nsICSSLoaderObserver* aObserver);
nsICSSLoaderObserver* aObserver,
PRBool* aCompleted,
PRBool* aIsAlternate);
NS_IMETHOD LoadStyleLink(nsIContent* aElement,
nsIURI* aURL,
const nsSubstring& aTitle,
const nsSubstring& aMedia,
const nsSubstring& aMedia,
PRBool aHasAlternateRel,
nsIParser* aParserToUnblock,
PRBool& aCompleted,
nsICSSLoaderObserver* aObserver);
nsICSSLoaderObserver* aObserver,
PRBool* aIsAlternate);
NS_IMETHOD LoadChildSheet(nsICSSStyleSheet* aParentSheet,
nsIURI* aURL,
nsMediaList* aMedia,
nsICSSImportRule* aRule);
NS_IMETHOD LoadAgentSheet(nsIURI* aURL, nsICSSStyleSheet** aSheet);
NS_IMETHOD LoadSheetSync(nsIURI* aURL, nsICSSStyleSheet** aSheet);
NS_IMETHOD LoadAgentSheet(nsIURI* aURL, nsICSSLoaderObserver* aObserver);
NS_IMETHOD LoadSheet(nsIURI* aURL, nsICSSLoaderObserver* aObserver);
// stop loading all sheets
NS_IMETHOD Stop(void);
@ -277,7 +291,10 @@ public:
NS_IMETHOD SetEnabled(PRBool aEnabled);
// local helper methods (some are public for access from statics)
PRBool IsAlternate(const nsAString& aTitle);
// 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,
@ -295,10 +312,14 @@ private:
// Pass in either a media string or the nsMediaList from the
// CSSParser. Don't pass both.
// If aIsAlternate is non-null, this method will set *aIsAlternate to
// correspond to the sheet's enabled state (which it will set no matter what)
nsresult PrepareSheet(nsICSSStyleSheet* aSheet,
const nsSubstring& aTitle,
const nsSubstring& aMediaString,
nsMediaList* aMediaList);
nsMediaList* aMediaList,
PRBool aHasAlternateRel = PR_FALSE,
PRBool *aIsAlternate = nsnull);
nsresult InsertSheetInDoc(nsICSSStyleSheet* aSheet,
nsIContent* aLinkingContent,
@ -308,11 +329,29 @@ private:
nsICSSStyleSheet* aParentSheet,
nsICSSImportRule* aParentRule);
nsresult InternalLoadAgentSheet(nsIURI* aURL,
nsICSSStyleSheet** aSheet,
nsICSSLoaderObserver* aObserver);
nsresult InternalLoadNonDocumentSheet(nsIURI* aURL,
nsICSSStyleSheet** aSheet,
nsICSSLoaderObserver* aObserver);
// Post a load event for aObserver to be notified about aSheet. The
// notification will be sent with status NS_OK unless the load event is
// 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,
nsIParser* aParserToUnblock,
PRBool aWasAlternate);
public:
// Handle an event posted by PostLoadEvent
void HandleLoadEvent(SheetLoadData* aEvent);
// Destroy an event created by PostLoadEvent
void DestroyLoadEvent(SheetLoadData* aEvent);
// Note: LoadSheet is responsible for releasing aLoadData and setting the
// sheet to complete on failure.
nsresult LoadSheet(SheetLoadData* aLoadData, StyleSheetState aSheetState);
protected:
@ -325,7 +364,7 @@ protected:
PRBool& aCompleted);
public:
void SheetComplete(SheetLoadData* aLoadData, PRBool aSucceeded);
void SheetComplete(SheetLoadData* aLoadData, nsresult aStatus);
static nsCOMArray<nsICSSParser>* gParsers; // array of idle CSS parsers
@ -351,6 +390,11 @@ private:
// some. Allocate some storage, what the hell.
nsAutoVoidArray mParsingDatas;
public:
// The array of posted stylesheet loaded events (SheetLoadDatas) we have.
// Note that these are rare. Public so the event destructor func can get at
// it easily.
nsSmallVoidArray mPostedEvents;
};
#endif // nsCSSLoader_h__

View File

@ -46,7 +46,6 @@
#include "nsCRT.h"
#include "nsIAtom.h"
#include "nsIURL.h"
#include "nsIServiceManager.h"
#include "nsISupportsArray.h"
#include "pldhash.h"
@ -2566,7 +2565,9 @@ nsCSSStyleSheet::ReplaceRuleInGroup(nsICSSGroupRule* aGroup,
// nsICSSLoaderObserver implementation
NS_IMETHODIMP
nsCSSStyleSheet::StyleSheetLoaded(nsICSSStyleSheet*aSheet, PRBool aNotify)
nsCSSStyleSheet::StyleSheetLoaded(nsICSSStyleSheet* aSheet,
PRBool aWasAlternate,
nsresult aStatus)
{
#ifdef DEBUG
nsCOMPtr<nsIStyleSheet> styleSheet(do_QueryInterface(aSheet));
@ -2578,7 +2579,7 @@ nsCSSStyleSheet::StyleSheetLoaded(nsICSSStyleSheet*aSheet, PRBool aNotify)
NS_ASSERTION(thisSheet == parentSheet, "We are being notified of a sheet load for a sheet that is not our child!\n");
#endif
if (mDocument && aNotify) {
if (mDocument && NS_SUCCEEDED(aStatus)) {
nsCOMPtr<nsICSSImportRule> ownerRule;
aSheet->GetOwnerRule(getter_AddRefs(ownerRule));

View File

@ -147,7 +147,8 @@ public:
NS_IMETHOD DropRuleProcessor(nsCSSRuleProcessor* aProcessor);
// nsICSSLoaderObserver interface
NS_IMETHOD StyleSheetLoaded(nsICSSStyleSheet*aSheet, PRBool aNotify);
NS_IMETHOD StyleSheetLoaded(nsICSSStyleSheet* aSheet, PRBool aWasAlternate,
nsresult aStatus);
nsresult EnsureUniqueInner();

View File

@ -55,9 +55,9 @@ class nsMediaList;
class nsICSSImportRule;
// IID for the nsICSSLoader interface
// 6c50f676-c764-4f2f-b62b-99d1076b44e9
// ff43802a-b355-41f2-919d-5c7ab3dbfb91
#define NS_ICSS_LOADER_IID \
{0x6c50f676, 0xc764, 0x4f2f, {0xb6, 0x2b, 0x99, 0xd1, 0x07, 0x6b, 0x44, 0xe9}}
{0xff43802a, 0xb355, 0x41f2, {0x91, 0x9d, 0x5c, 0x7a, 0xb3, 0xdb, 0xfb, 0x91}}
typedef void (*nsCSSLoaderCallbackFunc)(nsICSSStyleSheet* aSheet, void *aData, PRBool aDidNotify);
@ -78,53 +78,135 @@ public:
nsICSSParser** aParser) = 0;
NS_IMETHOD RecycleParser(nsICSSParser* aParser) = 0;
// XXX No one uses the aDefaultNameSpaceID params.... do we need them?
// XXXbz sort out what the deal is with events! When should they fire?
// Load an inline style sheet
// - if aCompleted is PR_TRUE, the sheet is fully loaded, don't
// block for it.
// - if aCompleted is PR_FALSE, the sheet is still loading and
// will be marked complete when complete
/**
* Load an inline style sheet. If a successful result is returned and
* *aCompleted is false, then aObserver is guaranteed to be notified
* asynchronously once the sheet is marked complete. If an error is
* returned, or if *aCompleted is true, aObserver will not be notified. In
* addition to parsing the sheet, this method will insert it into the
* stylesheet list of this CSSLoader's document.
*
* @param aElement the element linking to the stylesheet. This must not be
* null and must implement nsIStyleSheetLinkingElement.
* @param aStream the character stream that holds the stylesheet data.
* @param aLineNumber the line number at which the stylesheet data started.
* @param aTitle the title of the sheet.
* @param aMedia the media string for the sheet.
* @param aParserToUnblock the parser to unblock when the load completes.
* Only loads that returned false for both aIsAlternate and
* aCompleted will unblock the parser.
* @param aObserver the observer to notify when the load completes.
* May be null.
* @param [out] aCompleted whether parsing of the sheet completed.
* @param [out] aIsAlternate whether the stylesheet ended up being an
* alternate sheet.
*/
NS_IMETHOD LoadInlineStyle(nsIContent* aElement,
nsIUnicharInputStream* aStream,
PRUint32 aLineNumber,
const nsSubstring& aTitle,
const nsSubstring& aMedia,
const nsSubstring& aTitle,
const nsSubstring& aMedia,
nsIParser* aParserToUnblock,
PRBool& aCompleted,
nsICSSLoaderObserver* aObserver) = 0;
nsICSSLoaderObserver* aObserver,
PRBool* aCompleted,
PRBool* aIsAlternate) = 0;
// Load a linked style sheet
// - if aCompleted is PR_TRUE, the sheet is fully loaded, don't
// block for it.
// - if aCompleted is PR_FALSE, the sheet is still loading and
// will be marked complete when complete
/**
* Load a linked (document) stylesheet. If a successful result is returned,
* aObserver is guaranteed to be notified asynchronously once the sheet is
* loaded and marked complete. If an error is returned, aObserver will not
* be notified. In addition to loading the sheet, this method will insert it
* into the stylesheet list of this CSSLoader's document.
*
* @param aElement the element linking to the the stylesheet. May be null.
* @param aURL the URL of the sheet.
* @param aTitle the title of the sheet.
* @param aMedia the media string for the sheet.
* @param aHasAlternateRel whether the rel for this link included
* "alternate".
* @param aParserToUnblock the parser to unblock when the load completes.
* Only loads that returned false for aIsAlternate will unblock
* the parser.
* @param aObserver the observer to notify when the load completes.
* May be null.
* @param [out] aIsAlternate whether the stylesheet actually ended up beinga
* an alternate sheet. Note that this need not match
* aHasAlternateRel.
*/
NS_IMETHOD LoadStyleLink(nsIContent* aElement,
nsIURI* aURL,
const nsSubstring& aTitle,
const nsSubstring& aMedia,
const nsSubstring& aTitle,
const nsSubstring& aMedia,
PRBool aHasAlternateRel,
nsIParser* aParserToUnblock,
PRBool& aCompleted,
nsICSSLoaderObserver* aObserver) = 0;
nsICSSLoaderObserver* aObserver,
PRBool* aIsAlternate) = 0;
// Load a child style sheet (@import)
/**
* Load a child (@import-ed) style sheet. In addition to loading the sheet,
* this method will insert it into the child sheet list of aParentSheet. If
* there is no sheet currently being parsed and the child sheet is not
* complete when this method returns, then when the child sheet becomes
* complete aParentSheet will be QIed to nsICSSLoaderObserver and
* asynchronously notified, just like for LoadStyleLink. Note that if the
* child sheet is already complete when this method returns, no
* nsICSSLoaderObserver notification will be sent.
*
* @param aParentSheet the parent of this child sheet
* @param aURL the URL of the child sheet
* @param aMedia the already-parsed media list for the child sheet
* @param aRule the @import rule importing this child. This is used to
* properly order the child sheet list of aParentSheet.
*/
NS_IMETHOD LoadChildSheet(nsICSSStyleSheet* aParentSheet,
nsIURI* aURL,
nsMediaList* aMedia,
nsICSSImportRule* aRule) = 0;
// Load a user agent or user sheet. The sheet is loaded
// synchronously, including @imports from it.
NS_IMETHOD LoadAgentSheet(nsIURI* aURL, nsICSSStyleSheet** aSheet) = 0;
/**
* Synchronously load and return the stylesheet at aURL. Any child sheets
* will also be loaded synchronously. Note that synchronous loads over some
* protocols may involve spinning up a new event loop, so use of this method
* does NOT guarantee not receiving any events before the sheet loads. This
* method can be used to load sheets not associated with a document.
*
* @param aURL the URL of the sheet to load
* @param [out] aSheet the loaded, complete sheet.
*
* NOTE: At the moment, this method assumes the sheet will be UTF-8, but
* ideally it would allow arbitrary encodings. Callers should NOT depend on
* non-UTF8 sheets being treated as UTF-8 by this method.
*
* NOTE: A successful return from this method doesn't indicate anything about
* whether the data could be parsed as CSS and doesn't indicate anything
* about the status of child sheets of the returned sheet.
*/
NS_IMETHOD LoadSheetSync(nsIURI* aURL, nsICSSStyleSheet** aSheet) = 0;
// Load a user agent or user sheet. The sheet is loaded
// asynchronously and the observer notified when the load finishes.
NS_IMETHOD LoadAgentSheet(nsIURI* aURL, nsICSSLoaderObserver* aObserver) = 0;
/**
* Asynchronously load the stylesheet at aURL. If a successful result is
* returned, aObserver is guaranteed to be notified asynchronously once the
* sheet is loaded and marked complete. This method can be used to load
* sheets not associated with a document.
*
* @param aURL the URL of the sheet to load
* @param aObserver the observer to notify when the load completes.
* Must not be null.
*/
NS_IMETHOD LoadSheet(nsIURI* aURL, nsICSSLoaderObserver* aObserver) = 0;
// stop loading all sheets
/**
* Stop loading all sheets. All nsICSSLoaderObservers involved will be
* notified with NS_BINDING_ABORTED as the status, possibly synchronously.
*/
NS_IMETHOD Stop(void) = 0;
// stop loading one sheet
/**
* Stop loading one sheet. The nsICSSLoaderObserver involved will be
* notified with NS_BINDING_ABORTED as the status, possibly synchronously.
*/
NS_IMETHOD StopLoadingSheet(nsIURI* aURL) = 0;
/**

View File

@ -40,8 +40,8 @@
#include "nsISupports.h"
#define NS_ICSSLOADEROBSERVER_IID \
{ 0xa6cf9116, 0x15b3, 0x11d2, \
{0x93, 0x2e, 0x00, 0x80, 0x5f, 0x8a, 0xdd, 0x32} }
{ 0xf5e8eb0f, 0x4c44, 0x49d5, \
{0xb1, 0xe9, 0xab, 0x39, 0x23, 0x93, 0xc0, 0xf8} }
class nsICSSStyleSheet;
@ -49,7 +49,22 @@ class nsICSSLoaderObserver : public nsISupports {
public:
NS_DEFINE_STATIC_IID_ACCESSOR(NS_ICSSLOADEROBSERVER_IID)
NS_IMETHOD StyleSheetLoaded(nsICSSStyleSheet*aSheet, PRBool aNotify) = 0;
/**
* StyleSheetLoaded is called after aSheet is marked complete and before any
* load events associated with aSheet are fired.
* @param aSheet the sheet that was loaded
* @param aWasAlternate whether the sheet was an alternate. This will always
* match the value LoadStyleLink or LoadInlineStyle returned in
* aIsAlternate if one of those methods were used to load the sheet,
* and will always be false otherwise.
* @param aStatus is a success code if the sheet loaded successfully and a
* failure code otherwise. Note that successful load of aSheet
* doesn't indicate anything about whether the data actually parsed
* as CSS, and doesn't indicate anything about the status of any child
* sheets of aSheet.
*/
NS_IMETHOD StyleSheetLoaded(nsICSSStyleSheet* aSheet, PRBool aWasAlternate,
nsresult aStatus) = 0;
};
#endif // nsICSSLoaderObserver_h___

View File

@ -229,7 +229,7 @@ nsLayoutStylesheetCache::LoadSheet(nsIURI* aURI, nsCOMPtr<nsICSSStyleSheet> &aSh
nsCOMPtr<nsICSSLoader> cssLoader = do_GetService(kCSSLoaderCID);
if (!cssLoader) return;
cssLoader->LoadAgentSheet(aURI, getter_AddRefs(aSheet));
cssLoader->LoadSheetSync(aURI, getter_AddRefs(aSheet));
}
nsLayoutStylesheetCache*

View File

@ -2987,7 +2987,7 @@ nsresult nsChromeRegistry::LoadStyleSheetWithURL(nsIURI* aURL, nsICSSStyleSheet*
nsCOMPtr<nsICSSLoader> cssLoader = do_GetService(kCSSLoaderCID);
if (!cssLoader) return NS_ERROR_FAILURE;
return cssLoader->LoadAgentSheet(aURL, aSheet);
return cssLoader->LoadSheetSync(aURL, aSheet);
}
nsresult nsChromeRegistry::LoadInstallDataSource()