Bug 1732219 - Add API for fetching the preview image. r=geckoview-reviewers,agi,mconley
[gecko.git] / gfx / src / nsFontCache.cpp
blob8f833d822cdd1cae296ba988209bedfb1c6511a0
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"
10 #include "nsCRT.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) {
20 mContext = aContext;
21 // register as a memory-pressure observer to free font resources
22 // in low-memory situations.
23 nsCOMPtr<nsIObserverService> obs = GetObserverService();
24 if (obs) {
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();
36 if (obs) {
37 obs->RemoveObserver(this, "memory-pressure");
39 Flush();
42 NS_IMETHODIMP
43 nsFontCache::Observe(nsISupports*, const char* aTopic, const char16_t*) {
44 if (!nsCRT::strcmp(aTopic, "memory-pressure")) {
45 Compact();
47 return NS_OK;
50 already_AddRefed<nsFontMetrics> nsFontCache::GetMetricsFor(
51 const nsFont& aFont, const nsFontMetrics::Params& aParams) {
52 nsAtom* language = aParams.language && !aParams.language->IsEmpty()
53 ? aParams.language
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) {
66 if (i != n) {
67 // promote it to the end of the cache
68 mFontMetrics.RemoveElementAt(i);
69 mFontMetrics.AppendElement(fm);
71 fm->GetThebesFontGroup()->UpdateUserFonts();
72 return do_AddRef(fm);
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);
83 } else {
84 mFlushPending = true;
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());
96 return fm.forget();
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
114 // the way
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
119 // notified
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
125 NS_ADDREF(oldfm);
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
140 fm->Destroy();
141 NS_RELEASE(fm);
143 mFontMetrics.RemoveElementsAt(0, n);