Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / gfx / src / nsDeviceContext.cpp
blob224f65935bff6131f72aa661d0f9352e3f78705d
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
16 * The Original Code is mozilla.org code.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998
21 * the Initial Developer. All Rights Reserved.
23 * Contributor(s):
24 * Roland Mainz <Roland.Mainz@informatik.med.uni-giessen.de>
25 * IBM Corp.
27 * Alternatively, the contents of this file may be used under the terms of
28 * either of the GNU General Public License Version 2 or later (the "GPL"),
29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
41 #include "nsDeviceContext.h"
42 #include "nsFont.h"
43 #include "nsIView.h"
44 #include "nsGfxCIID.h"
45 #include "nsVoidArray.h"
46 #include "nsIFontMetrics.h"
47 #include "nsHashtable.h"
48 #include "nsILanguageAtomService.h"
49 #include "nsIServiceManager.h"
50 #include "nsUnicharUtils.h"
51 #include "nsCRT.h"
52 #include "nsIRenderingContext.h"
53 #include "gfxUserFontSet.h"
54 #include "nsIThebesFontMetrics.h"
56 NS_IMPL_ISUPPORTS3(DeviceContextImpl, nsIDeviceContext, nsIObserver, nsISupportsWeakReference)
58 DeviceContextImpl::DeviceContextImpl()
60 mAppUnitsPerDevPixel = -1;
61 mAppUnitsPerInch = -1;
62 mAppUnitsPerDevNotScaledPixel = -1;
63 mPixelScale = 1.0f;
65 mFontCache = nsnull;
66 mWidget = nsnull;
67 mFontAliasTable = nsnull;
69 #ifdef NS_DEBUG
70 mInitialized = PR_FALSE;
71 #endif
74 static PRBool DeleteValue(nsHashKey* aKey, void* aValue, void* closure)
76 delete ((nsString*)aValue);
77 return PR_TRUE;
80 DeviceContextImpl::~DeviceContextImpl()
82 nsCOMPtr<nsIObserverService> obs(do_GetService("@mozilla.org/observer-service;1"));
83 if (obs)
84 obs->RemoveObserver(this, "memory-pressure");
86 if (nsnull != mFontCache)
88 delete mFontCache;
89 mFontCache = nsnull;
92 if (nsnull != mFontAliasTable) {
93 mFontAliasTable->Enumerate(DeleteValue);
94 delete mFontAliasTable;
99 NS_IMETHODIMP
100 DeviceContextImpl::Observe(nsISupports* aSubject, const char* aTopic, const PRUnichar* aSomeData)
102 if (mFontCache && !nsCRT::strcmp(aTopic, "memory-pressure")) {
103 mFontCache->Compact();
105 return NS_OK;
108 NS_IMETHODIMP DeviceContextImpl::Init(nsNativeWidget aWidget)
110 mWidget = aWidget;
112 CommonInit();
114 return NS_OK;
117 void DeviceContextImpl::CommonInit(void)
119 #ifdef NS_DEBUG
120 NS_ASSERTION(!mInitialized, "device context is initialized twice!");
121 mInitialized = PR_TRUE;
122 #endif
124 // register as a memory-pressure observer to free font resources
125 // in low-memory situations.
126 nsCOMPtr<nsIObserverService> obs(do_GetService("@mozilla.org/observer-service;1"));
127 if (obs)
128 obs->AddObserver(this, "memory-pressure", PR_TRUE);
131 NS_IMETHODIMP DeviceContextImpl::CreateRenderingContext(nsIView *aView, nsIRenderingContext *&aContext)
133 nsresult rv;
135 aContext = nsnull;
136 nsCOMPtr<nsIRenderingContext> pContext;
137 rv = CreateRenderingContextInstance(*getter_AddRefs(pContext));
138 if (NS_SUCCEEDED(rv)) {
139 rv = InitRenderingContext(pContext, aView->GetWidget());
140 if (NS_SUCCEEDED(rv)) {
141 aContext = pContext;
142 NS_ADDREF(aContext);
146 return rv;
149 NS_IMETHODIMP DeviceContextImpl::CreateRenderingContext(nsIWidget *aWidget, nsIRenderingContext *&aContext)
151 nsresult rv;
153 aContext = nsnull;
154 nsCOMPtr<nsIRenderingContext> pContext;
155 rv = CreateRenderingContextInstance(*getter_AddRefs(pContext));
156 if (NS_SUCCEEDED(rv)) {
157 rv = InitRenderingContext(pContext, aWidget);
158 if (NS_SUCCEEDED(rv)) {
159 aContext = pContext;
160 NS_ADDREF(aContext);
164 return rv;
167 NS_IMETHODIMP DeviceContextImpl::CreateRenderingContextInstance(nsIRenderingContext *&aContext)
169 static NS_DEFINE_CID(kRenderingContextCID, NS_RENDERING_CONTEXT_CID);
171 nsresult rv;
172 nsCOMPtr<nsIRenderingContext> pContext = do_CreateInstance(kRenderingContextCID, &rv);
173 if (NS_SUCCEEDED(rv)) {
174 aContext = pContext;
175 NS_ADDREF(aContext);
177 return rv;
180 nsresult DeviceContextImpl::InitRenderingContext(nsIRenderingContext *aContext, nsIWidget *aWin)
182 return aContext->Init(this, aWin);
185 NS_IMETHODIMP DeviceContextImpl::CreateFontCache()
187 mFontCache = new nsFontCache();
188 if (!mFontCache) {
189 return NS_ERROR_OUT_OF_MEMORY;
191 return mFontCache->Init(this);
194 NS_IMETHODIMP DeviceContextImpl::FontMetricsDeleted(const nsIFontMetrics* aFontMetrics)
196 if (mFontCache) {
197 mFontCache->FontMetricsDeleted(aFontMetrics);
199 return NS_OK;
202 void
203 DeviceContextImpl::GetLocaleLangGroup(void)
205 if (!mLocaleLangGroup) {
206 nsCOMPtr<nsILanguageAtomService> langService;
207 langService = do_GetService(NS_LANGUAGEATOMSERVICE_CONTRACTID);
208 if (langService) {
209 mLocaleLangGroup = langService->GetLocaleLanguageGroup();
211 if (!mLocaleLangGroup) {
212 mLocaleLangGroup = do_GetAtom("x-western");
217 NS_IMETHODIMP DeviceContextImpl::GetMetricsFor(const nsFont& aFont,
218 nsIAtom* aLangGroup, gfxUserFontSet *aUserFontSet, nsIFontMetrics*& aMetrics)
220 if (nsnull == mFontCache) {
221 nsresult rv = CreateFontCache();
222 if (NS_FAILED(rv)) {
223 aMetrics = nsnull;
224 return rv;
226 // XXX temporary fix for performance problem -- erik
227 GetLocaleLangGroup();
230 // XXX figure out why aLangGroup is NULL sometimes
231 if (!aLangGroup) {
232 aLangGroup = mLocaleLangGroup;
235 return mFontCache->GetMetricsFor(aFont, aLangGroup, aUserFontSet, aMetrics);
238 NS_IMETHODIMP DeviceContextImpl::GetMetricsFor(const nsFont& aFont,
239 gfxUserFontSet *aUserFontSet,
240 nsIFontMetrics*& aMetrics)
242 if (nsnull == mFontCache) {
243 nsresult rv = CreateFontCache();
244 if (NS_FAILED(rv)) {
245 aMetrics = nsnull;
246 return rv;
248 // XXX temporary fix for performance problem -- erik
249 GetLocaleLangGroup();
251 return mFontCache->GetMetricsFor(aFont, mLocaleLangGroup, aUserFontSet,
252 aMetrics);
255 NS_IMETHODIMP DeviceContextImpl::GetDepth(PRUint32& aDepth)
257 aDepth = 24;
258 return NS_OK;
261 NS_IMETHODIMP DeviceContextImpl::GetPaletteInfo(nsPaletteInfo& aPaletteInfo)
263 aPaletteInfo.isPaletteDevice = PR_FALSE;
264 aPaletteInfo.sizePalette = 0;
265 aPaletteInfo.numReserved = 0;
266 aPaletteInfo.palette = nsnull;
267 return NS_OK;
270 struct FontEnumData {
271 FontEnumData(nsIDeviceContext* aDC, nsString& aFaceName)
272 : mDC(aDC), mFaceName(aFaceName)
274 nsIDeviceContext* mDC;
275 nsString& mFaceName;
278 static PRBool FontEnumCallback(const nsString& aFamily, PRBool aGeneric, void *aData)
280 FontEnumData* data = (FontEnumData*)aData;
281 // XXX for now, all generic fonts are presumed to exist
282 // we may want to actually check if there's an installed conversion
283 if (aGeneric) {
284 data->mFaceName = aFamily;
285 return PR_FALSE; // found one, stop.
287 else {
288 nsAutoString local;
289 PRBool aliased;
290 data->mDC->GetLocalFontName(aFamily, local, aliased);
291 if (aliased || (NS_SUCCEEDED(data->mDC->CheckFontExistence(local)))) {
292 data->mFaceName = local;
293 return PR_FALSE; // found one, stop.
296 return PR_TRUE; // didn't exist, continue looking
299 NS_IMETHODIMP DeviceContextImpl::FirstExistingFont(const nsFont& aFont, nsString& aFaceName)
301 FontEnumData data(this, aFaceName);
302 if (aFont.EnumerateFamilies(FontEnumCallback, &data)) {
303 return NS_ERROR_FAILURE; // ran out
305 return NS_OK;
308 class FontAliasKey: public nsHashKey
310 public:
311 FontAliasKey(const nsString& aString)
312 {mString.Assign(aString);}
314 virtual PRUint32 HashCode(void) const;
315 virtual PRBool Equals(const nsHashKey *aKey) const;
316 virtual nsHashKey *Clone(void) const;
318 nsString mString;
321 PRUint32 FontAliasKey::HashCode(void) const
323 PRUint32 hash = 0;
324 const PRUnichar* string = mString.get();
325 PRUnichar ch;
326 while ((ch = *string++) != 0) {
327 // FYI: hash = hash*37 + ch
328 ch = ToUpperCase(ch);
329 hash = ((hash << 5) + (hash << 2) + hash) + ch;
331 return hash;
334 PRBool FontAliasKey::Equals(const nsHashKey *aKey) const
336 return mString.Equals(((FontAliasKey*)aKey)->mString, nsCaseInsensitiveStringComparator());
339 nsHashKey* FontAliasKey::Clone(void) const
341 return new FontAliasKey(mString);
343 nsresult DeviceContextImpl::CreateFontAliasTable()
345 nsresult result = NS_OK;
347 if (nsnull == mFontAliasTable) {
348 mFontAliasTable = new nsHashtable();
349 if (nsnull != mFontAliasTable) {
351 nsAutoString times; times.AssignLiteral("Times");
352 nsAutoString timesNewRoman; timesNewRoman.AssignLiteral("Times New Roman");
353 nsAutoString timesRoman; timesRoman.AssignLiteral("Times Roman");
354 nsAutoString arial; arial.AssignLiteral("Arial");
355 nsAutoString helvetica; helvetica.AssignLiteral("Helvetica");
356 nsAutoString courier; courier.AssignLiteral("Courier");
357 nsAutoString courierNew; courierNew.AssignLiteral("Courier New");
358 nsAutoString nullStr;
360 AliasFont(times, timesNewRoman, timesRoman, PR_FALSE);
361 AliasFont(timesRoman, timesNewRoman, times, PR_FALSE);
362 AliasFont(timesNewRoman, timesRoman, times, PR_FALSE);
363 AliasFont(arial, helvetica, nullStr, PR_FALSE);
364 AliasFont(helvetica, arial, nullStr, PR_FALSE);
365 AliasFont(courier, courierNew, nullStr, PR_TRUE);
366 AliasFont(courierNew, courier, nullStr, PR_FALSE);
368 else {
369 result = NS_ERROR_OUT_OF_MEMORY;
372 return result;
375 nsresult DeviceContextImpl::AliasFont(const nsString& aFont,
376 const nsString& aAlias, const nsString& aAltAlias,
377 PRBool aForceAlias)
379 nsresult result = NS_OK;
381 if (nsnull != mFontAliasTable) {
382 if (aForceAlias || NS_FAILED(CheckFontExistence(aFont))) {
383 if (NS_SUCCEEDED(CheckFontExistence(aAlias))) {
384 nsString* entry = new nsString(aAlias);
385 if (nsnull != entry) {
386 FontAliasKey key(aFont);
387 mFontAliasTable->Put(&key, entry);
389 else {
390 result = NS_ERROR_OUT_OF_MEMORY;
393 else if (!aAltAlias.IsEmpty() && NS_SUCCEEDED(CheckFontExistence(aAltAlias))) {
394 nsString* entry = new nsString(aAltAlias);
395 if (nsnull != entry) {
396 FontAliasKey key(aFont);
397 mFontAliasTable->Put(&key, entry);
399 else {
400 result = NS_ERROR_OUT_OF_MEMORY;
405 else {
406 result = NS_ERROR_FAILURE;
408 return result;
411 NS_IMETHODIMP DeviceContextImpl::GetLocalFontName(const nsString& aFaceName, nsString& aLocalName,
412 PRBool& aAliased)
414 nsresult result = NS_OK;
416 if (nsnull == mFontAliasTable) {
417 result = CreateFontAliasTable();
420 if (nsnull != mFontAliasTable) {
421 FontAliasKey key(aFaceName);
422 const nsString* alias = (const nsString*)mFontAliasTable->Get(&key);
423 if (nsnull != alias) {
424 aLocalName = *alias;
425 aAliased = PR_TRUE;
427 else {
428 aLocalName = aFaceName;
429 aAliased = PR_FALSE;
432 return result;
435 NS_IMETHODIMP DeviceContextImpl::FlushFontCache(void)
437 if (nsnull != mFontCache)
438 mFontCache->Flush();
440 return NS_OK;
443 /////////////////////////////////////////////////////////////
445 nsFontCache::nsFontCache()
447 MOZ_COUNT_CTOR(nsFontCache);
448 mContext = nsnull;
451 nsFontCache::~nsFontCache()
453 MOZ_COUNT_DTOR(nsFontCache);
454 Flush();
457 nsresult
458 nsFontCache::Init(nsIDeviceContext* aContext)
460 NS_PRECONDITION(nsnull != aContext, "null ptr");
461 // Note: we don't hold a reference to the device context, because it
462 // holds a reference to us and we don't want circular references
463 mContext = aContext;
464 return NS_OK;
467 nsresult
468 nsFontCache::GetDeviceContext(nsIDeviceContext *&aContext) const
470 aContext = mContext;
471 NS_IF_ADDREF(aContext);
472 return NS_OK;
475 nsresult
476 nsFontCache::GetMetricsFor(const nsFont& aFont, nsIAtom* aLangGroup,
477 gfxUserFontSet *aUserFontSet, nsIFontMetrics *&aMetrics)
479 // First check our cache
480 // start from the end, which is where we put the most-recent-used element
482 nsIFontMetrics* fm;
483 PRInt32 n = mFontMetrics.Count() - 1;
484 for (PRInt32 i = n; i >= 0; --i) {
485 fm = static_cast<nsIFontMetrics*>(mFontMetrics[i]);
486 nsIThebesFontMetrics* tfm = static_cast<nsIThebesFontMetrics*>(fm);
487 if (fm->Font().Equals(aFont) && tfm->GetUserFontSet() == aUserFontSet) {
488 nsCOMPtr<nsIAtom> langGroup;
489 fm->GetLangGroup(getter_AddRefs(langGroup));
490 if (aLangGroup == langGroup.get()) {
491 if (i != n) {
492 // promote it to the end of the cache
493 mFontMetrics.MoveElement(i, n);
495 tfm->GetThebesFontGroup()->UpdateFontList();
496 NS_ADDREF(aMetrics = fm);
497 return NS_OK;
502 // It's not in the cache. Get font metrics and then cache them.
504 aMetrics = nsnull;
505 nsresult rv = CreateFontMetricsInstance(&fm);
506 if (NS_FAILED(rv)) return rv;
507 rv = fm->Init(aFont, aLangGroup, mContext, aUserFontSet);
508 if (NS_SUCCEEDED(rv)) {
509 // the mFontMetrics list has the "head" at the end, because append is
510 // cheaper than insert
511 mFontMetrics.AppendElement(fm);
512 aMetrics = fm;
513 NS_ADDREF(aMetrics);
514 return NS_OK;
516 fm->Destroy();
517 NS_RELEASE(fm);
519 // One reason why Init() fails is because the system is running out of resources.
520 // e.g., on Win95/98 only a very limited number of GDI objects are available.
521 // Compact the cache and try again.
523 Compact();
524 rv = CreateFontMetricsInstance(&fm);
525 if (NS_FAILED(rv)) return rv;
526 rv = fm->Init(aFont, aLangGroup, mContext, aUserFontSet);
527 if (NS_SUCCEEDED(rv)) {
528 mFontMetrics.AppendElement(fm);
529 aMetrics = fm;
530 NS_ADDREF(aMetrics);
531 return NS_OK;
533 fm->Destroy();
534 NS_RELEASE(fm);
536 // could not setup a new one, send an old one (XXX search a "best match"?)
538 n = mFontMetrics.Count() - 1; // could have changed in Compact()
539 if (n >= 0) {
540 aMetrics = static_cast<nsIFontMetrics*>(mFontMetrics[n]);
541 NS_ADDREF(aMetrics);
542 return NS_OK;
545 NS_POSTCONDITION(NS_SUCCEEDED(rv), "font metrics should not be null - bug 136248");
546 return rv;
549 /* PostScript module may override this method to create
550 * nsIFontMetrics objects with their own classes
552 nsresult
553 nsFontCache::CreateFontMetricsInstance(nsIFontMetrics** fm)
555 static NS_DEFINE_CID(kFontMetricsCID, NS_FONT_METRICS_CID);
556 return CallCreateInstance(kFontMetricsCID, fm);
559 nsresult nsFontCache::FontMetricsDeleted(const nsIFontMetrics* aFontMetrics)
561 mFontMetrics.RemoveElement((void*)aFontMetrics);
562 return NS_OK;
565 nsresult nsFontCache::Compact()
567 // Need to loop backward because the running element can be removed on the way
568 for (PRInt32 i = mFontMetrics.Count()-1; i >= 0; --i) {
569 nsIFontMetrics* fm = static_cast<nsIFontMetrics*>(mFontMetrics[i]);
570 nsIFontMetrics* oldfm = fm;
571 // Destroy() isn't here because we want our device context to be notified
572 NS_RELEASE(fm); // this will reset fm to nsnull
573 // if the font is really gone, it would have called back in
574 // FontMetricsDeleted() and would have removed itself
575 if (mFontMetrics.IndexOf(oldfm) >= 0) {
576 // nope, the font is still there, so let's hold onto it too
577 NS_ADDREF(oldfm);
580 return NS_OK;
583 nsresult nsFontCache::Flush()
585 for (PRInt32 i = mFontMetrics.Count()-1; i >= 0; --i) {
586 nsIFontMetrics* fm = static_cast<nsIFontMetrics*>(mFontMetrics[i]);
587 // Destroy() will unhook our device context from the fm so that we won't
588 // waste time in triggering the notification of FontMetricsDeleted()
589 // in the subsequent release
590 fm->Destroy();
591 NS_RELEASE(fm);
594 mFontMetrics.Clear();
596 return NS_OK;
599 NS_IMETHODIMP
600 DeviceContextImpl::PrepareNativeWidget(nsIWidget *aWidget, void **aOut)
602 return NS_ERROR_NOT_IMPLEMENTED;
605 NS_IMETHODIMP
606 DeviceContextImpl::ClearCachedSystemFonts()
608 return NS_OK;