Back to index

lightning-sunbird  0.9+nobinonly
nsDeviceContext.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
00002  *
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is mozilla.org code.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 1998
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Roland Mainz <Roland.Mainz@informatik.med.uni-giessen.de>
00025  *   IBM Corp.
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either of the GNU General Public License Version 2 or later (the "GPL"),
00029  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 #include "nsDeviceContext.h"
00042 #include "nsFont.h"
00043 #include "nsIView.h"
00044 #include "nsGfxCIID.h"
00045 #include "nsVoidArray.h"
00046 #include "nsIFontMetrics.h"
00047 #include "nsHashtable.h"
00048 #include "nsILanguageAtomService.h"
00049 #include "nsIServiceManager.h"
00050 #include "nsUnicharUtils.h"
00051 #include "nsCRT.h"
00052 
00053 
00054 NS_IMPL_ISUPPORTS3(DeviceContextImpl, nsIDeviceContext, nsIObserver, nsISupportsWeakReference)
00055 
00056 DeviceContextImpl::DeviceContextImpl()
00057 {
00058   mFontCache = nsnull;
00059   mDevUnitsToAppUnits = 1.0f;
00060   mAppUnitsToDevUnits = 1.0f;
00061   mCPixelScale = 1.0f;
00062   mZoom = 1.0f;
00063   mWidget = nsnull;
00064   mFontAliasTable = nsnull;
00065 
00066 #ifdef NS_PRINT_PREVIEW
00067   mUseAltDC = kUseAltDCFor_NONE;
00068 #endif
00069 #ifdef NS_DEBUG
00070   mInitialized = PR_FALSE;
00071 #endif
00072 }
00073 
00074 static PRBool PR_CALLBACK DeleteValue(nsHashKey* aKey, void* aValue, void* closure)
00075 {
00076   delete ((nsString*)aValue);
00077   return PR_TRUE;
00078 }
00079 
00080 DeviceContextImpl::~DeviceContextImpl()
00081 {
00082   nsCOMPtr<nsIObserverService> obs(do_GetService("@mozilla.org/observer-service;1"));
00083   if (obs)
00084     obs->RemoveObserver(this, "memory-pressure");
00085 
00086   if (nsnull != mFontCache)
00087   {
00088     delete mFontCache;
00089     mFontCache = nsnull;
00090   }
00091 
00092   if (nsnull != mFontAliasTable) {
00093     mFontAliasTable->Enumerate(DeleteValue);
00094     delete mFontAliasTable;
00095   }
00096 
00097 }
00098 
00099 NS_IMETHODIMP
00100 DeviceContextImpl::Observe(nsISupports* aSubject, const char* aTopic, const PRUnichar* aSomeData)
00101 {
00102   if (mFontCache && !nsCRT::strcmp(aTopic, "memory-pressure")) {
00103     mFontCache->Compact();
00104   }
00105   return NS_OK;
00106 }
00107 
00108 NS_IMETHODIMP DeviceContextImpl::Init(nsNativeWidget aWidget)
00109 {
00110   mWidget = aWidget;
00111 
00112   CommonInit();
00113 
00114   return NS_OK;
00115 }
00116 
00117 void DeviceContextImpl::CommonInit(void)
00118 {
00119 #ifdef NS_DEBUG
00120   NS_ASSERTION(!mInitialized, "device context is initialized twice!");
00121   mInitialized = PR_TRUE;
00122 #endif
00123 
00124   // register as a memory-pressure observer to free font resources
00125   // in low-memory situations.
00126   nsCOMPtr<nsIObserverService> obs(do_GetService("@mozilla.org/observer-service;1"));
00127   if (obs)
00128     obs->AddObserver(this, "memory-pressure", PR_TRUE);
00129 }
00130 
00131 NS_IMETHODIMP DeviceContextImpl::GetCanonicalPixelScale(float &aScale) const
00132 {
00133   aScale = mCPixelScale;
00134   return NS_OK;
00135 }
00136 
00137 NS_IMETHODIMP DeviceContextImpl::SetCanonicalPixelScale(float aScale)
00138 {
00139   mCPixelScale = aScale;
00140   return NS_OK;
00141 }
00142 
00143 NS_IMETHODIMP DeviceContextImpl::CreateRenderingContext(nsIView *aView, nsIRenderingContext *&aContext)
00144 {
00145 #ifdef NS_PRINT_PREVIEW
00146   // AltDC NEVER use widgets to create their DCs
00147   if (mAltDC && (mUseAltDC & kUseAltDCFor_CREATERC_PAINT)) {
00148     return mAltDC->CreateRenderingContext(aContext);
00149   }
00150 #endif
00151 
00152   nsresult            rv;
00153 
00154   aContext = nsnull;
00155   nsCOMPtr<nsIRenderingContext> pContext;
00156   rv = CreateRenderingContextInstance(*getter_AddRefs(pContext));
00157   if (NS_SUCCEEDED(rv)) {
00158     rv = InitRenderingContext(pContext, aView->GetWidget());
00159     if (NS_SUCCEEDED(rv)) {
00160       aContext = pContext;
00161       NS_ADDREF(aContext);
00162     }
00163   }
00164   
00165   return rv;
00166 }
00167 
00168 NS_IMETHODIMP DeviceContextImpl::CreateRenderingContext(nsIDrawingSurface* aSurface, nsIRenderingContext *&aContext)
00169 {
00170 #ifdef NS_PRINT_PREVIEW
00171   // AltDC NEVER use widgets to create their DCs
00172   if (mAltDC && (mUseAltDC & kUseAltDCFor_CREATERC_PAINT)) {
00173     return mAltDC->CreateRenderingContext(aContext);
00174   }
00175 #endif /* NS_PRINT_PREVIEW */
00176 
00177   nsresult rv;
00178 
00179   aContext = nsnull;
00180   nsCOMPtr<nsIRenderingContext> pContext;
00181   rv = CreateRenderingContextInstance(*getter_AddRefs(pContext));
00182   if (NS_SUCCEEDED(rv)) {
00183     rv = InitRenderingContext(pContext, aSurface);
00184     if (NS_SUCCEEDED(rv)) {
00185       aContext = pContext;
00186       NS_ADDREF(aContext);
00187     }
00188   }
00189   
00190   return rv;
00191 }
00192 
00193 NS_IMETHODIMP DeviceContextImpl::CreateRenderingContext(nsIWidget *aWidget, nsIRenderingContext *&aContext)
00194 {
00195   nsresult rv;
00196 
00197 #ifdef NS_PRINT_PREVIEW
00198   // AltDC NEVER use widgets to create their DCs
00199   // NOTE: The mAltDC will call it;s own init
00200   // so we can return here
00201   if (mAltDC && (mUseAltDC & kUseAltDCFor_CREATERC_REFLOW)) {
00202     return mAltDC->CreateRenderingContext(aContext);
00203   }
00204 #endif
00205 
00206   aContext = nsnull;
00207   nsCOMPtr<nsIRenderingContext> pContext;
00208   rv = CreateRenderingContextInstance(*getter_AddRefs(pContext));
00209   if (NS_SUCCEEDED(rv)) {
00210     rv = InitRenderingContext(pContext, aWidget);
00211     if (NS_SUCCEEDED(rv)) {
00212       aContext = pContext;
00213       NS_ADDREF(aContext);
00214     }
00215   }    
00216   
00217   return rv;
00218 }
00219 
00220 NS_IMETHODIMP DeviceContextImpl::CreateRenderingContextInstance(nsIRenderingContext *&aContext)
00221 {
00222   static NS_DEFINE_CID(kRenderingContextCID, NS_RENDERING_CONTEXT_CID);
00223 
00224   nsresult rv;
00225   nsCOMPtr<nsIRenderingContext> pContext = do_CreateInstance(kRenderingContextCID, &rv);
00226   if (NS_SUCCEEDED(rv)) {
00227     aContext = pContext;
00228     NS_ADDREF(aContext);
00229   }
00230   return rv;
00231 }
00232 
00233 nsresult DeviceContextImpl::InitRenderingContext(nsIRenderingContext *aContext, nsIWidget *aWin)
00234 {
00235 #ifdef NS_PRINT_PREVIEW
00236   // there are a couple of cases where the kUseAltDCFor_CREATERC_xxx flag has been turned off
00237   // but we still need to initialize with the Alt DC
00238   if (mAltDC) {
00239     return aContext->Init(mAltDC, aWin);
00240   } else {
00241     return aContext->Init(this, aWin);
00242   }
00243 #else
00244   return aContext->Init(this, aWin);
00245 #endif
00246 }
00247 
00248 nsresult DeviceContextImpl::InitRenderingContext(nsIRenderingContext *aContext, nsIDrawingSurface* aSurface)
00249 {
00250 #ifdef NS_PRINT_PREVIEW
00251   // there are a couple of cases where the kUseAltDCFor_CREATERC_xxx flag has been turned off
00252   // but we still need to initialize with the Alt DC
00253   if (mAltDC) {
00254     return aContext->Init(mAltDC, aSurface);
00255   } else {
00256     return aContext->Init(this, aSurface);
00257   }
00258 #else
00259   return aContext->Init(this, aSurface);
00260 #endif /* NS_PRINT_PREVIEW */
00261 }
00262 
00263 NS_IMETHODIMP DeviceContextImpl::CreateFontCache()
00264 {
00265   mFontCache = new nsFontCache();
00266   if (!mFontCache) {
00267     return NS_ERROR_OUT_OF_MEMORY;
00268   }
00269   return mFontCache->Init(this);
00270 }
00271 
00272 NS_IMETHODIMP DeviceContextImpl::FontMetricsDeleted(const nsIFontMetrics* aFontMetrics)
00273 {
00274   if (mFontCache) {
00275     mFontCache->FontMetricsDeleted(aFontMetrics);
00276   }
00277   return NS_OK;
00278 }
00279 
00280 void
00281 DeviceContextImpl::GetLocaleLangGroup(void)
00282 {
00283   if (!mLocaleLangGroup) {
00284     nsCOMPtr<nsILanguageAtomService> langService;
00285     langService = do_GetService(NS_LANGUAGEATOMSERVICE_CONTRACTID);
00286     if (langService) {
00287       mLocaleLangGroup = langService->GetLocaleLanguageGroup();
00288     }
00289     if (!mLocaleLangGroup) {
00290       mLocaleLangGroup = do_GetAtom("x-western");
00291     }
00292   }
00293 }
00294 
00295 NS_IMETHODIMP DeviceContextImpl::GetMetricsFor(const nsFont& aFont,
00296   nsIAtom* aLangGroup, nsIFontMetrics*& aMetrics)
00297 {
00298 #ifdef NS_PRINT_PREVIEW
00299   // Defer to Alt when there is one
00300   if (mAltDC != nsnull && (mUseAltDC & kUseAltDCFor_FONTMETRICS)) {
00301     return mAltDC->GetMetricsFor(aFont, aLangGroup, aMetrics);
00302   }
00303 #endif
00304 
00305   if (nsnull == mFontCache) {
00306     nsresult  rv = CreateFontCache();
00307     if (NS_FAILED(rv)) {
00308       aMetrics = nsnull;
00309       return rv;
00310     }
00311     // XXX temporary fix for performance problem -- erik
00312     GetLocaleLangGroup();
00313   }
00314 
00315   // XXX figure out why aLangGroup is NULL sometimes
00316   if (!aLangGroup) {
00317     aLangGroup = mLocaleLangGroup;
00318   }
00319 
00320   return mFontCache->GetMetricsFor(aFont, aLangGroup, aMetrics);
00321 }
00322 
00323 NS_IMETHODIMP DeviceContextImpl::GetMetricsFor(const nsFont& aFont, nsIFontMetrics*& aMetrics)
00324 {
00325 #ifdef NS_PRINT_PREVIEW
00326   // Defer to Alt when there is one
00327   if (mAltDC != nsnull && (mUseAltDC & kUseAltDCFor_FONTMETRICS)) {
00328     return mAltDC->GetMetricsFor(aFont, aMetrics);
00329   }
00330 #endif
00331 
00332   if (nsnull == mFontCache) {
00333     nsresult  rv = CreateFontCache();
00334     if (NS_FAILED(rv)) {
00335       aMetrics = nsnull;
00336       return rv;
00337     }
00338     // XXX temporary fix for performance problem -- erik
00339     GetLocaleLangGroup();
00340   }
00341   return mFontCache->GetMetricsFor(aFont, mLocaleLangGroup, aMetrics);
00342 }
00343 
00344 NS_IMETHODIMP DeviceContextImpl::SetZoom(float aZoom)
00345 {
00346   mZoom = aZoom;
00347   return NS_OK;
00348 }
00349 
00350 NS_IMETHODIMP DeviceContextImpl::GetZoom(float &aZoom) const
00351 {
00352   aZoom = mZoom;
00353   return NS_OK;
00354 }
00355 
00356 NS_IMETHODIMP DeviceContextImpl::GetDepth(PRUint32& aDepth)
00357 {
00358   aDepth = 24;
00359   return NS_OK;
00360 }
00361 
00362 NS_IMETHODIMP DeviceContextImpl::GetPaletteInfo(nsPaletteInfo& aPaletteInfo)
00363 {
00364   aPaletteInfo.isPaletteDevice = PR_FALSE;
00365   aPaletteInfo.sizePalette = 0;
00366   aPaletteInfo.numReserved = 0;
00367   aPaletteInfo.palette = nsnull;
00368   return NS_OK;
00369 }
00370 
00371 struct FontEnumData {
00372   FontEnumData(nsIDeviceContext* aDC, nsString& aFaceName)
00373     : mDC(aDC), mFaceName(aFaceName)
00374   {}
00375   nsIDeviceContext* mDC;
00376   nsString&         mFaceName;
00377 };
00378 
00379 static PRBool FontEnumCallback(const nsString& aFamily, PRBool aGeneric, void *aData)
00380 {
00381   FontEnumData* data = (FontEnumData*)aData;
00382   // XXX for now, all generic fonts are presumed to exist
00383   //     we may want to actually check if there's an installed conversion
00384   if (aGeneric) {
00385     data->mFaceName = aFamily;
00386     return PR_FALSE; // found one, stop.
00387   }
00388   else {
00389     nsAutoString  local;
00390     PRBool        aliased;
00391     data->mDC->GetLocalFontName(aFamily, local, aliased);
00392     if (aliased || (NS_SUCCEEDED(data->mDC->CheckFontExistence(local)))) {
00393       data->mFaceName = local;
00394       return PR_FALSE; // found one, stop.
00395     }
00396   }
00397   return PR_TRUE; // didn't exist, continue looking
00398 }
00399 
00400 NS_IMETHODIMP DeviceContextImpl::FirstExistingFont(const nsFont& aFont, nsString& aFaceName)
00401 {
00402   FontEnumData  data(this, aFaceName);
00403   if (aFont.EnumerateFamilies(FontEnumCallback, &data)) {
00404     return NS_ERROR_FAILURE;  // ran out
00405   }
00406   return NS_OK;
00407 }
00408 
00409 class FontAliasKey: public nsHashKey 
00410 {
00411 public:
00412   FontAliasKey(const nsString& aString)
00413   {mString.Assign(aString);}
00414 
00415   virtual PRUint32 HashCode(void) const;
00416   virtual PRBool Equals(const nsHashKey *aKey) const;
00417   virtual nsHashKey *Clone(void) const;
00418 
00419   nsString  mString;
00420 };
00421 
00422 PRUint32 FontAliasKey::HashCode(void) const
00423 {
00424   PRUint32 hash = 0;
00425   const PRUnichar* string = mString.get();
00426   PRUnichar ch;
00427   while ((ch = *string++) != 0) {
00428     // FYI: hash = hash*37 + ch
00429     ch = ToUpperCase(ch);
00430     hash = ((hash << 5) + (hash << 2) + hash) + ch;
00431   }
00432   return hash;
00433 }
00434 
00435 PRBool FontAliasKey::Equals(const nsHashKey *aKey) const
00436 {
00437   return mString.Equals(((FontAliasKey*)aKey)->mString, nsCaseInsensitiveStringComparator());
00438 }
00439 
00440 nsHashKey* FontAliasKey::Clone(void) const
00441 {
00442   return new FontAliasKey(mString);
00443 }
00444 nsresult DeviceContextImpl::CreateFontAliasTable()
00445 {
00446   nsresult result = NS_OK;
00447 
00448   if (nsnull == mFontAliasTable) {
00449     mFontAliasTable = new nsHashtable();
00450     if (nsnull != mFontAliasTable) {
00451 
00452       nsAutoString  times;              times.AssignLiteral("Times");
00453       nsAutoString  timesNewRoman;      timesNewRoman.AssignLiteral("Times New Roman");
00454       nsAutoString  timesRoman;         timesRoman.AssignLiteral("Times Roman");
00455       nsAutoString  arial;              arial.AssignLiteral("Arial");
00456       nsAutoString  helvetica;          helvetica.AssignLiteral("Helvetica");
00457       nsAutoString  courier;            courier.AssignLiteral("Courier");
00458       nsAutoString  courierNew;         courierNew.AssignLiteral("Courier New");
00459       nsAutoString  nullStr;
00460 
00461       AliasFont(times, timesNewRoman, timesRoman, PR_FALSE);
00462       AliasFont(timesRoman, timesNewRoman, times, PR_FALSE);
00463       AliasFont(timesNewRoman, timesRoman, times, PR_FALSE);
00464       AliasFont(arial, helvetica, nullStr, PR_FALSE);
00465       AliasFont(helvetica, arial, nullStr, PR_FALSE);
00466       AliasFont(courier, courierNew, nullStr, PR_TRUE);
00467       AliasFont(courierNew, courier, nullStr, PR_FALSE);
00468     }
00469     else {
00470       result = NS_ERROR_OUT_OF_MEMORY;
00471     }
00472   }
00473   return result;
00474 }
00475 
00476 nsresult DeviceContextImpl::AliasFont(const nsString& aFont, 
00477                                       const nsString& aAlias, const nsString& aAltAlias,
00478                                       PRBool aForceAlias)
00479 {
00480   nsresult result = NS_OK;
00481 
00482   if (nsnull != mFontAliasTable) {
00483     if (aForceAlias || NS_FAILED(CheckFontExistence(aFont))) {
00484       if (NS_SUCCEEDED(CheckFontExistence(aAlias))) {
00485         nsString* entry = new nsString(aAlias);
00486         if (nsnull != entry) {
00487           FontAliasKey key(aFont);
00488           mFontAliasTable->Put(&key, entry);
00489         }
00490         else {
00491           result = NS_ERROR_OUT_OF_MEMORY;
00492         }
00493       }
00494       else if (!aAltAlias.IsEmpty() && NS_SUCCEEDED(CheckFontExistence(aAltAlias))) {
00495         nsString* entry = new nsString(aAltAlias);
00496         if (nsnull != entry) {
00497           FontAliasKey key(aFont);
00498           mFontAliasTable->Put(&key, entry);
00499         }
00500         else {
00501           result = NS_ERROR_OUT_OF_MEMORY;
00502         }
00503       }
00504     }
00505   }
00506   else {
00507     result = NS_ERROR_FAILURE;
00508   }
00509   return result;
00510 }
00511 
00512 NS_IMETHODIMP DeviceContextImpl::GetLocalFontName(const nsString& aFaceName, nsString& aLocalName,
00513                                                   PRBool& aAliased)
00514 {
00515   nsresult result = NS_OK;
00516 
00517   if (nsnull == mFontAliasTable) {
00518     result = CreateFontAliasTable();
00519   }
00520 
00521   if (nsnull != mFontAliasTable) {
00522     FontAliasKey key(aFaceName);
00523     const nsString* alias = (const nsString*)mFontAliasTable->Get(&key);
00524     if (nsnull != alias) {
00525       aLocalName = *alias;
00526       aAliased = PR_TRUE;
00527     }
00528     else {
00529       aLocalName = aFaceName;
00530       aAliased = PR_FALSE;
00531     }
00532   }
00533   return result;
00534 }
00535 
00536 NS_IMETHODIMP DeviceContextImpl::FlushFontCache(void)
00537 {
00538   if (nsnull != mFontCache)
00539     mFontCache->Flush();
00540 
00541   return NS_OK;
00542 }
00543 
00544 #ifdef NS_PRINT_PREVIEW
00545 NS_IMETHODIMP DeviceContextImpl::SetAltDevice(nsIDeviceContext* aAltDC)
00546 {
00547   mAltDC = aAltDC;
00548 
00549   // Can't use it if it isn't there
00550   if (aAltDC == nsnull) {
00551     mUseAltDC = kUseAltDCFor_NONE;
00552   }
00553   return NS_OK;
00554 }
00555 
00556 NS_IMETHODIMP DeviceContextImpl::SetUseAltDC(PRUint8 aValue, PRBool aOn)
00557 {
00558   if (aOn) {
00559     mUseAltDC |= aValue;
00560   } else {
00561     mUseAltDC &= ~aValue;
00562   }
00563   return NS_OK;
00564 }
00565 #endif
00566 
00568 
00569 MOZ_DECL_CTOR_COUNTER(nsFontCache)
00570 
00571 nsFontCache::nsFontCache()
00572 {
00573   MOZ_COUNT_CTOR(nsFontCache);
00574   mContext = nsnull;
00575 }
00576 
00577 nsFontCache::~nsFontCache()
00578 {
00579   MOZ_COUNT_DTOR(nsFontCache);
00580   Flush();
00581 }
00582 
00583 nsresult
00584 nsFontCache::Init(nsIDeviceContext* aContext)
00585 {
00586   NS_PRECONDITION(nsnull != aContext, "null ptr");
00587   // Note: we don't hold a reference to the device context, because it
00588   // holds a reference to us and we don't want circular references
00589   mContext = aContext;
00590   return NS_OK;
00591 }
00592 
00593 nsresult
00594 nsFontCache::GetDeviceContext(nsIDeviceContext *&aContext) const
00595 {
00596   aContext = mContext;
00597   NS_IF_ADDREF(aContext);
00598   return NS_OK;
00599 }
00600 
00601 nsresult
00602 nsFontCache::GetMetricsFor(const nsFont& aFont, nsIAtom* aLangGroup,
00603   nsIFontMetrics *&aMetrics)
00604 {
00605   // First check our cache
00606   // start from the end, which is where we put the most-recent-used element
00607 
00608   nsIFontMetrics* fm;
00609   PRInt32 n = mFontMetrics.Count() - 1;
00610   for (PRInt32 i = n; i >= 0; --i) {
00611     fm = NS_STATIC_CAST(nsIFontMetrics*, mFontMetrics[i]);
00612     if (fm->Font().Equals(aFont)) {
00613       nsCOMPtr<nsIAtom> langGroup;
00614       fm->GetLangGroup(getter_AddRefs(langGroup));
00615       if (aLangGroup == langGroup.get()) {
00616         if (i != n) {
00617           // promote it to the end of the cache
00618           mFontMetrics.MoveElement(i, n);
00619         }
00620         NS_ADDREF(aMetrics = fm);
00621         return NS_OK;
00622       }
00623     }
00624   }
00625 
00626   // It's not in the cache. Get font metrics and then cache them.
00627 
00628   aMetrics = nsnull;
00629   nsresult rv = CreateFontMetricsInstance(&fm);
00630   if (NS_FAILED(rv)) return rv;
00631   rv = fm->Init(aFont, aLangGroup, mContext);
00632   if (NS_SUCCEEDED(rv)) {
00633     // the mFontMetrics list has the "head" at the end, because append is
00634     // cheaper than insert
00635     mFontMetrics.AppendElement(fm);
00636     aMetrics = fm;
00637     NS_ADDREF(aMetrics);
00638     return NS_OK;
00639   }
00640   fm->Destroy();
00641   NS_RELEASE(fm);
00642 
00643   // One reason why Init() fails is because the system is running out of resources. 
00644   // e.g., on Win95/98 only a very limited number of GDI objects are available.
00645   // Compact the cache and try again.
00646 
00647   Compact();
00648   rv = CreateFontMetricsInstance(&fm);
00649   if (NS_FAILED(rv)) return rv;
00650   rv = fm->Init(aFont, aLangGroup, mContext);
00651   if (NS_SUCCEEDED(rv)) {
00652     mFontMetrics.AppendElement(fm);
00653     aMetrics = fm;
00654     NS_ADDREF(aMetrics);
00655     return NS_OK;
00656   }
00657   fm->Destroy();
00658   NS_RELEASE(fm);
00659 
00660   // could not setup a new one, send an old one (XXX search a "best match"?)
00661 
00662   n = mFontMetrics.Count() - 1; // could have changed in Compact()
00663   if (n >= 0) {
00664     aMetrics = NS_STATIC_CAST(nsIFontMetrics*, mFontMetrics[n]);
00665     NS_ADDREF(aMetrics);
00666     return NS_OK;
00667   }
00668 
00669   NS_POSTCONDITION(NS_SUCCEEDED(rv), "font metrics should not be null - bug 136248");
00670   return rv;
00671 }
00672 
00673 /* PostScript and Xprint module may override this method to create 
00674  * nsIFontMetrics objects with their own classes 
00675  */
00676 nsresult
00677 nsFontCache::CreateFontMetricsInstance(nsIFontMetrics** fm)
00678 {
00679   static NS_DEFINE_CID(kFontMetricsCID, NS_FONT_METRICS_CID);
00680   return CallCreateInstance(kFontMetricsCID, fm);
00681 }
00682 
00683 nsresult nsFontCache::FontMetricsDeleted(const nsIFontMetrics* aFontMetrics)
00684 {
00685   mFontMetrics.RemoveElement((void*)aFontMetrics);
00686   return NS_OK;
00687 }
00688 
00689 nsresult nsFontCache::Compact()
00690 {
00691   // Need to loop backward because the running element can be removed on the way
00692   for (PRInt32 i = mFontMetrics.Count()-1; i >= 0; --i) {
00693     nsIFontMetrics* fm = NS_STATIC_CAST(nsIFontMetrics*, mFontMetrics[i]);
00694     nsIFontMetrics* oldfm = fm;
00695     // Destroy() isn't here because we want our device context to be notified
00696     NS_RELEASE(fm); // this will reset fm to nsnull
00697     // if the font is really gone, it would have called back in
00698     // FontMetricsDeleted() and would have removed itself
00699     if (mFontMetrics.IndexOf(oldfm) >= 0) {
00700       // nope, the font is still there, so let's hold onto it too
00701       NS_ADDREF(oldfm);
00702     }
00703   }
00704   return NS_OK;
00705 }
00706 
00707 nsresult nsFontCache::Flush()
00708 {
00709   for (PRInt32 i = mFontMetrics.Count()-1; i >= 0; --i) {
00710     nsIFontMetrics* fm = NS_STATIC_CAST(nsIFontMetrics*, mFontMetrics[i]);
00711     // Destroy() will unhook our device context from the fm so that we won't
00712     // waste time in triggering the notification of FontMetricsDeleted()
00713     // in the subsequent release
00714     fm->Destroy();
00715     NS_RELEASE(fm);
00716   }
00717 
00718   mFontMetrics.Clear();
00719 
00720   return NS_OK;
00721 }
00722