gfx/thebes/gfxUserFontSet.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     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/. */
     6 #ifdef MOZ_LOGGING
     7 #define FORCE_PR_LOG /* Allow logging in the release build */
     8 #endif /* MOZ_LOGGING */
     9 #include "prlog.h"
    11 #include "gfxUserFontSet.h"
    12 #include "nsFont.h"
    13 #include "gfxPlatform.h"
    14 #include "nsUnicharUtils.h"
    15 #include "nsNetUtil.h"
    16 #include "nsICacheService.h"
    17 #include "nsIProtocolHandler.h"
    18 #include "nsIPrincipal.h"
    19 #include "gfxFontConstants.h"
    20 #include "mozilla/Services.h"
    21 #include "mozilla/gfx/2D.h"
    22 #include "gfxPlatformFontList.h"
    24 #include "opentype-sanitiser.h"
    25 #include "ots-memory-stream.h"
    27 using namespace mozilla;
    29 #ifdef PR_LOGGING
    30 PRLogModuleInfo *
    31 gfxUserFontSet::GetUserFontsLog()
    32 {
    33     static PRLogModuleInfo *sLog;
    34     if (!sLog)
    35         sLog = PR_NewLogModule("userfonts");
    36     return sLog;
    37 }
    38 #endif /* PR_LOGGING */
    40 #define LOG(args) PR_LOG(GetUserFontsLog(), PR_LOG_DEBUG, args)
    41 #define LOG_ENABLED() PR_LOG_TEST(GetUserFontsLog(), PR_LOG_DEBUG)
    43 static uint64_t sFontSetGeneration = 0;
    45 // TODO: support for unicode ranges not yet implemented
    47 gfxProxyFontEntry::gfxProxyFontEntry(const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
    48              uint32_t aWeight,
    49              int32_t aStretch,
    50              uint32_t aItalicStyle,
    51              const nsTArray<gfxFontFeature>& aFeatureSettings,
    52              uint32_t aLanguageOverride,
    53              gfxSparseBitSet *aUnicodeRanges)
    54     : gfxFontEntry(NS_LITERAL_STRING("Proxy")),
    55       mLoadingState(NOT_LOADING),
    56       mUnsupportedFormat(false),
    57       mLoader(nullptr)
    58 {
    59     mIsProxy = true;
    60     mSrcList = aFontFaceSrcList;
    61     mSrcIndex = 0;
    62     mWeight = aWeight;
    63     mStretch = aStretch;
    64     // XXX Currently, we don't distinguish 'italic' and 'oblique' styles;
    65     // we need to fix this. (Bug 543715)
    66     mItalic = (aItalicStyle & (NS_FONT_STYLE_ITALIC | NS_FONT_STYLE_OBLIQUE)) != 0;
    67     mFeatureSettings.AppendElements(aFeatureSettings);
    68     mLanguageOverride = aLanguageOverride;
    69     mIsUserFont = true;
    70 }
    72 gfxProxyFontEntry::~gfxProxyFontEntry()
    73 {
    74 }
    76 bool
    77 gfxProxyFontEntry::Matches(const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
    78                            uint32_t aWeight,
    79                            int32_t aStretch,
    80                            uint32_t aItalicStyle,
    81                            const nsTArray<gfxFontFeature>& aFeatureSettings,
    82                            uint32_t aLanguageOverride,
    83                            gfxSparseBitSet *aUnicodeRanges)
    84 {
    85     // XXX font entries don't distinguish italic from oblique (bug 543715)
    86     bool isItalic =
    87         (aItalicStyle & (NS_FONT_STYLE_ITALIC | NS_FONT_STYLE_OBLIQUE)) != 0;
    89     return mWeight == aWeight &&
    90            mStretch == aStretch &&
    91            mItalic == isItalic &&
    92            mFeatureSettings == aFeatureSettings &&
    93            mLanguageOverride == aLanguageOverride &&
    94            mSrcList == aFontFaceSrcList;
    95            // XXX once we support unicode-range (bug 475891),
    96            // we'll need to compare that here as well
    97 }
    99 gfxFont*
   100 gfxProxyFontEntry::CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold)
   101 {
   102     // cannot create an actual font for a proxy entry
   103     return nullptr;
   104 }
   106 gfxUserFontSet::gfxUserFontSet()
   107     : mFontFamilies(5), mLocalRulesUsed(false)
   108 {
   109     IncrementGeneration();
   110     gfxPlatformFontList *fp = gfxPlatformFontList::PlatformFontList();
   111     if (fp) {
   112         fp->AddUserFontSet(this);
   113     }
   114 }
   116 gfxUserFontSet::~gfxUserFontSet()
   117 {
   118     gfxPlatformFontList *fp = gfxPlatformFontList::PlatformFontList();
   119     if (fp) {
   120         fp->RemoveUserFontSet(this);
   121     }
   122 }
   124 gfxFontEntry*
   125 gfxUserFontSet::AddFontFace(const nsAString& aFamilyName,
   126                             const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
   127                             uint32_t aWeight,
   128                             int32_t aStretch,
   129                             uint32_t aItalicStyle,
   130                             const nsTArray<gfxFontFeature>& aFeatureSettings,
   131                             const nsString& aLanguageOverride,
   132                             gfxSparseBitSet *aUnicodeRanges)
   133 {
   134     nsAutoString key(aFamilyName);
   135     ToLowerCase(key);
   137     bool found;
   139     if (aWeight == 0)
   140         aWeight = NS_FONT_WEIGHT_NORMAL;
   142     // stretch, italic/oblique ==> zero implies normal
   144     gfxMixedFontFamily *family = mFontFamilies.GetWeak(key, &found);
   145     if (!family) {
   146         family = new gfxMixedFontFamily(aFamilyName);
   147         mFontFamilies.Put(key, family);
   148     }
   150     uint32_t languageOverride =
   151         gfxFontStyle::ParseFontLanguageOverride(aLanguageOverride);
   153     // If there's already a proxy in the family whose descriptors all match,
   154     // we can just move it to the end of the list instead of adding a new
   155     // face that will always "shadow" the old one.
   156     // Note that we can't do this for "real" (non-proxy) entries, even if the
   157     // style descriptors match, as they might have had a different source list,
   158     // but we no longer have the old source list available to check.
   159     nsTArray<nsRefPtr<gfxFontEntry> >& fontList = family->GetFontList();
   160     for (uint32_t i = 0, count = fontList.Length(); i < count; i++) {
   161         if (!fontList[i]->mIsProxy) {
   162             continue;
   163         }
   165         gfxProxyFontEntry *existingProxyEntry =
   166             static_cast<gfxProxyFontEntry*>(fontList[i].get());
   167         if (!existingProxyEntry->Matches(aFontFaceSrcList,
   168                                          aWeight, aStretch, aItalicStyle,
   169                                          aFeatureSettings, languageOverride,
   170                                          aUnicodeRanges)) {
   171             continue;
   172         }
   174         // We've found an entry that matches the new face exactly, so just add
   175         // it to the end of the list. gfxMixedFontFamily::AddFontEntry() will
   176         // automatically remove any earlier occurrence of the same proxy.
   177         family->AddFontEntry(existingProxyEntry);
   178         return existingProxyEntry;
   179     }
   181     // construct a new face and add it into the family
   182     gfxProxyFontEntry *proxyEntry =
   183         new gfxProxyFontEntry(aFontFaceSrcList, aWeight, aStretch,
   184                               aItalicStyle,
   185                               aFeatureSettings,
   186                               languageOverride,
   187                               aUnicodeRanges);
   188     family->AddFontEntry(proxyEntry);
   189 #ifdef PR_LOGGING
   190     if (LOG_ENABLED()) {
   191         LOG(("userfonts (%p) added (%s) with style: %s weight: %d stretch: %d",
   192              this, NS_ConvertUTF16toUTF8(aFamilyName).get(),
   193              (aItalicStyle & NS_FONT_STYLE_ITALIC ? "italic" :
   194                  (aItalicStyle & NS_FONT_STYLE_OBLIQUE ? "oblique" : "normal")),
   195              aWeight, aStretch));
   196     }
   197 #endif
   199     return proxyEntry;
   200 }
   202 void
   203 gfxUserFontSet::AddFontFace(const nsAString& aFamilyName,
   204                             gfxFontEntry     *aFontEntry)
   205 {
   206     nsAutoString key(aFamilyName);
   207     ToLowerCase(key);
   209     bool found;
   211     gfxMixedFontFamily *family = mFontFamilies.GetWeak(key, &found);
   212     if (!family) {
   213         family = new gfxMixedFontFamily(aFamilyName);
   214         mFontFamilies.Put(key, family);
   215     }
   217     family->AddFontEntry(aFontEntry);
   218 }
   220 gfxFontEntry*
   221 gfxUserFontSet::FindFontEntry(gfxFontFamily *aFamily,
   222                               const gfxFontStyle& aFontStyle,
   223                               bool& aNeedsBold,
   224                               bool& aWaitForUserFont)
   225 {
   226     aWaitForUserFont = false;
   227     gfxMixedFontFamily *family = static_cast<gfxMixedFontFamily*>(aFamily);
   229     gfxFontEntry* fe = family->FindFontForStyle(aFontStyle, aNeedsBold);
   231     // if not a proxy, font has already been loaded
   232     if (!fe->mIsProxy) {
   233         return fe;
   234     }
   236     gfxProxyFontEntry *proxyEntry = static_cast<gfxProxyFontEntry*> (fe);
   238     // if currently loading, return null for now
   239     if (proxyEntry->mLoadingState > gfxProxyFontEntry::NOT_LOADING) {
   240         aWaitForUserFont =
   241             (proxyEntry->mLoadingState < gfxProxyFontEntry::LOADING_SLOWLY);
   242         return nullptr;
   243     }
   245     // hasn't been loaded yet, start the load process
   246     LoadStatus status;
   248     // NOTE that if all sources in the entry fail, this will delete proxyEntry,
   249     // so we cannot use it again if status==STATUS_END_OF_LIST
   250     status = LoadNext(family, proxyEntry);
   252     // if the load succeeded immediately, the font entry was replaced so
   253     // search again
   254     if (status == STATUS_LOADED) {
   255         return family->FindFontForStyle(aFontStyle, aNeedsBold);
   256     }
   258     // check whether we should wait for load to complete before painting
   259     // a fallback font -- but not if all sources failed (bug 633500)
   260     aWaitForUserFont = (status != STATUS_END_OF_LIST) &&
   261         (proxyEntry->mLoadingState < gfxProxyFontEntry::LOADING_SLOWLY);
   263     // if either loading or an error occurred, return null
   264     return nullptr;
   265 }
   267 // Based on ots::ExpandingMemoryStream from ots-memory-stream.h,
   268 // adapted to use Mozilla allocators and to allow the final
   269 // memory buffer to be adopted by the client.
   270 class ExpandingMemoryStream : public ots::OTSStream {
   271 public:
   272     ExpandingMemoryStream(size_t initial, size_t limit)
   273         : mLength(initial), mLimit(limit), mOff(0) {
   274         mPtr = NS_Alloc(mLength);
   275     }
   277     ~ExpandingMemoryStream() {
   278         NS_Free(mPtr);
   279     }
   281     // return the buffer, and give up ownership of it
   282     // so the caller becomes responsible to call NS_Free
   283     // when finished with it
   284     void* forget() {
   285         void* p = mPtr;
   286         mPtr = nullptr;
   287         return p;
   288     }
   290     bool WriteRaw(const void *data, size_t length) {
   291         if ((mOff + length > mLength) ||
   292             (mLength > std::numeric_limits<size_t>::max() - mOff)) {
   293             if (mLength == mLimit) {
   294                 return false;
   295             }
   296             size_t newLength = (mLength + 1) * 2;
   297             if (newLength < mLength) {
   298                 return false;
   299             }
   300             if (newLength > mLimit) {
   301                 newLength = mLimit;
   302             }
   303             mPtr = NS_Realloc(mPtr, newLength);
   304             mLength = newLength;
   305             return WriteRaw(data, length);
   306         }
   307         std::memcpy(static_cast<char*>(mPtr) + mOff, data, length);
   308         mOff += length;
   309         return true;
   310     }
   312     bool Seek(off_t position) {
   313         if (position < 0) {
   314             return false;
   315         }
   316         if (static_cast<size_t>(position) > mLength) {
   317             return false;
   318         }
   319         mOff = position;
   320         return true;
   321     }
   323     off_t Tell() const {
   324         return mOff;
   325     }
   327 private:
   328     void*        mPtr;
   329     size_t       mLength;
   330     const size_t mLimit;
   331     off_t        mOff;
   332 };
   334 static ots::TableAction
   335 OTSTableAction(uint32_t aTag, void *aUserData)
   336 {
   337     // preserve Graphite and SVG tables
   338     if (aTag == TRUETYPE_TAG('S', 'i', 'l', 'f') ||
   339         aTag == TRUETYPE_TAG('S', 'i', 'l', 'l') ||
   340         aTag == TRUETYPE_TAG('G', 'l', 'o', 'c') ||
   341         aTag == TRUETYPE_TAG('G', 'l', 'a', 't') ||
   342         aTag == TRUETYPE_TAG('F', 'e', 'a', 't') ||
   343         aTag == TRUETYPE_TAG('S', 'V', 'G', ' ')) {
   344         return ots::TABLE_ACTION_PASSTHRU;
   345     }
   346     return ots::TABLE_ACTION_DEFAULT;
   347 }
   349 struct OTSCallbackUserData {
   350     gfxUserFontSet     *mFontSet;
   351     gfxMixedFontFamily *mFamily;
   352     gfxProxyFontEntry  *mProxy;
   353 };
   355 /* static */ bool
   356 gfxUserFontSet::OTSMessage(void *aUserData, const char *format, ...)
   357 {
   358     va_list va;
   359     va_start(va, format);
   361     // buf should be more than adequate for any message OTS generates,
   362     // so we don't worry about checking the result of vsnprintf()
   363     char buf[512];
   364     (void)vsnprintf(buf, sizeof(buf), format, va);
   366     va_end(va);
   368     OTSCallbackUserData *d = static_cast<OTSCallbackUserData*>(aUserData);
   369     d->mFontSet->LogMessage(d->mFamily, d->mProxy, buf);
   371     return false;
   372 }
   374 // Call the OTS library to sanitize an sfnt before attempting to use it.
   375 // Returns a newly-allocated block, or nullptr in case of fatal errors.
   376 const uint8_t*
   377 gfxUserFontSet::SanitizeOpenTypeData(gfxMixedFontFamily *aFamily,
   378                                      gfxProxyFontEntry *aProxy,
   379                                      const uint8_t* aData, uint32_t aLength,
   380                                      uint32_t& aSaneLength, bool aIsCompressed)
   381 {
   382     // limit output/expansion to 256MB
   383     ExpandingMemoryStream output(aIsCompressed ? aLength * 2 : aLength,
   384                                  1024 * 1024 * 256);
   386     OTSCallbackUserData userData;
   387     userData.mFontSet = this;
   388     userData.mFamily = aFamily;
   389     userData.mProxy = aProxy;
   391     ots::SetTableActionCallback(&OTSTableAction, nullptr);
   392     ots::SetMessageCallback(&gfxUserFontSet::OTSMessage, &userData);
   394     if (ots::Process(&output, aData, aLength)) {
   395         aSaneLength = output.Tell();
   396         return static_cast<uint8_t*>(output.forget());
   397     } else {
   398         aSaneLength = 0;
   399         return nullptr;
   400     }
   401 }
   403 static void
   404 StoreUserFontData(gfxFontEntry* aFontEntry, gfxProxyFontEntry* aProxy,
   405                   bool aPrivate, const nsAString& aOriginalName,
   406                   FallibleTArray<uint8_t>* aMetadata, uint32_t aMetaOrigLen)
   407 {
   408     if (!aFontEntry->mUserFontData) {
   409         aFontEntry->mUserFontData = new gfxUserFontData;
   410     }
   411     gfxUserFontData* userFontData = aFontEntry->mUserFontData;
   412     userFontData->mSrcIndex = aProxy->mSrcIndex;
   413     const gfxFontFaceSrc& src = aProxy->mSrcList[aProxy->mSrcIndex];
   414     if (src.mIsLocal) {
   415         userFontData->mLocalName = src.mLocalName;
   416     } else {
   417         userFontData->mURI = src.mURI;
   418         userFontData->mPrincipal = aProxy->mPrincipal;
   419     }
   420     userFontData->mPrivate = aPrivate;
   421     userFontData->mFormat = src.mFormatFlags;
   422     userFontData->mRealName = aOriginalName;
   423     if (aMetadata) {
   424         userFontData->mMetadata.SwapElements(*aMetadata);
   425         userFontData->mMetaOrigLen = aMetaOrigLen;
   426     }
   427 }
   429 struct WOFFHeader {
   430     AutoSwap_PRUint32 signature;
   431     AutoSwap_PRUint32 flavor;
   432     AutoSwap_PRUint32 length;
   433     AutoSwap_PRUint16 numTables;
   434     AutoSwap_PRUint16 reserved;
   435     AutoSwap_PRUint32 totalSfntSize;
   436     AutoSwap_PRUint16 majorVersion;
   437     AutoSwap_PRUint16 minorVersion;
   438     AutoSwap_PRUint32 metaOffset;
   439     AutoSwap_PRUint32 metaCompLen;
   440     AutoSwap_PRUint32 metaOrigLen;
   441     AutoSwap_PRUint32 privOffset;
   442     AutoSwap_PRUint32 privLen;
   443 };
   445 void
   446 gfxUserFontSet::CopyWOFFMetadata(const uint8_t* aFontData,
   447                                  uint32_t aLength,
   448                                  FallibleTArray<uint8_t>* aMetadata,
   449                                  uint32_t* aMetaOrigLen)
   450 {
   451     // This function may be called with arbitrary, unvalidated "font" data
   452     // from @font-face, so it needs to be careful to bounds-check, etc.,
   453     // before trying to read anything.
   454     // This just saves a copy of the compressed data block; it does NOT check
   455     // that the block can be successfully decompressed, or that it contains
   456     // well-formed/valid XML metadata.
   457     if (aLength < sizeof(WOFFHeader)) {
   458         return;
   459     }
   460     const WOFFHeader* woff = reinterpret_cast<const WOFFHeader*>(aFontData);
   461     uint32_t metaOffset = woff->metaOffset;
   462     uint32_t metaCompLen = woff->metaCompLen;
   463     if (!metaOffset || !metaCompLen || !woff->metaOrigLen) {
   464         return;
   465     }
   466     if (metaOffset >= aLength || metaCompLen > aLength - metaOffset) {
   467         return;
   468     }
   469     if (!aMetadata->SetLength(woff->metaCompLen)) {
   470         return;
   471     }
   472     memcpy(aMetadata->Elements(), aFontData + metaOffset, metaCompLen);
   473     *aMetaOrigLen = woff->metaOrigLen;
   474 }
   476 // This is called when a font download finishes.
   477 // Ownership of aFontData passes in here, and the font set must
   478 // ensure that it is eventually deleted via NS_Free().
   479 bool
   480 gfxUserFontSet::OnLoadComplete(gfxMixedFontFamily *aFamily,
   481                                gfxProxyFontEntry *aProxy,
   482                                const uint8_t *aFontData, uint32_t aLength,
   483                                nsresult aDownloadStatus)
   484 {
   485     // forget about the loader, as we no longer potentially need to cancel it
   486     // if the entry is obsoleted
   487     aProxy->mLoader = nullptr;
   489     // download successful, make platform font using font data
   490     if (NS_SUCCEEDED(aDownloadStatus)) {
   491         gfxFontEntry *fe = LoadFont(aFamily, aProxy, aFontData, aLength);
   492         aFontData = nullptr;
   494         if (fe) {
   495             IncrementGeneration();
   496             return true;
   497         }
   499     } else {
   500         // download failed
   501         LogMessage(aFamily, aProxy,
   502                    "download failed", nsIScriptError::errorFlag,
   503                    aDownloadStatus);
   504     }
   506     if (aFontData) {
   507         NS_Free((void*)aFontData);
   508     }
   510     // error occurred, load next src
   511     (void)LoadNext(aFamily, aProxy);
   513     // We ignore the status returned by LoadNext();
   514     // even if loading failed, we need to bump the font-set generation
   515     // and return true in order to trigger reflow, so that fallback
   516     // will be used where the text was "masked" by the pending download
   517     IncrementGeneration();
   518     return true;
   519 }
   522 gfxUserFontSet::LoadStatus
   523 gfxUserFontSet::LoadNext(gfxMixedFontFamily *aFamily,
   524                          gfxProxyFontEntry *aProxyEntry)
   525 {
   526     uint32_t numSrc = aProxyEntry->mSrcList.Length();
   528     NS_ASSERTION(aProxyEntry->mSrcIndex < numSrc,
   529                  "already at the end of the src list for user font");
   531     if (aProxyEntry->mLoadingState == gfxProxyFontEntry::NOT_LOADING) {
   532         aProxyEntry->mLoadingState = gfxProxyFontEntry::LOADING_STARTED;
   533         aProxyEntry->mUnsupportedFormat = false;
   534     } else {
   535         // we were already loading; move to the next source,
   536         // but don't reset state - if we've already timed out,
   537         // that counts against the new download
   538         aProxyEntry->mSrcIndex++;
   539     }
   541     /* If there are any urls, prefer them to local */
   542     bool listHasURL = false; 
   543     for (uint32_t i = aProxyEntry->mSrcIndex; i < numSrc; i++) {
   544         const gfxFontFaceSrc& currSrc = aProxyEntry->mSrcList[i];
   545         if (!currSrc.mIsLocal) {
   546            listHasURL = true;
   547            break;
   548         }
   549     }
   550     nsPresContext *pres = GetPresContext();
   551     /* If we have no pres context, simply fail this load */
   552     if (!pres) listHasURL = true;
   554     // load each src entry in turn, until a local face is found
   555     // or a download begins successfully
   556     while (aProxyEntry->mSrcIndex < numSrc) {
   557         const gfxFontFaceSrc& currSrc = aProxyEntry->mSrcList[aProxyEntry->mSrcIndex];
   559         // src local ==> lookup and load immediately
   561         if (!listHasURL && currSrc.mIsLocal) {
   562             nsFont font;
   563             font.name = currSrc.mLocalName;
   564             gfxFontEntry *fe =
   565                 gfxPlatform::GetPlatform()->LookupLocalFont(aProxyEntry,
   566                                                             currSrc.mLocalName);                                                            
   567             pres->AddFontAttempt(font);
   569             /* No more fonts for you */
   570             if (pres->FontAttemptCountReached(font) || 
   571                 pres->FontUseCountReached(font)) {
   572                 break;
   573             }
   575             mLocalRulesUsed = true;
   576             if (fe) {
   577                 pres->AddFontUse(font);
   578                 LOG(("userfonts (%p) [src %d] loaded local: (%s) for (%s) gen: %8.8x\n",
   579                      this, aProxyEntry->mSrcIndex,
   580                      NS_ConvertUTF16toUTF8(currSrc.mLocalName).get(),
   581                      NS_ConvertUTF16toUTF8(aFamily->Name()).get(),
   582                      uint32_t(mGeneration)));
   583                 fe->mFeatureSettings.AppendElements(aProxyEntry->mFeatureSettings);
   584                 fe->mLanguageOverride = aProxyEntry->mLanguageOverride;
   585                 // For src:local(), we don't care whether the request is from
   586                 // a private window as there's no issue of caching resources;
   587                 // local fonts are just available all the time.
   588                 StoreUserFontData(fe, aProxyEntry, false, nsString(), nullptr, 0);
   589                 ReplaceFontEntry(aFamily, aProxyEntry, fe);
   590                 return STATUS_LOADED;
   591             } else {
   592                 LOG(("userfonts (%p) [src %d] failed local: (%s) for (%s)\n",
   593                      this, aProxyEntry->mSrcIndex,
   594                      NS_ConvertUTF16toUTF8(currSrc.mLocalName).get(),
   595                      NS_ConvertUTF16toUTF8(aFamily->Name()).get()));
   596             }
   597         }
   599         // src url ==> start the load process
   600         else {
   601             if (gfxPlatform::GetPlatform()->IsFontFormatSupported(currSrc.mURI,
   602                     currSrc.mFormatFlags)) {
   604                 nsIPrincipal *principal = nullptr;
   605                 bool bypassCache;
   606                 nsresult rv = CheckFontLoad(&currSrc, &principal, &bypassCache);
   608                 if (NS_SUCCEEDED(rv) && principal != nullptr) {
   609                     if (!bypassCache) {
   610                         // see if we have an existing entry for this source
   611                         gfxFontEntry *fe =
   612                             UserFontCache::GetFont(currSrc.mURI, principal,
   613                                                    aProxyEntry,
   614                                                    GetPrivateBrowsing());
   615                         if (fe) {
   616                             ReplaceFontEntry(aFamily, aProxyEntry, fe);
   617                             return STATUS_LOADED;
   618                         }
   619                     }
   621                     // record the principal returned by CheckFontLoad,
   622                     // for use when creating a channel
   623                     // and when caching the loaded entry
   624                     aProxyEntry->mPrincipal = principal;
   626                     bool loadDoesntSpin = false;
   627                     rv = NS_URIChainHasFlags(currSrc.mURI,
   628                            nsIProtocolHandler::URI_SYNC_LOAD_IS_OK,
   629                            &loadDoesntSpin);
   630                     if (NS_SUCCEEDED(rv) && loadDoesntSpin) {
   631                         uint8_t *buffer = nullptr;
   632                         uint32_t bufferLength = 0;
   634                         // sync load font immediately
   635                         rv = SyncLoadFontData(aProxyEntry, &currSrc,
   636                                               buffer, bufferLength);
   637                         if (NS_SUCCEEDED(rv) && LoadFont(aFamily, aProxyEntry,
   638                                                          buffer, bufferLength)) {
   639                             return STATUS_LOADED;
   640                         } else {
   641                             LogMessage(aFamily, aProxyEntry,
   642                                        "font load failed",
   643                                        nsIScriptError::errorFlag, rv);
   644                         }
   645                     } else {
   646                         // otherwise load font async
   647                         rv = StartLoad(aFamily, aProxyEntry, &currSrc);
   648                         if (NS_SUCCEEDED(rv)) {
   649 #ifdef PR_LOGGING
   650                             if (LOG_ENABLED()) {
   651                                 nsAutoCString fontURI;
   652                                 currSrc.mURI->GetSpec(fontURI);
   653                                 LOG(("userfonts (%p) [src %d] loading uri: (%s) for (%s)\n",
   654                                      this, aProxyEntry->mSrcIndex, fontURI.get(),
   655                                      NS_ConvertUTF16toUTF8(aFamily->Name()).get()));
   656                             }
   657 #endif
   658                             return STATUS_LOADING;
   659                         } else {
   660                             LogMessage(aFamily, aProxyEntry,
   661                                        "download failed",
   662                                        nsIScriptError::errorFlag, rv);
   663                         }
   664                     }
   665                 } else {
   666                     LogMessage(aFamily, aProxyEntry, "download not allowed",
   667                                nsIScriptError::errorFlag, rv);
   668                 }
   669             } else {
   670                 // We don't log a warning to the web console yet,
   671                 // as another source may load successfully
   672                 aProxyEntry->mUnsupportedFormat = true;
   673             }
   674         }
   676         aProxyEntry->mSrcIndex++;
   677     }
   679     if (aProxyEntry->mUnsupportedFormat) {
   680         LogMessage(aFamily, aProxyEntry, "no supported format found",
   681                    nsIScriptError::warningFlag);
   682     }
   684     // all src's failed; mark this entry as unusable (so fallback will occur)
   685     LOG(("userfonts (%p) failed all src for (%s)\n",
   686         this, NS_ConvertUTF16toUTF8(aFamily->Name()).get()));
   687     aProxyEntry->mLoadingState = gfxProxyFontEntry::LOADING_FAILED;
   689     return STATUS_END_OF_LIST;
   690 }
   692 void
   693 gfxUserFontSet::IncrementGeneration()
   694 {
   695     // add one, increment again if zero
   696     ++sFontSetGeneration;
   697     if (sFontSetGeneration == 0)
   698        ++sFontSetGeneration;
   699     mGeneration = sFontSetGeneration;
   700 }
   702 void
   703 gfxUserFontSet::RebuildLocalRules()
   704 {
   705     if (mLocalRulesUsed) {
   706         DoRebuildUserFontSet();
   707     }
   708 }
   710 gfxFontEntry*
   711 gfxUserFontSet::LoadFont(gfxMixedFontFamily *aFamily,
   712                          gfxProxyFontEntry *aProxy,
   713                          const uint8_t *aFontData, uint32_t &aLength)
   714 {
   715     gfxFontEntry *fe = nullptr;
   717     gfxUserFontType fontType =
   718         gfxFontUtils::DetermineFontDataType(aFontData, aLength);
   720     // Unwrap/decompress/sanitize or otherwise munge the downloaded data
   721     // to make a usable sfnt structure.
   723     // Because platform font activation code may replace the name table
   724     // in the font with a synthetic one, we save the original name so that
   725     // it can be reported via the nsIDOMFontFace API.
   726     nsAutoString originalFullName;
   728     // Call the OTS sanitizer; this will also decode WOFF to sfnt
   729     // if necessary. The original data in aFontData is left unchanged.
   730     uint32_t saneLen;
   731     const uint8_t* saneData =
   732         SanitizeOpenTypeData(aFamily, aProxy, aFontData, aLength, saneLen,
   733                              fontType == GFX_USERFONT_WOFF);
   734     if (!saneData) {
   735         LogMessage(aFamily, aProxy, "rejected by sanitizer");
   736     }
   737     if (saneData) {
   738         // The sanitizer ensures that we have a valid sfnt and a usable
   739         // name table, so this should never fail unless we're out of
   740         // memory, and GetFullNameFromSFNT is not directly exposed to
   741         // arbitrary/malicious data from the web.
   742         gfxFontUtils::GetFullNameFromSFNT(saneData, saneLen,
   743                                           originalFullName);
   744         // Here ownership of saneData is passed to the platform,
   745         // which will delete it when no longer required
   746         fe = gfxPlatform::GetPlatform()->MakePlatformFont(aProxy,
   747                                                           saneData,
   748                                                           saneLen);
   749         if (!fe) {
   750             LogMessage(aFamily, aProxy, "not usable by platform");
   751         }
   752     }
   754     if (fe) {
   755         // Save a copy of the metadata block (if present) for nsIDOMFontFace
   756         // to use if required. Ownership of the metadata block will be passed
   757         // to the gfxUserFontData record below.
   758         FallibleTArray<uint8_t> metadata;
   759         uint32_t metaOrigLen = 0;
   760         if (fontType == GFX_USERFONT_WOFF) {
   761             CopyWOFFMetadata(aFontData, aLength, &metadata, &metaOrigLen);
   762         }
   764         // copy OpenType feature/language settings from the proxy to the
   765         // newly-created font entry
   766         fe->mFeatureSettings.AppendElements(aProxy->mFeatureSettings);
   767         fe->mLanguageOverride = aProxy->mLanguageOverride;
   768         StoreUserFontData(fe, aProxy, GetPrivateBrowsing(),
   769                           originalFullName, &metadata, metaOrigLen);
   770 #ifdef PR_LOGGING
   771         if (LOG_ENABLED()) {
   772             nsAutoCString fontURI;
   773             aProxy->mSrcList[aProxy->mSrcIndex].mURI->GetSpec(fontURI);
   774             LOG(("userfonts (%p) [src %d] loaded uri: (%s) for (%s) gen: %8.8x\n",
   775                  this, aProxy->mSrcIndex, fontURI.get(),
   776                  NS_ConvertUTF16toUTF8(aFamily->Name()).get(),
   777                  uint32_t(mGeneration)));
   778         }
   779 #endif
   780         ReplaceFontEntry(aFamily, aProxy, fe);
   781         UserFontCache::CacheFont(fe);
   782     } else {
   783 #ifdef PR_LOGGING
   784         if (LOG_ENABLED()) {
   785             nsAutoCString fontURI;
   786             aProxy->mSrcList[aProxy->mSrcIndex].mURI->GetSpec(fontURI);
   787             LOG(("userfonts (%p) [src %d] failed uri: (%s) for (%s)"
   788                  " error making platform font\n",
   789                  this, aProxy->mSrcIndex, fontURI.get(),
   790                  NS_ConvertUTF16toUTF8(aFamily->Name()).get()));
   791         }
   792 #endif
   793     }
   795     // The downloaded data can now be discarded; the font entry is using the
   796     // sanitized copy
   797     NS_Free((void*)aFontData);
   799     return fe;
   800 }
   802 gfxFontFamily*
   803 gfxUserFontSet::GetFamily(const nsAString& aFamilyName) const
   804 {
   805     nsAutoString key(aFamilyName);
   806     ToLowerCase(key);
   808     return mFontFamilies.GetWeak(key);
   809 }
   811 struct FindFamilyCallbackData {
   812     gfxFontEntry  *mFontEntry;
   813     gfxFontFamily *mFamily;
   814 };
   816 static PLDHashOperator
   817 FindFamilyCallback(const nsAString&    aName,
   818                    gfxMixedFontFamily* aFamily,
   819                    void*               aUserArg)
   820 {
   821     FindFamilyCallbackData *d = static_cast<FindFamilyCallbackData*>(aUserArg);
   822     if (aFamily->ContainsFace(d->mFontEntry)) {
   823         d->mFamily = aFamily;
   824         return PL_DHASH_STOP;
   825     }
   827     return PL_DHASH_NEXT;
   828 }
   830 gfxFontFamily*
   831 gfxUserFontSet::FindFamilyFor(gfxFontEntry* aFontEntry) const
   832 {
   833     FindFamilyCallbackData d = { aFontEntry, nullptr };
   834     mFontFamilies.EnumerateRead(FindFamilyCallback, &d);
   835     return d.mFamily;
   836 }
   838 ///////////////////////////////////////////////////////////////////////////////
   839 // gfxUserFontSet::UserFontCache - re-use platform font entries for user fonts
   840 // across pages/fontsets rather than instantiating new platform fonts.
   841 //
   842 // Entries are added to this cache when a platform font is instantiated from
   843 // downloaded data, and removed when the platform font entry is destroyed.
   844 // We don't need to use a timed expiration scheme here because the gfxFontEntry
   845 // for a downloaded font will be kept alive by its corresponding gfxFont
   846 // instance(s) until they are deleted, and *that* happens using an expiration
   847 // tracker (gfxFontCache). The result is that the downloaded font instances
   848 // recorded here will persist between pages and can get reused (provided the
   849 // source URI and principal match, of course).
   850 ///////////////////////////////////////////////////////////////////////////////
   852 nsTHashtable<gfxUserFontSet::UserFontCache::Entry>*
   853     gfxUserFontSet::UserFontCache::sUserFonts = nullptr;
   855 NS_IMPL_ISUPPORTS(gfxUserFontSet::UserFontCache::Flusher, nsIObserver)
   857 PLDHashOperator
   858 gfxUserFontSet::UserFontCache::Entry::RemoveIfPrivate(Entry* aEntry,
   859                                                       void* aUserData)
   860 {
   861     return aEntry->mPrivate ? PL_DHASH_REMOVE : PL_DHASH_NEXT;
   862 }
   864 PLDHashOperator
   865 gfxUserFontSet::UserFontCache::Entry::RemoveIfMatches(Entry* aEntry,
   866                                                       void* aUserData)
   867 {
   868     return aEntry->GetFontEntry() == static_cast<gfxFontEntry*>(aUserData) ?
   869         PL_DHASH_REMOVE : PL_DHASH_NEXT;
   870 }
   872 PLDHashOperator
   873 gfxUserFontSet::UserFontCache::Entry::DisconnectSVG(Entry* aEntry,
   874                                                     void* aUserData)
   875 {
   876     aEntry->GetFontEntry()->DisconnectSVG();
   877     return PL_DHASH_NEXT;
   878 }
   880 NS_IMETHODIMP
   881 gfxUserFontSet::UserFontCache::Flusher::Observe(nsISupports* aSubject,
   882                                                 const char* aTopic,
   883                                                 const char16_t* aData)
   884 {
   885     if (!sUserFonts) {
   886         return NS_OK;
   887     }
   889     if (!strcmp(aTopic, NS_CACHESERVICE_EMPTYCACHE_TOPIC_ID)) {
   890         sUserFonts->Clear();
   891     } else if (!strcmp(aTopic, "last-pb-context-exited")) {
   892         sUserFonts->EnumerateEntries(Entry::RemoveIfPrivate, nullptr);
   893     } else if (!strcmp(aTopic, "xpcom-shutdown")) {
   894         sUserFonts->EnumerateEntries(Entry::DisconnectSVG, nullptr);
   895     } else {
   896         NS_NOTREACHED("unexpected topic");
   897     }
   899     return NS_OK;
   900 }
   902 bool
   903 gfxUserFontSet::UserFontCache::Entry::KeyEquals(const KeyTypePointer aKey) const
   904 {
   905     bool equal;
   906     if (NS_FAILED(mURI->Equals(aKey->mURI, &equal)) || !equal) {
   907         return false;
   908     }
   910     if (NS_FAILED(mPrincipal->Equals(aKey->mPrincipal, &equal)) || !equal) {
   911         return false;
   912     }
   914     if (mPrivate != aKey->mPrivate) {
   915         return false;
   916     }
   918     const gfxFontEntry *fe = aKey->mFontEntry;
   919     if (mFontEntry->mItalic           != fe->mItalic          ||
   920         mFontEntry->mWeight           != fe->mWeight          ||
   921         mFontEntry->mStretch          != fe->mStretch         ||
   922         mFontEntry->mFeatureSettings  != fe->mFeatureSettings ||
   923         mFontEntry->mLanguageOverride != fe->mLanguageOverride ||
   924         mFontEntry->mFamilyName       != fe->mFamilyName) {
   925         return false;
   926     }
   928     return true;
   929 }
   931 void
   932 gfxUserFontSet::UserFontCache::CacheFont(gfxFontEntry *aFontEntry)
   933 {
   934     NS_ASSERTION(aFontEntry->mFamilyName.Length() != 0,
   935                  "caching a font associated with no family yet");
   936     if (!sUserFonts) {
   937         sUserFonts = new nsTHashtable<Entry>;
   939         nsCOMPtr<nsIObserverService> obs =
   940             mozilla::services::GetObserverService();
   941         if (obs) {
   942             Flusher *flusher = new Flusher;
   943             obs->AddObserver(flusher, NS_CACHESERVICE_EMPTYCACHE_TOPIC_ID,
   944                              false);
   945             obs->AddObserver(flusher, "last-pb-context-exited", false);
   946             obs->AddObserver(flusher, "xpcom-shutdown", false);
   947         }
   948     }
   950     gfxUserFontData *data = aFontEntry->mUserFontData;
   951     sUserFonts->PutEntry(Key(data->mURI, data->mPrincipal, aFontEntry,
   952                              data->mPrivate));
   954 #ifdef DEBUG_USERFONT_CACHE
   955     printf("userfontcache added fontentry: %p\n", aFontEntry);
   956     Dump();
   957 #endif
   958 }
   960 void
   961 gfxUserFontSet::UserFontCache::ForgetFont(gfxFontEntry *aFontEntry)
   962 {
   963     if (!sUserFonts) {
   964         // if we've already deleted the cache (i.e. during shutdown),
   965         // just ignore this
   966         return;
   967     }
   969     // We can't simply use RemoveEntry here because it's possible the principal
   970     // may have changed since the font was cached, in which case the lookup
   971     // would no longer find the entry (bug 838105).
   972     sUserFonts->EnumerateEntries(
   973         gfxUserFontSet::UserFontCache::Entry::RemoveIfMatches, aFontEntry);
   975 #ifdef DEBUG_USERFONT_CACHE
   976     printf("userfontcache removed fontentry: %p\n", aFontEntry);
   977     Dump();
   978 #endif
   979 }
   981 gfxFontEntry*
   982 gfxUserFontSet::UserFontCache::GetFont(nsIURI            *aSrcURI,
   983                                        nsIPrincipal      *aPrincipal,
   984                                        gfxProxyFontEntry *aProxy,
   985                                        bool               aPrivate)
   986 {
   987     if (!sUserFonts) {
   988         return nullptr;
   989     }
   991     Entry* entry = sUserFonts->GetEntry(Key(aSrcURI, aPrincipal, aProxy,
   992                                             aPrivate));
   993     if (entry) {
   994         return entry->GetFontEntry();
   995     }
   997     return nullptr;
   998 }
  1000 void
  1001 gfxUserFontSet::UserFontCache::Shutdown()
  1003     if (sUserFonts) {
  1004         delete sUserFonts;
  1005         sUserFonts = nullptr;
  1009 #ifdef DEBUG_USERFONT_CACHE
  1011 PLDHashOperator
  1012 gfxUserFontSet::UserFontCache::Entry::DumpEntry(Entry* aEntry, void* aUserData)
  1014     nsresult rv;
  1016     nsAutoCString principalURISpec;
  1018     nsCOMPtr<nsIURI> principalURI;
  1019     rv = aEntry->mPrincipal->GetURI(getter_AddRefs(principalURI));
  1020     if (NS_SUCCEEDED(rv)) {
  1021         principalURI->GetSpec(principalURISpec);
  1024     bool setDomain = false;
  1025     nsCOMPtr<nsIURI> domainURI;
  1027     aEntry->mPrincipal->GetDomain(getter_AddRefs(domainURI));
  1028     if (domainURI) {
  1029         setDomain = true;
  1032     NS_ASSERTION(aEntry->mURI, "null URI in userfont cache entry");
  1034     printf("userfontcache fontEntry: %p fonturihash: %8.8x family: %s domainset: %s principal: [%s]\n",
  1035             aEntry->mFontEntry,
  1036             nsURIHashKey::HashKey(aEntry->mURI),
  1037             NS_ConvertUTF16toUTF8(aEntry->mFontEntry->FamilyName()).get(),
  1038             (setDomain ? "true" : "false"),
  1039             principalURISpec.get()
  1040            );
  1041     return PL_DHASH_NEXT;
  1044 void
  1045 gfxUserFontSet::UserFontCache::Dump()
  1047     if (!sUserFonts) {
  1048         return;
  1051     printf("userfontcache dump count: %d ========\n", sUserFonts->Count());
  1052     sUserFonts->EnumerateEntries(Entry::DumpEntry, nullptr);
  1053     printf("userfontcache dump ==================\n");
  1056 #endif

mercurial