Compact the font cache in low-memory situations or when font handles are running out. b=109974, r=shanjian, sr=waterson

This commit is contained in:
rbs%maths.uq.edu.au 2001-11-20 19:47:28 +00:00
Родитель 770e9da69c
Коммит d8ce96d9f4
12 изменённых файлов: 163 добавлений и 44 удалений

Просмотреть файл

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

Просмотреть файл

@ -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.

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

@ -125,9 +125,12 @@ nsFontMetricsPh :: ~nsFontMetricsPh( )
delete mFont;
mFont = nsnull;
}
mDeviceContext = nsnull;
if (mFontHandle)
free (mFontHandle);
if (mDeviceContext) {
mDeviceContext->FontMetricsDeleted(this);
mDeviceContext = nsnull;
}
FreeGlobals();
}

Просмотреть файл

@ -66,8 +66,10 @@ nsFontMetricsPS :: ~nsFontMetricsPS()
mAFMInfo = nsnull;
}
mDeviceContext = nsnull;
if (mDeviceContext) {
mDeviceContext->FontMetricsDeleted(this);
mDeviceContext = nsnull;
}
}
NS_IMPL_ISUPPORTS1(nsFontMetricsPS, nsIFontMetrics)

Просмотреть файл

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

Просмотреть файл

@ -403,7 +403,10 @@ nsFontMetricsWin::~nsFontMetricsWin()
}
mLoadedFonts.Clear();
mDeviceContext = nsnull;
if (mDeviceContext) {
mDeviceContext->FontMetricsDeleted(this);
mDeviceContext = nsnull;
}
}
#ifdef LEAK_DEBUG

Просмотреть файл

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