Bug 1874684 - Part 28: Return DateDuration from DifferenceISODateTime. r=mgaudet
[gecko.git] / gfx / thebes / gfxFontInfoLoader.cpp
blob41cb519164b961266194e89faf0acb725be60bc8
1 /* -*- Mode: C++; tab-width: 20; 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 "gfxFontInfoLoader.h"
7 #include "mozilla/gfx/Logging.h"
8 #include "mozilla/AppShutdown.h"
9 #include "nsCRT.h"
10 #include "nsIObserverService.h"
11 #include "nsThreadUtils.h" // for nsRunnable
12 #include "gfxPlatformFontList.h"
14 #ifdef XP_WIN
15 # include <windows.h>
16 #endif
18 using namespace mozilla;
19 using services::GetObserverService;
21 #define LOG_FONTINIT(args) \
22 MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontinit), LogLevel::Debug, args)
23 #define LOG_FONTINIT_ENABLED() \
24 MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_fontinit), LogLevel::Debug)
26 void FontInfoData::Load() {
27 TimeStamp start = TimeStamp::Now();
29 uint32_t i, n = mFontFamiliesToLoad.Length();
30 mLoadStats.families = n;
31 for (i = 0; i < n && !mCanceled; i++) {
32 // font file memory mapping sometimes causes exceptions - bug 1100949
33 MOZ_SEH_TRY { LoadFontFamilyData(mFontFamiliesToLoad[i]); }
34 MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
35 gfxCriticalError() << "Exception occurred reading font data for "
36 << mFontFamiliesToLoad[i].get();
40 mLoadTime = TimeStamp::Now() - start;
43 class FontInfoLoadCompleteEvent : public Runnable {
44 virtual ~FontInfoLoadCompleteEvent() = default;
46 public:
47 NS_INLINE_DECL_REFCOUNTING_INHERITED(FontInfoLoadCompleteEvent, Runnable)
49 explicit FontInfoLoadCompleteEvent(FontInfoData* aFontInfo)
50 : mozilla::Runnable("FontInfoLoadCompleteEvent"), mFontInfo(aFontInfo) {}
52 NS_IMETHOD Run() override;
54 private:
55 RefPtr<FontInfoData> mFontInfo;
58 class AsyncFontInfoLoader : public Runnable {
59 virtual ~AsyncFontInfoLoader() = default;
61 public:
62 NS_INLINE_DECL_REFCOUNTING_INHERITED(AsyncFontInfoLoader, Runnable)
64 explicit AsyncFontInfoLoader(FontInfoData* aFontInfo)
65 : mozilla::Runnable("AsyncFontInfoLoader"), mFontInfo(aFontInfo) {
66 mCompleteEvent = new FontInfoLoadCompleteEvent(aFontInfo);
69 NS_IMETHOD Run() override;
71 private:
72 RefPtr<FontInfoData> mFontInfo;
73 RefPtr<FontInfoLoadCompleteEvent> mCompleteEvent;
76 class ShutdownThreadEvent : public Runnable {
77 virtual ~ShutdownThreadEvent() = default;
79 public:
80 NS_INLINE_DECL_REFCOUNTING_INHERITED(ShutdownThreadEvent, Runnable)
82 explicit ShutdownThreadEvent(nsIThread* aThread)
83 : mozilla::Runnable("ShutdownThreadEvent"), mThread(aThread) {}
84 NS_IMETHOD Run() override {
85 mThread->Shutdown();
86 return NS_OK;
89 private:
90 nsCOMPtr<nsIThread> mThread;
93 // runs on main thread after async font info loading is done
94 nsresult FontInfoLoadCompleteEvent::Run() {
95 gfxFontInfoLoader* loader =
96 static_cast<gfxFontInfoLoader*>(gfxPlatformFontList::PlatformFontList());
98 loader->FinalizeLoader(mFontInfo);
100 return NS_OK;
103 // runs on separate thread
104 nsresult AsyncFontInfoLoader::Run() {
105 // load platform-specific font info
106 mFontInfo->Load();
108 // post a completion event that transfer the data to the fontlist
109 NS_DispatchToMainThread(mCompleteEvent);
111 return NS_OK;
114 NS_IMPL_ISUPPORTS(gfxFontInfoLoader::ShutdownObserver, nsIObserver)
116 NS_IMETHODIMP
117 gfxFontInfoLoader::ShutdownObserver::Observe(nsISupports* aSubject,
118 const char* aTopic,
119 const char16_t* someData) {
120 if (!nsCRT::strcmp(aTopic, "quit-application") ||
121 !nsCRT::strcmp(aTopic, "xpcom-shutdown")) {
122 mLoader->CancelLoader();
123 } else {
124 MOZ_ASSERT_UNREACHABLE("unexpected notification topic");
126 return NS_OK;
129 // StartLoader is usually called at startup with a (prefs-derived) delay value,
130 // so that the async loader runs shortly after startup, to avoid competing for
131 // disk i/o etc with other more critical operations.
132 // However, it may be called with aDelay=0 if we find that the font info (e.g.
133 // localized names) is needed for layout. In this case we start the loader
134 // immediately; however, it is still an async process and we may use fallback
135 // fonts to satisfy layout until it completes.
136 void gfxFontInfoLoader::StartLoader(uint32_t aDelay) {
137 if (aDelay == 0 && (mState == stateTimerOff || mState == stateAsyncLoad)) {
138 // We were asked to load (async) without delay, but have already started,
139 // so just return and let the loader proceed.
140 return;
143 // We observe for "quit-application" above, so avoid initialization after it.
144 if (NS_WARN_IF(AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdown))) {
145 MOZ_ASSERT(!aDelay, "Delayed gfxFontInfoLoader startup after AppShutdown?");
146 return;
149 // sanity check
150 if (mState != stateInitial && mState != stateTimerOff &&
151 mState != stateTimerOnDelay) {
152 CancelLoader();
155 // Create mFontInfo when we're initially called to set up the delay, rather
156 // than when called by the DelayedStartCallback, because on the initial call
157 // we know we'll be holding the gfxPlatformFontList lock.
158 if (!mFontInfo) {
159 mFontInfo = CreateFontInfoData();
160 if (!mFontInfo) {
161 // The platform doesn't want anything loaded, so just bail out.
162 mState = stateTimerOff;
163 return;
167 AddShutdownObserver();
169 // Caller asked for a delay? ==> start async thread after a delay
170 if (aDelay) {
171 // Set up delay timer, or if there is already a timer in place, just
172 // leave it to do its thing. (This can happen if a StartLoader runnable
173 // was posted to the main thread from the InitFontList thread, but then
174 // before it had a chance to run and call StartLoader, the main thread
175 // re-initialized the list due to a platform notification and called
176 // StartLoader directly.)
177 if (mTimer) {
178 return;
180 mTimer = NS_NewTimer();
181 mTimer->InitWithNamedFuncCallback(DelayedStartCallback, this, aDelay,
182 nsITimer::TYPE_ONE_SHOT,
183 "gfxFontInfoLoader::StartLoader");
184 mState = stateTimerOnDelay;
185 return;
188 // Either we've been called back by the DelayedStartCallback when its timer
189 // fired, or a layout caller has passed aDelay=0 to ask the loader to run
190 // without further delay.
192 // Cancel the delay timer, if any.
193 if (mTimer) {
194 mTimer->Cancel();
195 mTimer = nullptr;
198 // initialize
199 InitLoader();
201 // start async load
202 nsresult rv = NS_NewNamedThread("Font Loader",
203 getter_AddRefs(mFontLoaderThread), nullptr);
204 if (NS_WARN_IF(NS_FAILED(rv))) {
205 return;
208 PRThread* prThread;
209 if (NS_SUCCEEDED(mFontLoaderThread->GetPRThread(&prThread))) {
210 PR_SetThreadPriority(prThread, PR_PRIORITY_LOW);
213 mState = stateAsyncLoad;
215 nsCOMPtr<nsIRunnable> loadEvent = new AsyncFontInfoLoader(mFontInfo);
217 mFontLoaderThread->Dispatch(loadEvent.forget(), NS_DISPATCH_NORMAL);
219 if (LOG_FONTINIT_ENABLED()) {
220 LOG_FONTINIT(
221 ("(fontinit) fontloader started (fontinfo: %p)\n", mFontInfo.get()));
225 class FinalizeLoaderRunnable : public Runnable {
226 virtual ~FinalizeLoaderRunnable() = default;
228 public:
229 NS_INLINE_DECL_REFCOUNTING_INHERITED(FinalizeLoaderRunnable, Runnable)
231 explicit FinalizeLoaderRunnable(gfxFontInfoLoader* aLoader)
232 : mozilla::Runnable("FinalizeLoaderRunnable"), mLoader(aLoader) {}
234 NS_IMETHOD Run() override {
235 nsresult rv;
236 if (mLoader->LoadFontInfo()) {
237 mLoader->CancelLoader();
238 rv = NS_OK;
239 } else {
240 nsCOMPtr<nsIRunnable> runnable = this;
241 rv = NS_DispatchToCurrentThreadQueue(
242 runnable.forget(), PR_INTERVAL_NO_TIMEOUT, EventQueuePriority::Idle);
244 return rv;
247 private:
248 gfxFontInfoLoader* mLoader;
251 void gfxFontInfoLoader::FinalizeLoader(FontInfoData* aFontInfo) {
252 // Avoid loading data if loader has already been canceled.
253 // This should mean that CancelLoader() ran and the Load
254 // thread has already Shutdown(), and likely before processing
255 // the Shutdown event it handled the load event and sent back
256 // our Completion event, thus we end up here.
257 if (mState != stateAsyncLoad || mFontInfo != aFontInfo) {
258 return;
261 mLoadTime = mFontInfo->mLoadTime;
263 MOZ_ASSERT(NS_IsMainThread());
264 nsCOMPtr<nsIRunnable> runnable = new FinalizeLoaderRunnable(this);
265 if (NS_FAILED(NS_DispatchToCurrentThreadQueue(runnable.forget(),
266 PR_INTERVAL_NO_TIMEOUT,
267 EventQueuePriority::Idle))) {
268 NS_WARNING("Failed to finalize async font info");
272 void gfxFontInfoLoader::CancelLoader() {
273 if (mState == stateInitial) {
274 return;
276 mState = stateTimerOff;
277 if (mTimer) {
278 mTimer->Cancel();
279 mTimer = nullptr;
281 if (mFontInfo) // null during any initial delay
282 mFontInfo->mCanceled = true;
283 if (mFontLoaderThread) {
284 NS_DispatchToMainThread(new ShutdownThreadEvent(mFontLoaderThread));
285 mFontLoaderThread = nullptr;
287 RemoveShutdownObserver();
288 CleanupLoader();
291 gfxFontInfoLoader::~gfxFontInfoLoader() {
292 RemoveShutdownObserver();
293 MOZ_COUNT_DTOR(gfxFontInfoLoader);
296 void gfxFontInfoLoader::AddShutdownObserver() {
297 if (mObserver) {
298 return;
301 nsCOMPtr<nsIObserverService> obs = GetObserverService();
302 if (obs) {
303 mObserver = new ShutdownObserver(this);
304 obs->AddObserver(mObserver, "quit-application", false);
305 obs->AddObserver(mObserver, "xpcom-shutdown", false);
309 void gfxFontInfoLoader::RemoveShutdownObserver() {
310 if (mObserver) {
311 nsCOMPtr<nsIObserverService> obs = GetObserverService();
312 if (obs) {
313 obs->RemoveObserver(mObserver, "quit-application");
314 obs->RemoveObserver(mObserver, "xpcom-shutdown");
315 mObserver = nullptr;