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