/src/mozilla-central/gfx/thebes/gfxFontInfoLoader.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
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/. */ |
5 | | |
6 | | #include "gfxFontInfoLoader.h" |
7 | | #include "nsCRT.h" |
8 | | #include "nsIObserverService.h" |
9 | | #include "nsThreadUtils.h" // for nsRunnable |
10 | | #include "gfxPlatformFontList.h" |
11 | | |
12 | | #ifdef XP_WIN |
13 | | #include <windows.h> |
14 | | #endif |
15 | | |
16 | | using namespace mozilla; |
17 | | using services::GetObserverService; |
18 | | |
19 | 0 | #define LOG_FONTINIT(args) MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontinit), \ |
20 | 0 | LogLevel::Debug, args) |
21 | 0 | #define LOG_FONTINIT_ENABLED() MOZ_LOG_TEST( \ |
22 | 0 | gfxPlatform::GetLog(eGfxLog_fontinit), \ |
23 | 0 | LogLevel::Debug) |
24 | | |
25 | | void |
26 | | FontInfoData::Load() |
27 | 0 | { |
28 | 0 | TimeStamp start = TimeStamp::Now(); |
29 | 0 |
|
30 | 0 | uint32_t i, n = mFontFamiliesToLoad.Length(); |
31 | 0 | mLoadStats.families = n; |
32 | 0 | for (i = 0; i < n && !mCanceled; i++) { |
33 | 0 | // font file memory mapping sometimes causes exceptions - bug 1100949 |
34 | 0 | MOZ_SEH_TRY { |
35 | 0 | LoadFontFamilyData(mFontFamiliesToLoad[i]); |
36 | 0 | } MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { |
37 | 0 | gfxCriticalError() << |
38 | 0 | "Exception occurred reading font data for " << |
39 | 0 | mFontFamiliesToLoad[i].get(); |
40 | 0 | } |
41 | 0 | } |
42 | 0 |
|
43 | 0 | mLoadTime = TimeStamp::Now() - start; |
44 | 0 | } |
45 | | |
46 | | class FontInfoLoadCompleteEvent : public Runnable { |
47 | 0 | virtual ~FontInfoLoadCompleteEvent() {} |
48 | | |
49 | | public: |
50 | | NS_INLINE_DECL_REFCOUNTING_INHERITED(FontInfoLoadCompleteEvent, Runnable) |
51 | | |
52 | | explicit FontInfoLoadCompleteEvent(FontInfoData* aFontInfo) |
53 | | : mozilla::Runnable("FontInfoLoadCompleteEvent") |
54 | | , mFontInfo(aFontInfo) |
55 | 0 | {} |
56 | | |
57 | | NS_IMETHOD Run() override; |
58 | | |
59 | | private: |
60 | | RefPtr<FontInfoData> mFontInfo; |
61 | | }; |
62 | | |
63 | | class AsyncFontInfoLoader : public Runnable { |
64 | 0 | virtual ~AsyncFontInfoLoader() {} |
65 | | |
66 | | public: |
67 | | NS_INLINE_DECL_REFCOUNTING_INHERITED(AsyncFontInfoLoader, Runnable) |
68 | | |
69 | | explicit AsyncFontInfoLoader(FontInfoData* aFontInfo) |
70 | | : mozilla::Runnable("AsyncFontInfoLoader") |
71 | | , mFontInfo(aFontInfo) |
72 | 0 | { |
73 | 0 | mCompleteEvent = new FontInfoLoadCompleteEvent(aFontInfo); |
74 | 0 | } |
75 | | |
76 | | NS_IMETHOD Run() override; |
77 | | |
78 | | private: |
79 | | RefPtr<FontInfoData> mFontInfo; |
80 | | RefPtr<FontInfoLoadCompleteEvent> mCompleteEvent; |
81 | | }; |
82 | | |
83 | | class ShutdownThreadEvent : public Runnable { |
84 | 0 | virtual ~ShutdownThreadEvent() {} |
85 | | |
86 | | public: |
87 | | NS_INLINE_DECL_REFCOUNTING_INHERITED(ShutdownThreadEvent, Runnable) |
88 | | |
89 | | explicit ShutdownThreadEvent(nsIThread* aThread) |
90 | | : mozilla::Runnable("ShutdownThreadEvent") |
91 | | , mThread(aThread) |
92 | 0 | { |
93 | 0 | } |
94 | 0 | NS_IMETHOD Run() override { |
95 | 0 | mThread->Shutdown(); |
96 | 0 | return NS_OK; |
97 | 0 | } |
98 | | |
99 | | private: |
100 | | nsCOMPtr<nsIThread> mThread; |
101 | | }; |
102 | | |
103 | | // runs on main thread after async font info loading is done |
104 | | nsresult |
105 | | FontInfoLoadCompleteEvent::Run() |
106 | 0 | { |
107 | 0 | gfxFontInfoLoader *loader = |
108 | 0 | static_cast<gfxFontInfoLoader*>(gfxPlatformFontList::PlatformFontList()); |
109 | 0 |
|
110 | 0 | loader->FinalizeLoader(mFontInfo); |
111 | 0 |
|
112 | 0 | return NS_OK; |
113 | 0 | } |
114 | | |
115 | | // runs on separate thread |
116 | | nsresult |
117 | | AsyncFontInfoLoader::Run() |
118 | 0 | { |
119 | 0 | // load platform-specific font info |
120 | 0 | mFontInfo->Load(); |
121 | 0 |
|
122 | 0 | // post a completion event that transfer the data to the fontlist |
123 | 0 | NS_DispatchToMainThread(mCompleteEvent); |
124 | 0 |
|
125 | 0 | return NS_OK; |
126 | 0 | } |
127 | | |
128 | | NS_IMPL_ISUPPORTS(gfxFontInfoLoader::ShutdownObserver, nsIObserver) |
129 | | |
130 | | NS_IMETHODIMP |
131 | | gfxFontInfoLoader::ShutdownObserver::Observe(nsISupports *aSubject, |
132 | | const char *aTopic, |
133 | | const char16_t *someData) |
134 | 0 | { |
135 | 0 | if (!nsCRT::strcmp(aTopic, "quit-application")) { |
136 | 0 | mLoader->CancelLoader(); |
137 | 0 | } else { |
138 | 0 | MOZ_ASSERT_UNREACHABLE("unexpected notification topic"); |
139 | 0 | } |
140 | 0 | return NS_OK; |
141 | 0 | } |
142 | | |
143 | | void |
144 | | gfxFontInfoLoader::StartLoader(uint32_t aDelay, uint32_t aInterval) |
145 | 0 | { |
146 | 0 | mInterval = aInterval; |
147 | 0 |
|
148 | 0 | NS_ASSERTION(!mFontInfo, |
149 | 0 | "fontinfo should be null when starting font loader"); |
150 | 0 |
|
151 | 0 | // sanity check |
152 | 0 | if (mState != stateInitial && |
153 | 0 | mState != stateTimerOff && |
154 | 0 | mState != stateTimerOnDelay) { |
155 | 0 | CancelLoader(); |
156 | 0 | } |
157 | 0 |
|
158 | 0 | // set up timer |
159 | 0 | if (!mTimer) { |
160 | 0 | mTimer = NS_NewTimer(); |
161 | 0 | if (!mTimer) { |
162 | 0 | NS_WARNING("Failure to create font info loader timer"); |
163 | 0 | return; |
164 | 0 | } |
165 | 0 | } |
166 | 0 |
|
167 | 0 | AddShutdownObserver(); |
168 | 0 |
|
169 | 0 | // delay? ==> start async thread after a delay |
170 | 0 | if (aDelay) { |
171 | 0 | mState = stateTimerOnDelay; |
172 | 0 | mTimer->InitWithNamedFuncCallback(DelayedStartCallback, |
173 | 0 | this, |
174 | 0 | aDelay, |
175 | 0 | nsITimer::TYPE_ONE_SHOT, |
176 | 0 | "gfxFontInfoLoader::StartLoader"); |
177 | 0 | return; |
178 | 0 | } |
179 | 0 | |
180 | 0 | mFontInfo = CreateFontInfoData(); |
181 | 0 |
|
182 | 0 | // initialize |
183 | 0 | InitLoader(); |
184 | 0 |
|
185 | 0 | // start async load |
186 | 0 | nsresult rv = NS_NewNamedThread("Font Loader", |
187 | 0 | getter_AddRefs(mFontLoaderThread), |
188 | 0 | nullptr); |
189 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
190 | 0 | return; |
191 | 0 | } |
192 | 0 | mState = stateAsyncLoad; |
193 | 0 |
|
194 | 0 | nsCOMPtr<nsIRunnable> loadEvent = new AsyncFontInfoLoader(mFontInfo); |
195 | 0 |
|
196 | 0 | mFontLoaderThread->Dispatch(loadEvent.forget(), NS_DISPATCH_NORMAL); |
197 | 0 |
|
198 | 0 | if (LOG_FONTINIT_ENABLED()) { |
199 | 0 | LOG_FONTINIT(("(fontinit) fontloader started (fontinfo: %p)\n", |
200 | 0 | mFontInfo.get())); |
201 | 0 | } |
202 | 0 | } |
203 | | |
204 | | void |
205 | | gfxFontInfoLoader::FinalizeLoader(FontInfoData *aFontInfo) |
206 | 0 | { |
207 | 0 | // Avoid loading data if loader has already been canceled. |
208 | 0 | // This should mean that CancelLoader() ran and the Load |
209 | 0 | // thread has already Shutdown(), and likely before processing |
210 | 0 | // the Shutdown event it handled the load event and sent back |
211 | 0 | // our Completion event, thus we end up here. |
212 | 0 | if (mState != stateAsyncLoad || mFontInfo != aFontInfo) { |
213 | 0 | return; |
214 | 0 | } |
215 | 0 | |
216 | 0 | mLoadTime = mFontInfo->mLoadTime; |
217 | 0 |
|
218 | 0 | // try to load all font data immediately |
219 | 0 | if (LoadFontInfo()) { |
220 | 0 | CancelLoader(); |
221 | 0 | return; |
222 | 0 | } |
223 | 0 | |
224 | 0 | // not all work completed ==> run load on interval |
225 | 0 | mState = stateTimerOnInterval; |
226 | 0 | mTimer->InitWithNamedFuncCallback(LoadFontInfoCallback, |
227 | 0 | this, |
228 | 0 | mInterval, |
229 | 0 | nsITimer::TYPE_REPEATING_SLACK, |
230 | 0 | "gfxFontInfoLoader::FinalizeLoader"); |
231 | 0 | } |
232 | | |
233 | | void |
234 | | gfxFontInfoLoader::CancelLoader() |
235 | 0 | { |
236 | 0 | if (mState == stateInitial) { |
237 | 0 | return; |
238 | 0 | } |
239 | 0 | mState = stateTimerOff; |
240 | 0 | if (mTimer) { |
241 | 0 | mTimer->Cancel(); |
242 | 0 | mTimer = nullptr; |
243 | 0 | } |
244 | 0 | if (mFontInfo) // null during any initial delay |
245 | 0 | mFontInfo->mCanceled = true; |
246 | 0 | if (mFontLoaderThread) { |
247 | 0 | NS_DispatchToMainThread(new ShutdownThreadEvent(mFontLoaderThread)); |
248 | 0 | mFontLoaderThread = nullptr; |
249 | 0 | } |
250 | 0 | RemoveShutdownObserver(); |
251 | 0 | CleanupLoader(); |
252 | 0 | } |
253 | | |
254 | | void |
255 | | gfxFontInfoLoader::LoadFontInfoTimerFire() |
256 | 0 | { |
257 | 0 | if (mState == stateTimerOnDelay) { |
258 | 0 | mState = stateTimerOnInterval; |
259 | 0 | mTimer->SetDelay(mInterval); |
260 | 0 | } |
261 | 0 |
|
262 | 0 | bool done = LoadFontInfo(); |
263 | 0 | if (done) { |
264 | 0 | CancelLoader(); |
265 | 0 | } |
266 | 0 | } |
267 | | |
268 | | gfxFontInfoLoader::~gfxFontInfoLoader() |
269 | 0 | { |
270 | 0 | RemoveShutdownObserver(); |
271 | 0 | MOZ_COUNT_DTOR(gfxFontInfoLoader); |
272 | 0 | } |
273 | | |
274 | | void |
275 | | gfxFontInfoLoader::AddShutdownObserver() |
276 | 0 | { |
277 | 0 | if (mObserver) { |
278 | 0 | return; |
279 | 0 | } |
280 | 0 | |
281 | 0 | nsCOMPtr<nsIObserverService> obs = GetObserverService(); |
282 | 0 | if (obs) { |
283 | 0 | mObserver = new ShutdownObserver(this); |
284 | 0 | obs->AddObserver(mObserver, "quit-application", false); |
285 | 0 | } |
286 | 0 | } |
287 | | |
288 | | void |
289 | | gfxFontInfoLoader::RemoveShutdownObserver() |
290 | 0 | { |
291 | 0 | if (mObserver) { |
292 | 0 | nsCOMPtr<nsIObserverService> obs = GetObserverService(); |
293 | 0 | if (obs) { |
294 | 0 | obs->RemoveObserver(mObserver, "quit-application"); |
295 | 0 | mObserver = nullptr; |
296 | 0 | } |
297 | 0 | } |
298 | 0 | } |