summaryrefslogtreecommitdiffstats
path: root/gfx/thebes/gfxFontInfoLoader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/thebes/gfxFontInfoLoader.cpp')
-rw-r--r--gfx/thebes/gfxFontInfoLoader.cpp323
1 files changed, 323 insertions, 0 deletions
diff --git a/gfx/thebes/gfxFontInfoLoader.cpp b/gfx/thebes/gfxFontInfoLoader.cpp
new file mode 100644
index 0000000000..a245644ae5
--- /dev/null
+++ b/gfx/thebes/gfxFontInfoLoader.cpp
@@ -0,0 +1,323 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gfxFontInfoLoader.h"
+#include "mozilla/gfx/Logging.h"
+#include "nsCRT.h"
+#include "nsIObserverService.h"
+#include "nsXPCOM.h" // for gXPCOMThreadsShutDown
+#include "nsThreadUtils.h" // for nsRunnable
+#include "gfxPlatformFontList.h"
+
+#ifdef XP_WIN
+# include <windows.h>
+#endif
+
+using namespace mozilla;
+using services::GetObserverService;
+
+#define LOG_FONTINIT(args) \
+ MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontinit), LogLevel::Debug, args)
+#define LOG_FONTINIT_ENABLED() \
+ MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_fontinit), LogLevel::Debug)
+
+void FontInfoData::Load() {
+ TimeStamp start = TimeStamp::Now();
+
+ uint32_t i, n = mFontFamiliesToLoad.Length();
+ mLoadStats.families = n;
+ for (i = 0; i < n && !mCanceled; i++) {
+ // font file memory mapping sometimes causes exceptions - bug 1100949
+ MOZ_SEH_TRY { LoadFontFamilyData(mFontFamiliesToLoad[i]); }
+ MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
+ gfxCriticalError() << "Exception occurred reading font data for "
+ << mFontFamiliesToLoad[i].get();
+ }
+ }
+
+ mLoadTime = TimeStamp::Now() - start;
+}
+
+class FontInfoLoadCompleteEvent : public Runnable {
+ virtual ~FontInfoLoadCompleteEvent() = default;
+
+ public:
+ NS_INLINE_DECL_REFCOUNTING_INHERITED(FontInfoLoadCompleteEvent, Runnable)
+
+ explicit FontInfoLoadCompleteEvent(FontInfoData* aFontInfo)
+ : mozilla::Runnable("FontInfoLoadCompleteEvent"), mFontInfo(aFontInfo) {}
+
+ NS_IMETHOD Run() override;
+
+ private:
+ RefPtr<FontInfoData> mFontInfo;
+};
+
+class AsyncFontInfoLoader : public Runnable {
+ virtual ~AsyncFontInfoLoader() = default;
+
+ public:
+ NS_INLINE_DECL_REFCOUNTING_INHERITED(AsyncFontInfoLoader, Runnable)
+
+ explicit AsyncFontInfoLoader(FontInfoData* aFontInfo)
+ : mozilla::Runnable("AsyncFontInfoLoader"), mFontInfo(aFontInfo) {
+ mCompleteEvent = new FontInfoLoadCompleteEvent(aFontInfo);
+ }
+
+ NS_IMETHOD Run() override;
+
+ private:
+ RefPtr<FontInfoData> mFontInfo;
+ RefPtr<FontInfoLoadCompleteEvent> mCompleteEvent;
+};
+
+class ShutdownThreadEvent : public Runnable {
+ virtual ~ShutdownThreadEvent() = default;
+
+ public:
+ NS_INLINE_DECL_REFCOUNTING_INHERITED(ShutdownThreadEvent, Runnable)
+
+ explicit ShutdownThreadEvent(nsIThread* aThread)
+ : mozilla::Runnable("ShutdownThreadEvent"), mThread(aThread) {}
+ NS_IMETHOD Run() override {
+ mThread->Shutdown();
+ return NS_OK;
+ }
+
+ private:
+ nsCOMPtr<nsIThread> mThread;
+};
+
+// runs on main thread after async font info loading is done
+nsresult FontInfoLoadCompleteEvent::Run() {
+ gfxFontInfoLoader* loader =
+ static_cast<gfxFontInfoLoader*>(gfxPlatformFontList::PlatformFontList());
+
+ loader->FinalizeLoader(mFontInfo);
+
+ return NS_OK;
+}
+
+// runs on separate thread
+nsresult AsyncFontInfoLoader::Run() {
+ // load platform-specific font info
+ mFontInfo->Load();
+
+ // post a completion event that transfer the data to the fontlist
+ NS_DispatchToMainThread(mCompleteEvent);
+
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(gfxFontInfoLoader::ShutdownObserver, nsIObserver)
+
+static bool sFontLoaderShutdownObserved = false;
+
+NS_IMETHODIMP
+gfxFontInfoLoader::ShutdownObserver::Observe(nsISupports* aSubject,
+ const char* aTopic,
+ const char16_t* someData) {
+ if (!nsCRT::strcmp(aTopic, "quit-application") ||
+ !nsCRT::strcmp(aTopic, "xpcom-shutdown")) {
+ mLoader->CancelLoader();
+ sFontLoaderShutdownObserved = true;
+ } else {
+ MOZ_ASSERT_UNREACHABLE("unexpected notification topic");
+ }
+ return NS_OK;
+}
+
+// StartLoader is usually called at startup with a (prefs-derived) delay value,
+// so that the async loader runs shortly after startup, to avoid competing for
+// disk i/o etc with other more critical operations.
+// However, it may be called with aDelay=0 if we find that the font info (e.g.
+// localized names) is needed for layout. In this case we start the loader
+// immediately; however, it is still an async process and we may use fallback
+// fonts to satisfy layout until it completes.
+void gfxFontInfoLoader::StartLoader(uint32_t aDelay) {
+ if (aDelay == 0 && (mState == stateTimerOff || mState == stateAsyncLoad)) {
+ // We were asked to load (async) without delay, but have already started,
+ // so just return and let the loader proceed.
+ return;
+ }
+
+ NS_ASSERTION(!mFontInfo, "fontinfo should be null when starting font loader");
+
+ // sanity check
+ if (mState != stateInitial && mState != stateTimerOff &&
+ mState != stateTimerOnDelay) {
+ CancelLoader();
+ }
+
+ AddShutdownObserver();
+
+ // Caller asked for a delay? ==> start async thread after a delay
+ if (aDelay) {
+ NS_ASSERTION(!sFontLoaderShutdownObserved,
+ "Bug 1508626 - Setting delay timer for font loader after "
+ "shutdown observed");
+ NS_ASSERTION(!gXPCOMThreadsShutDown,
+ "Bug 1508626 - Setting delay timer for font loader after "
+ "shutdown but before observer");
+ // Set up delay timer. We don't expect to be called with a delay once
+ // the timer already exists, but if that happens we'll just ignore the
+ // extra call and leave the existing timer to do its thing.
+ MOZ_ASSERT(!mTimer, "duplicate use of StartLoader() with delay?");
+ if (mTimer) {
+ return;
+ }
+ mTimer = NS_NewTimer();
+ mTimer->InitWithNamedFuncCallback(DelayedStartCallback, this, aDelay,
+ nsITimer::TYPE_ONE_SHOT,
+ "gfxFontInfoLoader::StartLoader");
+ mState = stateTimerOnDelay;
+ return;
+ }
+
+ // Either we've been called back by the DelayedStartCallback when its timer
+ // fired, or a layout caller has passed aDelay=0 to ask the loader to run
+ // without further delay.
+
+ // Cancel the delay timer, if any.
+ if (mTimer) {
+ mTimer->Cancel();
+ mTimer = nullptr;
+ }
+
+ NS_ASSERTION(
+ !sFontLoaderShutdownObserved,
+ "Bug 1508626 - Initializing font loader after shutdown observed");
+ NS_ASSERTION(!gXPCOMThreadsShutDown,
+ "Bug 1508626 - Initializing font loader after shutdown but "
+ "before observer");
+
+ mFontInfo = CreateFontInfoData();
+ if (!mFontInfo) {
+ // The platform doesn't want anything loaded, so just bail out.
+ mState = stateTimerOff;
+ return;
+ }
+
+ // initialize
+ InitLoader();
+
+ // start async load
+ nsresult rv = NS_NewNamedThread("Font Loader",
+ getter_AddRefs(mFontLoaderThread), nullptr);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return;
+ }
+
+ PRThread* prThread;
+ if (NS_SUCCEEDED(mFontLoaderThread->GetPRThread(&prThread))) {
+ PR_SetThreadPriority(prThread, PR_PRIORITY_LOW);
+ }
+
+ mState = stateAsyncLoad;
+
+ nsCOMPtr<nsIRunnable> loadEvent = new AsyncFontInfoLoader(mFontInfo);
+
+ mFontLoaderThread->Dispatch(loadEvent.forget(), NS_DISPATCH_NORMAL);
+
+ if (LOG_FONTINIT_ENABLED()) {
+ LOG_FONTINIT(
+ ("(fontinit) fontloader started (fontinfo: %p)\n", mFontInfo.get()));
+ }
+}
+
+class FinalizeLoaderRunnable : public Runnable {
+ virtual ~FinalizeLoaderRunnable() = default;
+
+ public:
+ NS_INLINE_DECL_REFCOUNTING_INHERITED(FinalizeLoaderRunnable, Runnable)
+
+ explicit FinalizeLoaderRunnable(gfxFontInfoLoader* aLoader)
+ : mozilla::Runnable("FinalizeLoaderRunnable"), mLoader(aLoader) {}
+
+ NS_IMETHOD Run() override {
+ nsresult rv;
+ if (mLoader->LoadFontInfo()) {
+ mLoader->CancelLoader();
+ rv = NS_OK;
+ } else {
+ nsCOMPtr<nsIRunnable> runnable = this;
+ rv = NS_DispatchToCurrentThreadQueue(
+ runnable.forget(), PR_INTERVAL_NO_TIMEOUT, EventQueuePriority::Idle);
+ }
+ return rv;
+ }
+
+ private:
+ gfxFontInfoLoader* mLoader;
+};
+
+void gfxFontInfoLoader::FinalizeLoader(FontInfoData* aFontInfo) {
+ // Avoid loading data if loader has already been canceled.
+ // This should mean that CancelLoader() ran and the Load
+ // thread has already Shutdown(), and likely before processing
+ // the Shutdown event it handled the load event and sent back
+ // our Completion event, thus we end up here.
+ if (mState != stateAsyncLoad || mFontInfo != aFontInfo) {
+ return;
+ }
+
+ mLoadTime = mFontInfo->mLoadTime;
+
+ MOZ_ASSERT(NS_IsMainThread());
+ nsCOMPtr<nsIRunnable> runnable = new FinalizeLoaderRunnable(this);
+ if (NS_FAILED(NS_DispatchToCurrentThreadQueue(runnable.forget(),
+ PR_INTERVAL_NO_TIMEOUT,
+ EventQueuePriority::Idle))) {
+ NS_WARNING("Failed to finalize async font info");
+ }
+}
+
+void gfxFontInfoLoader::CancelLoader() {
+ if (mState == stateInitial) {
+ return;
+ }
+ mState = stateTimerOff;
+ if (mTimer) {
+ mTimer->Cancel();
+ mTimer = nullptr;
+ }
+ if (mFontInfo) // null during any initial delay
+ mFontInfo->mCanceled = true;
+ if (mFontLoaderThread) {
+ NS_DispatchToMainThread(new ShutdownThreadEvent(mFontLoaderThread));
+ mFontLoaderThread = nullptr;
+ }
+ RemoveShutdownObserver();
+ CleanupLoader();
+}
+
+gfxFontInfoLoader::~gfxFontInfoLoader() {
+ RemoveShutdownObserver();
+ MOZ_COUNT_DTOR(gfxFontInfoLoader);
+}
+
+void gfxFontInfoLoader::AddShutdownObserver() {
+ if (mObserver) {
+ return;
+ }
+
+ nsCOMPtr<nsIObserverService> obs = GetObserverService();
+ if (obs) {
+ mObserver = new ShutdownObserver(this);
+ obs->AddObserver(mObserver, "quit-application", false);
+ obs->AddObserver(mObserver, "xpcom-shutdown", false);
+ }
+}
+
+void gfxFontInfoLoader::RemoveShutdownObserver() {
+ if (mObserver) {
+ nsCOMPtr<nsIObserverService> obs = GetObserverService();
+ if (obs) {
+ obs->RemoveObserver(mObserver, "quit-application");
+ obs->RemoveObserver(mObserver, "xpcom-shutdown");
+ mObserver = nullptr;
+ }
+ }
+}