tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

gfxFontInfoLoader.cpp (9512B)


      1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      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 "mozilla/gfx/Logging.h"
      8 #include "mozilla/AppShutdown.h"
      9 #include "nsCRT.h"
     10 #include "nsIObserverService.h"
     11 #include "nsThreadUtils.h"  // for nsRunnable
     12 #include "gfxPlatformFontList.h"
     13 
     14 #ifdef XP_WIN
     15 #  include <windows.h>
     16 #endif
     17 
     18 using namespace mozilla;
     19 using services::GetObserverService;
     20 
     21 #define LOG_FONTINIT(args) \
     22  MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontinit), LogLevel::Debug, args)
     23 #define LOG_FONTINIT_ENABLED() \
     24  MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_fontinit), LogLevel::Debug)
     25 
     26 void FontInfoData::Load() {
     27  TimeStamp start = TimeStamp::Now();
     28 
     29  uint32_t i, n = mFontFamiliesToLoad.Length();
     30  mLoadStats.families = n;
     31  for (i = 0; i < n && !mCanceled; i++) {
     32    // font file memory mapping sometimes causes exceptions - bug 1100949
     33    MOZ_SEH_TRY { LoadFontFamilyData(mFontFamiliesToLoad[i]); }
     34    MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
     35      gfxCriticalError() << "Exception occurred reading font data for "
     36                         << mFontFamiliesToLoad[i].get();
     37    }
     38  }
     39 
     40  mLoadTime = TimeStamp::Now() - start;
     41 }
     42 
     43 class FontInfoLoadCompleteEvent : public Runnable {
     44  virtual ~FontInfoLoadCompleteEvent() = default;
     45 
     46 public:
     47  NS_INLINE_DECL_REFCOUNTING_INHERITED(FontInfoLoadCompleteEvent, Runnable)
     48 
     49  explicit FontInfoLoadCompleteEvent(FontInfoData* aFontInfo)
     50      : mozilla::Runnable("FontInfoLoadCompleteEvent"), mFontInfo(aFontInfo) {}
     51 
     52  NS_IMETHOD Run() override;
     53 
     54 private:
     55  RefPtr<FontInfoData> mFontInfo;
     56 };
     57 
     58 class AsyncFontInfoLoader : public Runnable {
     59  virtual ~AsyncFontInfoLoader() = default;
     60 
     61 public:
     62  NS_INLINE_DECL_REFCOUNTING_INHERITED(AsyncFontInfoLoader, Runnable)
     63 
     64  explicit AsyncFontInfoLoader(FontInfoData* aFontInfo)
     65      : mozilla::Runnable("AsyncFontInfoLoader"), mFontInfo(aFontInfo) {
     66    mCompleteEvent = new FontInfoLoadCompleteEvent(aFontInfo);
     67  }
     68 
     69  NS_IMETHOD Run() override;
     70 
     71 private:
     72  RefPtr<FontInfoData> mFontInfo;
     73  RefPtr<FontInfoLoadCompleteEvent> mCompleteEvent;
     74 };
     75 
     76 // runs on main thread after async font info loading is done
     77 nsresult FontInfoLoadCompleteEvent::Run() {
     78  gfxFontInfoLoader* loader =
     79      static_cast<gfxFontInfoLoader*>(gfxPlatformFontList::PlatformFontList());
     80 
     81  loader->FinalizeLoader(mFontInfo);
     82 
     83  return NS_OK;
     84 }
     85 
     86 // runs on separate thread
     87 nsresult AsyncFontInfoLoader::Run() {
     88  // load platform-specific font info
     89  mFontInfo->Load();
     90 
     91  // post a completion event that transfer the data to the fontlist
     92  NS_DispatchToMainThread(mCompleteEvent);
     93 
     94  return NS_OK;
     95 }
     96 
     97 NS_IMPL_ISUPPORTS(gfxFontInfoLoader::ShutdownObserver, nsIObserver)
     98 
     99 NS_IMETHODIMP
    100 gfxFontInfoLoader::ShutdownObserver::Observe(nsISupports* aSubject,
    101                                             const char* aTopic,
    102                                             const char16_t* someData) {
    103  if (!nsCRT::strcmp(aTopic, "quit-application") ||
    104      !nsCRT::strcmp(aTopic, "xpcom-shutdown")) {
    105    mLoader->CancelLoader();
    106  } else {
    107    MOZ_ASSERT_UNREACHABLE("unexpected notification topic");
    108  }
    109  return NS_OK;
    110 }
    111 
    112 // StartLoader is usually called at startup with a (prefs-derived) delay value,
    113 // so that the async loader runs shortly after startup, to avoid competing for
    114 // disk i/o etc with other more critical operations.
    115 // However, it may be called with aDelay=0 if we find that the font info (e.g.
    116 // localized names) is needed for layout. In this case we start the loader
    117 // immediately; however, it is still an async process and we may use fallback
    118 // fonts to satisfy layout until it completes.
    119 void gfxFontInfoLoader::StartLoader(uint32_t aDelay) {
    120  if (aDelay == 0 && (mState == stateTimerOff || mState == stateAsyncLoad)) {
    121    // We were asked to load (async) without delay, but have already started,
    122    // so just return and let the loader proceed.
    123    return;
    124  }
    125 
    126  // We observe for "quit-application" above, so avoid initialization after it.
    127  if (NS_WARN_IF(AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdown))) {
    128    MOZ_ASSERT(!aDelay, "Delayed gfxFontInfoLoader startup after AppShutdown?");
    129    return;
    130  }
    131 
    132  // sanity check
    133  if (mState != stateInitial && mState != stateTimerOff &&
    134      mState != stateTimerOnDelay) {
    135    CancelLoader();
    136  }
    137 
    138  // Create mFontInfo when we're initially called to set up the delay, rather
    139  // than when called by the DelayedStartCallback, because on the initial call
    140  // we know we'll be holding the gfxPlatformFontList lock.
    141  if (!mFontInfo) {
    142    mFontInfo = CreateFontInfoData();
    143    if (!mFontInfo) {
    144      // The platform doesn't want anything loaded, so just bail out.
    145      mState = stateTimerOff;
    146      return;
    147    }
    148  }
    149 
    150  AddShutdownObserver();
    151 
    152  // Caller asked for a delay? ==> start async thread after a delay
    153  if (aDelay) {
    154    // Set up delay timer, or if there is already a timer in place, just
    155    // leave it to do its thing. (This can happen if a StartLoader runnable
    156    // was posted to the main thread from the InitFontList thread, but then
    157    // before it had a chance to run and call StartLoader, the main thread
    158    // re-initialized the list due to a platform notification and called
    159    // StartLoader directly.)
    160    if (mTimer) {
    161      return;
    162    }
    163    mTimer = NS_NewTimer();
    164    mTimer->InitWithNamedFuncCallback(DelayedStartCallback, this, aDelay,
    165                                      nsITimer::TYPE_ONE_SHOT,
    166                                      "gfxFontInfoLoader::StartLoader"_ns);
    167    mState = stateTimerOnDelay;
    168    return;
    169  }
    170 
    171  // Either we've been called back by the DelayedStartCallback when its timer
    172  // fired, or a layout caller has passed aDelay=0 to ask the loader to run
    173  // without further delay.
    174 
    175  // Cancel the delay timer, if any.
    176  if (mTimer) {
    177    mTimer->Cancel();
    178    mTimer = nullptr;
    179  }
    180 
    181  // initialize
    182  InitLoader();
    183 
    184  // start async load
    185  nsresult rv = NS_NewNamedThread("Font Loader",
    186                                  getter_AddRefs(mFontLoaderThread), nullptr);
    187  if (NS_WARN_IF(NS_FAILED(rv))) {
    188    return;
    189  }
    190 
    191  PRThread* prThread;
    192  if (NS_SUCCEEDED(mFontLoaderThread->GetPRThread(&prThread))) {
    193    PR_SetThreadPriority(prThread, PR_PRIORITY_LOW);
    194  }
    195 
    196  mState = stateAsyncLoad;
    197 
    198  nsCOMPtr<nsIRunnable> loadEvent = new AsyncFontInfoLoader(mFontInfo);
    199 
    200  mFontLoaderThread->Dispatch(loadEvent.forget(), NS_DISPATCH_NORMAL);
    201 
    202  if (LOG_FONTINIT_ENABLED()) {
    203    LOG_FONTINIT(
    204        ("(fontinit) fontloader started (fontinfo: %p)\n", mFontInfo.get()));
    205  }
    206 }
    207 
    208 class FinalizeLoaderRunnable : public Runnable {
    209  virtual ~FinalizeLoaderRunnable() = default;
    210 
    211 public:
    212  NS_INLINE_DECL_REFCOUNTING_INHERITED(FinalizeLoaderRunnable, Runnable)
    213 
    214  explicit FinalizeLoaderRunnable(gfxFontInfoLoader* aLoader)
    215      : mozilla::Runnable("FinalizeLoaderRunnable"), mLoader(aLoader) {}
    216 
    217  NS_IMETHOD Run() override {
    218    nsresult rv;
    219    if (mLoader->LoadFontInfo()) {
    220      mLoader->CancelLoader();
    221      rv = NS_OK;
    222    } else {
    223      nsCOMPtr<nsIRunnable> runnable = this;
    224      rv = NS_DispatchToCurrentThreadQueue(
    225          runnable.forget(), PR_INTERVAL_NO_TIMEOUT, EventQueuePriority::Idle);
    226    }
    227    return rv;
    228  }
    229 
    230 private:
    231  gfxFontInfoLoader* mLoader;
    232 };
    233 
    234 void gfxFontInfoLoader::FinalizeLoader(FontInfoData* aFontInfo) {
    235  // Avoid loading data if loader has already been canceled.
    236  // This should mean that CancelLoader() ran and the Load
    237  // thread has already Shutdown(), and likely before processing
    238  // the Shutdown event it handled the load event and sent back
    239  // our Completion event, thus we end up here.
    240  if (mState != stateAsyncLoad || mFontInfo != aFontInfo) {
    241    return;
    242  }
    243 
    244  mLoadTime = mFontInfo->mLoadTime;
    245 
    246  MOZ_ASSERT(NS_IsMainThread());
    247  nsCOMPtr<nsIRunnable> runnable = new FinalizeLoaderRunnable(this);
    248  if (NS_FAILED(NS_DispatchToCurrentThreadQueue(runnable.forget(),
    249                                                PR_INTERVAL_NO_TIMEOUT,
    250                                                EventQueuePriority::Idle))) {
    251    NS_WARNING("Failed to finalize async font info");
    252  }
    253 }
    254 
    255 void gfxFontInfoLoader::CancelLoader() {
    256  if (mState == stateInitial) {
    257    return;
    258  }
    259  mState = stateTimerOff;
    260  if (mTimer) {
    261    mTimer->Cancel();
    262    mTimer = nullptr;
    263  }
    264  if (mFontInfo)  // null during any initial delay
    265    mFontInfo->mCanceled = true;
    266  if (mFontLoaderThread) {
    267    NS_DispatchToMainThread(NS_NewRunnableFunction(
    268        __func__,
    269        [thread = std::move(mFontLoaderThread)]() { thread->Shutdown(); }));
    270  }
    271  RemoveShutdownObserver();
    272  CleanupLoader();
    273 }
    274 
    275 gfxFontInfoLoader::~gfxFontInfoLoader() {
    276  RemoveShutdownObserver();
    277  MOZ_COUNT_DTOR(gfxFontInfoLoader);
    278 }
    279 
    280 void gfxFontInfoLoader::AddShutdownObserver() {
    281  if (mObserver) {
    282    return;
    283  }
    284 
    285  nsCOMPtr<nsIObserverService> obs = GetObserverService();
    286  if (obs) {
    287    mObserver = new ShutdownObserver(this);
    288    obs->AddObserver(mObserver, "quit-application", false);
    289    obs->AddObserver(mObserver, "xpcom-shutdown", false);
    290  }
    291 }
    292 
    293 void gfxFontInfoLoader::RemoveShutdownObserver() {
    294  if (mObserver) {
    295    nsCOMPtr<nsIObserverService> obs = GetObserverService();
    296    if (obs) {
    297      obs->RemoveObserver(mObserver, "quit-application");
    298      obs->RemoveObserver(mObserver, "xpcom-shutdown");
    299      mObserver = nullptr;
    300    }
    301  }
    302 }