From 0e81ab301beef39928ed4ff4d6344c4cae094584 Mon Sep 17 00:00:00 2001 From: "bzbarsky%mit.edu" Date: Thu, 8 Jun 2006 14:32:20 +0000 Subject: [PATCH] Make stylesheet service sheets apply dynamically. Bug 335689, r+sr=dbaron git-svn-id: svn://10.0.0.236/trunk@199502 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/layout/base/nsDocumentViewer.cpp | 2 + mozilla/layout/base/nsIStyleSheetService.idl | 9 +- mozilla/layout/base/nsPresShell.cpp | 95 ++++++++++++++++++++ mozilla/layout/base/nsStyleSheetService.cpp | 40 ++++++++- mozilla/layout/base/nsStyleSheetService.h | 5 ++ mozilla/layout/style/nsCSSRuleProcessor.h | 1 + mozilla/layout/style/nsStyleSet.h | 2 + 7 files changed, 147 insertions(+), 7 deletions(-) diff --git a/mozilla/layout/base/nsDocumentViewer.cpp b/mozilla/layout/base/nsDocumentViewer.cpp index 9300b28dac1..0ad5cb317e1 100644 --- a/mozilla/layout/base/nsDocumentViewer.cpp +++ b/mozilla/layout/base/nsDocumentViewer.cpp @@ -2165,6 +2165,8 @@ nsresult DocumentViewerImpl::CreateStyleSet(nsIDocument* aDocument, nsStyleSet** aStyleSet) { + // Make sure this does the same thing as PresShell::AddSheet wrt ordering. + // this should eventually get expanded to allow for creating // different sets for different media if (!mUAStyleSheet) { diff --git a/mozilla/layout/base/nsIStyleSheetService.idl b/mozilla/layout/base/nsIStyleSheetService.idl index 375ec5f0e2b..326af11058b 100644 --- a/mozilla/layout/base/nsIStyleSheetService.idl +++ b/mozilla/layout/base/nsIStyleSheetService.idl @@ -66,6 +66,9 @@ interface nsIStyleSheetService : nsISupports * * The relative ordering of two user or two agent sheets loaded via * this API is undefined. + * + * Sheets added via this API take effect on all documents, including + * already-loaded ones, immediately. */ void loadAndRegisterSheet(in nsIURI sheetURI, in unsigned long type); @@ -76,9 +79,9 @@ interface nsIStyleSheetService : nsISupports boolean sheetRegistered(in nsIURI sheetURI, in unsigned long type); /** - * Remove the style sheet at |sheetURI| from the list of style - * sheets specified by |type|. All documents loaded after - * this call will no longer use the style sheet. + * Remove the style sheet at |sheetURI| from the list of style sheets + * specified by |type|. The removal takes effect immediately, even for + * already-loaded documents. */ void unregisterSheet(in nsIURI sheetURI, in unsigned long type); }; diff --git a/mozilla/layout/base/nsPresShell.cpp b/mozilla/layout/base/nsPresShell.cpp index ec785566d0c..93e4f813699 100644 --- a/mozilla/layout/base/nsPresShell.cpp +++ b/mozilla/layout/base/nsPresShell.cpp @@ -161,6 +161,7 @@ #include "nsNetUtil.h" #include "nsEventDispatcher.h" #include "nsThreadUtils.h" +#include "nsStyleSheetService.h" // Drag & Drop, Clipboard #include "nsWidgetsCID.h" @@ -1358,6 +1359,14 @@ protected: nsresult SetPrefNoScriptRule(); nsresult SetPrefNoFramesRule(void); + /** + * Methods to handle changes to user and UA sheet lists that we get + * notified about. + */ + void AddUserSheet(nsISupports* aSheet); + void AddAgentSheet(nsISupports* aSheet); + void RemoveSheet(nsStyleSet::sheetType aType, nsISupports* aSheet); + nsICSSStyleSheet* mPrefStyleSheet; // mStyleSet owns it but we maintain a ref, may be null #ifdef DEBUG PRUint32 mUpdateCount; @@ -1794,6 +1803,10 @@ PresShell::Init(nsIDocument* aDocument, do_GetService("@mozilla.org/observer-service;1", &result); if (os) { os->AddObserver(this, NS_LINK_VISITED_EVENT_TOPIC, PR_FALSE); + os->AddObserver(this, "agent-sheet-added", PR_FALSE); + os->AddObserver(this, "user-sheet-added", PR_FALSE); + os->AddObserver(this, "agent-sheet-removed", PR_FALSE); + os->AddObserver(this, "user-sheet-removed", PR_FALSE); #ifdef MOZ_XUL os->AddObserver(this, "chrome-flush-skin-caches", PR_FALSE); #endif @@ -1852,6 +1865,10 @@ PresShell::Destroy() do_GetService("@mozilla.org/observer-service;1"); if (os) { os->RemoveObserver(this, NS_LINK_VISITED_EVENT_TOPIC); + os->RemoveObserver(this, "agent-sheet-added"); + os->RemoveObserver(this, "user-sheet-added"); + os->RemoveObserver(this, "agent-sheet-removed"); + os->RemoveObserver(this, "user-sheet-removed"); #ifdef MOZ_XUL os->RemoveObserver(this, "chrome-flush-skin-caches"); #endif @@ -2548,6 +2565,64 @@ nsresult PresShell::SetPrefFocusRules(void) return result; } +void +PresShell::AddUserSheet(nsISupports* aSheet) +{ + // Make sure this does what DocumentViewerImpl::CreateStyleSet does wrt + // ordering. We want this new sheet to come after all the existing stylesheet + // service sheets, but before other user sheets; see nsIStyleSheetService.idl + // for the ordering. Just remove and readd all the nsStyleSheetService + // sheets. + nsCOMPtr dummy = + do_GetService(NS_STYLESHEETSERVICE_CONTRACTID); + + mStyleSet->BeginUpdate(); + + nsStyleSheetService *sheetService = nsStyleSheetService::gInstance; + nsCOMArray & userSheets = *sheetService->UserStyleSheets(); + PRInt32 i; + // Iterate forwards when removing so the searches for RemoveStyleSheet are as + // short as possible. + for (i = 0; i < userSheets.Count(); ++i) { + mStyleSet->RemoveStyleSheet(nsStyleSet::eUserSheet, userSheets[i]); + } + + // Now iterate backwards, so that the order of userSheets will be the same as + // the order of sheets from it in the style set. + for (i = userSheets.Count() - 1; i >= 0; --i) { + mStyleSet->PrependStyleSheet(nsStyleSet::eUserSheet, userSheets[i]); + } + + mStyleSet->EndUpdate(); + + ReconstructStyleData(); +} + +void +PresShell::AddAgentSheet(nsISupports* aSheet) +{ + // Make sure this does what DocumentViewerImpl::CreateStyleSet does + // wrt ordering. + nsCOMPtr sheet = do_QueryInterface(aSheet); + if (!sheet) { + return; + } + + mStyleSet->AppendStyleSheet(nsStyleSet::eAgentSheet, sheet); + ReconstructStyleData(); +} + +void +PresShell::RemoveSheet(nsStyleSet::sheetType aType, nsISupports* aSheet) +{ + nsCOMPtr sheet = do_QueryInterface(aSheet); + if (!sheet) { + return; + } + + mStyleSet->RemoveStyleSheet(aType, sheet); + ReconstructStyleData(); +} NS_IMETHODIMP PresShell::SetDisplaySelection(PRInt16 aToggle) @@ -6800,6 +6875,26 @@ PresShell::Observe(nsISupports* aSubject, return NS_OK; } + if (!nsCRT::strcmp(aTopic, "agent-sheet-added") && mStyleSet) { + AddAgentSheet(aSubject); + return NS_OK; + } + + if (!nsCRT::strcmp(aTopic, "user-sheet-added") && mStyleSet) { + AddUserSheet(aSubject); + return NS_OK; + } + + if (!nsCRT::strcmp(aTopic, "agent-sheet-removed") && mStyleSet) { + RemoveSheet(nsStyleSet::eAgentSheet, aSubject); + return NS_OK; + } + + if (!nsCRT::strcmp(aTopic, "user-sheet-removed") && mStyleSet) { + RemoveSheet(nsStyleSet::eUserSheet, aSubject); + return NS_OK; + } + NS_WARNING("unrecognized topic in PresShell::Observe"); return NS_ERROR_FAILURE; } diff --git a/mozilla/layout/base/nsStyleSheetService.cpp b/mozilla/layout/base/nsStyleSheetService.cpp index 27ebe39a07e..ccfacfc9380 100644 --- a/mozilla/layout/base/nsStyleSheetService.cpp +++ b/mozilla/layout/base/nsStyleSheetService.cpp @@ -49,6 +49,7 @@ #include "nsICategoryManager.h" #include "nsISupportsPrimitives.h" #include "nsNetUtil.h" +#include "nsIObserverService.h" static NS_DEFINE_CID(kCSSLoaderCID, NS_CSS_LOADER_CID); @@ -96,7 +97,7 @@ nsStyleSheetService::RegisterFromEnumerator(nsICategoryManager *aManager, nsCOMPtr uri; NS_NewURI(getter_AddRefs(uri), spec); if (uri) - LoadAndRegisterSheet(uri, aSheetType); + LoadAndRegisterSheetInternal(uri, aSheetType); } } @@ -142,6 +143,26 @@ nsStyleSheetService::Init() NS_IMETHODIMP nsStyleSheetService::LoadAndRegisterSheet(nsIURI *aSheetURI, PRUint32 aSheetType) +{ + nsresult rv = LoadAndRegisterSheetInternal(aSheetURI, aSheetType); + if (NS_SUCCEEDED(rv)) { + const char* message = (aSheetType == AGENT_SHEET) ? + "agent-sheet-added" : "user-sheet-added"; + nsCOMPtr serv = + do_GetService("@mozilla.org/observer-service;1"); + if (serv) { + // We're guaranteed that the new sheet is the last sheet in + // mSheets[aSheetType] + const nsCOMArray & sheets = mSheets[aSheetType]; + serv->NotifyObservers(sheets[sheets.Count() - 1], message, nsnull); + } + } + return rv; +} + +nsresult +nsStyleSheetService::LoadAndRegisterSheetInternal(nsIURI *aSheetURI, + PRUint32 aSheetType) { NS_ENSURE_ARG(aSheetType == AGENT_SHEET || aSheetType == USER_SHEET); NS_ENSURE_ARG_POINTER(aSheetURI); @@ -151,9 +172,11 @@ nsStyleSheetService::LoadAndRegisterSheet(nsIURI *aSheetURI, nsresult rv = loader->LoadSheetSync(aSheetURI, getter_AddRefs(sheet)); NS_ENSURE_SUCCESS(rv, rv); - mSheets[aSheetType].AppendObject(sheet); + if (!mSheets[aSheetType].AppendObject(sheet)) { + rv = NS_ERROR_OUT_OF_MEMORY; + } - return NS_OK; + return rv; } NS_IMETHODIMP @@ -177,7 +200,16 @@ nsStyleSheetService::UnregisterSheet(nsIURI *sheetURI, PRUint32 aSheetType) PRInt32 foundIndex = FindSheetByURI(mSheets[aSheetType], sheetURI); NS_ENSURE_TRUE(foundIndex >= 0, NS_ERROR_INVALID_ARG); + nsCOMPtr sheet = mSheets[aSheetType][foundIndex]; mSheets[aSheetType].RemoveObjectAt(foundIndex); - + + const char* message = (aSheetType == AGENT_SHEET) ? + "agent-sheet-removed" : "user-sheet-removed"; + nsCOMPtr serv = + do_GetService("@mozilla.org/observer-service;1"); + if (serv) { + serv->NotifyObservers(sheet, message, nsnull); + } + return NS_OK; } diff --git a/mozilla/layout/base/nsStyleSheetService.h b/mozilla/layout/base/nsStyleSheetService.h index fe63ef71a63..1f9c85547d2 100644 --- a/mozilla/layout/base/nsStyleSheetService.h +++ b/mozilla/layout/base/nsStyleSheetService.h @@ -80,6 +80,11 @@ class nsStyleSheetService : public nsIStyleSheetService NS_HIDDEN_(PRInt32) FindSheetByURI(const nsCOMArray &sheets, nsIURI *sheetURI); + // Like LoadAndRegisterSheet, but doesn't notify. If succesful, the + // new sheet will be the last sheet in mSheets[aSheetType]. + NS_HIDDEN_(nsresult) LoadAndRegisterSheetInternal(nsIURI *aSheetURI, + PRUint32 aSheetType); + nsCOMArray mSheets[2]; }; diff --git a/mozilla/layout/style/nsCSSRuleProcessor.h b/mozilla/layout/style/nsCSSRuleProcessor.h index 3704b50013a..d536b2908bf 100644 --- a/mozilla/layout/style/nsCSSRuleProcessor.h +++ b/mozilla/layout/style/nsCSSRuleProcessor.h @@ -85,6 +85,7 @@ public: protected: RuleCascadeData* GetRuleCascade(nsPresContext* aPresContext); + // The sheet order here is the same as in nsStyleSet::mSheets nsCOMArray mSheets; RuleCascadeData* mRuleCascades; diff --git a/mozilla/layout/style/nsStyleSet.h b/mozilla/layout/style/nsStyleSet.h index 0dd2151a972..079ca3bac11 100644 --- a/mozilla/layout/style/nsStyleSet.h +++ b/mozilla/layout/style/nsStyleSet.h @@ -241,6 +241,8 @@ class nsStyleSet static nsIURI *gQuirkURI; + // The sheets in each array in mSheets are stored with the most significant + // sheet last. nsCOMArray mSheets[eSheetTypeCount]; nsCOMPtr mRuleProcessors[eSheetTypeCount];