diff --git a/gfx/public/nsDeviceContext.h b/gfx/public/nsDeviceContext.h index 099e419fd460..0d8c40280553 100644 --- a/gfx/public/nsDeviceContext.h +++ b/gfx/public/nsDeviceContext.h @@ -45,6 +45,8 @@ #include "nsCOMPtr.h" #include "nsIAtom.h" #include "nsVoidArray.h" +#include "nsIObserver.h" +#include "nsIObserverService.h" class nsIImageRequest; class nsHashtable; @@ -59,6 +61,9 @@ public: NS_IMETHOD GetDeviceContext(nsIDeviceContext *&aContext) const; NS_IMETHOD GetMetricsFor(const nsFont& aFont, nsIAtom* aLangGroup, nsIFontMetrics *&aMetrics); + + nsresult FontMetricsDeleted(const nsIFontMetrics* aFontMetrics); + nsresult Compact(); nsresult Flush(); /* printer device context classes may create their own * subclasses of nsFontCache (and override this method) and override @@ -72,12 +77,14 @@ protected: // ownership is implied. MMP. }; -class NS_GFX DeviceContextImpl : public nsIDeviceContext +class NS_GFX DeviceContextImpl : public nsIDeviceContext, + public nsIObserver { public: DeviceContextImpl(); NS_DECL_ISUPPORTS + NS_DECL_NSIOBSERVER NS_IMETHOD Init(nsNativeWidget aWidget); @@ -119,7 +126,7 @@ public: PRBool& aAliased); NS_IMETHOD CreateFontCache(); - + NS_IMETHOD FontMetricsDeleted(const nsIFontMetrics* aFontMetrics); NS_IMETHOD FlushFontCache(void); NS_IMETHOD GetDepth(PRUint32& aDepth); diff --git a/gfx/public/nsIDeviceContext.h b/gfx/public/nsIDeviceContext.h index 642c23c232a7..42e7978f2952 100644 --- a/gfx/public/nsIDeviceContext.h +++ b/gfx/public/nsIDeviceContext.h @@ -338,6 +338,12 @@ public: NS_IMETHOD GetLocalFontName(const nsString& aFaceName, nsString& aLocalName, PRBool& aAliased) = 0; + /** + * Notification when a font metrics instance created for this device is + * about to be deleted + */ + NS_IMETHOD FontMetricsDeleted(const nsIFontMetrics* aFontMetrics) = 0; + /** * Attempt to free up resoruces by flushing out any fonts no longer * referenced by anything other than the font cache itself. diff --git a/gfx/src/beos/nsFontMetricsBeOS.cpp b/gfx/src/beos/nsFontMetricsBeOS.cpp index 104ce05654d5..dedb8dbcd449 100644 --- a/gfx/src/beos/nsFontMetricsBeOS.cpp +++ b/gfx/src/beos/nsFontMetricsBeOS.cpp @@ -66,6 +66,10 @@ nsFontMetricsBeOS::~nsFontMetricsBeOS() delete mFont; mFont = nsnull; } + if (mDeviceContext) { + mDeviceContext->FontMetricsDeleted(this); + mDeviceContext = nsnull; + } } NS_IMPL_ISUPPORTS1(nsFontMetricsBeOS, nsIFontMetrics) @@ -94,6 +98,7 @@ NS_IMETHODIMP nsFontMetricsBeOS::Init(const nsFont& aFont, nsIAtom* aLangGroup, nsAutoString firstFace; mLangGroup = aLangGroup; + mDeviceContext = aContext; if (NS_OK != aContext->FirstExistingFont(aFont, firstFace)) { aFont.GetFirstFamily(firstFace); } @@ -166,6 +171,7 @@ NS_IMETHODIMP nsFontMetricsBeOS::Init(const nsFont& aFont, nsIAtom* aLangGroup, NS_IMETHODIMP nsFontMetricsBeOS::Destroy() { + mDeviceContext = nsnull; return NS_OK; } diff --git a/gfx/src/gtk/nsFontMetricsGTK.cpp b/gfx/src/gtk/nsFontMetricsGTK.cpp index 9f0b5ef05b0a..85c1750d5b6e 100644 --- a/gfx/src/gtk/nsFontMetricsGTK.cpp +++ b/gfx/src/gtk/nsFontMetricsGTK.cpp @@ -1069,6 +1069,11 @@ nsFontMetricsGTK::~nsFontMetricsGTK() mWesternFont = nsnull; + if (mDeviceContext) { + mDeviceContext->FontMetricsDeleted(this); + mDeviceContext = nsnull; + } + if (!--gFontMetricsGTKCount) { FreeGlobals(); } @@ -1237,7 +1242,7 @@ NS_IMETHODIMP nsFontMetricsGTK::Init(const nsFont& aFont, nsIAtom* aLangGroup, NS_IMETHODIMP nsFontMetricsGTK::Destroy() { -// NS_IF_RELEASE(mDeviceContext); + mDeviceContext = nsnull; return NS_OK; } diff --git a/gfx/src/mac/nsFontMetricsMac.cpp b/gfx/src/mac/nsFontMetricsMac.cpp index 253260210ec2..9fef8f78dde6 100644 --- a/gfx/src/mac/nsFontMetricsMac.cpp +++ b/gfx/src/mac/nsFontMetricsMac.cpp @@ -63,6 +63,10 @@ nsFontMetricsMac :: ~nsFontMetricsMac() delete mFont; mFont = nsnull; } + if (mContext) { + mContext->FontMetricsDeleted(this); + mContext = nsnull; + } } //------------------------------------------------------------------------ @@ -240,6 +244,7 @@ void nsFontMetricsMac::RealizeFont() NS_IMETHODIMP nsFontMetricsMac::Destroy() { + mContext = nsnull; return NS_OK; } diff --git a/gfx/src/nsDeviceContext.cpp b/gfx/src/nsDeviceContext.cpp index 0ddf77e8be6c..62bf34f15c56 100644 --- a/gfx/src/nsDeviceContext.cpp +++ b/gfx/src/nsDeviceContext.cpp @@ -34,7 +34,7 @@ #include "nsUnicharUtils.h" -NS_IMPL_ISUPPORTS1(DeviceContextImpl, nsIDeviceContext) +NS_IMPL_ISUPPORTS2(DeviceContextImpl, nsIDeviceContext, nsIObserver) DeviceContextImpl :: DeviceContextImpl() { @@ -63,6 +63,10 @@ static PRBool PR_CALLBACK DeleteValue(nsHashKey* aKey, void* aValue, void* closu DeviceContextImpl :: ~DeviceContextImpl() { + nsCOMPtr obs(do_GetService("@mozilla.org/observer-service;1")); + if (obs) + obs->RemoveObserver(this, "memory-pressure"); + if (nsnull != mFontCache) { delete mFontCache; @@ -82,12 +86,27 @@ DeviceContextImpl :: ~DeviceContextImpl() } +NS_IMETHODIMP +DeviceContextImpl::Observe(nsISupports* aSubject, const char* aTopic, const PRUnichar* aSomeData) +{ + if (mFontCache && !nsCRT::strcmp(aTopic, "memory-pressure")) { + mFontCache->Compact(); + } + return NS_OK; +} + NS_IMETHODIMP DeviceContextImpl :: Init(nsNativeWidget aWidget) { mWidget = aWidget; CommonInit(); + // register as a memory-pressure observer to free font resources + // in low-memory situations. + nsCOMPtr obs(do_GetService("@mozilla.org/observer-service;1")); + if (obs) + obs->AddObserver(this, "memory-pressure", PR_FALSE); + return NS_OK; } @@ -223,6 +242,14 @@ NS_IMETHODIMP DeviceContextImpl::CreateFontCache() return NS_OK; } +NS_IMETHODIMP DeviceContextImpl::FontMetricsDeleted(const nsIFontMetrics* aFontMetrics) +{ + if (mFontCache) { + mFontCache->FontMetricsDeleted(aFontMetrics); + } + return NS_OK; +} + void DeviceContextImpl::GetLocaleLangGroup(void) { @@ -604,24 +631,23 @@ nsFontCache :: GetMetricsFor(const nsFont& aFont, nsIAtom* aLangGroup, nsIFontMetrics *&aMetrics) { // First check our cache - PRInt32 n = mFontMetrics.Count()-1; - // start from the end, which is where we put the most-recent-used element - for (PRInt32 cnt = n; cnt >= 0; --cnt) - { - nsIFontMetrics* metrics = NS_STATIC_CAST(nsIFontMetrics*, mFontMetrics[cnt]); + nsIFontMetrics* fm; + PRInt32 n = mFontMetrics.Count() - 1; + for (PRInt32 i = n; i >= 0; --i) { + fm = NS_STATIC_CAST(nsIFontMetrics*, mFontMetrics[i]); const nsFont* font; - metrics->GetFont(font); - if (aFont.Equals(*font)) { + fm->GetFont(font); + if (font->Equals(aFont)) { nsCOMPtr langGroup; - metrics->GetLangGroup(getter_AddRefs(langGroup)); + fm->GetLangGroup(getter_AddRefs(langGroup)); if (aLangGroup == langGroup.get()) { - if (cnt != n) { + if (i != n) { // promote it to the end of the cache - mFontMetrics.MoveElement(cnt, n); + mFontMetrics.MoveElement(i, n); } - NS_ADDREF(aMetrics = metrics); + NS_ADDREF(aMetrics = fm); return NS_OK; } } @@ -629,28 +655,48 @@ nsFontCache :: GetMetricsFor(const nsFont& aFont, nsIAtom* aLangGroup, // It's not in the cache. Get font metrics and then cache them. - nsIFontMetrics *fm = nsnull; + aMetrics = nsnull; nsresult rv = CreateFontMetricsInstance(&fm); - - if (NS_FAILED(rv)) { - aMetrics = nsnull; - return rv; - } - + if (NS_FAILED(rv)) return rv; rv = fm->Init(aFont, aLangGroup, mContext); + if (NS_SUCCEEDED(rv)) { + // the mFontMetrics list has the "head" at the end, because append is + // cheaper than insert + mFontMetrics.AppendElement(fm); + aMetrics = fm; + NS_ADDREF(aMetrics); + return NS_OK; + } + fm->Destroy(); + NS_RELEASE(fm); - if (NS_FAILED(rv)) { - aMetrics = nsnull; - return rv; + // One reason why Init() fails is because the system is running out of resources. + // e.g., on Win95/98 only a very limited number of GDI objects are available. + // Compact the cache and try again. + + Compact(); + rv = CreateFontMetricsInstance(&fm); + if (NS_FAILED(rv)) return rv; + rv = fm->Init(aFont, aLangGroup, mContext); + if (NS_SUCCEEDED(rv)) { + mFontMetrics.AppendElement(fm); + aMetrics = fm; + NS_ADDREF(aMetrics); + return NS_OK; + } + fm->Destroy(); + NS_RELEASE(fm); + + // could not setup a new one, send an old one (XXX search a "best match"?) + + n = mFontMetrics.Count() - 1; // could have changed in Compact() + if (n >= 0) { + aMetrics = NS_STATIC_CAST(nsIFontMetrics*, mFontMetrics[n]); + NS_ADDREF(aMetrics); + return NS_OK; } - // the mFontMetrics list has the "head" at the end, because append is - // cheaper than insert - mFontMetrics.AppendElement(fm); - - NS_ADDREF(fm); - aMetrics = fm; - return NS_OK; + return rv; } /* PostScript and Xprint module may override this method to create @@ -663,14 +709,37 @@ nsFontCache::CreateFontMetricsInstance(nsIFontMetrics** fm) return CallCreateInstance(kFontMetricsCID, fm); } +nsresult nsFontCache::FontMetricsDeleted(const nsIFontMetrics* aFontMetrics) +{ + mFontMetrics.RemoveElement((void*)aFontMetrics); + return NS_OK; +} + +nsresult nsFontCache::Compact() +{ + // Need to loop backward because the running element can be removed on the way + for (PRInt32 i = mFontMetrics.Count()-1; i >= 0; --i) { + nsIFontMetrics* fm = NS_STATIC_CAST(nsIFontMetrics*, mFontMetrics[i]); + nsIFontMetrics* oldfm = fm; + // Destroy() isn't here because we want our device context to be notified + NS_RELEASE(fm); // this will reset fm to nsnull + // if the font is really gone, it would have called back in + // FontMetricsDeleted() and would have removed itself + if (mFontMetrics.IndexOf(oldfm) >= 0) { + // nope, the font is still there, so let's hold onto it too + NS_ADDREF(oldfm); + } + } + return NS_OK; +} nsresult nsFontCache :: Flush() { - PRInt32 i, n = mFontMetrics.Count(); - - for (i = 0; i < n; i++) - { - nsIFontMetrics* fm = (nsIFontMetrics*) mFontMetrics.ElementAt(i); + for (PRInt32 i = mFontMetrics.Count()-1; i >= 0; --i) { + nsIFontMetrics* fm = NS_STATIC_CAST(nsIFontMetrics*, mFontMetrics[i]); + // Destroy() will unhook our device context from the fm so that we won't + // waste time in triggering the notification of FontMetricsDeleted() + // in the subsequent release fm->Destroy(); NS_RELEASE(fm); } diff --git a/gfx/src/os2/nsFontMetricsOS2.cpp b/gfx/src/os2/nsFontMetricsOS2.cpp index c9426ccdd6e8..ca03446ddd57 100644 --- a/gfx/src/os2/nsFontMetricsOS2.cpp +++ b/gfx/src/os2/nsFontMetricsOS2.cpp @@ -274,10 +274,12 @@ nsFontMetricsOS2::nsFontMetricsOS2() nsFontMetricsOS2::~nsFontMetricsOS2() { - Destroy(); - delete mFont; delete mFontHandle; + if (mDeviceContext) { + mDeviceContext->FontMetricsDeleted(this); + mDeviceContext = nsnull; + } if (0 == --gFontMetricsOS2Count) { FreeGlobals(); } @@ -321,6 +323,7 @@ nsFontMetricsOS2::Init( const nsFont &aFont, nsIAtom* aLangGroup, nsresult nsFontMetricsOS2::Destroy() { + mDeviceContext = nsnull; return NS_OK; } diff --git a/gfx/src/photon/nsFontMetricsPh.cpp b/gfx/src/photon/nsFontMetricsPh.cpp index e2b7cdec7063..d3fdd26f77ab 100644 --- a/gfx/src/photon/nsFontMetricsPh.cpp +++ b/gfx/src/photon/nsFontMetricsPh.cpp @@ -125,9 +125,12 @@ nsFontMetricsPh :: ~nsFontMetricsPh( ) delete mFont; mFont = nsnull; } - mDeviceContext = nsnull; if (mFontHandle) free (mFontHandle); + if (mDeviceContext) { + mDeviceContext->FontMetricsDeleted(this); + mDeviceContext = nsnull; + } FreeGlobals(); } diff --git a/gfx/src/ps/nsFontMetricsPS.cpp b/gfx/src/ps/nsFontMetricsPS.cpp index 90d46a3f0725..255e620ef5f2 100644 --- a/gfx/src/ps/nsFontMetricsPS.cpp +++ b/gfx/src/ps/nsFontMetricsPS.cpp @@ -66,8 +66,10 @@ nsFontMetricsPS :: ~nsFontMetricsPS() mAFMInfo = nsnull; } - - mDeviceContext = nsnull; + if (mDeviceContext) { + mDeviceContext->FontMetricsDeleted(this); + mDeviceContext = nsnull; + } } NS_IMPL_ISUPPORTS1(nsFontMetricsPS, nsIFontMetrics) diff --git a/gfx/src/qt/nsFontMetricsQT.cpp b/gfx/src/qt/nsFontMetricsQT.cpp index 486e265de5d6..507247b97656 100644 --- a/gfx/src/qt/nsFontMetricsQT.cpp +++ b/gfx/src/qt/nsFontMetricsQT.cpp @@ -684,6 +684,10 @@ nsFontMetricsQT::~nsFontMetricsQT() delete mUserDefinedFont; mUserDefinedFont = nsnull; } + if (mDeviceContext) { + mDeviceContext->FontMetricsDeleted(this); + mDeviceContext = nsnull; + } if (!--gFontMetricsQTCount) { FreeGlobals(); if (mQFontDB) { @@ -893,6 +897,7 @@ QFontDatabase *nsFontMetricsQT::GetQFontDB() NS_IMETHODIMP nsFontMetricsQT::Destroy() { + mDeviceContext = nsnull; return NS_OK; } diff --git a/gfx/src/windows/nsFontMetricsWin.cpp b/gfx/src/windows/nsFontMetricsWin.cpp index ea421bc16a0f..e36992e4e35d 100644 --- a/gfx/src/windows/nsFontMetricsWin.cpp +++ b/gfx/src/windows/nsFontMetricsWin.cpp @@ -403,7 +403,10 @@ nsFontMetricsWin::~nsFontMetricsWin() } mLoadedFonts.Clear(); - mDeviceContext = nsnull; + if (mDeviceContext) { + mDeviceContext->FontMetricsDeleted(this); + mDeviceContext = nsnull; + } } #ifdef LEAK_DEBUG diff --git a/gfx/src/xlib/nsFontMetricsXlib.cpp b/gfx/src/xlib/nsFontMetricsXlib.cpp index 99d88ff225af..985e9af0b8f9 100644 --- a/gfx/src/xlib/nsFontMetricsXlib.cpp +++ b/gfx/src/xlib/nsFontMetricsXlib.cpp @@ -985,6 +985,11 @@ nsFontMetricsXlib::~nsFontMetricsXlib() mWesternFont = nsnull; mFontHandle = nsnull; + if (mDeviceContext) { + mDeviceContext->FontMetricsDeleted(this); + mDeviceContext = nsnull; + } + if (!--gFontMetricsXlibCount) { FreeGlobals(); } @@ -1192,7 +1197,7 @@ nsFontMetricsXlib::Init(const nsFont& aFont, nsIAtom* aLangGroup, NS_IMETHODIMP nsFontMetricsXlib::Destroy() { -// NS_IF_RELEASE(mDeviceContext); + mDeviceContext = nsnull; return NS_OK; }