Commit 05688c73 authored by Mike Perry's avatar Mike Perry
Browse files

Limit the number of fonts per document.

We create two prefs:
browser.display.max_font_count and browser.display.max_font_attempts.
max_font_count sets a limit on the number of fonts actually used in the
document, and max_font_attempts sets a limit on the total number of CSS
queries that a document is allowed to perform.

Once either limit is reached, the browser behaves as if
browser.display.use_document_fonts was set to 0 for subsequent font queries.

If a pref is not set or is negative, that limit does not apply.

The use of "User Fonts" (aka WebFonts, aka @font-face fonts) are exempt from
both of these limits. The patch also makes such fonts take precedence over
local fonts. This vastly improves typography on many sites that would
otherwise hit these limits.

This is done to address:
https://www.torproject.org/projects/torbrowser/design/#fingerprinting-linkability
parent 52643ac2
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -3949,6 +3949,7 @@ gfxFontGroup::FindPlatformFont(const nsAString& aName,
    }

    // Not known in the user font set ==> check system fonts
    // XXX: Fallback is bad..
    if (!family) {
        gfxPlatformFontList *fontList = gfxPlatformFontList::PlatformFontList();
        family = fontList->FindFamily(aName);
@@ -4173,6 +4174,7 @@ gfxFontGroup::ForEachFontInternal(const nsAString& aFamilies,
                    }
                    if (!foundFamily) {
                        gfxPlatform *pf = gfxPlatform::GetPlatform();
                        // XXX: Fallback is bad
                        rv = pf->ResolveFontName(family,
                                                 gfxFontGroup::FontResolverProc,
                                                 &data, aborted);
+1 −0
Original line number Diff line number Diff line
@@ -966,6 +966,7 @@ gfxFcFontSet::SortPreferredFonts(bool &aWaitForUserFont)
        const nsTArray< nsCountedRef<FcPattern> > *familyFonts = nullptr;

        // Is this an @font-face family?
        // XXX: Make use of this + pass to nsFont??
        bool isUserFont = false;
        if (mUserFontSet) {
            // Have some @font-face definitions
+26 −2
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@
#include "prlog.h"

#include "gfxUserFontSet.h"
#include "nsFont.h"
#include "gfxPlatform.h"
#include "nsReadableUtils.h"
#include "nsUnicharUtils.h"
@@ -468,18 +469,41 @@ gfxUserFontSet::LoadNext(gfxMixedFontFamily *aFamily,
        aProxyEntry->mSrcIndex++;
    }

    /* If there are any urls, prefer them to local */
    bool listHasURL = false; 
    for (uint32_t i = aProxyEntry->mSrcIndex; i < numSrc; i++) {
        const gfxFontFaceSrc& currSrc = aProxyEntry->mSrcList[i];
        if (!currSrc.mIsLocal) {
           listHasURL = true;
           break;
        }
    }
    nsPresContext *pres = GetPresContext();
    /* If we have no pres context, simply fail this load */
    if (!pres) listHasURL = true;

    // load each src entry in turn, until a local face is found
    // or a download begins successfully
    while (aProxyEntry->mSrcIndex < numSrc) {
        const gfxFontFaceSrc& currSrc = aProxyEntry->mSrcList[aProxyEntry->mSrcIndex];

        // src local ==> lookup and load immediately

        if (currSrc.mIsLocal) {
        if (!listHasURL && currSrc.mIsLocal) {
            nsFont font;
            font.name = currSrc.mLocalName;
            gfxFontEntry *fe =
                gfxPlatform::GetPlatform()->LookupLocalFont(aProxyEntry,
                                                            currSrc.mLocalName);
            pres->AddFontAttempt(font);

            /* No more fonts for you */
            if (pres->FontAttemptCountReached(font) || 
                pres->FontUseCountReached(font)) {
                break;
            }

            if (fe) {
                pres->AddFontUse(font);
                LOG(("userfonts (%p) [src %d] loaded local: (%s) for (%s) gen: %8.8x\n",
                     this, aProxyEntry->mSrcIndex,
                     NS_ConvertUTF16toUTF8(currSrc.mLocalName).get(),
+3 −0
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@
#include "gfxTypes.h"
#include "gfxFont.h"
#include "gfxFontUtils.h"
#include "nsPresContext.h"
#include "nsRefPtrHashtable.h"
#include "nsAutoPtr.h"
#include "nsCOMPtr.h"
@@ -242,6 +243,8 @@ public:
    // increment the generation on font load
    void IncrementGeneration();

    virtual nsPresContext *GetPresContext() { return NULL; }

    class UserFontCache {
    public:
        // Record a loaded user-font in the cache. This requires that the
+100 −0
Original line number Diff line number Diff line
@@ -67,6 +67,8 @@
#include "mozilla/css/ImageLoader.h"
#include "mozilla/dom/PBrowserChild.h"
#include "mozilla/dom/TabChild.h"
#include "nsString.h"
#include "nsUnicharUtils.h"

#ifdef IBMBIDI
#include "nsBidiPresUtils.h"
@@ -735,6 +737,10 @@ nsPresContext::GetUserPreferences()
  // * use fonts?
  mUseDocumentFonts =
    Preferences::GetInt("browser.display.use_document_fonts") != 0;
  mMaxFonts =
    Preferences::GetInt("browser.display.max_font_count", -1);
  mMaxFontAttempts =
    Preferences::GetInt("browser.display.max_font_attempts", -1);

  // * replace backslashes with Yen signs? (bug 245770)
  mEnableJapaneseTransform =
@@ -1395,6 +1401,100 @@ nsPresContext::GetDefaultFont(uint8_t aFontID, nsIAtom *aLanguage) const
  return font;
}

PRBool
nsPresContext::FontUseCountReached(const nsFont &font) {
  if (mMaxFonts < 0) {
    return PR_FALSE;
  }

  for (PRUint32 i = 0; i < mFontsUsed.Length(); i++) {
    if (mFontsUsed[i].name.Equals(font.name,
                                  nsCaseInsensitiveStringComparator())
        // XXX: Style is sometimes filled with garbage??
        /*&& mFontsUsed[i].style == font.style*/) {
      // seen it before: OK
      return PR_FALSE;
    }
  }

  if (mFontsUsed.Length() >= (unsigned)mMaxFonts) {
    return PR_TRUE;
  }

  return PR_FALSE;
}

PRBool
nsPresContext::FontAttemptCountReached(const nsFont &font) {
  if (mMaxFontAttempts < 0) {
    return PR_FALSE;
  }

  for (PRUint32 i = 0; i < mFontsTried.Length(); i++) {
    if (mFontsTried[i].name.Equals(font.name,
                                  nsCaseInsensitiveStringComparator())
        // XXX: Style is sometimes filled with garbage??
        /*&& mFontsTried[i].style == font.style*/) {
      // seen it before: OK
      return PR_FALSE;
    }
  }

  if (mFontsTried.Length() >= (unsigned)mMaxFontAttempts) {
    return PR_TRUE;
  }

  return PR_FALSE;
}

void
nsPresContext::AddFontUse(const nsFont &font) {
  if (mMaxFonts < 0) {
    return;
  }

  for (PRUint32 i = 0; i < mFontsUsed.Length(); i++) {
    if (mFontsUsed[i].name.Equals(font.name,
                                  nsCaseInsensitiveStringComparator())
        // XXX: Style is sometimes filled with garbage??
        /*&& mFontsUsed[i].style == font.style*/) {
      // seen it before: OK
      return;
    }
  }

  if (mFontsUsed.Length() >= (unsigned)mMaxFonts) {
    return;
  }
   
  mFontsUsed.AppendElement(font);
  return;
}

void
nsPresContext::AddFontAttempt(const nsFont &font) {
  if (mMaxFontAttempts < 0) {
    return;
  }

  for (PRUint32 i = 0; i < mFontsTried.Length(); i++) {
    if (mFontsTried[i].name.Equals(font.name,
                                  nsCaseInsensitiveStringComparator())
        // XXX: Style is sometimes filled with garbage??
        /*&& mFontsTried[i].style == font.style*/) {
      // seen it before: OK
      return;
    }
  }

  if (mFontsTried.Length() >= (unsigned)mMaxFontAttempts) {
    return;
  }
   
  mFontsTried.AppendElement(font);
  return;
}

void
nsPresContext::SetFullZoom(float aZoom)
{
Loading