Bug 906643. Part 7: Hook up animation support for gfxSVGGlyphs. r=jfkthame
authorRobert O'Callahan <robert@ocallahan.org>
Tue, 20 Aug 2013 01:08:45 +1200
changeset 159915 0b516c2f2bd1c49c5e8e6daac487836b9f0d49c2
parent 159914 edc896e0b128ffc79a24e6feaba1f31b3cd01884
child 159916 c78a87a9aa669505b9a01ed28e92cd646274f670
push id2961
push userlsblakk@mozilla.com
push dateMon, 28 Oct 2013 21:59:28 +0000
treeherdermozilla-beta@73ef4f13486f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjfkthame
bugs906643
milestone26.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 906643. Part 7: Hook up animation support for gfxSVGGlyphs. r=jfkthame There's a few things mashed together in this patch: -- Enable animations in glyph documents -- Make gfxSVGGlyphsDocument monitor the document's refresh driver to detect updates -- Forward updates to the gfxFontEntry
gfx/thebes/gfxFont.cpp
gfx/thebes/gfxFont.h
gfx/thebes/gfxSVGGlyphs.cpp
gfx/thebes/gfxSVGGlyphs.h
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -84,34 +84,84 @@ gfxCharacterMap::NotifyReleased()
 {
     gfxPlatformFontList *fontlist = gfxPlatformFontList::PlatformFontList();
     if (mShared) {
         fontlist->RemoveCmap(this);
     }
     delete this;
 }
 
+gfxFontEntry::gfxFontEntry() :
+    mItalic(false), mFixedPitch(false),
+    mIsProxy(false), mIsValid(true),
+    mIsBadUnderlineFont(false),
+    mIsUserFont(false),
+    mIsLocalUserFont(false),
+    mStandardFace(false),
+    mSymbolFont(false),
+    mIgnoreGDEF(false),
+    mIgnoreGSUB(false),
+    mSVGInitialized(false),
+    mHasSpaceFeaturesInitialized(false),
+    mHasSpaceFeatures(false),
+    mHasSpaceFeaturesKerning(false),
+    mHasSpaceFeaturesNonKerning(false),
+    mHasSpaceFeaturesSubDefault(false),
+    mCheckedForGraphiteTables(false),
+    mHasCmapTable(false),
+    mGrFaceInitialized(false),
+    mWeight(500), mStretch(NS_FONT_STRETCH_NORMAL),
+    mUVSOffset(0), mUVSData(nullptr),
+    mLanguageOverride(NO_FONT_LANGUAGE_OVERRIDE),
+    mHBFace(nullptr),
+    mGrFace(nullptr),
+    mGrFaceRefCnt(0)
+{
+}
+
+gfxFontEntry::gfxFontEntry(const nsAString& aName, bool aIsStandardFace) :
+    mName(aName), mItalic(false), mFixedPitch(false),
+    mIsProxy(false), mIsValid(true),
+    mIsBadUnderlineFont(false), mIsUserFont(false),
+    mIsLocalUserFont(false), mStandardFace(aIsStandardFace),
+    mSymbolFont(false),
+    mIgnoreGDEF(false),
+    mIgnoreGSUB(false),
+    mSVGInitialized(false),
+    mHasSpaceFeaturesInitialized(false),
+    mHasSpaceFeatures(false),
+    mHasSpaceFeaturesKerning(false),
+    mHasSpaceFeaturesNonKerning(false),
+    mHasSpaceFeaturesSubDefault(false),
+    mCheckedForGraphiteTables(false),
+    mHasCmapTable(false),
+    mGrFaceInitialized(false),
+    mWeight(500), mStretch(NS_FONT_STRETCH_NORMAL),
+    mUVSOffset(0), mUVSData(nullptr),
+    mLanguageOverride(NO_FONT_LANGUAGE_OVERRIDE),
+    mHBFace(nullptr),
+    mGrFace(nullptr),
+    mGrFaceRefCnt(0)
+{
+    memset(&mHasSpaceFeaturesSub, 0, sizeof(mHasSpaceFeaturesSub));
+}
+
 gfxFontEntry::~gfxFontEntry()
 {
     // For downloaded fonts, we need to tell the user font cache that this
     // entry is being deleted.
     if (!mIsProxy && IsUserFont() && !IsLocalUserFont()) {
         gfxUserFontSet::UserFontCache::ForgetFont(this);
     }
 
     // By the time the entry is destroyed, all font instances that were
     // using it should already have been deleted, and so the HB and/or Gr
     // face objects should have been released.
     MOZ_ASSERT(!mHBFace);
     MOZ_ASSERT(!mGrFaceInitialized);
-
-    if (mSVGGlyphs) {
-        delete mSVGGlyphs;
-    }
-    delete mUserFontData;
 }
 
 bool gfxFontEntry::IsSymbolFont() 
 {
     return mSymbolFont;
 }
 
 bool gfxFontEntry::TestCharacterMap(uint32_t aCh)
@@ -261,17 +311,17 @@ gfxFontEntry::TryGetSVGData(gfxFont* aFo
         // blobs to the gfxSVGGlyphs, once we've confirmed the tables exist
         hb_blob_t *svgTable = GetFontTable(TRUETYPE_TAG('S','V','G',' '));
         if (!svgTable) {
             return false;
         }
 
         // gfxSVGGlyphs will hb_blob_destroy() the table when it is finished
         // with it.
-        mSVGGlyphs = new gfxSVGGlyphs(svgTable);
+        mSVGGlyphs = new gfxSVGGlyphs(svgTable, this);
     }
 
     if (!mFontsUsingSVGGlyphs.Contains(aFont)) {
         mFontsUsingSVGGlyphs.AppendElement(aFont);
     }
 
     return !!mSVGGlyphs;
 }
--- a/gfx/thebes/gfxFont.h
+++ b/gfx/thebes/gfxFont.h
@@ -223,45 +223,17 @@ private:
     gfxCharacterMap(const gfxCharacterMap&);
     gfxCharacterMap& operator=(const gfxCharacterMap&);
 };
 
 class gfxFontEntry {
 public:
     NS_INLINE_DECL_REFCOUNTING(gfxFontEntry)
 
-    gfxFontEntry(const nsAString& aName, bool aIsStandardFace = false) :
-        mName(aName), mItalic(false), mFixedPitch(false),
-        mIsProxy(false), mIsValid(true), 
-        mIsBadUnderlineFont(false), mIsUserFont(false),
-        mIsLocalUserFont(false), mStandardFace(aIsStandardFace),
-        mSymbolFont(false),
-        mIgnoreGDEF(false),
-        mIgnoreGSUB(false),
-        mSVGInitialized(false),
-        mHasSpaceFeaturesInitialized(false),
-        mHasSpaceFeatures(false),
-        mHasSpaceFeaturesKerning(false),
-        mHasSpaceFeaturesNonKerning(false),
-        mHasSpaceFeaturesSubDefault(false),
-        mCheckedForGraphiteTables(false),
-        mHasCmapTable(false),
-        mGrFaceInitialized(false),
-        mWeight(500), mStretch(NS_FONT_STRETCH_NORMAL),
-        mUVSOffset(0), mUVSData(nullptr),
-        mUserFontData(nullptr),
-        mSVGGlyphs(nullptr),
-        mLanguageOverride(NO_FONT_LANGUAGE_OVERRIDE),
-        mHBFace(nullptr),
-        mGrFace(nullptr),
-        mGrFaceRefCnt(0)
-    {
-        memset(&mHasSpaceFeaturesSub, 0, sizeof(mHasSpaceFeaturesSub));
-    }
-
+    gfxFontEntry(const nsAString& aName, bool aIsStandardFace = false);
     virtual ~gfxFontEntry();
 
     // unique name for the face, *not* the family; not necessarily the
     // "real" or user-friendly name, may be an internal identifier
     const nsString& Name() const { return mName; }
 
     // family name
     const nsString& FamilyName() const { return mFamilyName; }
@@ -457,58 +429,31 @@ public:
     uint32_t         mHasSpaceFeaturesSub[(MOZ_NUM_SCRIPT_CODES + 31) / 32];
 
     uint16_t         mWeight;
     int16_t          mStretch;
 
     nsRefPtr<gfxCharacterMap> mCharacterMap;
     uint32_t         mUVSOffset;
     nsAutoArrayPtr<uint8_t> mUVSData;
-    gfxUserFontData* mUserFontData;
-    gfxSVGGlyphs    *mSVGGlyphs;
+    nsAutoPtr<gfxUserFontData> mUserFontData;
+    nsAutoPtr<gfxSVGGlyphs> mSVGGlyphs;
     // list of gfxFonts that are using SVG glyphs
     nsTArray<gfxFont*> mFontsUsingSVGGlyphs;
     nsTArray<gfxFontFeature> mFeatureSettings;
     uint32_t         mLanguageOverride;
 
 protected:
     friend class gfxPlatformFontList;
     friend class gfxMacPlatformFontList;
     friend class gfxUserFcFontEntry;
     friend class gfxFontFamily;
     friend class gfxSingleFaceMacFontFamily;
 
-    gfxFontEntry() :
-        mItalic(false), mFixedPitch(false),
-        mIsProxy(false), mIsValid(true), 
-        mIsBadUnderlineFont(false),
-        mIsUserFont(false),
-        mIsLocalUserFont(false),
-        mStandardFace(false),
-        mSymbolFont(false),
-        mIgnoreGDEF(false),
-        mIgnoreGSUB(false),
-        mSVGInitialized(false),
-        mHasSpaceFeaturesInitialized(false),
-        mHasSpaceFeatures(false),
-        mHasSpaceFeaturesKerning(false),
-        mHasSpaceFeaturesNonKerning(false),
-        mHasSpaceFeaturesSubDefault(false),
-        mCheckedForGraphiteTables(false),
-        mHasCmapTable(false),
-        mGrFaceInitialized(false),
-        mWeight(500), mStretch(NS_FONT_STRETCH_NORMAL),
-        mUVSOffset(0), mUVSData(nullptr),
-        mUserFontData(nullptr),
-        mSVGGlyphs(nullptr),
-        mLanguageOverride(NO_FONT_LANGUAGE_OVERRIDE),
-        mHBFace(nullptr),
-        mGrFace(nullptr),
-        mGrFaceRefCnt(0)
-    { }
+    gfxFontEntry();
 
     virtual gfxFont *CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold) {
         NS_NOTREACHED("oops, somebody didn't override CreateFontInstance");
         return nullptr;
     }
 
     virtual void CheckForGraphiteTables();
 
--- a/gfx/thebes/gfxSVGGlyphs.cpp
+++ b/gfx/thebes/gfxSVGGlyphs.cpp
@@ -26,33 +26,37 @@
 #include "nsStringStream.h"
 #include "nsStreamUtils.h"
 #include "nsIPrincipal.h"
 #include "mozilla/dom/Element.h"
 #include "nsSVGUtils.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsHostObjectProtocolHandler.h"
 #include "nsContentUtils.h"
+#include "gfxFont.h"
+#include "nsSMILAnimationController.h"
 #include "harfbuzz/hb.h"
 
 #define SVG_CONTENT_TYPE NS_LITERAL_CSTRING("image/svg+xml")
 #define UTF8_CHARSET NS_LITERAL_CSTRING("utf-8")
 
+using namespace mozilla;
+
 typedef mozilla::dom::Element Element;
 
 mozilla::gfx::UserDataKey gfxTextContextPaint::sUserDataKey;
 
 const float gfxSVGGlyphs::SVG_UNITS_PER_EM = 1000.0f;
 
 const gfxRGBA SimpleTextContextPaint::sZero = gfxRGBA(0.0f, 0.0f, 0.0f, 0.0f);
 
-gfxSVGGlyphs::gfxSVGGlyphs(hb_blob_t *aSVGTable)
+gfxSVGGlyphs::gfxSVGGlyphs(hb_blob_t *aSVGTable, gfxFontEntry *aFontEntry)
+    : mSVGData(aSVGTable)
+    , mFontEntry(aFontEntry)
 {
-    mSVGData = aSVGTable;
-
     unsigned int length;
     const char* svgData = hb_blob_get_data(mSVGData, &length);
     mHeader = reinterpret_cast<const Header*>(svgData);
     mDocIndex = nullptr;
 
     if (sizeof(Header) <= length && uint16_t(mHeader->mVersion) == 0 &&
         uint64_t(mHeader->mDocIndexOffset) + 2 <= length) {
         const DocIndex* docIndex = reinterpret_cast<const DocIndex*>
@@ -65,16 +69,22 @@ gfxSVGGlyphs::gfxSVGGlyphs(hb_blob_t *aS
     }
 }
 
 gfxSVGGlyphs::~gfxSVGGlyphs()
 {
     hb_blob_destroy(mSVGData);
 }
 
+void
+gfxSVGGlyphs::DidRefresh()
+{
+    mFontEntry->NotifyGlyphsChanged();
+}
+
 /*
  * Comparison operator for finding a range containing a given glyph ID. Simply
  *   checks whether |key| is less (greater) than every element of |range|, in
  *   which case return |key| < |range| (|key| > |range|). Otherwise |key| is in
  *   |range|, in which case return equality.
  * The total ordering here is guaranteed by
  *   (1) the index ranges being disjoint; and
  *   (2) the (sole) key always being a singleton, so intersection => containment
@@ -115,29 +125,27 @@ gfxSVGGlyphs::FindOrCreateGlyphsDocument
     gfxSVGGlyphsDocument *result = mGlyphDocs.Get(entry->mDocOffset);
 
     if (!result) {
         unsigned int length;
         const uint8_t *data = (const uint8_t*)hb_blob_get_data(mSVGData, &length);
         if (entry->mDocOffset > 0 &&
             uint64_t(mHeader->mDocIndexOffset) + entry->mDocOffset + entry->mDocLength <= length) {
             result = new gfxSVGGlyphsDocument(data + mHeader->mDocIndexOffset + entry->mDocOffset,
-                                              entry->mDocLength);
+                                              entry->mDocLength, this);
             mGlyphDocs.Put(entry->mDocOffset, result);
         }
     }
 
     return result;
 }
 
 nsresult
 gfxSVGGlyphsDocument::SetupPresentation()
 {
-    mDocument->SetIsBeingUsedAsImage();
-
     nsCOMPtr<nsICategoryManager> catMan = do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
     nsXPIDLCString contractId;
     nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", "image/svg+xml", getter_Copies(contractId));
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory = do_GetService(contractId);
     NS_ASSERTION(docLoaderFactory, "Couldn't get DocumentLoaderFactory");
 
@@ -149,32 +157,46 @@ gfxSVGGlyphsDocument::SetupPresentation(
     if (NS_SUCCEEDED(rv)) {
         rv = viewer->Open(nullptr, nullptr);
         NS_ENSURE_SUCCESS(rv, rv);
     }
 
     nsCOMPtr<nsIPresShell> presShell;
     rv = viewer->GetPresShell(getter_AddRefs(presShell));
     NS_ENSURE_SUCCESS(rv, rv);
-    presShell->GetPresContext()->SetIsGlyph(true);
+    nsPresContext* presContext = presShell->GetPresContext();
+    presContext->SetIsGlyph(true);
 
     if (!presShell->DidInitialize()) {
-        nsRect rect = presShell->GetPresContext()->GetVisibleArea();
+        nsRect rect = presContext->GetVisibleArea();
         rv = presShell->Initialize(rect.width, rect.height);
         NS_ENSURE_SUCCESS(rv, rv);
     }
 
     mDocument->FlushPendingNotifications(Flush_Layout);
 
+    nsSMILAnimationController* controller = mDocument->GetAnimationController();
+    if (controller) {
+      controller->Resume(nsSMILTimeContainer::PAUSE_IMAGE);
+    }
+    mDocument->SetImagesNeedAnimating(true);
+
     mViewer = viewer;
     mPresShell = presShell;
+    mPresShell->AddPostRefreshObserver(this);
 
     return NS_OK;
 }
 
+void
+gfxSVGGlyphsDocument::DidRefresh()
+{
+    mOwner->DidRefresh();
+}
+
 /**
  * Walk the DOM tree to find all glyph elements and insert them into the lookup
  * table
  * @param aElem The element to search from
  */
 void
 gfxSVGGlyphsDocument::FindGlyphElements(Element *aElem)
 {
@@ -246,17 +268,20 @@ gfxSVGGlyphs::HasSVGGlyph(uint32_t aGlyp
 }
 
 Element *
 gfxSVGGlyphsDocument::GetGlyphElement(uint32_t aGlyphId)
 {
     return mGlyphIdMap.Get(aGlyphId);
 }
 
-gfxSVGGlyphsDocument::gfxSVGGlyphsDocument(const uint8_t *aBuffer, uint32_t aBufLen)
+gfxSVGGlyphsDocument::gfxSVGGlyphsDocument(const uint8_t *aBuffer,
+                                           uint32_t aBufLen,
+                                           gfxSVGGlyphs *aSVGGlyphs)
+    : mOwner(aSVGGlyphs)
 {
     ParseDocument(aBuffer, aBufLen);
     if (!mDocument) {
         NS_WARNING("Could not parse SVG glyphs document");
         return;
     }
 
     Element *root = mDocument->GetRootElement();
@@ -269,16 +294,32 @@ gfxSVGGlyphsDocument::gfxSVGGlyphsDocume
     if (NS_FAILED(rv)) {
         NS_WARNING("Couldn't setup presentation for SVG glyphs document");
         return;
     }
 
     FindGlyphElements(root);
 }
 
+gfxSVGGlyphsDocument::~gfxSVGGlyphsDocument()
+{
+    if (mDocument) {
+        nsSMILAnimationController* controller = mDocument->GetAnimationController();
+        if (controller) {
+            controller->Pause(nsSMILTimeContainer::PAUSE_PAGEHIDE);
+        }
+    }
+    if (mPresShell) {
+        mPresShell->RemovePostRefreshObserver(this);
+    }
+    if (mViewer) {
+        mViewer->Destroy();
+    }
+}
+
 static nsresult
 CreateBufferedStream(const uint8_t *aBuffer, uint32_t aBufLen,
                      nsCOMPtr<nsIInputStream> &aResult)
 {
     nsCOMPtr<nsIInputStream> stream;
     nsresult rv = NS_NewByteInputStream(getter_AddRefs(stream),
                                         reinterpret_cast<const char *>(aBuffer),
                                         aBufLen, NS_ASSIGNMENT_DEPEND);
@@ -334,16 +375,18 @@ gfxSVGGlyphsDocument::ParseDocument(cons
 
     nsCOMPtr<nsIChannel> channel;
     rv = NS_NewInputStreamChannel(getter_AddRefs(channel), uri, nullptr /* stream */,
                                   SVG_CONTENT_TYPE, UTF8_CHARSET);
     NS_ENSURE_SUCCESS(rv, rv);
 
     channel->SetOwner(principal);
 
+    // Set this early because various decisions during page-load depend on it.
+    document->SetIsBeingUsedAsImage();
     document->SetReadyStateInternal(nsIDocument::READYSTATE_UNINITIALIZED);
 
     nsCOMPtr<nsIStreamListener> listener;
     rv = document->StartDocumentLoad("external-resource", channel,
                                      nullptr,    // aLoadGroup
                                      nullptr,    // aContainer
                                      getter_AddRefs(listener),
                                      true /* aReset */);
--- a/gfx/thebes/gfxSVGGlyphs.h
+++ b/gfx/thebes/gfxSVGGlyphs.h
@@ -12,51 +12,55 @@
 #include "nsIContentViewer.h"
 #include "nsIPresShell.h"
 #include "nsClassHashtable.h"
 #include "nsBaseHashtable.h"
 #include "nsHashKeys.h"
 #include "gfxPattern.h"
 #include "gfxFont.h"
 #include "mozilla/gfx/UserData.h"
+#include "nsRefreshDriver.h"
+ 
+class gfxSVGGlyphs;
 
 
 /**
  * Wraps an SVG document contained in the SVG table of an OpenType font.
  * There may be multiple SVG documents in an SVG table which we lazily parse
  *   so we have an instance of this class for every document in the SVG table
  *   which contains a glyph ID which has been used
  * Finds and looks up elements contained in the SVG document which have glyph
  *   mappings to be drawn by gfxSVGGlyphs
  */
-class gfxSVGGlyphsDocument
+class gfxSVGGlyphsDocument MOZ_FINAL : public nsAPostRefreshObserver
 {
     typedef mozilla::dom::Element Element;
     typedef gfxFont::DrawMode DrawMode;
 
 public:
-    gfxSVGGlyphsDocument(const uint8_t *aBuffer, uint32_t aBufLen);
+    gfxSVGGlyphsDocument(const uint8_t *aBuffer, uint32_t aBufLen,
+                         gfxSVGGlyphs *aSVGGlyphs);
 
     Element *GetGlyphElement(uint32_t aGlyphId);
 
-    ~gfxSVGGlyphsDocument() {
-        if (mViewer) {
-            mViewer->Destroy();
-        }
-    }
+    ~gfxSVGGlyphsDocument();
+
+    virtual void DidRefresh() MOZ_OVERRIDE;
 
 private:
     nsresult ParseDocument(const uint8_t *aBuffer, uint32_t aBufLen);
 
     nsresult SetupPresentation();
 
     void FindGlyphElements(Element *aElement);
 
     void InsertGlyphId(Element *aGlyphElement);
 
+    // Weak so as not to create a cycle. mOwner owns us so this can't dangle.
+    gfxSVGGlyphs* mOwner;
     nsCOMPtr<nsIDocument> mDocument;
     nsCOMPtr<nsIContentViewer> mViewer;
     nsCOMPtr<nsIPresShell> mPresShell;
 
     nsBaseHashtable<nsUint32HashKey, Element*, Element*> mGlyphIdMap;
 
     nsAutoCString mSVGGlyphsDocumentURI;
 };
@@ -78,24 +82,29 @@ public:
 
     /**
      * @param aSVGTable The SVG table from the OpenType font
      *
      * The gfxSVGGlyphs object takes over ownership of the blob references
      * that are passed in, and will hb_blob_destroy() them when finished;
      * the caller should -not- destroy these references.
      */
-    gfxSVGGlyphs(hb_blob_t *aSVGTable);
+    gfxSVGGlyphs(hb_blob_t *aSVGTable, gfxFontEntry *aFontEntry);
 
     /**
-     * Releases our references to the SVG table.
+     * Releases our references to the SVG table and cleans up everything else.
      */
     ~gfxSVGGlyphs();
 
     /**
+     * This is called when the refresh driver has ticked.
+     */
+    void DidRefresh();
+
+    /**
      * Find the |gfxSVGGlyphsDocument| containing an SVG glyph for |aGlyphId|.
      * If |aGlyphId| does not map to an SVG document, return null.
      * If a |gfxSVGGlyphsDocument| has not been created for the document, create one.
      */
     gfxSVGGlyphsDocument *FindOrCreateGlyphsDocument(uint32_t aGlyphId);
 
     /**
      * Return true iff there is an SVG glyph for |aGlyphId|
@@ -121,16 +130,17 @@ public:
 
 private:
     Element *GetGlyphElement(uint32_t aGlyphId);
 
     nsClassHashtable<nsUint32HashKey, gfxSVGGlyphsDocument> mGlyphDocs;
     nsBaseHashtable<nsUint32HashKey, Element*, Element*> mGlyphIdMap;
 
     hb_blob_t *mSVGData;
+    gfxFontEntry *mFontEntry;
 
     const struct Header {
         mozilla::AutoSwap_PRUint16 mVersion;
         mozilla::AutoSwap_PRUint32 mDocIndexOffset;
         mozilla::AutoSwap_PRUint32 mColorPalettesOffset;
     } *mHeader;
 
     struct IndexEntry {