layout/style/nsFontFaceLoader.cpp

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing 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/. */
     7 /* code for loading in @font-face defined font data */
     9 #ifdef MOZ_LOGGING
    10 #define FORCE_PR_LOG /* Allow logging in the release build */
    11 #endif /* MOZ_LOGGING */
    12 #include "prlog.h"
    14 #include "nsFontFaceLoader.h"
    16 #include "nsError.h"
    17 #include "nsNetUtil.h"
    18 #include "nsContentUtils.h"
    19 #include "mozilla/Preferences.h"
    21 #include "nsPresContext.h"
    22 #include "nsIPresShell.h"
    23 #include "nsIPrincipal.h"
    24 #include "nsIScriptSecurityManager.h"
    26 #include "nsIContentPolicy.h"
    27 #include "nsContentPolicyUtils.h"
    28 #include "nsCrossSiteListenerProxy.h"
    29 #include "nsIContentSecurityPolicy.h"
    30 #include "nsIDocShell.h"
    31 #include "nsIWebNavigation.h"
    32 #include "nsISupportsPriority.h"
    33 #include "nsINetworkSeer.h"
    35 #include "nsIConsoleService.h"
    37 #include "nsStyleSet.h"
    38 #include "nsPrintfCString.h"
    39 #include "mozilla/gfx/2D.h"
    41 using namespace mozilla;
    43 #ifdef PR_LOGGING
    44 static PRLogModuleInfo* 
    45 GetFontDownloaderLog()
    46 {
    47   static PRLogModuleInfo* sLog;
    48   if (!sLog)
    49     sLog = PR_NewLogModule("fontdownloader");
    50   return sLog;
    51 }
    52 #endif /* PR_LOGGING */
    54 #define LOG(args) PR_LOG(GetFontDownloaderLog(), PR_LOG_DEBUG, args)
    55 #define LOG_ENABLED() PR_LOG_TEST(GetFontDownloaderLog(), PR_LOG_DEBUG)
    58 nsFontFaceLoader::nsFontFaceLoader(gfxMixedFontFamily* aFontFamily,
    59                                    gfxProxyFontEntry* aProxy,
    60                                    nsIURI* aFontURI,
    61                                    nsUserFontSet* aFontSet,
    62                                    nsIChannel* aChannel)
    63   : mFontFamily(aFontFamily),
    64     mFontEntry(aProxy),
    65     mFontURI(aFontURI),
    66     mFontSet(aFontSet),
    67     mChannel(aChannel)
    68 {
    69 }
    71 nsFontFaceLoader::~nsFontFaceLoader()
    72 {
    73   if (mFontEntry) {
    74     mFontEntry->mLoader = nullptr;
    75   }
    76   if (mLoadTimer) {
    77     mLoadTimer->Cancel();
    78     mLoadTimer = nullptr;
    79   }
    80   if (mFontSet) {
    81     mFontSet->RemoveLoader(this);
    82   }
    83 }
    85 void
    86 nsFontFaceLoader::StartedLoading(nsIStreamLoader* aStreamLoader)
    87 {
    88   int32_t loadTimeout =
    89     Preferences::GetInt("gfx.downloadable_fonts.fallback_delay", 3000);
    90   if (loadTimeout > 0) {
    91     mLoadTimer = do_CreateInstance("@mozilla.org/timer;1");
    92     if (mLoadTimer) {
    93       mLoadTimer->InitWithFuncCallback(LoadTimerCallback,
    94                                        static_cast<void*>(this),
    95                                        loadTimeout,
    96                                        nsITimer::TYPE_ONE_SHOT);
    97     }
    98   } else if (loadTimeout == 0) {
    99     mFontEntry->mLoadingState = gfxProxyFontEntry::LOADING_SLOWLY;
   100   } // -1 disables fallback
   101   mStreamLoader = aStreamLoader;
   102 }
   104 void
   105 nsFontFaceLoader::LoadTimerCallback(nsITimer* aTimer, void* aClosure)
   106 {
   107   nsFontFaceLoader* loader = static_cast<nsFontFaceLoader*>(aClosure);
   109   if (!loader->mFontSet) {
   110     // We've been canceled
   111     return;
   112   }
   114   gfxProxyFontEntry* pe = loader->mFontEntry.get();
   115   bool updateUserFontSet = true;
   117   // If the entry is loading, check whether it's >75% done; if so,
   118   // we allow another timeout period before showing a fallback font.
   119   if (pe->mLoadingState == gfxProxyFontEntry::LOADING_STARTED) {
   120     int64_t contentLength;
   121     uint32_t numBytesRead;
   122     if (NS_SUCCEEDED(loader->mChannel->GetContentLength(&contentLength)) &&
   123         contentLength > 0 &&
   124         contentLength < UINT32_MAX &&
   125         NS_SUCCEEDED(loader->mStreamLoader->GetNumBytesRead(&numBytesRead)) &&
   126         numBytesRead > 3 * (uint32_t(contentLength) >> 2))
   127     {
   128       // More than 3/4 the data has been downloaded, so allow 50% extra
   129       // time and hope the remainder will arrive before the additional
   130       // time expires.
   131       pe->mLoadingState = gfxProxyFontEntry::LOADING_ALMOST_DONE;
   132       uint32_t delay;
   133       loader->mLoadTimer->GetDelay(&delay);
   134       loader->mLoadTimer->InitWithFuncCallback(LoadTimerCallback,
   135                                                static_cast<void*>(loader),
   136                                                delay >> 1,
   137                                                nsITimer::TYPE_ONE_SHOT);
   138       updateUserFontSet = false;
   139       LOG(("fontdownloader (%p) 75%% done, resetting timer\n", loader));
   140     }
   141   }
   143   // If the font is not 75% loaded, or if we've already timed out once
   144   // before, we mark this entry as "loading slowly", so the fallback
   145   // font will be used in the meantime, and tell the context to refresh.
   146   if (updateUserFontSet) {
   147     pe->mLoadingState = gfxProxyFontEntry::LOADING_SLOWLY;
   148     gfxUserFontSet* fontSet = loader->mFontSet;
   149     nsPresContext* ctx = loader->mFontSet->GetPresContext();
   150     NS_ASSERTION(ctx, "userfontset doesn't have a presContext?");
   151     if (ctx) {
   152       fontSet->IncrementGeneration();
   153       ctx->UserFontSetUpdated();
   154       LOG(("fontdownloader (%p) timeout reflow\n", loader));
   155     }
   156   }
   157 }
   159 NS_IMPL_ISUPPORTS(nsFontFaceLoader, nsIStreamLoaderObserver)
   161 NS_IMETHODIMP
   162 nsFontFaceLoader::OnStreamComplete(nsIStreamLoader* aLoader,
   163                                    nsISupports* aContext,
   164                                    nsresult aStatus,
   165                                    uint32_t aStringLen,
   166                                    const uint8_t* aString)
   167 {
   168   if (!mFontSet) {
   169     // We've been canceled
   170     return aStatus;
   171   }
   173   mFontSet->RemoveLoader(this);
   175 #ifdef PR_LOGGING
   176   if (LOG_ENABLED()) {
   177     nsAutoCString fontURI;
   178     mFontURI->GetSpec(fontURI);
   179     if (NS_SUCCEEDED(aStatus)) {
   180       LOG(("fontdownloader (%p) download completed - font uri: (%s)\n", 
   181            this, fontURI.get()));
   182     } else {
   183       LOG(("fontdownloader (%p) download failed - font uri: (%s) error: %8.8x\n", 
   184            this, fontURI.get(), aStatus));
   185     }
   186   }
   187 #endif
   189   nsPresContext* ctx = mFontSet->GetPresContext();
   190   NS_ASSERTION(ctx && !ctx->PresShell()->IsDestroying(),
   191                "We should have been canceled already");
   193   if (NS_SUCCEEDED(aStatus)) {
   194     // for HTTP requests, check whether the request _actually_ succeeded;
   195     // the "request status" in aStatus does not necessarily indicate this,
   196     // because HTTP responses such as 404 (Not Found) will still result in
   197     // a success code and potentially an HTML error page from the server
   198     // as the resulting data. We don't want to use that as a font.
   199     nsCOMPtr<nsIRequest> request;
   200     nsCOMPtr<nsIHttpChannel> httpChannel;
   201     aLoader->GetRequest(getter_AddRefs(request));
   202     httpChannel = do_QueryInterface(request);
   203     if (httpChannel) {
   204       bool succeeded;
   205       nsresult rv = httpChannel->GetRequestSucceeded(&succeeded);
   206       if (NS_SUCCEEDED(rv) && !succeeded) {
   207         aStatus = NS_ERROR_NOT_AVAILABLE;
   208       }
   209     }
   210   }
   212   // The userFontSet is responsible for freeing the downloaded data
   213   // (aString) when finished with it; the pointer is no longer valid
   214   // after OnLoadComplete returns.
   215   // This is called even in the case of a failed download (HTTP 404, etc),
   216   // as there may still be data to be freed (e.g. an error page),
   217   // and we need the fontSet to initiate loading the next source.
   218   bool fontUpdate = mFontSet->OnLoadComplete(mFontFamily, mFontEntry, aString,
   219                                              aStringLen, aStatus);
   221   // when new font loaded, need to reflow
   222   if (fontUpdate) {
   223     // Update layout for the presence of the new font.  Since this is
   224     // asynchronous, reflows will coalesce.
   225     ctx->UserFontSetUpdated();
   226     LOG(("fontdownloader (%p) reflow\n", this));
   227   }
   229   // done with font set
   230   mFontSet = nullptr;
   231   if (mLoadTimer) {
   232     mLoadTimer->Cancel();
   233     mLoadTimer = nullptr;
   234   }
   236   return NS_SUCCESS_ADOPTED_DATA;
   237 }
   239 void
   240 nsFontFaceLoader::Cancel()
   241 {
   242   mFontEntry->mLoadingState = gfxProxyFontEntry::NOT_LOADING;
   243   mFontEntry->mLoader = nullptr;
   244   mFontSet = nullptr;
   245   if (mLoadTimer) {
   246     mLoadTimer->Cancel();
   247     mLoadTimer = nullptr;
   248   }
   249   mChannel->Cancel(NS_BINDING_ABORTED);
   250 }
   252 nsresult
   253 nsFontFaceLoader::CheckLoadAllowed(nsIPrincipal* aSourcePrincipal,
   254                                    nsIURI* aTargetURI,
   255                                    nsISupports* aContext)
   256 {
   257   nsresult rv;
   259   if (!aSourcePrincipal)
   260     return NS_OK;
   262   // check with the security manager
   263   nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
   264   rv = secMan->CheckLoadURIWithPrincipal(aSourcePrincipal, aTargetURI,
   265                                         nsIScriptSecurityManager::STANDARD);
   266   if (NS_FAILED(rv)) {
   267     return rv;
   268   }
   270   // check content policy
   271   int16_t shouldLoad = nsIContentPolicy::ACCEPT;
   272   rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_FONT,
   273                                  aTargetURI,
   274                                  aSourcePrincipal,
   275                                  aContext,
   276                                  EmptyCString(), // mime type
   277                                  nullptr,
   278                                  &shouldLoad,
   279                                  nsContentUtils::GetContentPolicy(),
   280                                  nsContentUtils::GetSecurityManager());
   282   if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
   283     return NS_ERROR_CONTENT_BLOCKED;
   284   }
   286   return NS_OK;
   287 }
   289 nsUserFontSet::nsUserFontSet(nsPresContext* aContext)
   290   : mPresContext(aContext)
   291 {
   292   NS_ASSERTION(mPresContext, "null context passed to nsUserFontSet");
   293 }
   295 nsUserFontSet::~nsUserFontSet()
   296 {
   297   NS_ASSERTION(mLoaders.Count() == 0, "mLoaders should have been emptied");
   298 }
   300 static PLDHashOperator DestroyIterator(nsPtrHashKey<nsFontFaceLoader>* aKey,
   301                                        void* aUserArg)
   302 {
   303   aKey->GetKey()->Cancel();
   304   return PL_DHASH_REMOVE;
   305 }
   307 void
   308 nsUserFontSet::Destroy()
   309 {
   310   mPresContext = nullptr;
   311   mLoaders.EnumerateEntries(DestroyIterator, nullptr);
   312   mRules.Clear();
   313 }
   315 void
   316 nsUserFontSet::RemoveLoader(nsFontFaceLoader* aLoader)
   317 {
   318   mLoaders.RemoveEntry(aLoader);
   319 }
   321 nsresult
   322 nsUserFontSet::StartLoad(gfxMixedFontFamily* aFamily,
   323                          gfxProxyFontEntry* aProxy,
   324                          const gfxFontFaceSrc* aFontFaceSrc)
   325 {
   326   nsresult rv;
   328   nsIPresShell* ps = mPresContext->PresShell();
   329   if (!ps)
   330     return NS_ERROR_FAILURE;
   332   nsCOMPtr<nsIStreamLoader> streamLoader;
   333   nsCOMPtr<nsILoadGroup> loadGroup(ps->GetDocument()->GetDocumentLoadGroup());
   335   nsCOMPtr<nsIChannel> channel;
   336   // get Content Security Policy from principal to pass into channel
   337   nsCOMPtr<nsIChannelPolicy> channelPolicy;
   338   nsCOMPtr<nsIContentSecurityPolicy> csp;
   339   rv = aProxy->mPrincipal->GetCsp(getter_AddRefs(csp));
   340   NS_ENSURE_SUCCESS(rv, rv);
   341   if (csp) {
   342     channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
   343     channelPolicy->SetContentSecurityPolicy(csp);
   344     channelPolicy->SetLoadType(nsIContentPolicy::TYPE_FONT);
   345   }
   346   rv = NS_NewChannel(getter_AddRefs(channel),
   347                      aFontFaceSrc->mURI,
   348                      nullptr,
   349                      loadGroup,
   350                      nullptr,
   351                      nsIRequest::LOAD_NORMAL,
   352                      channelPolicy);
   354   NS_ENSURE_SUCCESS(rv, rv);
   356   nsRefPtr<nsFontFaceLoader> fontLoader =
   357     new nsFontFaceLoader(aFamily, aProxy, aFontFaceSrc->mURI, this, channel);
   359   if (!fontLoader)
   360     return NS_ERROR_OUT_OF_MEMORY;
   362 #ifdef PR_LOGGING
   363   if (LOG_ENABLED()) {
   364     nsAutoCString fontURI, referrerURI;
   365     aFontFaceSrc->mURI->GetSpec(fontURI);
   366     if (aFontFaceSrc->mReferrer)
   367       aFontFaceSrc->mReferrer->GetSpec(referrerURI);
   368     LOG(("fontdownloader (%p) download start - font uri: (%s) "
   369          "referrer uri: (%s)\n",
   370          fontLoader.get(), fontURI.get(), referrerURI.get()));
   371   }
   372 #endif
   374   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
   375   if (httpChannel)
   376     httpChannel->SetReferrer(aFontFaceSrc->mReferrer);
   377   nsCOMPtr<nsISupportsPriority> priorityChannel(do_QueryInterface(channel));
   378   if (priorityChannel) {
   379     priorityChannel->AdjustPriority(nsISupportsPriority::PRIORITY_HIGH);
   380   }
   382   rv = NS_NewStreamLoader(getter_AddRefs(streamLoader), fontLoader);
   383   NS_ENSURE_SUCCESS(rv, rv);
   385   nsIDocument *document = ps->GetDocument();
   386   mozilla::net::SeerLearn(aFontFaceSrc->mURI, document->GetDocumentURI(),
   387                           nsINetworkSeer::LEARN_LOAD_SUBRESOURCE, loadGroup);
   389   bool inherits = false;
   390   rv = NS_URIChainHasFlags(aFontFaceSrc->mURI,
   391                            nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT,
   392                            &inherits);
   393   if (NS_SUCCEEDED(rv) && inherits) {
   394     // allow data, javascript, etc URI's
   395     rv = channel->AsyncOpen(streamLoader, nullptr);
   396   } else {
   397     nsRefPtr<nsCORSListenerProxy> listener =
   398       new nsCORSListenerProxy(streamLoader, aProxy->mPrincipal, false);
   399     rv = listener->Init(channel);
   400     if (NS_SUCCEEDED(rv)) {
   401       rv = channel->AsyncOpen(listener, nullptr);
   402     }
   403     if (NS_FAILED(rv)) {
   404       fontLoader->DropChannel();  // explicitly need to break ref cycle
   405     }
   406   }
   408   if (NS_SUCCEEDED(rv)) {
   409     mLoaders.PutEntry(fontLoader);
   410     fontLoader->StartedLoading(streamLoader);
   411     aProxy->mLoader = fontLoader; // let the font entry remember the loader,
   412                                   // in case we need to cancel it
   413   }
   415   return rv;
   416 }
   418 static PLDHashOperator DetachFontEntries(const nsAString& aKey,
   419                                          nsRefPtr<gfxMixedFontFamily>& aFamily,
   420                                          void* aUserArg)
   421 {
   422   aFamily->DetachFontEntries();
   423   return PL_DHASH_NEXT;
   424 }
   426 static PLDHashOperator RemoveIfEmpty(const nsAString& aKey,
   427                                      nsRefPtr<gfxMixedFontFamily>& aFamily,
   428                                      void* aUserArg)
   429 {
   430   return aFamily->GetFontList().Length() ? PL_DHASH_NEXT : PL_DHASH_REMOVE;
   431 }
   433 bool
   434 nsUserFontSet::UpdateRules(const nsTArray<nsFontFaceRuleContainer>& aRules)
   435 {
   436   bool modified = false;
   438   // The @font-face rules that make up the user font set have changed,
   439   // so we need to update the set. However, we want to preserve existing
   440   // font entries wherever possible, so that we don't discard and then
   441   // re-download resources in the (common) case where at least some of the
   442   // same rules are still present.
   444   nsTArray<FontFaceRuleRecord> oldRules;
   445   mRules.SwapElements(oldRules);
   447   // Remove faces from the font family records; we need to re-insert them
   448   // because we might end up with faces in a different order even if they're
   449   // the same font entries as before. (The order can affect font selection
   450   // where multiple faces match the requested style, perhaps with overlapping
   451   // unicode-range coverage.)
   452   mFontFamilies.Enumerate(DetachFontEntries, nullptr);
   454   for (uint32_t i = 0, i_end = aRules.Length(); i < i_end; ++i) {
   455     // Insert each rule into our list, migrating old font entries if possible
   456     // rather than creating new ones; set  modified  to true if we detect
   457     // that rule ordering has changed, or if a new entry is created.
   458     InsertRule(aRules[i].mRule, aRules[i].mSheetType, oldRules, modified);
   459   }
   461   // Remove any residual families that have no font entries (i.e., they were
   462   // not defined at all by the updated set of @font-face rules).
   463   mFontFamilies.Enumerate(RemoveIfEmpty, nullptr);
   465   // If any rules are left in the old list, note that the set has changed
   466   // (even if the new set was built entirely by migrating old font entries).
   467   if (oldRules.Length() > 0) {
   468     modified = true;
   469     // Any in-progress loaders for obsolete rules should be cancelled,
   470     // as the resource being downloaded will no longer be required.
   471     // We need to explicitly remove any loaders here, otherwise the loaders
   472     // will keep their "orphaned" font entries alive until they complete,
   473     // even after the oldRules array is deleted.
   474     size_t count = oldRules.Length();
   475     for (size_t i = 0; i < count; ++i) {
   476       gfxFontEntry* fe = oldRules[i].mFontEntry;
   477       if (!fe->mIsProxy) {
   478         continue;
   479       }
   480       gfxProxyFontEntry* proxy = static_cast<gfxProxyFontEntry*>(fe);
   481       nsFontFaceLoader* loader = proxy->mLoader;
   482       if (loader) {
   483         loader->Cancel();
   484         RemoveLoader(loader);
   485       }
   486     }
   487   }
   489   if (modified) {
   490     IncrementGeneration();
   491   }
   493   // local rules have been rebuilt, so clear the flag
   494   mLocalRulesUsed = false;
   496   return modified;
   497 }
   499 static bool
   500 HasLocalSrc(const nsCSSValue::Array *aSrcArr)
   501 {
   502   size_t numSrc = aSrcArr->Count();
   503   for (size_t i = 0; i < numSrc; i++) {
   504     if (aSrcArr->Item(i).GetUnit() == eCSSUnit_Local_Font) {
   505       return true;
   506     }
   507   }
   508   return false;
   509 }
   511 void
   512 nsUserFontSet::InsertRule(nsCSSFontFaceRule* aRule, uint8_t aSheetType,
   513                           nsTArray<FontFaceRuleRecord>& aOldRules,
   514                           bool& aFontSetModified)
   515 {
   516   NS_ABORT_IF_FALSE(aRule->GetType() == mozilla::css::Rule::FONT_FACE_RULE,
   517                     "InsertRule passed a non-fontface CSS rule");
   519   // set up family name
   520   nsAutoString fontfamily;
   521   nsCSSValue val;
   522   uint32_t unit;
   524   aRule->GetDesc(eCSSFontDesc_Family, val);
   525   unit = val.GetUnit();
   526   if (unit == eCSSUnit_String) {
   527     val.GetStringValue(fontfamily);
   528   } else {
   529     NS_ASSERTION(unit == eCSSUnit_Null,
   530                  "@font-face family name has unexpected unit");
   531   }
   532   if (fontfamily.IsEmpty()) {
   533     // If there is no family name, this rule cannot contribute a
   534     // usable font, so there is no point in processing it further.
   535     return;
   536   }
   538   // first, we check in oldRules; if the rule exists there, just move it
   539   // to the new rule list, and put the entry into the appropriate family
   540   for (uint32_t i = 0; i < aOldRules.Length(); ++i) {
   541     const FontFaceRuleRecord& ruleRec = aOldRules[i];
   543     if (ruleRec.mContainer.mRule == aRule &&
   544         ruleRec.mContainer.mSheetType == aSheetType) {
   546       // if local rules were used, don't use the old font entry
   547       // for rules containing src local usage
   548       if (mLocalRulesUsed) {
   549         aRule->GetDesc(eCSSFontDesc_Src, val);
   550         unit = val.GetUnit();
   551         if (unit == eCSSUnit_Array && HasLocalSrc(val.GetArrayValue())) {
   552           break;
   553         }
   554       }
   556       AddFontFace(fontfamily, ruleRec.mFontEntry);
   557       mRules.AppendElement(ruleRec);
   558       aOldRules.RemoveElementAt(i);
   559       // note the set has been modified if an old rule was skipped to find
   560       // this one - something has been dropped, or ordering changed
   561       if (i > 0) {
   562         aFontSetModified = true;
   563       }
   564       return;
   565     }
   566   }
   568   // this is a new rule:
   570   uint32_t weight = NS_STYLE_FONT_WEIGHT_NORMAL;
   571   int32_t stretch = NS_STYLE_FONT_STRETCH_NORMAL;
   572   uint32_t italicStyle = NS_STYLE_FONT_STYLE_NORMAL;
   573   nsString languageOverride;
   575   // set up weight
   576   aRule->GetDesc(eCSSFontDesc_Weight, val);
   577   unit = val.GetUnit();
   578   if (unit == eCSSUnit_Integer || unit == eCSSUnit_Enumerated) {
   579     weight = val.GetIntValue();
   580   } else if (unit == eCSSUnit_Normal) {
   581     weight = NS_STYLE_FONT_WEIGHT_NORMAL;
   582   } else {
   583     NS_ASSERTION(unit == eCSSUnit_Null,
   584                  "@font-face weight has unexpected unit");
   585   }
   587   // set up stretch
   588   aRule->GetDesc(eCSSFontDesc_Stretch, val);
   589   unit = val.GetUnit();
   590   if (unit == eCSSUnit_Enumerated) {
   591     stretch = val.GetIntValue();
   592   } else if (unit == eCSSUnit_Normal) {
   593     stretch = NS_STYLE_FONT_STRETCH_NORMAL;
   594   } else {
   595     NS_ASSERTION(unit == eCSSUnit_Null,
   596                  "@font-face stretch has unexpected unit");
   597   }
   599   // set up font style
   600   aRule->GetDesc(eCSSFontDesc_Style, val);
   601   unit = val.GetUnit();
   602   if (unit == eCSSUnit_Enumerated) {
   603     italicStyle = val.GetIntValue();
   604   } else if (unit == eCSSUnit_Normal) {
   605     italicStyle = NS_STYLE_FONT_STYLE_NORMAL;
   606   } else {
   607     NS_ASSERTION(unit == eCSSUnit_Null,
   608                  "@font-face style has unexpected unit");
   609   }
   611   // set up font features
   612   nsTArray<gfxFontFeature> featureSettings;
   613   aRule->GetDesc(eCSSFontDesc_FontFeatureSettings, val);
   614   unit = val.GetUnit();
   615   if (unit == eCSSUnit_Normal) {
   616     // empty list of features
   617   } else if (unit == eCSSUnit_PairList || unit == eCSSUnit_PairListDep) {
   618     nsRuleNode::ComputeFontFeatures(val.GetPairListValue(), featureSettings);
   619   } else {
   620     NS_ASSERTION(unit == eCSSUnit_Null,
   621                  "@font-face font-feature-settings has unexpected unit");
   622   }
   624   // set up font language override
   625   aRule->GetDesc(eCSSFontDesc_FontLanguageOverride, val);
   626   unit = val.GetUnit();
   627   if (unit == eCSSUnit_Normal) {
   628     // empty feature string
   629   } else if (unit == eCSSUnit_String) {
   630     val.GetStringValue(languageOverride);
   631   } else {
   632     NS_ASSERTION(unit == eCSSUnit_Null,
   633                  "@font-face font-language-override has unexpected unit");
   634   }
   636   // set up src array
   637   nsTArray<gfxFontFaceSrc> srcArray;
   639   aRule->GetDesc(eCSSFontDesc_Src, val);
   640   unit = val.GetUnit();
   641   if (unit == eCSSUnit_Array) {
   642     nsCSSValue::Array* srcArr = val.GetArrayValue();
   643     size_t numSrc = srcArr->Count();
   645     for (size_t i = 0; i < numSrc; i++) {
   646       val = srcArr->Item(i);
   647       unit = val.GetUnit();
   648       gfxFontFaceSrc* face = srcArray.AppendElements(1);
   649       if (!face)
   650         return;
   652       switch (unit) {
   654       case eCSSUnit_Local_Font:
   655         val.GetStringValue(face->mLocalName);
   656         face->mIsLocal = true;
   657         face->mURI = nullptr;
   658         face->mFormatFlags = 0;
   659         break;
   660       case eCSSUnit_URL:
   661         face->mIsLocal = false;
   662         face->mURI = val.GetURLValue();
   663         face->mReferrer = val.GetURLStructValue()->mReferrer;
   664         face->mOriginPrincipal = val.GetURLStructValue()->mOriginPrincipal;
   665         NS_ASSERTION(face->mOriginPrincipal, "null origin principal in @font-face rule");
   667         // agent and user stylesheets are treated slightly differently,
   668         // the same-site origin check and access control headers are
   669         // enforced against the sheet principal rather than the document
   670         // principal to allow user stylesheets to include @font-face rules
   671         face->mUseOriginPrincipal = (aSheetType == nsStyleSet::eUserSheet ||
   672                                      aSheetType == nsStyleSet::eAgentSheet);
   674         face->mLocalName.Truncate();
   675         face->mFormatFlags = 0;
   676         while (i + 1 < numSrc && (val = srcArr->Item(i+1), 
   677                  val.GetUnit() == eCSSUnit_Font_Format)) {
   678           nsDependentString valueString(val.GetStringBufferValue());
   679           if (valueString.LowerCaseEqualsASCII("woff")) {
   680             face->mFormatFlags |= FLAG_FORMAT_WOFF;
   681           } else if (valueString.LowerCaseEqualsASCII("opentype")) {
   682             face->mFormatFlags |= FLAG_FORMAT_OPENTYPE;
   683           } else if (valueString.LowerCaseEqualsASCII("truetype")) {
   684             face->mFormatFlags |= FLAG_FORMAT_TRUETYPE;
   685           } else if (valueString.LowerCaseEqualsASCII("truetype-aat")) {
   686             face->mFormatFlags |= FLAG_FORMAT_TRUETYPE_AAT;
   687           } else if (valueString.LowerCaseEqualsASCII("embedded-opentype")) {
   688             face->mFormatFlags |= FLAG_FORMAT_EOT;
   689           } else if (valueString.LowerCaseEqualsASCII("svg")) {
   690             face->mFormatFlags |= FLAG_FORMAT_SVG;
   691           } else {
   692             // unknown format specified, mark to distinguish from the
   693             // case where no format hints are specified
   694             face->mFormatFlags |= FLAG_FORMAT_UNKNOWN;
   695           }
   696           i++;
   697         }
   698         if (!face->mURI) {
   699           // if URI not valid, omit from src array
   700           srcArray.RemoveElementAt(srcArray.Length() - 1);
   701           NS_WARNING("null url in @font-face rule");
   702           continue;
   703         }
   704         break;
   705       default:
   706         NS_ASSERTION(unit == eCSSUnit_Local_Font || unit == eCSSUnit_URL,
   707                      "strange unit type in font-face src array");
   708         break;
   709       }
   710      }
   711   } else {
   712     NS_ASSERTION(unit == eCSSUnit_Null, "@font-face src has unexpected unit");
   713   }
   715   if (srcArray.Length() > 0) {
   716     FontFaceRuleRecord ruleRec;
   717     ruleRec.mContainer.mRule = aRule;
   718     ruleRec.mContainer.mSheetType = aSheetType;
   719     ruleRec.mFontEntry = AddFontFace(fontfamily, srcArray,
   720                                      weight, stretch, italicStyle,
   721                                      featureSettings, languageOverride);
   722     if (ruleRec.mFontEntry) {
   723       mRules.AppendElement(ruleRec);
   724     }
   725     // this was a new rule and fontEntry, so note that the set was modified
   726     aFontSetModified = true;
   727   }
   728 }
   730 void
   731 nsUserFontSet::ReplaceFontEntry(gfxMixedFontFamily* aFamily,
   732                                 gfxProxyFontEntry* aProxy,
   733                                 gfxFontEntry* aFontEntry)
   734 {
   735   // aProxy is being supplanted by the "real" font aFontEntry, so we need to
   736   // update any rules that refer to it. Note that there may be multiple rules
   737   // that refer to the same proxy - e.g. if a stylesheet was loaded multiple
   738   // times, so that several identical @font-face rules are present.
   739   for (uint32_t i = 0; i < mRules.Length(); ++i) {
   740     if (mRules[i].mFontEntry == aProxy) {
   741       mRules[i].mFontEntry = aFontEntry;
   742     }
   743   }
   744   aFamily->ReplaceFontEntry(aProxy, aFontEntry);
   745 }
   747 nsCSSFontFaceRule*
   748 nsUserFontSet::FindRuleForEntry(gfxFontEntry* aFontEntry)
   749 {
   750   for (uint32_t i = 0; i < mRules.Length(); ++i) {
   751     if (mRules[i].mFontEntry == aFontEntry) {
   752       return mRules[i].mContainer.mRule;
   753     }
   754   }
   755   return nullptr;
   756 }
   758 nsresult
   759 nsUserFontSet::LogMessage(gfxMixedFontFamily* aFamily,
   760                           gfxProxyFontEntry* aProxy,
   761                           const char*        aMessage,
   762                           uint32_t          aFlags,
   763                           nsresult          aStatus)
   764 {
   765   nsCOMPtr<nsIConsoleService>
   766     console(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
   767   if (!console) {
   768     return NS_ERROR_NOT_AVAILABLE;
   769   }
   771   NS_ConvertUTF16toUTF8 familyName(aFamily->Name());
   772   nsAutoCString fontURI;
   773   if (aProxy->mSrcIndex == aProxy->mSrcList.Length()) {
   774     fontURI.AppendLiteral("(end of source list)");
   775   } else {
   776     if (aProxy->mSrcList[aProxy->mSrcIndex].mURI) {
   777       aProxy->mSrcList[aProxy->mSrcIndex].mURI->GetSpec(fontURI);
   778     } else {
   779       fontURI.AppendLiteral("(invalid URI)");
   780     }
   781   }
   783   char weightKeywordBuf[8]; // plenty to sprintf() a uint16_t
   784   const char* weightKeyword;
   785   const nsAFlatCString& weightKeywordString =
   786     nsCSSProps::ValueToKeyword(aProxy->Weight(),
   787                                nsCSSProps::kFontWeightKTable);
   788   if (weightKeywordString.Length() > 0) {
   789     weightKeyword = weightKeywordString.get();
   790   } else {
   791     sprintf(weightKeywordBuf, "%u", aProxy->Weight());
   792     weightKeyword = weightKeywordBuf;
   793   }
   795   nsPrintfCString message
   796        ("downloadable font: %s "
   797         "(font-family: \"%s\" style:%s weight:%s stretch:%s src index:%d)",
   798         aMessage,
   799         familyName.get(),
   800         aProxy->IsItalic() ? "italic" : "normal",
   801         weightKeyword,
   802         nsCSSProps::ValueToKeyword(aProxy->Stretch(),
   803                                    nsCSSProps::kFontStretchKTable).get(),
   804         aProxy->mSrcIndex);
   806   if (NS_FAILED(aStatus)) {
   807     message.Append(": ");
   808     switch (aStatus) {
   809     case NS_ERROR_DOM_BAD_URI:
   810       message.Append("bad URI or cross-site access not allowed");
   811       break;
   812     case NS_ERROR_CONTENT_BLOCKED:
   813       message.Append("content blocked");
   814       break;
   815     default:
   816       message.Append("status=");
   817       message.AppendInt(static_cast<uint32_t>(aStatus));
   818       break;
   819     }
   820   }
   821   message.Append("\nsource: ");
   822   message.Append(fontURI);
   824 #ifdef PR_LOGGING
   825   if (PR_LOG_TEST(GetUserFontsLog(), PR_LOG_DEBUG)) {
   826     PR_LOG(GetUserFontsLog(), PR_LOG_DEBUG,
   827            ("userfonts (%p) %s", this, message.get()));
   828   }
   829 #endif
   831   // try to give the user an indication of where the rule came from
   832   nsCSSFontFaceRule* rule = FindRuleForEntry(aProxy);
   833   nsString href;
   834   nsString text;
   835   nsresult rv;
   836   if (rule) {
   837     rv = rule->GetCssText(text);
   838     NS_ENSURE_SUCCESS(rv, rv);
   839     nsCOMPtr<nsIDOMCSSStyleSheet> sheet;
   840     rv = rule->GetParentStyleSheet(getter_AddRefs(sheet));
   841     NS_ENSURE_SUCCESS(rv, rv);
   842     // if the style sheet is removed while the font is loading can be null
   843     if (sheet) {
   844       rv = sheet->GetHref(href);
   845       NS_ENSURE_SUCCESS(rv, rv);
   846     } else {
   847       NS_WARNING("null parent stylesheet for @font-face rule");
   848       href.AssignLiteral("unknown");
   849     }
   850   }
   852   nsCOMPtr<nsIScriptError> scriptError =
   853     do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
   854   NS_ENSURE_SUCCESS(rv, rv);
   856   uint64_t innerWindowID = GetPresContext()->Document()->InnerWindowID();
   857   rv = scriptError->InitWithWindowID(NS_ConvertUTF8toUTF16(message),
   858                                      href,         // file
   859                                      text,         // src line
   860                                      0, 0,         // line & column number
   861                                      aFlags,       // flags
   862                                      "CSS Loader", // category (make separate?)
   863                                      innerWindowID);
   864   if (NS_SUCCEEDED(rv)) {
   865     console->LogMessage(scriptError);
   866   }
   868   return NS_OK;
   869 }
   871 nsresult
   872 nsUserFontSet::CheckFontLoad(const gfxFontFaceSrc* aFontFaceSrc,
   873                              nsIPrincipal** aPrincipal,
   874                              bool* aBypassCache)
   875 {
   876   // check same-site origin
   877   nsIPresShell* ps = mPresContext->PresShell();
   878   if (!ps)
   879     return NS_ERROR_FAILURE;
   881   NS_ASSERTION(aFontFaceSrc && !aFontFaceSrc->mIsLocal,
   882                "bad font face url passed to fontloader");
   883   NS_ASSERTION(aFontFaceSrc->mURI, "null font uri");
   884   if (!aFontFaceSrc->mURI)
   885     return NS_ERROR_FAILURE;
   887   // use document principal, original principal if flag set
   888   // this enables user stylesheets to load font files via
   889   // @font-face rules
   890   *aPrincipal = ps->GetDocument()->NodePrincipal();
   892   NS_ASSERTION(aFontFaceSrc->mOriginPrincipal,
   893                "null origin principal in @font-face rule");
   894   if (aFontFaceSrc->mUseOriginPrincipal) {
   895     *aPrincipal = aFontFaceSrc->mOriginPrincipal;
   896   }
   898   nsresult rv = nsFontFaceLoader::CheckLoadAllowed(*aPrincipal,
   899                                                    aFontFaceSrc->mURI,
   900                                                    ps->GetDocument());
   901   if (NS_FAILED(rv)) {
   902     return rv;
   903   }
   905   *aBypassCache = false;
   907   nsCOMPtr<nsIDocShell> docShell = ps->GetDocument()->GetDocShell();
   908   if (docShell) {
   909     uint32_t loadType;
   910     if (NS_SUCCEEDED(docShell->GetLoadType(&loadType))) {
   911       if ((loadType >> 16) & nsIWebNavigation::LOAD_FLAGS_BYPASS_CACHE) {
   912         *aBypassCache = true;
   913       }
   914     }
   915   }
   917   return rv;
   918 }
   920 nsresult
   921 nsUserFontSet::SyncLoadFontData(gfxProxyFontEntry* aFontToLoad,
   922                                 const gfxFontFaceSrc* aFontFaceSrc,
   923                                 uint8_t*& aBuffer,
   924                                 uint32_t& aBufferLength)
   925 {
   926   nsresult rv;
   928   nsCOMPtr<nsIChannel> channel;
   929   // get Content Security Policy from principal to pass into channel
   930   nsCOMPtr<nsIChannelPolicy> channelPolicy;
   931   nsCOMPtr<nsIContentSecurityPolicy> csp;
   932   rv = aFontToLoad->mPrincipal->GetCsp(getter_AddRefs(csp));
   933   NS_ENSURE_SUCCESS(rv, rv);
   934   if (csp) {
   935     channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
   936     channelPolicy->SetContentSecurityPolicy(csp);
   937     channelPolicy->SetLoadType(nsIContentPolicy::TYPE_FONT);
   938   }
   939   rv = NS_NewChannel(getter_AddRefs(channel),
   940                      aFontFaceSrc->mURI,
   941                      nullptr,
   942                      nullptr,
   943                      nullptr,
   944                      nsIRequest::LOAD_NORMAL,
   945                      channelPolicy);
   947   NS_ENSURE_SUCCESS(rv, rv);
   949   // blocking stream is OK for data URIs
   950   nsCOMPtr<nsIInputStream> stream;
   951   rv = channel->Open(getter_AddRefs(stream));
   952   NS_ENSURE_SUCCESS(rv, rv);
   954   uint64_t bufferLength64;
   955   rv = stream->Available(&bufferLength64);
   956   NS_ENSURE_SUCCESS(rv, rv);
   957   if (bufferLength64 == 0) {
   958     return NS_ERROR_FAILURE;
   959   }
   960   if (bufferLength64 > UINT32_MAX) {
   961     return NS_ERROR_FILE_TOO_BIG;
   962   }
   963   aBufferLength = static_cast<uint32_t>(bufferLength64);
   965   // read all the decoded data
   966   aBuffer = static_cast<uint8_t*> (NS_Alloc(sizeof(uint8_t) * aBufferLength));
   967   if (!aBuffer) {
   968     aBufferLength = 0;
   969     return NS_ERROR_OUT_OF_MEMORY;
   970   }
   972   uint32_t numRead, totalRead = 0;
   973   while (NS_SUCCEEDED(rv =
   974            stream->Read(reinterpret_cast<char*>(aBuffer + totalRead),
   975                         aBufferLength - totalRead, &numRead)) &&
   976          numRead != 0)
   977   {
   978     totalRead += numRead;
   979     if (totalRead > aBufferLength) {
   980       rv = NS_ERROR_FAILURE;
   981       break;
   982     }
   983   }
   985   // make sure there's a mime type
   986   if (NS_SUCCEEDED(rv)) {
   987     nsAutoCString mimeType;
   988     rv = channel->GetContentType(mimeType);
   989     aBufferLength = totalRead;
   990   }
   992   if (NS_FAILED(rv)) {
   993     NS_Free(aBuffer);
   994     aBuffer = nullptr;
   995     aBufferLength = 0;
   996     return rv;
   997   }
   999   return NS_OK;
  1002 bool
  1003 nsUserFontSet::GetPrivateBrowsing()
  1005   nsIPresShell* ps = mPresContext->PresShell();
  1006   if (!ps) {
  1007     return false;
  1010   nsCOMPtr<nsILoadContext> loadContext = ps->GetDocument()->GetLoadContext();
  1011   return loadContext && loadContext->UsePrivateBrowsing();
  1014 void
  1015 nsUserFontSet::DoRebuildUserFontSet()
  1017   if (!mPresContext) {
  1018     // AFAICS, this can only happen if someone has already called Destroy() on
  1019     // this font-set, which means it is in the process of being torn down --
  1020     // so there's no point trying to update its rules.
  1021     return;
  1024   mPresContext->RebuildUserFontSet();

mercurial