LCOV - code coverage report
Current view: top level - layout/style - nsFontFaceLoader.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 133 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 13 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : // vim:cindent:ts=2:et:sw=2:
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : /* code for loading in @font-face defined font data */
       8             : 
       9             : #include "mozilla/IntegerPrintfMacros.h"
      10             : #include "mozilla/Logging.h"
      11             : 
      12             : #include "nsFontFaceLoader.h"
      13             : 
      14             : #include "nsError.h"
      15             : #include "nsContentUtils.h"
      16             : #include "mozilla/Preferences.h"
      17             : #include "mozilla/Telemetry.h"
      18             : #include "FontFaceSet.h"
      19             : #include "nsPresContext.h"
      20             : #include "nsIPrincipal.h"
      21             : #include "nsIScriptSecurityManager.h"
      22             : #include "nsIHttpChannel.h"
      23             : #include "nsIContentPolicy.h"
      24             : #include "nsContentPolicyUtils.h"
      25             : 
      26             : #include "mozilla/gfx/2D.h"
      27             : 
      28             : using namespace mozilla;
      29             : using namespace mozilla::dom;
      30             : 
      31             : #define LOG(args) MOZ_LOG(gfxUserFontSet::GetUserFontsLog(), mozilla::LogLevel::Debug, args)
      32             : #define LOG_ENABLED() MOZ_LOG_TEST(gfxUserFontSet::GetUserFontsLog(), \
      33             :                                   LogLevel::Debug)
      34             : 
      35             : static uint32_t
      36           0 : GetFallbackDelay()
      37             : {
      38           0 :   return Preferences::GetInt("gfx.downloadable_fonts.fallback_delay", 3000);
      39             : }
      40             : 
      41             : static uint32_t
      42           0 : GetShortFallbackDelay()
      43             : {
      44           0 :   return Preferences::GetInt("gfx.downloadable_fonts.fallback_delay_short", 100);
      45             : }
      46             : 
      47           0 : nsFontFaceLoader::nsFontFaceLoader(gfxUserFontEntry* aUserFontEntry,
      48             :                                    nsIURI* aFontURI,
      49             :                                    FontFaceSet* aFontFaceSet,
      50           0 :                                    nsIChannel* aChannel)
      51             :   : mUserFontEntry(aUserFontEntry),
      52             :     mFontURI(aFontURI),
      53             :     mFontFaceSet(aFontFaceSet),
      54           0 :     mChannel(aChannel)
      55             : {
      56           0 :   MOZ_ASSERT(mFontFaceSet,
      57             :              "We should get a valid FontFaceSet from the caller!");
      58           0 :   mStartTime = TimeStamp::Now();
      59           0 : }
      60             : 
      61           0 : nsFontFaceLoader::~nsFontFaceLoader()
      62             : {
      63           0 :   if (mUserFontEntry) {
      64           0 :     mUserFontEntry->mLoader = nullptr;
      65             :   }
      66           0 :   if (mLoadTimer) {
      67           0 :     mLoadTimer->Cancel();
      68           0 :     mLoadTimer = nullptr;
      69             :   }
      70           0 :   if (mFontFaceSet) {
      71           0 :     mFontFaceSet->RemoveLoader(this);
      72             :   }
      73           0 : }
      74             : 
      75             : void
      76           0 : nsFontFaceLoader::StartedLoading(nsIStreamLoader* aStreamLoader)
      77             : {
      78             :   int32_t loadTimeout;
      79           0 :   uint8_t fontDisplay = GetFontDisplay();
      80           0 :   if (fontDisplay == NS_FONT_DISPLAY_AUTO ||
      81             :       fontDisplay == NS_FONT_DISPLAY_BLOCK) {
      82           0 :     loadTimeout = GetFallbackDelay();
      83             :   } else {
      84           0 :     loadTimeout = GetShortFallbackDelay();
      85             :   }
      86             : 
      87           0 :   if (loadTimeout > 0) {
      88           0 :     mLoadTimer = do_CreateInstance("@mozilla.org/timer;1");
      89           0 :     if (mLoadTimer) {
      90           0 :       mLoadTimer->SetTarget(
      91           0 :         mFontFaceSet->Document()->EventTargetFor(TaskCategory::Other));
      92           0 :       mLoadTimer->InitWithNamedFuncCallback(LoadTimerCallback,
      93             :                                             static_cast<void*>(this),
      94             :                                             loadTimeout,
      95             :                                             nsITimer::TYPE_ONE_SHOT,
      96           0 :                                             "LoadTimerCallback");
      97             :     }
      98             :   } else {
      99           0 :     mUserFontEntry->mFontDataLoadingState = gfxUserFontEntry::LOADING_SLOWLY;
     100             :   }
     101           0 :   mStreamLoader = aStreamLoader;
     102           0 : }
     103             : 
     104             : /* static */ void
     105           0 : nsFontFaceLoader::LoadTimerCallback(nsITimer* aTimer, void* aClosure)
     106             : {
     107           0 :   nsFontFaceLoader* loader = static_cast<nsFontFaceLoader*>(aClosure);
     108             : 
     109           0 :   if (!loader->mFontFaceSet) {
     110             :     // We've been canceled
     111           0 :     return;
     112             :   }
     113             : 
     114           0 :   gfxUserFontEntry* ufe = loader->mUserFontEntry.get();
     115           0 :   uint8_t fontDisplay = loader->GetFontDisplay();
     116             : 
     117             :   // Depending upon the value of the font-display descriptor for the font,
     118             :   // their may be one or two timeouts associated with each font. The LOADING_SLOWLY
     119             :   // state indicates that the fallback font is shown. The LOADING_TIMED_OUT
     120             :   // state indicates that the fallback font is shown *and* the downloaded font
     121             :   // resource will not replace the fallback font when the load completes.
     122             : 
     123           0 :   bool updateUserFontSet = true;
     124           0 :   switch (fontDisplay) {
     125             :     case NS_FONT_DISPLAY_AUTO:
     126             :     case NS_FONT_DISPLAY_BLOCK:
     127             :       // If the entry is loading, check whether it's >75% done; if so,
     128             :       // we allow another timeout period before showing a fallback font.
     129           0 :       if (ufe->mFontDataLoadingState == gfxUserFontEntry::LOADING_STARTED) {
     130             :         int64_t contentLength;
     131             :         uint32_t numBytesRead;
     132           0 :         if (NS_SUCCEEDED(loader->mChannel->GetContentLength(&contentLength)) &&
     133           0 :             contentLength > 0 &&
     134           0 :             contentLength < UINT32_MAX &&
     135           0 :             NS_SUCCEEDED(loader->mStreamLoader->GetNumBytesRead(&numBytesRead)) &&
     136           0 :             numBytesRead > 3 * (uint32_t(contentLength) >> 2))
     137             :         {
     138             :           // More than 3/4 the data has been downloaded, so allow 50% extra
     139             :           // time and hope the remainder will arrive before the additional
     140             :           // time expires.
     141           0 :           ufe->mFontDataLoadingState = gfxUserFontEntry::LOADING_ALMOST_DONE;
     142             :           uint32_t delay;
     143           0 :           loader->mLoadTimer->GetDelay(&delay);
     144           0 :           loader->mLoadTimer->InitWithNamedFuncCallback(
     145             :             LoadTimerCallback,
     146             :             static_cast<void*>(loader),
     147             :             delay >> 1,
     148             :             nsITimer::TYPE_ONE_SHOT,
     149           0 :             "nsFontFaceLoader::LoadTimerCallback");
     150           0 :           updateUserFontSet = false;
     151           0 :           LOG(("userfonts (%p) 75%% done, resetting timer\n", loader));
     152             :         }
     153             :       }
     154           0 :       if (updateUserFontSet) {
     155           0 :         ufe->mFontDataLoadingState = gfxUserFontEntry::LOADING_SLOWLY;
     156             :       }
     157           0 :       break;
     158             :     case NS_FONT_DISPLAY_SWAP:
     159           0 :       ufe->mFontDataLoadingState = gfxUserFontEntry::LOADING_SLOWLY;
     160           0 :       break;
     161             :     case NS_FONT_DISPLAY_FALLBACK: {
     162           0 :       if (ufe->mFontDataLoadingState == gfxUserFontEntry::LOADING_STARTED) {
     163           0 :         ufe->mFontDataLoadingState = gfxUserFontEntry::LOADING_SLOWLY;
     164             :       } else {
     165           0 :         ufe->mFontDataLoadingState = gfxUserFontEntry::LOADING_TIMED_OUT;
     166           0 :         updateUserFontSet = false;
     167             :       }
     168           0 :       break;
     169             :     }
     170             :     case NS_FONT_DISPLAY_OPTIONAL:
     171           0 :       ufe->mFontDataLoadingState = gfxUserFontEntry::LOADING_TIMED_OUT;
     172           0 :       break;
     173             : 
     174             :     default:
     175           0 :       NS_NOTREACHED("strange font-display value");
     176           0 :       break;
     177             :   }
     178             : 
     179             :   // If the font is not 75% loaded, or if we've already timed out once
     180             :   // before, we mark this entry as "loading slowly", so the fallback
     181             :   // font will be used in the meantime, and tell the context to refresh.
     182           0 :   if (updateUserFontSet) {
     183           0 :     nsTArray<gfxUserFontSet*> fontSets;
     184           0 :     ufe->GetUserFontSets(fontSets);
     185           0 :     for (gfxUserFontSet* fontSet : fontSets) {
     186           0 :       nsPresContext* ctx = FontFaceSet::GetPresContextFor(fontSet);
     187           0 :       if (ctx) {
     188           0 :         fontSet->IncrementGeneration();
     189           0 :         ctx->UserFontSetUpdated(ufe);
     190           0 :         LOG(("userfonts (%p) timeout reflow for pres context %p display %d\n",
     191             :              loader, ctx, fontDisplay));
     192             :       }
     193             :     }
     194             :   }
     195             : }
     196             : 
     197           0 : NS_IMPL_ISUPPORTS(nsFontFaceLoader, nsIStreamLoaderObserver)
     198             : 
     199             : NS_IMETHODIMP
     200           0 : nsFontFaceLoader::OnStreamComplete(nsIStreamLoader* aLoader,
     201             :                                    nsISupports* aContext,
     202             :                                    nsresult aStatus,
     203             :                                    uint32_t aStringLen,
     204             :                                    const uint8_t* aString)
     205             : {
     206           0 :   if (!mFontFaceSet) {
     207             :     // We've been canceled
     208           0 :     return aStatus;
     209             :   }
     210             : 
     211           0 :   mFontFaceSet->RemoveLoader(this);
     212             : 
     213           0 :   TimeStamp doneTime = TimeStamp::Now();
     214           0 :   TimeDuration downloadTime = doneTime - mStartTime;
     215           0 :   uint32_t downloadTimeMS = uint32_t(downloadTime.ToMilliseconds());
     216           0 :   Telemetry::Accumulate(Telemetry::WEBFONT_DOWNLOAD_TIME, downloadTimeMS);
     217             : 
     218           0 :   if (GetFontDisplay() == NS_FONT_DISPLAY_FALLBACK) {
     219           0 :     uint32_t loadTimeout = GetFallbackDelay();
     220           0 :     if (downloadTimeMS > loadTimeout &&
     221           0 :         (mUserFontEntry->mFontDataLoadingState ==
     222             :          gfxUserFontEntry::LOADING_SLOWLY)) {
     223           0 :       mUserFontEntry->mFontDataLoadingState =
     224             :         gfxUserFontEntry::LOADING_TIMED_OUT;
     225             :     }
     226             :   }
     227             : 
     228           0 :   if (LOG_ENABLED()) {
     229           0 :     if (NS_SUCCEEDED(aStatus)) {
     230           0 :       LOG(("userfonts (%p) download completed - font uri: (%s) time: %d ms\n",
     231             :            this, mFontURI->GetSpecOrDefault().get(), downloadTimeMS));
     232             :     } else {
     233           0 :       LOG(("userfonts (%p) download failed - font uri: (%s) error: %8.8" PRIx32 "\n",
     234             :            this, mFontURI->GetSpecOrDefault().get(), static_cast<uint32_t>(aStatus)));
     235             :     }
     236             :   }
     237             : 
     238           0 :   if (NS_SUCCEEDED(aStatus)) {
     239             :     // for HTTP requests, check whether the request _actually_ succeeded;
     240             :     // the "request status" in aStatus does not necessarily indicate this,
     241             :     // because HTTP responses such as 404 (Not Found) will still result in
     242             :     // a success code and potentially an HTML error page from the server
     243             :     // as the resulting data. We don't want to use that as a font.
     244           0 :     nsCOMPtr<nsIRequest> request;
     245           0 :     nsCOMPtr<nsIHttpChannel> httpChannel;
     246           0 :     aLoader->GetRequest(getter_AddRefs(request));
     247           0 :     httpChannel = do_QueryInterface(request);
     248           0 :     if (httpChannel) {
     249             :       bool succeeded;
     250           0 :       nsresult rv = httpChannel->GetRequestSucceeded(&succeeded);
     251           0 :       if (NS_SUCCEEDED(rv) && !succeeded) {
     252           0 :         aStatus = NS_ERROR_NOT_AVAILABLE;
     253             :       }
     254             :     }
     255             :   }
     256             : 
     257             :   // The userFontEntry is responsible for freeing the downloaded data
     258             :   // (aString) when finished with it; the pointer is no longer valid
     259             :   // after FontDataDownloadComplete returns.
     260             :   // This is called even in the case of a failed download (HTTP 404, etc),
     261             :   // as there may still be data to be freed (e.g. an error page),
     262             :   // and we need to load the next source.
     263             :   bool fontUpdate =
     264           0 :     mUserFontEntry->FontDataDownloadComplete(aString, aStringLen, aStatus);
     265             : 
     266           0 :   mFontFaceSet->GetUserFontSet()->RecordFontLoadDone(aStringLen, doneTime);
     267             : 
     268             :   // when new font loaded, need to reflow
     269           0 :   if (fontUpdate) {
     270           0 :     nsTArray<gfxUserFontSet*> fontSets;
     271           0 :     mUserFontEntry->GetUserFontSets(fontSets);
     272           0 :     for (gfxUserFontSet* fontSet : fontSets) {
     273           0 :       nsPresContext* ctx = FontFaceSet::GetPresContextFor(fontSet);
     274           0 :       if (ctx) {
     275             :         // Update layout for the presence of the new font.  Since this is
     276             :         // asynchronous, reflows will coalesce.
     277           0 :         ctx->UserFontSetUpdated(mUserFontEntry);
     278           0 :         LOG(("userfonts (%p) reflow for pres context %p\n", this, ctx));
     279             :       }
     280             :     }
     281             :   }
     282             : 
     283             :   // done with font set
     284           0 :   mFontFaceSet = nullptr;
     285           0 :   if (mLoadTimer) {
     286           0 :     mLoadTimer->Cancel();
     287           0 :     mLoadTimer = nullptr;
     288             :   }
     289             : 
     290           0 :   return NS_SUCCESS_ADOPTED_DATA;
     291             : }
     292             : 
     293             : void
     294           0 : nsFontFaceLoader::Cancel()
     295             : {
     296           0 :   mUserFontEntry->mFontDataLoadingState = gfxUserFontEntry::NOT_LOADING;
     297           0 :   mUserFontEntry->mLoader = nullptr;
     298           0 :   mFontFaceSet = nullptr;
     299           0 :   if (mLoadTimer) {
     300           0 :     mLoadTimer->Cancel();
     301           0 :     mLoadTimer = nullptr;
     302             :   }
     303           0 :   mChannel->Cancel(NS_BINDING_ABORTED);
     304           0 : }
     305             : 
     306             : uint8_t
     307           0 : nsFontFaceLoader::GetFontDisplay()
     308             : {
     309           0 :   uint8_t fontDisplay = NS_FONT_DISPLAY_AUTO;
     310           0 :   if (Preferences::GetBool("layout.css.font-display.enabled")) {
     311           0 :     fontDisplay = mUserFontEntry->GetFontDisplay();
     312             :   }
     313           0 :   return fontDisplay;
     314             : }

Generated by: LCOV version 1.13