Bug 426214, automatically sync Firefox's blocklist.xml from AMO (running on egg like...
[mozilla-1.9.git] / gfx / src / nsDeviceContext.cpp
blob18e613af539a0eb8a5a64f2fe84642c66e447b95
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"
54 NS_IMPL_ISUPPORTS3(DeviceContextImpl, nsIDeviceContext, nsIObserver, nsISupportsWeakReference)
56 DeviceContextImpl::DeviceContextImpl()
58 mAppUnitsPerDevPixel = -1;
59 mAppUnitsPerInch = -1;
60 mAppUnitsPerDevNotScaledPixel = -1;
61 mPixelScale = 1.0f;
63 mFontCache = nsnull;
64 mWidget = nsnull;
65 mFontAliasTable = nsnull;
67 #ifdef NS_DEBUG
68 mInitialized = PR_FALSE;
69 #endif
72 static PRBool PR_CALLBACK DeleteValue(nsHashKey* aKey, void* aValue, void* closure)
74 delete ((nsString*)aValue);
75 return PR_TRUE;
78 DeviceContextImpl::~DeviceContextImpl()
80 nsCOMPtr<nsIObserverService> obs(do_GetService("@mozilla.org/observer-service;1"));
81 if (obs)
82 obs->RemoveObserver(this, "memory-pressure");
84 if (nsnull != mFontCache)
86 delete mFontCache;
87 mFontCache = nsnull;
90 if (nsnull != mFontAliasTable) {
91 mFontAliasTable->Enumerate(DeleteValue);
92 delete mFontAliasTable;
97 NS_IMETHODIMP
98 DeviceContextImpl::Observe(nsISupports* aSubject, const char* aTopic, const PRUnichar* aSomeData)
100 if (mFontCache && !nsCRT::strcmp(aTopic, "memory-pressure")) {
101 mFontCache->Compact();
103 return NS_OK;
106 NS_IMETHODIMP DeviceContextImpl::Init(nsNativeWidget aWidget)
108 mWidget = aWidget;
110 CommonInit();
112 return NS_OK;
115 void DeviceContextImpl::CommonInit(void)
117 #ifdef NS_DEBUG
118 NS_ASSERTION(!mInitialized, "device context is initialized twice!");
119 mInitialized = PR_TRUE;
120 #endif
122 // register as a memory-pressure observer to free font resources
123 // in low-memory situations.
124 nsCOMPtr<nsIObserverService> obs(do_GetService("@mozilla.org/observer-service;1"));
125 if (obs)
126 obs->AddObserver(this, "memory-pressure", PR_TRUE);
129 NS_IMETHODIMP DeviceContextImpl::CreateRenderingContext(nsIView *aView, nsIRenderingContext *&aContext)
131 nsresult rv;
133 aContext = nsnull;
134 nsCOMPtr<nsIRenderingContext> pContext;
135 rv = CreateRenderingContextInstance(*getter_AddRefs(pContext));
136 if (NS_SUCCEEDED(rv)) {
137 rv = InitRenderingContext(pContext, aView->GetWidget());
138 if (NS_SUCCEEDED(rv)) {
139 aContext = pContext;
140 NS_ADDREF(aContext);
144 return rv;
147 NS_IMETHODIMP DeviceContextImpl::CreateRenderingContext(nsIWidget *aWidget, nsIRenderingContext *&aContext)
149 nsresult rv;
151 aContext = nsnull;
152 nsCOMPtr<nsIRenderingContext> pContext;
153 rv = CreateRenderingContextInstance(*getter_AddRefs(pContext));
154 if (NS_SUCCEEDED(rv)) {
155 rv = InitRenderingContext(pContext, aWidget);
156 if (NS_SUCCEEDED(rv)) {
157 aContext = pContext;
158 NS_ADDREF(aContext);
162 return rv;
165 NS_IMETHODIMP DeviceContextImpl::CreateRenderingContextInstance(nsIRenderingContext *&aContext)
167 static NS_DEFINE_CID(kRenderingContextCID, NS_RENDERING_CONTEXT_CID);
169 nsresult rv;
170 nsCOMPtr<nsIRenderingContext> pContext = do_CreateInstance(kRenderingContextCID, &rv);
171 if (NS_SUCCEEDED(rv)) {
172 aContext = pContext;
173 NS_ADDREF(aContext);
175 return rv;
178 nsresult DeviceContextImpl::InitRenderingContext(nsIRenderingContext *aContext, nsIWidget *aWin)
180 return aContext->Init(this, aWin);
183 NS_IMETHODIMP DeviceContextImpl::CreateFontCache()
185 mFontCache = new nsFontCache();
186 if (!mFontCache) {
187 return NS_ERROR_OUT_OF_MEMORY;
189 return mFontCache->Init(this);
192 NS_IMETHODIMP DeviceContextImpl::FontMetricsDeleted(const nsIFontMetrics* aFontMetrics)
194 if (mFontCache) {
195 mFontCache->FontMetricsDeleted(aFontMetrics);
197 return NS_OK;
200 void
201 DeviceContextImpl::GetLocaleLangGroup(void)
203 if (!mLocaleLangGroup) {
204 nsCOMPtr<nsILanguageAtomService> langService;
205 langService = do_GetService(NS_LANGUAGEATOMSERVICE_CONTRACTID);
206 if (langService) {
207 mLocaleLangGroup = langService->GetLocaleLanguageGroup();
209 if (!mLocaleLangGroup) {
210 mLocaleLangGroup = do_GetAtom("x-western");
215 NS_IMETHODIMP DeviceContextImpl::GetMetricsFor(const nsFont& aFont,
216 nsIAtom* aLangGroup, nsIFontMetrics*& aMetrics)
218 if (nsnull == mFontCache) {
219 nsresult rv = CreateFontCache();
220 if (NS_FAILED(rv)) {
221 aMetrics = nsnull;
222 return rv;
224 // XXX temporary fix for performance problem -- erik
225 GetLocaleLangGroup();
228 // XXX figure out why aLangGroup is NULL sometimes
229 if (!aLangGroup) {
230 aLangGroup = mLocaleLangGroup;
233 return mFontCache->GetMetricsFor(aFont, aLangGroup, aMetrics);
236 NS_IMETHODIMP DeviceContextImpl::GetMetricsFor(const nsFont& aFont, nsIFontMetrics*& aMetrics)
238 if (nsnull == mFontCache) {
239 nsresult rv = CreateFontCache();
240 if (NS_FAILED(rv)) {
241 aMetrics = nsnull;
242 return rv;
244 // XXX temporary fix for performance problem -- erik
245 GetLocaleLangGroup();
247 return mFontCache->GetMetricsFor(aFont, mLocaleLangGroup, aMetrics);
250 NS_IMETHODIMP DeviceContextImpl::GetDepth(PRUint32& aDepth)
252 aDepth = 24;
253 return NS_OK;
256 NS_IMETHODIMP DeviceContextImpl::GetPaletteInfo(nsPaletteInfo& aPaletteInfo)
258 aPaletteInfo.isPaletteDevice = PR_FALSE;
259 aPaletteInfo.sizePalette = 0;
260 aPaletteInfo.numReserved = 0;
261 aPaletteInfo.palette = nsnull;
262 return NS_OK;
265 struct FontEnumData {
266 FontEnumData(nsIDeviceContext* aDC, nsString& aFaceName)
267 : mDC(aDC), mFaceName(aFaceName)
269 nsIDeviceContext* mDC;
270 nsString& mFaceName;
273 static PRBool FontEnumCallback(const nsString& aFamily, PRBool aGeneric, void *aData)
275 FontEnumData* data = (FontEnumData*)aData;
276 // XXX for now, all generic fonts are presumed to exist
277 // we may want to actually check if there's an installed conversion
278 if (aGeneric) {
279 data->mFaceName = aFamily;
280 return PR_FALSE; // found one, stop.
282 else {
283 nsAutoString local;
284 PRBool aliased;
285 data->mDC->GetLocalFontName(aFamily, local, aliased);
286 if (aliased || (NS_SUCCEEDED(data->mDC->CheckFontExistence(local)))) {
287 data->mFaceName = local;
288 return PR_FALSE; // found one, stop.
291 return PR_TRUE; // didn't exist, continue looking
294 NS_IMETHODIMP DeviceContextImpl::FirstExistingFont(const nsFont& aFont, nsString& aFaceName)
296 FontEnumData data(this, aFaceName);
297 if (aFont.EnumerateFamilies(FontEnumCallback, &data)) {
298 return NS_ERROR_FAILURE; // ran out
300 return NS_OK;
303 class FontAliasKey: public nsHashKey
305 public:
306 FontAliasKey(const nsString& aString)
307 {mString.Assign(aString);}
309 virtual PRUint32 HashCode(void) const;
310 virtual PRBool Equals(const nsHashKey *aKey) const;
311 virtual nsHashKey *Clone(void) const;
313 nsString mString;
316 PRUint32 FontAliasKey::HashCode(void) const
318 PRUint32 hash = 0;
319 const PRUnichar* string = mString.get();
320 PRUnichar ch;
321 while ((ch = *string++) != 0) {
322 // FYI: hash = hash*37 + ch
323 ch = ToUpperCase(ch);
324 hash = ((hash << 5) + (hash << 2) + hash) + ch;
326 return hash;
329 PRBool FontAliasKey::Equals(const nsHashKey *aKey) const
331 return mString.Equals(((FontAliasKey*)aKey)->mString, nsCaseInsensitiveStringComparator());
334 nsHashKey* FontAliasKey::Clone(void) const
336 return new FontAliasKey(mString);
338 nsresult DeviceContextImpl::CreateFontAliasTable()
340 nsresult result = NS_OK;
342 if (nsnull == mFontAliasTable) {
343 mFontAliasTable = new nsHashtable();
344 if (nsnull != mFontAliasTable) {
346 nsAutoString times; times.AssignLiteral("Times");
347 nsAutoString timesNewRoman; timesNewRoman.AssignLiteral("Times New Roman");
348 nsAutoString timesRoman; timesRoman.AssignLiteral("Times Roman");
349 nsAutoString arial; arial.AssignLiteral("Arial");
350 nsAutoString helvetica; helvetica.AssignLiteral("Helvetica");
351 nsAutoString courier; courier.AssignLiteral("Courier");
352 nsAutoString courierNew; courierNew.AssignLiteral("Courier New");
353 nsAutoString nullStr;
355 AliasFont(times, timesNewRoman, timesRoman, PR_FALSE);
356 AliasFont(timesRoman, timesNewRoman, times, PR_FALSE);
357 AliasFont(timesNewRoman, timesRoman, times, PR_FALSE);
358 AliasFont(arial, helvetica, nullStr, PR_FALSE);
359 AliasFont(helvetica, arial, nullStr, PR_FALSE);
360 AliasFont(courier, courierNew, nullStr, PR_TRUE);
361 AliasFont(courierNew, courier, nullStr, PR_FALSE);
363 else {
364 result = NS_ERROR_OUT_OF_MEMORY;
367 return result;
370 nsresult DeviceContextImpl::AliasFont(const nsString& aFont,
371 const nsString& aAlias, const nsString& aAltAlias,
372 PRBool aForceAlias)
374 nsresult result = NS_OK;
376 if (nsnull != mFontAliasTable) {
377 if (aForceAlias || NS_FAILED(CheckFontExistence(aFont))) {
378 if (NS_SUCCEEDED(CheckFontExistence(aAlias))) {
379 nsString* entry = new nsString(aAlias);
380 if (nsnull != entry) {
381 FontAliasKey key(aFont);
382 mFontAliasTable->Put(&key, entry);
384 else {
385 result = NS_ERROR_OUT_OF_MEMORY;
388 else if (!aAltAlias.IsEmpty() && NS_SUCCEEDED(CheckFontExistence(aAltAlias))) {
389 nsString* entry = new nsString(aAltAlias);
390 if (nsnull != entry) {
391 FontAliasKey key(aFont);
392 mFontAliasTable->Put(&key, entry);
394 else {
395 result = NS_ERROR_OUT_OF_MEMORY;
400 else {
401 result = NS_ERROR_FAILURE;
403 return result;
406 NS_IMETHODIMP DeviceContextImpl::GetLocalFontName(const nsString& aFaceName, nsString& aLocalName,
407 PRBool& aAliased)
409 nsresult result = NS_OK;
411 if (nsnull == mFontAliasTable) {
412 result = CreateFontAliasTable();
415 if (nsnull != mFontAliasTable) {
416 FontAliasKey key(aFaceName);
417 const nsString* alias = (const nsString*)mFontAliasTable->Get(&key);
418 if (nsnull != alias) {
419 aLocalName = *alias;
420 aAliased = PR_TRUE;
422 else {
423 aLocalName = aFaceName;
424 aAliased = PR_FALSE;
427 return result;
430 NS_IMETHODIMP DeviceContextImpl::FlushFontCache(void)
432 if (nsnull != mFontCache)
433 mFontCache->Flush();
435 return NS_OK;
438 /////////////////////////////////////////////////////////////
440 nsFontCache::nsFontCache()
442 MOZ_COUNT_CTOR(nsFontCache);
443 mContext = nsnull;
446 nsFontCache::~nsFontCache()
448 MOZ_COUNT_DTOR(nsFontCache);
449 Flush();
452 nsresult
453 nsFontCache::Init(nsIDeviceContext* aContext)
455 NS_PRECONDITION(nsnull != aContext, "null ptr");
456 // Note: we don't hold a reference to the device context, because it
457 // holds a reference to us and we don't want circular references
458 mContext = aContext;
459 return NS_OK;
462 nsresult
463 nsFontCache::GetDeviceContext(nsIDeviceContext *&aContext) const
465 aContext = mContext;
466 NS_IF_ADDREF(aContext);
467 return NS_OK;
470 nsresult
471 nsFontCache::GetMetricsFor(const nsFont& aFont, nsIAtom* aLangGroup,
472 nsIFontMetrics *&aMetrics)
474 // First check our cache
475 // start from the end, which is where we put the most-recent-used element
477 nsIFontMetrics* fm;
478 PRInt32 n = mFontMetrics.Count() - 1;
479 for (PRInt32 i = n; i >= 0; --i) {
480 fm = static_cast<nsIFontMetrics*>(mFontMetrics[i]);
481 if (fm->Font().Equals(aFont)) {
482 nsCOMPtr<nsIAtom> langGroup;
483 fm->GetLangGroup(getter_AddRefs(langGroup));
484 if (aLangGroup == langGroup.get()) {
485 if (i != n) {
486 // promote it to the end of the cache
487 mFontMetrics.MoveElement(i, n);
489 NS_ADDREF(aMetrics = fm);
490 return NS_OK;
495 // It's not in the cache. Get font metrics and then cache them.
497 aMetrics = nsnull;
498 nsresult rv = CreateFontMetricsInstance(&fm);
499 if (NS_FAILED(rv)) return rv;
500 rv = fm->Init(aFont, aLangGroup, mContext);
501 if (NS_SUCCEEDED(rv)) {
502 // the mFontMetrics list has the "head" at the end, because append is
503 // cheaper than insert
504 mFontMetrics.AppendElement(fm);
505 aMetrics = fm;
506 NS_ADDREF(aMetrics);
507 return NS_OK;
509 fm->Destroy();
510 NS_RELEASE(fm);
512 // One reason why Init() fails is because the system is running out of resources.
513 // e.g., on Win95/98 only a very limited number of GDI objects are available.
514 // Compact the cache and try again.
516 Compact();
517 rv = CreateFontMetricsInstance(&fm);
518 if (NS_FAILED(rv)) return rv;
519 rv = fm->Init(aFont, aLangGroup, mContext);
520 if (NS_SUCCEEDED(rv)) {
521 mFontMetrics.AppendElement(fm);
522 aMetrics = fm;
523 NS_ADDREF(aMetrics);
524 return NS_OK;
526 fm->Destroy();
527 NS_RELEASE(fm);
529 // could not setup a new one, send an old one (XXX search a "best match"?)
531 n = mFontMetrics.Count() - 1; // could have changed in Compact()
532 if (n >= 0) {
533 aMetrics = static_cast<nsIFontMetrics*>(mFontMetrics[n]);
534 NS_ADDREF(aMetrics);
535 return NS_OK;
538 NS_POSTCONDITION(NS_SUCCEEDED(rv), "font metrics should not be null - bug 136248");
539 return rv;
542 /* PostScript module may override this method to create
543 * nsIFontMetrics objects with their own classes
545 nsresult
546 nsFontCache::CreateFontMetricsInstance(nsIFontMetrics** fm)
548 static NS_DEFINE_CID(kFontMetricsCID, NS_FONT_METRICS_CID);
549 return CallCreateInstance(kFontMetricsCID, fm);
552 nsresult nsFontCache::FontMetricsDeleted(const nsIFontMetrics* aFontMetrics)
554 mFontMetrics.RemoveElement((void*)aFontMetrics);
555 return NS_OK;
558 nsresult nsFontCache::Compact()
560 // Need to loop backward because the running element can be removed on the way
561 for (PRInt32 i = mFontMetrics.Count()-1; i >= 0; --i) {
562 nsIFontMetrics* fm = static_cast<nsIFontMetrics*>(mFontMetrics[i]);
563 nsIFontMetrics* oldfm = fm;
564 // Destroy() isn't here because we want our device context to be notified
565 NS_RELEASE(fm); // this will reset fm to nsnull
566 // if the font is really gone, it would have called back in
567 // FontMetricsDeleted() and would have removed itself
568 if (mFontMetrics.IndexOf(oldfm) >= 0) {
569 // nope, the font is still there, so let's hold onto it too
570 NS_ADDREF(oldfm);
573 return NS_OK;
576 nsresult nsFontCache::Flush()
578 for (PRInt32 i = mFontMetrics.Count()-1; i >= 0; --i) {
579 nsIFontMetrics* fm = static_cast<nsIFontMetrics*>(mFontMetrics[i]);
580 // Destroy() will unhook our device context from the fm so that we won't
581 // waste time in triggering the notification of FontMetricsDeleted()
582 // in the subsequent release
583 fm->Destroy();
584 NS_RELEASE(fm);
587 mFontMetrics.Clear();
589 return NS_OK;
592 NS_IMETHODIMP
593 DeviceContextImpl::PrepareNativeWidget(nsIWidget *aWidget, void **aOut)
595 return NS_ERROR_NOT_IMPLEMENTED;
598 NS_IMETHODIMP
599 DeviceContextImpl::ClearCachedSystemFonts()
601 return NS_OK;