Bug 1028497 - Part 19: Support disconnecting FontFaces that reflect @font-face rules. r=jdaggett,bzbarsky

This adds support for a CSS-connected FontFace to be disconnected from
its rule. This causes it to get its own copy of the descriptors on the
nsCSSFontFaceStyleDecl, and for the pointers between the FontFace and
the nsCSSFontFaceRule to be nulled out.

We start tracking now whether a given FontFace is in the FontFaceSet
(in the sense that it will appear on the DOM FontFaceSet object if we
inspect it with script). All FontFace objects created though, whether
they are currently "in" the FontFaceSet or not, are still tracked by the
FontFaceSet. We use the new mUnavailableFaces array on the FontFaceSet
for that.

We need to track these FontFaces that aren't in the FontFaceSet as
that's where we store their user font entry -- important if we call
load() on a FontFace before adding it to the FontFaceSet.
This commit is contained in:
Cameron McCormack 2014-10-02 12:32:08 +10:00
parent fccff046d0
commit 4ddb800587
5 changed files with 140 additions and 5 deletions

View File

@ -15,7 +15,30 @@
using namespace mozilla::dom;
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(FontFace, mParent, mLoaded, mRule)
NS_IMPL_CYCLE_COLLECTION_CLASS(FontFace)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(FontFace)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLoaded)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRule)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFontFaceSet)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(FontFace)
if (!tmp->IsInFontFaceSet()) {
tmp->mFontFaceSet->RemoveUnavailableFontFace(tmp);
}
NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mLoaded)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mRule)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mFontFaceSet)
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(FontFace)
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FontFace)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
@ -29,11 +52,11 @@ FontFace::FontFace(nsISupports* aParent, nsPresContext* aPresContext)
: mParent(aParent)
, mPresContext(aPresContext)
, mStatus(FontFaceLoadStatus::Unloaded)
, mFontFaceSet(aPresContext->Fonts())
, mInFontFaceSet(false)
{
MOZ_COUNT_CTOR(FontFace);
MOZ_ASSERT(mPresContext);
SetIsDOMBinding();
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aParent);
@ -49,6 +72,10 @@ FontFace::~FontFace()
MOZ_COUNT_DTOR(FontFace);
SetUserFontEntry(nullptr);
if (mFontFaceSet && !IsInFontFaceSet()) {
mFontFaceSet->RemoveUnavailableFontFace(this);
}
}
JSObject*
@ -84,6 +111,7 @@ FontFace::CreateForRule(nsISupports* aGlobal,
nsRefPtr<FontFace> obj = new FontFace(aGlobal, aPresContext);
obj->mRule = aRule;
obj->mInFontFaceSet = true;
obj->SetUserFontEntry(aUserFontEntry);
return obj.forget();
}
@ -116,6 +144,7 @@ FontFace::Constructor(const GlobalObject& aGlobal,
}
nsRefPtr<FontFace> obj = new FontFace(global, presContext);
obj->mFontFaceSet->AddUnavailableFontFace(obj);
return obj.forget();
}
@ -433,6 +462,20 @@ FontFace::GetFamilyName(nsString& aResult)
return !aResult.IsEmpty();
}
void
FontFace::DisconnectFromRule()
{
MOZ_ASSERT(IsConnected());
// Make a copy of the descriptors.
mDescriptors = new CSSFontFaceDescriptors;
mRule->GetDescriptors(*mDescriptors);
mRule->SetFontFace(nullptr);
mRule = nullptr;
mInFontFaceSet = false;
}
// -- FontFace::Entry --------------------------------------------------------
/* virtual */ void

View File

@ -19,6 +19,7 @@ namespace mozilla {
struct CSSFontFaceDescriptors;
namespace dom {
struct FontFaceDescriptors;
class FontFaceSet;
class Promise;
class StringOrArrayBufferOrArrayBufferView;
}
@ -78,6 +79,8 @@ public:
gfxUserFontEntry* GetUserFontEntry() const { return mUserFontEntry; }
void SetUserFontEntry(gfxUserFontEntry* aEntry);
bool IsInFontFaceSet() { return mInFontFaceSet; }
/**
* Gets the family name of the FontFace as a raw string (such as 'Times', as
* opposed to GetFamily, which returns a CSS-escaped string, such as
@ -85,6 +88,17 @@ public:
*/
bool GetFamilyName(nsString& aResult);
/**
* Returns whether this object is CSS-connected, i.e. reflecting an
* @font-face rule.
*/
bool IsConnected() const { return mRule; }
/**
* Breaks the connection between this FontFace and its @font-face rule.
*/
void DisconnectFromRule();
// Web IDL
static already_AddRefed<FontFace>
Constructor(const GlobalObject& aGlobal,
@ -162,6 +176,13 @@ private:
// a CSS-connected FontFace object. For CSS-connected objects, we use
// the descriptors stored in mRule.
nsAutoPtr<mozilla::CSSFontFaceDescriptors> mDescriptors;
// The FontFaceSet this FontFace is associated with, regardless of whether
// it is currently "in" the set.
nsRefPtr<FontFaceSet> mFontFaceSet;
// Whether this FontFace appears in the FontFaceSet.
bool mInFontFaceSet;
};
} // namespace dom

View File

@ -208,9 +208,14 @@ FontFaceSet::DestroyUserFontSet()
mPresContext = nullptr;
mLoaders.EnumerateEntries(DestroyIterator, nullptr);
for (size_t i = 0; i < mConnectedFaces.Length(); i++) {
mConnectedFaces[i].mFontFace->DisconnectFromRule();
mConnectedFaces[i].mFontFace->SetUserFontEntry(nullptr);
}
for (size_t i = 0; i < mUnavailableFaces.Length(); i++) {
mUnavailableFaces[i]->SetUserFontEntry(nullptr);
}
mConnectedFaces.Clear();
mUnavailableFaces.Clear();
mReady = nullptr;
mUserFontSet = nullptr;
}
@ -401,8 +406,8 @@ FontFaceSet::UpdateRules(const nsTArray<nsFontFaceRuleContainer>& aRules)
// it when the FontFace is GCed, if we can detect that.
size_t count = oldRecords.Length();
for (size_t i = 0; i < count; ++i) {
gfxUserFontEntry* userFontEntry =
oldRecords[i].mFontFace->GetUserFontEntry();
nsRefPtr<FontFace> f = oldRecords[i].mFontFace;
gfxUserFontEntry* userFontEntry = f->GetUserFontEntry();
if (userFontEntry) {
nsFontFaceLoader* loader = userFontEntry->GetLoader();
if (loader) {
@ -410,6 +415,13 @@ FontFaceSet::UpdateRules(const nsTArray<nsFontFaceRuleContainer>& aRules)
RemoveLoader(loader);
}
}
// Any left over FontFace objects should also cease being CSS-connected.
MOZ_ASSERT(!mUnavailableFaces.Contains(f),
"FontFace should not occur in mUnavailableFaces twice");
mUnavailableFaces.AppendElement(f);
f->DisconnectFromRule();
}
}
@ -455,6 +467,9 @@ FontFaceSet::InsertConnectedFontFace(
return;
}
bool remove = false;
size_t removeIndex;
// This is a CSS-connected FontFace. First, we check in aOldRecords; if
// the FontFace for the rule exists there, just move it to the new record
// list, and put the entry into the appropriate family.
@ -471,6 +486,10 @@ FontFaceSet::InsertConnectedFontFace(
aFontFace->GetDesc(eCSSFontDesc_Src, val);
nsCSSUnit unit = val.GetUnit();
if (unit == eCSSUnit_Array && HasLocalSrc(val.GetArrayValue())) {
// Remove the old record, but wait to see if we successfully create a
// new user font entry below.
remove = true;
removeIndex = i;
break;
}
}
@ -502,6 +521,16 @@ FontFaceSet::InsertConnectedFontFace(
return;
}
if (remove) {
// Although we broke out of the aOldRecords loop above, since we found
// src local usage, and we're not using the old user font entry, we still
// are adding a record to mConnectedFaces with the same FontFace object.
// Remove the old record so that we don't have the same FontFace listed
// in both mConnectedFaces and oldRecords, which would cause us to call
// DisconnectFromRule on a FontFace that should still be connected.
aOldRecords.RemoveElementAt(removeIndex);
}
FontFaceRecord rec;
rec.mFontFace = aFontFace;
rec.mSheetType = aSheetType;
@ -1017,6 +1046,29 @@ FontFaceSet::FontFaceForRule(nsCSSFontFaceRule* aRule)
return newFontFace;
}
void
FontFaceSet::AddUnavailableFontFace(FontFace* aFontFace)
{
MOZ_ASSERT(!aFontFace->IsInFontFaceSet());
MOZ_ASSERT(!mUnavailableFaces.Contains(aFontFace));
mUnavailableFaces.AppendElement(aFontFace);
}
void
FontFaceSet::RemoveUnavailableFontFace(FontFace* aFontFace)
{
MOZ_ASSERT(!aFontFace->IsConnected());
MOZ_ASSERT(!aFontFace->IsInFontFaceSet());
// We might not actually find the FontFace in mUnavailableFaces, since we
// might be shutting down the document and had DestroyUserFontSet called
// on us, which clears out mUnavailableFaces.
mUnavailableFaces.RemoveElement(aFontFace);
MOZ_ASSERT(!mUnavailableFaces.Contains(aFontFace));
}
// -- FontFaceSet::UserFontSet ------------------------------------------------
/* virtual */ nsresult

View File

@ -112,6 +112,18 @@ public:
void IncrementGeneration(bool aIsRebuild = false);
/**
* Adds the specified FontFace to the mUnavailableFaces array. This is called
* when a new FontFace object has just been created in JS by the author.
*/
void AddUnavailableFontFace(FontFace* aFontFace);
/**
* Removes the specified FontFace from the mUnavailableFaces array. This
* is called when a FontFace object is about be destroyed.
*/
void RemoveUnavailableFontFace(FontFace* aFontFace);
// -- Web IDL --------------------------------------------------------------
IMPL_EVENT_HANDLER(loading)
@ -193,6 +205,10 @@ private:
// The CSS-connected FontFace objects in the FontFaceSet.
nsTArray<FontFaceRecord> mConnectedFaces;
// The unconnected FontFace objects that have not been added to
// this FontFaceSet.
nsTArray<FontFace*> mUnavailableFaces;
};
} // namespace dom

View File

@ -280,6 +280,9 @@ public:
mozilla::dom::FontFace* GetFontFace() const { return mFontFace; }
void SetFontFace(mozilla::dom::FontFace* aFontFace) { mFontFace = aFontFace; }
void GetDescriptors(mozilla::CSSFontFaceDescriptors& aDescriptors) const
{ aDescriptors = mDecl.mDescriptors; }
protected:
~nsCSSFontFaceRule() {}