1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsFontCache.h"
8 #include "gfxTextRun.h"
9 #include "mozilla/Services.h"
12 using mozilla::services::GetObserverService
;
14 NS_IMPL_ISUPPORTS(nsFontCache
, nsIObserver
)
16 // The Init and Destroy methods are necessary because it's not
17 // safe to call AddObserver from a constructor or RemoveObserver
18 // from a destructor. That should be fixed.
19 void nsFontCache::Init(nsPresContext
* aContext
) {
21 // register as a memory-pressure observer to free font resources
22 // in low-memory situations.
23 nsCOMPtr
<nsIObserverService
> obs
= GetObserverService();
25 obs
->AddObserver(this, "memory-pressure", false);
28 mLocaleLanguage
= nsLanguageAtomService::GetService()->GetLocaleLanguage();
29 if (!mLocaleLanguage
) {
30 mLocaleLanguage
= NS_Atomize("x-western");
34 void nsFontCache::Destroy() {
35 nsCOMPtr
<nsIObserverService
> obs
= GetObserverService();
37 obs
->RemoveObserver(this, "memory-pressure");
43 nsFontCache::Observe(nsISupports
*, const char* aTopic
, const char16_t
*) {
44 if (!nsCRT::strcmp(aTopic
, "memory-pressure")) {
50 already_AddRefed
<nsFontMetrics
> nsFontCache::GetMetricsFor(
51 const nsFont
& aFont
, const nsFontMetrics::Params
& aParams
) {
52 nsAtom
* language
= aParams
.language
&& !aParams
.language
->IsEmpty()
54 : mLocaleLanguage
.get();
56 // First check our cache
57 // start from the end, which is where we put the most-recent-used element
58 const int32_t n
= mFontMetrics
.Length() - 1;
59 for (int32_t i
= n
; i
>= 0; --i
) {
60 nsFontMetrics
* fm
= mFontMetrics
[i
];
61 if (fm
->Font().Equals(aFont
) &&
62 fm
->GetUserFontSet() == aParams
.userFontSet
&&
63 fm
->Language() == language
&&
64 fm
->Orientation() == aParams
.orientation
&&
65 fm
->ExplicitLanguage() == aParams
.explicitLanguage
) {
67 // promote it to the end of the cache
68 mFontMetrics
.RemoveElementAt(i
);
69 mFontMetrics
.AppendElement(fm
);
71 fm
->GetThebesFontGroup()->UpdateUserFonts();
76 // It's not in the cache. Get font metrics and then cache them.
77 // If the cache has reached its size limit, drop the older half of the
78 // entries; but if we're on a stylo thread (the usual case), we have
79 // to post a task back to the main thread to do the flush.
80 if (n
>= kMaxCacheEntries
- 1 && !mFlushPending
) {
81 if (NS_IsMainThread()) {
82 Flush(mFontMetrics
.Length() - kMaxCacheEntries
/ 2);
85 nsCOMPtr
<nsIRunnable
> flushTask
= new FlushFontMetricsTask(this);
86 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(flushTask
));
90 nsFontMetrics::Params params
= aParams
;
91 params
.language
= language
;
92 RefPtr
<nsFontMetrics
> fm
= new nsFontMetrics(aFont
, params
, mContext
);
93 // the mFontMetrics list has the "head" at the end, because append
94 // is cheaper than insert
95 mFontMetrics
.AppendElement(do_AddRef(fm
).take());
99 void nsFontCache::UpdateUserFonts(gfxUserFontSet
* aUserFontSet
) {
100 for (nsFontMetrics
* fm
: mFontMetrics
) {
101 gfxFontGroup
* fg
= fm
->GetThebesFontGroup();
102 if (fg
->GetUserFontSet() == aUserFontSet
) {
103 fg
->UpdateUserFonts();
108 void nsFontCache::FontMetricsDeleted(const nsFontMetrics
* aFontMetrics
) {
109 mFontMetrics
.RemoveElement(aFontMetrics
);
112 void nsFontCache::Compact() {
113 // Need to loop backward because the running element can be removed on
115 for (int32_t i
= mFontMetrics
.Length() - 1; i
>= 0; --i
) {
116 nsFontMetrics
* fm
= mFontMetrics
[i
];
117 nsFontMetrics
* oldfm
= fm
;
118 // Destroy() isn't here because we want our device context to be
120 NS_RELEASE(fm
); // this will reset fm to nullptr
121 // if the font is really gone, it would have called back in
122 // FontMetricsDeleted() and would have removed itself
123 if (mFontMetrics
.IndexOf(oldfm
) != mFontMetrics
.NoIndex
) {
124 // nope, the font is still there, so let's hold onto it too
130 // Flush the aFlushCount oldest entries, or all if (aFlushCount < 0)
131 void nsFontCache::Flush(int32_t aFlushCount
) {
132 int32_t n
= aFlushCount
< 0
133 ? mFontMetrics
.Length()
134 : std::min
<int32_t>(aFlushCount
, mFontMetrics
.Length());
135 for (int32_t i
= n
- 1; i
>= 0; --i
) {
136 nsFontMetrics
* fm
= mFontMetrics
[i
];
137 // Destroy() will unhook our device context from the fm so that we
138 // won't waste time in triggering the notification of
139 // FontMetricsDeleted() in the subsequent release
143 mFontMetrics
.RemoveElementsAt(0, n
);