layout/style/FontFaceSet.h
author Xianzhu Wang <wangxianzhu@chromium.org>
Tue, 05 Mar 2019 12:18:01 +0000
changeset 464575 4325ba7b38576495a78289c2a8f4dff391922e66
parent 452446 f0a91d36587266d7454a450c6044d573664fbed5
child 465333 a2ff272ceb426e1d5051507354bc3d4d1bb2736d
permissions -rw-r--r--
Bug 1529059 [wpt PR 15420] - [BlinkGenPropertyTrees] Initiailize double_sided of synthetic effect, a=testonly Automatic update from web-platform-tests [BlinkGenPropertyTrees] Initiailize double_sided of synthetic effect Previously synthetic effects always had double_sided==false, causing the layer disappear when the backface was facing forward. Bug: 928190 Change-Id: I35534b40346d5c5918bc99c00a4ca6b4e3b68796 Reviewed-on: https://chromium-review.googlesource.com/c/1475815 Reviewed-by: Philip Rogers <pdr@chromium.org> Commit-Queue: Xianzhu Wang <wangxianzhu@chromium.org> Cr-Commit-Position: refs/heads/master@{#632764} -- wpt-commits: a89467050deaf1dcbd9140a2f0670b1b85e518ee wpt-pr: 15420

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef mozilla_dom_FontFaceSet_h
#define mozilla_dom_FontFaceSet_h

#include "mozilla/dom/FontFace.h"
#include "mozilla/dom/FontFaceSetBinding.h"
#include "mozilla/DOMEventTargetHelper.h"
#include "mozilla/FontPropertyTypes.h"
#include "gfxUserFontSet.h"
#include "nsICSSLoaderObserver.h"

struct gfxFontFaceSrc;
class gfxFontSrcPrincipal;
class gfxUserFontEntry;
class nsFontFaceLoader;
class nsIPrincipal;
class nsPIDOMWindowInner;
struct RawServoFontFaceRule;

namespace mozilla {
class PostTraversalTask;
class SharedFontList;
namespace dom {
class FontFace;
class Promise;
}  // namespace dom
}  // namespace mozilla

namespace mozilla {
namespace dom {

class FontFaceSet final : public DOMEventTargetHelper,
                          public nsIDOMEventListener,
                          public nsICSSLoaderObserver {
  friend class mozilla::PostTraversalTask;
  friend class UserFontSet;

 public:
  /**
   * A gfxUserFontSet that integrates with the layout and style systems to
   * manage @font-face rules and handle network requests for font loading.
   *
   * We would combine this class and FontFaceSet into the one class if it were
   * possible; it's not because FontFaceSet is cycle collected and
   * gfxUserFontSet isn't (and can't be, as gfx classes don't use the cycle
   * collector).  So UserFontSet exists just to override the needed virtual
   * methods from gfxUserFontSet and to forward them on FontFaceSet.
   */
  class UserFontSet final : public gfxUserFontSet {
    friend class FontFaceSet;

   public:
    explicit UserFontSet(FontFaceSet* aFontFaceSet)
        : mFontFaceSet(aFontFaceSet) {}

    FontFaceSet* GetFontFaceSet() { return mFontFaceSet; }

    gfxFontSrcPrincipal* GetStandardFontLoadPrincipal() const final {
      return mFontFaceSet ? mFontFaceSet->mStandardFontLoadPrincipal.get()
                          : nullptr;
    }

    bool IsFontLoadAllowed(const gfxFontFaceSrc&) final;

    void DispatchFontLoadViolations(
        nsTArray<nsCOMPtr<nsIRunnable>>& aViolations) override;

    virtual nsresult StartLoad(gfxUserFontEntry* aUserFontEntry,
                               const gfxFontFaceSrc* aFontFaceSrc) override;

    void RecordFontLoadDone(uint32_t aFontSize,
                            mozilla::TimeStamp aDoneTime) override;

    bool BypassCache() final {
      return mFontFaceSet && mFontFaceSet->mBypassCache;
    }

   protected:
    virtual bool GetPrivateBrowsing() override;
    virtual nsresult SyncLoadFontData(gfxUserFontEntry* aFontToLoad,
                                      const gfxFontFaceSrc* aFontFaceSrc,
                                      uint8_t*& aBuffer,
                                      uint32_t& aBufferLength) override;
    virtual nsresult LogMessage(gfxUserFontEntry* aUserFontEntry,
                                const char* aMessage,
                                uint32_t aFlags = nsIScriptError::errorFlag,
                                nsresult aStatus = NS_OK) override;
    virtual void DoRebuildUserFontSet() override;
    already_AddRefed<gfxUserFontEntry> CreateUserFontEntry(
        const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList, WeightRange aWeight,
        StretchRange aStretch, SlantStyleRange aStyle,
        const nsTArray<gfxFontFeature>& aFeatureSettings,
        const nsTArray<gfxFontVariation>& aVariationSettings,
        uint32_t aLanguageOverride, gfxCharacterMap* aUnicodeRanges,
        StyleFontDisplay aFontDisplay, RangeFlags aRangeFlags) override;

   private:
    RefPtr<FontFaceSet> mFontFaceSet;
  };

  NS_DECL_ISUPPORTS_INHERITED
  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(FontFaceSet, DOMEventTargetHelper)
  NS_DECL_NSIDOMEVENTLISTENER

  FontFaceSet(nsPIDOMWindowInner* aWindow, dom::Document* aDocument);

  virtual JSObject* WrapObject(JSContext* aCx,
                               JS::Handle<JSObject*> aGivenProto) override;

  UserFontSet* GetUserFontSet() { return mUserFontSet; }

  // Called by nsFontFaceLoader when the loader has completed normally.
  // It's removed from the mLoaders set.
  void RemoveLoader(nsFontFaceLoader* aLoader);

  bool UpdateRules(const nsTArray<nsFontFaceRuleContainer>& aRules);

  nsPresContext* GetPresContext();

  // search for @font-face rule that matches a platform font entry
  RawServoFontFaceRule* FindRuleForEntry(gfxFontEntry* aFontEntry);

  void IncrementGeneration(bool aIsRebuild = false);

  /**
   * Finds an existing entry in the user font cache or creates a new user
   * font entry for the given FontFace object.
   */
  static already_AddRefed<gfxUserFontEntry>
  FindOrCreateUserFontEntryFromFontFace(FontFace* aFontFace);

  /**
   * Notification method called by a FontFace to indicate that its loading
   * status has changed.
   */
  void OnFontFaceStatusChanged(FontFace* aFontFace);

  /**
   * Notification method called by the nsPresContext to indicate that the
   * refresh driver ticked and flushed style and layout.
   * were just flushed.
   */
  void DidRefresh();

  /**
   * Returns whether the "layout.css.font-loading-api.enabled" pref is true.
   */
  static bool PrefEnabled();

  // nsICSSLoaderObserver
  NS_IMETHOD StyleSheetLoaded(mozilla::StyleSheet* aSheet, bool aWasAlternate,
                              nsresult aStatus) override;

  FontFace* GetFontFaceAt(uint32_t aIndex);

  void FlushUserFontSet();

  static nsPresContext* GetPresContextFor(gfxUserFontSet* aUserFontSet) {
    FontFaceSet* set = static_cast<UserFontSet*>(aUserFontSet)->mFontFaceSet;
    return set ? set->GetPresContext() : nullptr;
  }

  void RefreshStandardFontLoadPrincipal();

  void CopyNonRuleFacesTo(FontFaceSet* aFontFaceSet) const;

  dom::Document* Document() const { return mDocument; }

  // -- Web IDL --------------------------------------------------------------

  IMPL_EVENT_HANDLER(loading)
  IMPL_EVENT_HANDLER(loadingdone)
  IMPL_EVENT_HANDLER(loadingerror)
  already_AddRefed<mozilla::dom::Promise> Load(JSContext* aCx,
                                               const nsAString& aFont,
                                               const nsAString& aText,
                                               mozilla::ErrorResult& aRv);
  bool Check(const nsAString& aFont, const nsAString& aText,
             mozilla::ErrorResult& aRv);
  mozilla::dom::Promise* GetReady(mozilla::ErrorResult& aRv);
  mozilla::dom::FontFaceSetLoadStatus Status();

  void Add(FontFace& aFontFace, mozilla::ErrorResult& aRv);
  void Clear();
  bool Delete(FontFace& aFontFace);
  bool Has(FontFace& aFontFace);
  uint32_t Size();
  already_AddRefed<mozilla::dom::FontFaceSetIterator> Entries();
  already_AddRefed<mozilla::dom::FontFaceSetIterator> Values();
  void ForEach(JSContext* aCx, FontFaceSetForEachCallback& aCallback,
               JS::Handle<JS::Value> aThisArg, mozilla::ErrorResult& aRv);

  // For ServoStyleSet to know ahead of time whether a font is loadable.
  void CacheFontLoadability();

 private:
  ~FontFaceSet();

  /**
   * Returns whether the given FontFace is currently "in" the FontFaceSet.
   */
  bool HasAvailableFontFace(FontFace* aFontFace);

  /**
   * Removes any listeners and observers.
   */
  void Disconnect();

  void RemoveDOMContentLoadedListener();

  /**
   * Returns whether there might be any pending font loads, which should cause
   * the mReady Promise not to be resolved yet.
   */
  bool MightHavePendingFontLoads();

  /**
   * Checks to see whether it is time to replace mReady and dispatch a
   * "loading" event.
   */
  void CheckLoadingStarted();

  /**
   * Checks to see whether it is time to resolve mReady and dispatch any
   * "loadingdone" and "loadingerror" events.
   */
  void CheckLoadingFinished();

  /**
   * Callback for invoking CheckLoadingFinished after going through the
   * event loop.  See OnFontFaceStatusChanged.
   */
  void CheckLoadingFinishedAfterDelay();

  /**
   * Dispatches a FontFaceSetLoadEvent to this object.
   */
  void DispatchLoadingFinishedEvent(
      const nsAString& aType, nsTArray<OwningNonNull<FontFace>>&& aFontFaces);

  // Note: if you add new cycle collected objects to FontFaceRecord,
  // make sure to update FontFaceSet's cycle collection macros
  // accordingly.
  struct FontFaceRecord {
    RefPtr<FontFace> mFontFace;
    SheetType mSheetType;  // only relevant for mRuleFaces entries

    // When true, indicates that when finished loading, the FontFace should be
    // included in the subsequent loadingdone/loadingerror event fired at the
    // FontFaceSet.
    bool mLoadEventShouldFire;
  };

  static already_AddRefed<gfxUserFontEntry>
  FindOrCreateUserFontEntryFromFontFace(const nsACString& aFamilyName,
                                        FontFace* aFontFace,
                                        SheetType aSheetType);

  // search for @font-face rule that matches a userfont font entry
  RawServoFontFaceRule* FindRuleForUserFontEntry(
      gfxUserFontEntry* aUserFontEntry);

  nsresult StartLoad(gfxUserFontEntry* aUserFontEntry,
                     const gfxFontFaceSrc* aFontFaceSrc);
  gfxFontSrcPrincipal* GetStandardFontLoadPrincipal();
  nsresult CheckFontLoad(const gfxFontFaceSrc* aFontFaceSrc,
                         gfxFontSrcPrincipal** aPrincipal, bool* aBypassCache);
  bool IsFontLoadAllowed(const gfxFontFaceSrc& aSrc);

  void DispatchFontLoadViolations(nsTArray<nsCOMPtr<nsIRunnable>>& aViolations);
  nsresult SyncLoadFontData(gfxUserFontEntry* aFontToLoad,
                            const gfxFontFaceSrc* aFontFaceSrc,
                            uint8_t*& aBuffer, uint32_t& aBufferLength);
  nsresult LogMessage(gfxUserFontEntry* aUserFontEntry, const char* aMessage,
                      uint32_t aFlags, nsresult aStatus);
  void MarkUserFontSetDirty();

  void InsertRuleFontFace(FontFace* aFontFace, SheetType aSheetType,
                          nsTArray<FontFaceRecord>& aOldRecords,
                          bool& aFontSetModified);
  void InsertNonRuleFontFace(FontFace* aFontFace, bool& aFontSetModified);

#ifdef DEBUG
  bool HasRuleFontFace(FontFace* aFontFace);
#endif

  /**
   * Returns whether we have any loading FontFace objects in the FontFaceSet.
   */
  bool HasLoadingFontFaces();

  // Whether mReady is pending, or would be when created.
  bool ReadyPromiseIsPending() const;

  // Helper function for HasLoadingFontFaces.
  void UpdateHasLoadingFontFaces();

  void ParseFontShorthandForMatching(const nsAString& aFont,
                                     RefPtr<SharedFontList>& aFamilyList,
                                     FontWeight& aWeight, FontStretch& aStretch,
                                     FontSlantStyle& aStyle, ErrorResult& aRv);
  void FindMatchingFontFaces(const nsAString& aFont, const nsAString& aText,
                             nsTArray<FontFace*>& aFontFaces,
                             mozilla::ErrorResult& aRv);

  void DispatchLoadingEventAndReplaceReadyPromise();
  void DispatchCheckLoadingFinishedAfterDelay();

  TimeStamp GetNavigationStartTimeStamp();

  RefPtr<UserFontSet> mUserFontSet;

  // The document this is a FontFaceSet for.
  RefPtr<dom::Document> mDocument;

  // The document's node principal, which is the principal font loads for
  // this FontFaceSet will generally use.  (This principal is not used for
  // @font-face rules in UA and user sheets, where the principal of the
  // sheet is used instead.)
  //
  // This field is used from GetStandardFontLoadPrincipal.  When on a
  // style worker thread, we use mStandardFontLoadPrincipal assuming
  // it is up to date.
  //
  // Because mDocument's principal can change over time,
  // its value must be updated by a call to ResetStandardFontLoadPrincipal.
  RefPtr<gfxFontSrcPrincipal> mStandardFontLoadPrincipal;

  // A Promise that is fulfilled once all of the FontFace objects
  // in mRuleFaces and mNonRuleFaces that started or were loading at the
  // time the Promise was created have finished loading.  It is rejected if
  // any of those fonts failed to load.  mReady is replaced with
  // a new Promise object whenever mReady is settled and another
  // FontFace in mRuleFaces or mNonRuleFaces starts to load.
  // Note that mReady is created lazily when GetReady() is called.
  RefPtr<mozilla::dom::Promise> mReady;
  // Whether the ready promise must be resolved when it's created.
  bool mResolveLazilyCreatedReadyPromise;

  // Set of all loaders pointing to us. These are not strong pointers,
  // but that's OK because nsFontFaceLoader always calls RemoveLoader on
  // us before it dies (unless we die first).
  nsTHashtable<nsPtrHashKey<nsFontFaceLoader>> mLoaders;

  // The @font-face rule backed FontFace objects in the FontFaceSet.
  nsTArray<FontFaceRecord> mRuleFaces;

  // The non rule backed FontFace objects that have been added to this
  // FontFaceSet.
  nsTArray<FontFaceRecord> mNonRuleFaces;

  // The overall status of the loading or loaded fonts in the FontFaceSet.
  mozilla::dom::FontFaceSetLoadStatus mStatus;

  // A map from gfxFontFaceSrc pointer identity to whether the load is allowed
  // by CSP or other checks. We store this here because querying CSP off the
  // main thread is not a great idea.
  //
  // We could use just the pointer and use this as a hash set, but then we'd
  // have no way to verify that we've checked all the loads we should.
  nsDataHashtable<nsPtrHashKey<const gfxFontFaceSrc>, bool> mAllowedFontLoads;

  // Whether mNonRuleFaces has changed since last time UpdateRules ran.
  bool mNonRuleFacesDirty;

  // Whether any FontFace objects in mRuleFaces or mNonRuleFaces are
  // loading.  Only valid when mHasLoadingFontFacesIsDirty is false.  Don't use
  // this variable directly; call the HasLoadingFontFaces method instead.
  bool mHasLoadingFontFaces;

  // This variable is only valid when mLoadingDirty is false.
  bool mHasLoadingFontFacesIsDirty;

  // Whether CheckLoadingFinished calls should be ignored.  See comment in
  // OnFontFaceStatusChanged.
  bool mDelayedLoadCheck;

  // Whether the docshell for our document indicated that loads should
  // bypass the cache.
  bool mBypassCache;

  // Whether the docshell for our document indicates that we are in private
  // browsing mode.
  bool mPrivateBrowsing;
};

}  // namespace dom
}  // namespace mozilla

#endif  // !defined(mozilla_dom_FontFaceSet_h)