michael@0: /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "gfxFontInfoLoader.h" michael@0: #include "nsCRT.h" michael@0: #include "nsIObserverService.h" michael@0: #include "nsThreadUtils.h" // for nsRunnable michael@0: #include "gfxPlatformFontList.h" michael@0: michael@0: using namespace mozilla; michael@0: using mozilla::services::GetObserverService; michael@0: michael@0: void michael@0: FontInfoData::Load() michael@0: { michael@0: TimeStamp start = TimeStamp::Now(); michael@0: michael@0: uint32_t i, n = mFontFamiliesToLoad.Length(); michael@0: mLoadStats.families = n; michael@0: for (i = 0; i < n; i++) { michael@0: LoadFontFamilyData(mFontFamiliesToLoad[i]); michael@0: } michael@0: michael@0: mLoadTime = TimeStamp::Now() - start; michael@0: } michael@0: michael@0: class FontInfoLoadCompleteEvent : public nsRunnable { michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: michael@0: FontInfoLoadCompleteEvent(FontInfoData *aFontInfo) : michael@0: mFontInfo(aFontInfo) michael@0: {} michael@0: virtual ~FontInfoLoadCompleteEvent() {} michael@0: michael@0: NS_IMETHOD Run(); michael@0: michael@0: nsRefPtr mFontInfo; michael@0: }; michael@0: michael@0: class AsyncFontInfoLoader : public nsRunnable { michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: michael@0: AsyncFontInfoLoader(FontInfoData *aFontInfo) : michael@0: mFontInfo(aFontInfo) michael@0: { michael@0: mCompleteEvent = new FontInfoLoadCompleteEvent(aFontInfo); michael@0: } michael@0: virtual ~AsyncFontInfoLoader() {} michael@0: michael@0: NS_IMETHOD Run(); michael@0: michael@0: nsRefPtr mFontInfo; michael@0: nsRefPtr mCompleteEvent; michael@0: }; michael@0: michael@0: // runs on main thread after async font info loading is done michael@0: nsresult michael@0: FontInfoLoadCompleteEvent::Run() michael@0: { michael@0: gfxFontInfoLoader *loader = michael@0: static_cast(gfxPlatformFontList::PlatformFontList()); michael@0: michael@0: loader->FinalizeLoader(mFontInfo); michael@0: michael@0: mFontInfo = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(FontInfoLoadCompleteEvent, nsIRunnable); michael@0: michael@0: // runs on separate thread michael@0: nsresult michael@0: AsyncFontInfoLoader::Run() michael@0: { michael@0: // load platform-specific font info michael@0: mFontInfo->Load(); michael@0: michael@0: // post a completion event that transfer the data to the fontlist michael@0: NS_DispatchToMainThread(mCompleteEvent, NS_DISPATCH_NORMAL); michael@0: mFontInfo = nullptr; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(AsyncFontInfoLoader, nsIRunnable); michael@0: michael@0: NS_IMPL_ISUPPORTS(gfxFontInfoLoader::ShutdownObserver, nsIObserver) michael@0: michael@0: NS_IMETHODIMP michael@0: gfxFontInfoLoader::ShutdownObserver::Observe(nsISupports *aSubject, michael@0: const char *aTopic, michael@0: const char16_t *someData) michael@0: { michael@0: if (!nsCRT::strcmp(aTopic, "quit-application")) { michael@0: mLoader->CancelLoader(); michael@0: } else { michael@0: NS_NOTREACHED("unexpected notification topic"); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: gfxFontInfoLoader::StartLoader(uint32_t aDelay, uint32_t aInterval) michael@0: { michael@0: mInterval = aInterval; michael@0: michael@0: // sanity check michael@0: if (mState != stateInitial && michael@0: mState != stateTimerOff && michael@0: mState != stateTimerOnDelay) { michael@0: CancelLoader(); michael@0: } michael@0: michael@0: // set up timer michael@0: if (!mTimer) { michael@0: mTimer = do_CreateInstance("@mozilla.org/timer;1"); michael@0: if (!mTimer) { michael@0: NS_WARNING("Failure to create font info loader timer"); michael@0: return; michael@0: } michael@0: } michael@0: michael@0: AddShutdownObserver(); michael@0: michael@0: // delay? ==> start async thread after a delay michael@0: if (aDelay) { michael@0: mState = stateTimerOnDelay; michael@0: mTimer->InitWithFuncCallback(DelayedStartCallback, this, aDelay, michael@0: nsITimer::TYPE_ONE_SHOT); michael@0: return; michael@0: } michael@0: michael@0: mFontInfo = CreateFontInfoData(); michael@0: michael@0: // initialize michael@0: InitLoader(); michael@0: michael@0: // start async load michael@0: mState = stateAsyncLoad; michael@0: nsresult rv = NS_NewNamedThread("Font Loader", michael@0: getter_AddRefs(mFontLoaderThread), michael@0: nullptr); michael@0: if (NS_FAILED(rv)) { michael@0: return; michael@0: } michael@0: michael@0: nsCOMPtr loadEvent = new AsyncFontInfoLoader(mFontInfo); michael@0: michael@0: mFontLoaderThread->Dispatch(loadEvent, NS_DISPATCH_NORMAL); michael@0: } michael@0: michael@0: void michael@0: gfxFontInfoLoader::FinalizeLoader(FontInfoData *aFontInfo) michael@0: { michael@0: // avoid loading data if loader has already been canceled michael@0: if (mState != stateAsyncLoad) { michael@0: return; michael@0: } michael@0: michael@0: mLoadTime = mFontInfo->mLoadTime; michael@0: michael@0: // try to load all font data immediately michael@0: if (LoadFontInfo()) { michael@0: CancelLoader(); michael@0: return; michael@0: } michael@0: michael@0: // not all work completed ==> run load on interval michael@0: mState = stateTimerOnInterval; michael@0: mTimer->InitWithFuncCallback(LoadFontInfoCallback, this, mInterval, michael@0: nsITimer::TYPE_REPEATING_SLACK); michael@0: } michael@0: michael@0: void michael@0: gfxFontInfoLoader::CancelLoader() michael@0: { michael@0: if (mState == stateInitial) { michael@0: return; michael@0: } michael@0: mState = stateTimerOff; michael@0: if (mTimer) { michael@0: mTimer->Cancel(); michael@0: mTimer = nullptr; michael@0: } michael@0: if (mFontLoaderThread) { michael@0: mFontLoaderThread->Shutdown(); michael@0: mFontLoaderThread = nullptr; michael@0: } michael@0: RemoveShutdownObserver(); michael@0: CleanupLoader(); michael@0: } michael@0: michael@0: void michael@0: gfxFontInfoLoader::LoadFontInfoTimerFire() michael@0: { michael@0: if (mState == stateTimerOnDelay) { michael@0: mState = stateTimerOnInterval; michael@0: mTimer->SetDelay(mInterval); michael@0: } michael@0: michael@0: bool done = LoadFontInfo(); michael@0: if (done) { michael@0: CancelLoader(); michael@0: } michael@0: } michael@0: michael@0: gfxFontInfoLoader::~gfxFontInfoLoader() michael@0: { michael@0: RemoveShutdownObserver(); michael@0: } michael@0: michael@0: void michael@0: gfxFontInfoLoader::AddShutdownObserver() michael@0: { michael@0: if (mObserver) { michael@0: return; michael@0: } michael@0: michael@0: nsCOMPtr obs = GetObserverService(); michael@0: if (obs) { michael@0: mObserver = new ShutdownObserver(this); michael@0: obs->AddObserver(mObserver, "quit-application", false); michael@0: } michael@0: } michael@0: michael@0: void michael@0: gfxFontInfoLoader::RemoveShutdownObserver() michael@0: { michael@0: if (mObserver) { michael@0: nsCOMPtr obs = GetObserverService(); michael@0: if (obs) { michael@0: obs->RemoveObserver(mObserver, "quit-application"); michael@0: mObserver = nullptr; michael@0: } michael@0: } michael@0: }