Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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/. */
6 #include "gfxFontInfoLoader.h"
7 #include "nsCRT.h"
8 #include "nsIObserverService.h"
9 #include "nsThreadUtils.h" // for nsRunnable
10 #include "gfxPlatformFontList.h"
12 using namespace mozilla;
13 using mozilla::services::GetObserverService;
15 void
16 FontInfoData::Load()
17 {
18 TimeStamp start = TimeStamp::Now();
20 uint32_t i, n = mFontFamiliesToLoad.Length();
21 mLoadStats.families = n;
22 for (i = 0; i < n; i++) {
23 LoadFontFamilyData(mFontFamiliesToLoad[i]);
24 }
26 mLoadTime = TimeStamp::Now() - start;
27 }
29 class FontInfoLoadCompleteEvent : public nsRunnable {
30 NS_DECL_THREADSAFE_ISUPPORTS
32 FontInfoLoadCompleteEvent(FontInfoData *aFontInfo) :
33 mFontInfo(aFontInfo)
34 {}
35 virtual ~FontInfoLoadCompleteEvent() {}
37 NS_IMETHOD Run();
39 nsRefPtr<FontInfoData> mFontInfo;
40 };
42 class AsyncFontInfoLoader : public nsRunnable {
43 NS_DECL_THREADSAFE_ISUPPORTS
45 AsyncFontInfoLoader(FontInfoData *aFontInfo) :
46 mFontInfo(aFontInfo)
47 {
48 mCompleteEvent = new FontInfoLoadCompleteEvent(aFontInfo);
49 }
50 virtual ~AsyncFontInfoLoader() {}
52 NS_IMETHOD Run();
54 nsRefPtr<FontInfoData> mFontInfo;
55 nsRefPtr<FontInfoLoadCompleteEvent> mCompleteEvent;
56 };
58 // runs on main thread after async font info loading is done
59 nsresult
60 FontInfoLoadCompleteEvent::Run()
61 {
62 gfxFontInfoLoader *loader =
63 static_cast<gfxFontInfoLoader*>(gfxPlatformFontList::PlatformFontList());
65 loader->FinalizeLoader(mFontInfo);
67 mFontInfo = nullptr;
68 return NS_OK;
69 }
71 NS_IMPL_ISUPPORTS(FontInfoLoadCompleteEvent, nsIRunnable);
73 // runs on separate thread
74 nsresult
75 AsyncFontInfoLoader::Run()
76 {
77 // load platform-specific font info
78 mFontInfo->Load();
80 // post a completion event that transfer the data to the fontlist
81 NS_DispatchToMainThread(mCompleteEvent, NS_DISPATCH_NORMAL);
82 mFontInfo = nullptr;
84 return NS_OK;
85 }
87 NS_IMPL_ISUPPORTS(AsyncFontInfoLoader, nsIRunnable);
89 NS_IMPL_ISUPPORTS(gfxFontInfoLoader::ShutdownObserver, nsIObserver)
91 NS_IMETHODIMP
92 gfxFontInfoLoader::ShutdownObserver::Observe(nsISupports *aSubject,
93 const char *aTopic,
94 const char16_t *someData)
95 {
96 if (!nsCRT::strcmp(aTopic, "quit-application")) {
97 mLoader->CancelLoader();
98 } else {
99 NS_NOTREACHED("unexpected notification topic");
100 }
101 return NS_OK;
102 }
104 void
105 gfxFontInfoLoader::StartLoader(uint32_t aDelay, uint32_t aInterval)
106 {
107 mInterval = aInterval;
109 // sanity check
110 if (mState != stateInitial &&
111 mState != stateTimerOff &&
112 mState != stateTimerOnDelay) {
113 CancelLoader();
114 }
116 // set up timer
117 if (!mTimer) {
118 mTimer = do_CreateInstance("@mozilla.org/timer;1");
119 if (!mTimer) {
120 NS_WARNING("Failure to create font info loader timer");
121 return;
122 }
123 }
125 AddShutdownObserver();
127 // delay? ==> start async thread after a delay
128 if (aDelay) {
129 mState = stateTimerOnDelay;
130 mTimer->InitWithFuncCallback(DelayedStartCallback, this, aDelay,
131 nsITimer::TYPE_ONE_SHOT);
132 return;
133 }
135 mFontInfo = CreateFontInfoData();
137 // initialize
138 InitLoader();
140 // start async load
141 mState = stateAsyncLoad;
142 nsresult rv = NS_NewNamedThread("Font Loader",
143 getter_AddRefs(mFontLoaderThread),
144 nullptr);
145 if (NS_FAILED(rv)) {
146 return;
147 }
149 nsCOMPtr<nsIRunnable> loadEvent = new AsyncFontInfoLoader(mFontInfo);
151 mFontLoaderThread->Dispatch(loadEvent, NS_DISPATCH_NORMAL);
152 }
154 void
155 gfxFontInfoLoader::FinalizeLoader(FontInfoData *aFontInfo)
156 {
157 // avoid loading data if loader has already been canceled
158 if (mState != stateAsyncLoad) {
159 return;
160 }
162 mLoadTime = mFontInfo->mLoadTime;
164 // try to load all font data immediately
165 if (LoadFontInfo()) {
166 CancelLoader();
167 return;
168 }
170 // not all work completed ==> run load on interval
171 mState = stateTimerOnInterval;
172 mTimer->InitWithFuncCallback(LoadFontInfoCallback, this, mInterval,
173 nsITimer::TYPE_REPEATING_SLACK);
174 }
176 void
177 gfxFontInfoLoader::CancelLoader()
178 {
179 if (mState == stateInitial) {
180 return;
181 }
182 mState = stateTimerOff;
183 if (mTimer) {
184 mTimer->Cancel();
185 mTimer = nullptr;
186 }
187 if (mFontLoaderThread) {
188 mFontLoaderThread->Shutdown();
189 mFontLoaderThread = nullptr;
190 }
191 RemoveShutdownObserver();
192 CleanupLoader();
193 }
195 void
196 gfxFontInfoLoader::LoadFontInfoTimerFire()
197 {
198 if (mState == stateTimerOnDelay) {
199 mState = stateTimerOnInterval;
200 mTimer->SetDelay(mInterval);
201 }
203 bool done = LoadFontInfo();
204 if (done) {
205 CancelLoader();
206 }
207 }
209 gfxFontInfoLoader::~gfxFontInfoLoader()
210 {
211 RemoveShutdownObserver();
212 }
214 void
215 gfxFontInfoLoader::AddShutdownObserver()
216 {
217 if (mObserver) {
218 return;
219 }
221 nsCOMPtr<nsIObserverService> obs = GetObserverService();
222 if (obs) {
223 mObserver = new ShutdownObserver(this);
224 obs->AddObserver(mObserver, "quit-application", false);
225 }
226 }
228 void
229 gfxFontInfoLoader::RemoveShutdownObserver()
230 {
231 if (mObserver) {
232 nsCOMPtr<nsIObserverService> obs = GetObserverService();
233 if (obs) {
234 obs->RemoveObserver(mObserver, "quit-application");
235 mObserver = nullptr;
236 }
237 }
238 }