Bug 1145506 - Make FontFace constructor fail on invalid src strings but otherwise create user font entries immediately. r=jdaggett
authorCameron McCormack <cam@mcc.id.au>
Fri, 27 Mar 2015 21:13:21 +1100
changeset 264946 1df07e80e3c7a8a127b95d0f98b71a4af77300df
parent 264945 346bbcaeff387faadd2cc493f1a6fd77b2fcea14
child 264947 f39fd4c98562e02f71a7e1797f9763bbdf869575
push id4718
push userraliiev@mozilla.com
push dateMon, 11 May 2015 18:39:53 +0000
treeherdermozilla-beta@c20c4ef55f08 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdaggett
bugs1145506
milestone39.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 1145506 - Make FontFace constructor fail on invalid src strings but otherwise create user font entries immediately. r=jdaggett
layout/style/FontFace.cpp
layout/style/FontFace.h
layout/style/FontFaceSet.cpp
layout/style/FontFaceSet.h
layout/style/test/test_font_loading_api.html
--- a/layout/style/FontFace.cpp
+++ b/layout/style/FontFace.cpp
@@ -37,17 +37,17 @@ private:
 };
 
 void
 FontFaceBufferSource::TakeBuffer(uint8_t*& aBuffer, uint32_t& aLength)
 {
   mFontFace->TakeBuffer(aBuffer, aLength);
 }
 
-// -- FontFaceInitializer ----------------------------------------------------
+// -- Utility functions ------------------------------------------------------
 
 template<typename T>
 static void
 GetDataFrom(const T& aObject, uint8_t*& aBuffer, uint32_t& aLength)
 {
   MOZ_ASSERT(!aBuffer);
   aObject.ComputeLengthAndData();
   // We use moz_malloc here rather than a FallibleTArray or fallible
@@ -56,130 +56,16 @@ GetDataFrom(const T& aObject, uint8_t*& 
   aBuffer = (uint8_t*) moz_malloc(aObject.Length());
   if (!aBuffer) {
     return;
   }
   memcpy((void*) aBuffer, aObject.Data(), aObject.Length());
   aLength = aObject.Length();
 }
 
-/**
- * A task that is dispatched to the event queue to call Initialize() on a
- * FontFace object with the source information that was passed to the JS
- * constructor.
- */
-class FontFaceInitializer : public nsIRunnable
-{
-public:
-  NS_DECL_ISUPPORTS
-
-  explicit FontFaceInitializer(FontFace* aFontFace)
-    : mFontFace(aFontFace)
-    , mSourceBuffer(nullptr)
-    , mSourceBufferLength(0) {}
-
-  void SetSource(const nsAString& aString);
-  void SetSource(const ArrayBuffer& aArrayBuffer);
-  void SetSource(const ArrayBufferView& aArrayBufferView);
-
-  NS_IMETHOD Run() override;
-  void TakeBuffer(uint8_t*& aBuffer, uint32_t& aLength);
-
-  nsRefPtr<FontFace> mFontFace;
-  FontFace::SourceType mSourceType;
-  nsString mSourceString;
-  uint8_t* mSourceBuffer;  // allocated with NS_Alloc
-  uint32_t mSourceBufferLength;
-
-protected:
-  virtual ~FontFaceInitializer();
-};
-
-NS_IMPL_ISUPPORTS(FontFaceInitializer, nsIRunnable)
-
-FontFaceInitializer::~FontFaceInitializer()
-{
-  if (mSourceBuffer) {
-    NS_Free(mSourceBuffer);
-  }
-}
-
-void
-FontFaceInitializer::SetSource(const nsAString& aString)
-{
-  mSourceType = FontFace::eSourceType_URLs;
-  mSourceString = aString;
-}
-
-void
-FontFaceInitializer::SetSource(const ArrayBuffer& aArrayBuffer)
-{
-  mSourceType = FontFace::eSourceType_Buffer;
-  GetDataFrom(aArrayBuffer, mSourceBuffer, mSourceBufferLength);
-}
-
-void
-FontFaceInitializer::SetSource(const ArrayBufferView& aArrayBufferView)
-{
-  mSourceType = FontFace::eSourceType_Buffer;
-  GetDataFrom(aArrayBufferView, mSourceBuffer, mSourceBufferLength);
-}
-
-NS_IMETHODIMP
-FontFaceInitializer::Run()
-{
-  mFontFace->Initialize(this);
-  return NS_OK;
-}
-
-void
-FontFaceInitializer::TakeBuffer(uint8_t*& aBuffer, uint32_t& aLength)
-{
-  aBuffer = mSourceBuffer;
-  aLength = mSourceBufferLength;
-
-  mSourceBuffer = nullptr;
-  mSourceBufferLength = 0;
-}
-
-// -- FontFaceStatusSetter ---------------------------------------------------
-
-/**
- * A task that is dispatched to the event queue to asynchronously call
- * SetStatus() on a FontFace object.
- */
-class FontFaceStatusSetter : public nsIRunnable
-{
-public:
-  NS_DECL_ISUPPORTS
-
-  FontFaceStatusSetter(FontFace* aFontFace,
-                       FontFaceLoadStatus aStatus)
-    : mFontFace(aFontFace)
-    , mStatus(aStatus) {}
-
-  NS_IMETHOD Run() override;
-
-protected:
-  virtual ~FontFaceStatusSetter() {}
-
-private:
-  nsRefPtr<FontFace> mFontFace;
-  FontFaceLoadStatus mStatus;
-};
-
-NS_IMPL_ISUPPORTS(FontFaceStatusSetter, nsIRunnable)
-
-NS_IMETHODIMP
-FontFaceStatusSetter::Run()
-{
-  mFontFace->SetStatus(mStatus);
-  return NS_OK;
-}
-
 // -- FontFace ---------------------------------------------------------------
 
 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)
@@ -214,18 +100,16 @@ FontFace::FontFace(nsISupports* aParent,
   : mParent(aParent)
   , mPresContext(aPresContext)
   , mStatus(FontFaceLoadStatus::Unloaded)
   , mSourceType(SourceType(0))
   , mSourceBuffer(nullptr)
   , mSourceBufferLength(0)
   , mFontFaceSet(aPresContext->Fonts())
   , mInFontFaceSet(false)
-  , mInitialized(false)
-  , mLoadWhenInitialized(false)
 {
   MOZ_COUNT_CTOR(FontFace);
 
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aParent);
 
   // If the pref is not set, don't create the Promise (which the page wouldn't
   // be able to get to anyway) as it causes the window.FontFace constructor
   // to be created.
@@ -276,17 +160,16 @@ LoadStateToStatus(gfxUserFontEntry::User
 already_AddRefed<FontFace>
 FontFace::CreateForRule(nsISupports* aGlobal,
                         nsPresContext* aPresContext,
                         nsCSSFontFaceRule* aRule)
 {
   nsCOMPtr<nsIGlobalObject> globalObject = do_QueryInterface(aGlobal);
 
   nsRefPtr<FontFace> obj = new FontFace(aGlobal, aPresContext);
-  obj->mInitialized = true;
   obj->mRule = aRule;
   obj->mSourceType = eSourceType_FontFaceRule;
   obj->mInFontFaceSet = true;
   return obj.forget();
 }
 
 already_AddRefed<FontFace>
 FontFace::Constructor(const GlobalObject& aGlobal,
@@ -316,84 +199,56 @@ FontFace::Constructor(const GlobalObject
   }
 
   nsRefPtr<FontFace> obj = new FontFace(global, presContext);
   obj->mFontFaceSet->AddUnavailableFontFace(obj);
   if (!obj->SetDescriptors(aFamily, aDescriptors)) {
     return obj.forget();
   }
 
-  nsRefPtr<FontFaceInitializer> task = new FontFaceInitializer(obj);
-
-  if (aSource.IsArrayBuffer()) {
-    task->SetSource(aSource.GetAsArrayBuffer());
-  } else if (aSource.IsArrayBufferView()) {
-    task->SetSource(aSource.GetAsArrayBufferView());
-  } else {
-    MOZ_ASSERT(aSource.IsString());
-    task->SetSource(aSource.GetAsString());
-  }
-
-  NS_DispatchToMainThread(task);
-
+  obj->InitializeSource(aSource);
   return obj.forget();
 }
 
 void
-FontFace::Initialize(FontFaceInitializer* aInitializer)
+FontFace::InitializeSource(const StringOrArrayBufferOrArrayBufferView& aSource)
 {
-  MOZ_ASSERT(!HasRule());
-  MOZ_ASSERT(mSourceType == SourceType(0));
-
-  if (aInitializer->mSourceType == eSourceType_URLs) {
+  if (aSource.IsString()) {
     if (!ParseDescriptor(eCSSFontDesc_Src,
-                         aInitializer->mSourceString,
+                         aSource.GetAsString(),
                          mDescriptors->mSrc)) {
       if (mLoaded) {
-        // The asynchronous SetStatus call we are about to do assumes that for
+        // The SetStatus call we are about to do assumes that for
         // FontFace objects with sources other than ArrayBuffer(View)s, that the
         // mLoaded Promise is rejected with a network error.  We get
         // in here beforehand to set it to the required syntax error.
         mLoaded->MaybeReject(NS_ERROR_DOM_SYNTAX_ERR);
       }
 
-      // Queue a task to set the status to "error".
-      nsCOMPtr<nsIRunnable> statusSetterTask =
-        new FontFaceStatusSetter(this, FontFaceLoadStatus::Error);
-      NS_DispatchToMainThread(statusSetterTask);
+      SetStatus(FontFaceLoadStatus::Error);
       return;
     }
 
     mSourceType = eSourceType_URLs;
-
-    // Now that we have parsed the src descriptor, we are initialized.
-    OnInitialized();
     return;
   }
 
-  // We've been given an ArrayBuffer or ArrayBufferView as the source.
-  MOZ_ASSERT(aInitializer->mSourceType == eSourceType_Buffer);
-
-  mSourceType = aInitializer->mSourceType;
-  aInitializer->TakeBuffer(mSourceBuffer, mSourceBufferLength);
+  mSourceType = FontFace::eSourceType_Buffer;
 
-  // Queue a task to set the status to "loading".
-  nsCOMPtr<nsIRunnable> statusSetterTask =
-    new FontFaceStatusSetter(this, FontFaceLoadStatus::Loading);
-  NS_DispatchToMainThread(statusSetterTask);
+  if (aSource.IsArrayBuffer()) {
+    GetDataFrom(aSource.GetAsArrayBuffer(),
+                mSourceBuffer, mSourceBufferLength);
+  } else {
+    MOZ_ASSERT(aSource.IsArrayBufferView());
+    GetDataFrom(aSource.GetAsArrayBufferView(),
+                mSourceBuffer, mSourceBufferLength);
+  }
 
-  // We are initialized.
-  OnInitialized();
-
-  // ArrayBuffer(View)-backed FontFace objects are loaded on construction,
-  // but we need to do this after going through the event loop so that the
-  // FontFaceStatusSetter runs before us.
-  nsCOMPtr<nsIRunnable> loaderTask =
-    NS_NewRunnableMethod(this, &FontFace::DoLoad);
-  NS_DispatchToMainThread(loaderTask);
+  SetStatus(FontFaceLoadStatus::Loading);
+  DoLoad();
 }
 
 void
 FontFace::GetFamily(nsString& aResult)
 {
   mPresContext->FlushUserFontSet();
 
   // Serialize the same way as in nsCSSFontFaceStyleDecl::GetPropertyValue.
@@ -539,32 +394,24 @@ FontFace::Load(ErrorResult& aRv)
     return mLoaded;
   }
 
   // Calling the user font entry's Load method will end up setting our
   // status to Loading, but the spec requires us to set it to Loading
   // here.
   SetStatus(FontFaceLoadStatus::Loading);
 
-  if (mInitialized) {
-    DoLoad();
-  } else {
-    // We can only load an initialized font; this will cause the font to be
-    // loaded once it has been initialized.
-    mLoadWhenInitialized = true;
-  }
+  DoLoad();
 
   return mLoaded;
 }
 
 void
 FontFace::DoLoad()
 {
-  MOZ_ASSERT(mInitialized);
-
   if (!mUserFontEntry) {
     MOZ_ASSERT(!HasRule(),
                "Rule backed FontFace objects should already have a user font "
                "entry by the time Load() can be called on them");
 
     nsRefPtr<gfxUserFontEntry> newEntry =
       mFontFaceSet->FindOrCreateUserFontEntryFromFontFace(this);
     if (!newEntry) {
@@ -718,35 +565,16 @@ FontFace::SetDescriptors(const nsAString
     SetStatus(FontFaceLoadStatus::Error);
     return false;
   }
 
   return true;
 }
 
 void
-FontFace::OnInitialized()
-{
-  MOZ_ASSERT(!mInitialized);
-
-  mInitialized = true;
-
-  // For a FontFace that was created and immediately had Load() called on
-  // it, before it had a chance to be initialized, we kick off its load now.
-  if (mLoadWhenInitialized) {
-    mLoadWhenInitialized = false;
-    DoLoad();
-  }
-
-  if (mInFontFaceSet) {
-    mFontFaceSet->OnFontFaceInitialized(this);
-  }
-}
-
-void
 FontFace::GetDesc(nsCSSFontDesc aDescID, nsCSSValue& aResult) const
 {
   if (HasRule()) {
     MOZ_ASSERT(mRule);
     MOZ_ASSERT(!mDescriptors);
     mRule->GetDesc(aDescID, aResult);
   } else {
     aResult = mDescriptors->Get(aDescID);
--- a/layout/style/FontFace.h
+++ b/layout/style/FontFace.h
@@ -17,32 +17,28 @@ class nsCSSFontFaceRule;
 class nsPresContext;
 
 namespace mozilla {
 struct CSSFontFaceDescriptors;
 namespace dom {
 class FontFaceBufferSource;
 struct FontFaceDescriptors;
 class FontFaceSet;
-class FontFaceInitializer;
-class FontFaceStatusSetter;
 class Promise;
 class StringOrArrayBufferOrArrayBufferView;
 }
 }
 
 namespace mozilla {
 namespace dom {
 
 class FontFace final : public nsISupports,
                            public nsWrapperCache
 {
   friend class mozilla::dom::FontFaceBufferSource;
-  friend class mozilla::dom::FontFaceInitializer;
-  friend class mozilla::dom::FontFaceStatusSetter;
   friend class Entry;
 
 public:
   class Entry final : public gfxUserFontEntry {
     friend class FontFace;
 
   public:
     Entry(gfxUserFontSet* aFontSet,
@@ -94,24 +90,16 @@ public:
    * FontFaceSet when Add, Remove, etc. are called.
    */
   void SetIsInFontFaceSet(bool aInFontFaceSet) {
     MOZ_ASSERT(!(!aInFontFaceSet && HasRule()),
                "use DisconnectFromRule instead");
     mInFontFaceSet = aInFontFaceSet;
   }
 
-  /**
-   * Returns whether this FontFace is initialized.  A rule backed
-   * FontFace is considered initialized at construction time.  For
-   * FontFace objects created using the FontFace JS constructor, it
-   * is once all the descriptors have been parsed.
-   */
-  bool IsInitialized() const { return mInitialized; }
-
   FontFaceSet* GetFontFaceSet() const { return mFontFaceSet; }
 
   /**
    * 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
    * '"Times"').  Returns whether a valid family name was available.
    */
   bool GetFamilyName(nsString& aResult);
@@ -171,23 +159,17 @@ public:
   mozilla::dom::FontFaceLoadStatus Status();
   mozilla::dom::Promise* Load(mozilla::ErrorResult& aRv);
   mozilla::dom::Promise* GetLoaded(mozilla::ErrorResult& aRv);
 
 private:
   FontFace(nsISupports* aParent, nsPresContext* aPresContext);
   ~FontFace();
 
-  /**
-   * Initializes the source and descriptors on this object based on values that
-   * were passed in to the JS constructor.  If the source was specified as
-   * an ArrayBuffer or ArrayBufferView, parsing of the font data in there
-   * will be started.
-   */
-  void Initialize(FontFaceInitializer* aInitializer);
+  void InitializeSource(const StringOrArrayBufferOrArrayBufferView& aSource);
 
   // Helper function for Load.
   void DoLoad();
 
   /**
    * Parses a @font-face descriptor value, storing the result in aResult.
    * Returns whether the parsing was successful.
    */
@@ -202,22 +184,16 @@ private:
   /**
    * Sets all of the descriptor values in mDescriptors using values passed
    * to the JS constructor.
    */
   bool SetDescriptors(const nsAString& aFamily,
                       const FontFaceDescriptors& aDescriptors);
 
   /**
-   * Marks the FontFace as initialized and informs the FontFaceSet it is in,
-   * if any.
-   */
-  void OnInitialized();
-
-  /**
    * Sets the current loading status.
    */
   void SetStatus(mozilla::dom::FontFaceLoadStatus aStatus);
 
   void GetDesc(nsCSSFontDesc aDescID,
                nsCSSProperty aPropID,
                nsString& aResult) const;
 
@@ -268,24 +244,14 @@ private:
   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;
-
-  // Whether the FontFace has been fully initialized.  This takes at least one
-  // run around the event loop, as the parsing of the src descriptor is done
-  // off an event queue task.
-  bool mInitialized;
-
-  // Records whether Load() was called on this FontFace before it was
-  // initialized.  When the FontFace eventually does become initialized,
-  // mLoadPending is checked and Load() is called if needed.
-  bool mLoadWhenInitialized;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // !defined(mozilla_dom_FontFace_h)
--- a/layout/style/FontFaceSet.cpp
+++ b/layout/style/FontFaceSet.cpp
@@ -659,24 +659,16 @@ FontFaceSet::IncrementGeneration(bool aI
   MOZ_ASSERT(mUserFontSet);
   mUserFontSet->IncrementGeneration(aIsRebuild);
 }
 
 void
 FontFaceSet::InsertNonRuleFontFace(FontFace* aFontFace,
                                    bool& aFontSetModified)
 {
-  if (!aFontFace->IsInitialized()) {
-    // The FontFace is still waiting to be initialized, so don't create a
-    // user font entry for it yet.  Once it has been initialized, it will
-    // call OnFontFaceInitialized on us, which will end rebuild the user
-    // font set and end up back in here.
-    return;
-  }
-
   nsAutoString fontfamily;
   if (!aFontFace->GetFamilyName(fontfamily)) {
     // If there is no family name, this rule cannot contribute a
     // usable font, so there is no point in processing it further.
     return;
   }
 
   // Just create a new font entry if we haven't got one already.
@@ -812,17 +804,17 @@ FontFaceSet::FindOrCreateUserFontEntryFr
 }
 
 already_AddRefed<gfxUserFontEntry>
 FontFaceSet::FindOrCreateUserFontEntryFromFontFace(const nsAString& aFamilyName,
                                                    FontFace* aFontFace,
                                                    uint8_t aSheetType)
 {
   nsCSSValue val;
-  uint32_t unit;
+  nsCSSUnit unit;
 
   uint32_t weight = NS_STYLE_FONT_WEIGHT_NORMAL;
   int32_t stretch = NS_STYLE_FONT_STRETCH_NORMAL;
   uint32_t italicStyle = NS_STYLE_FONT_STYLE_NORMAL;
   uint32_t languageOverride = NO_FONT_LANGUAGE_OVERRIDE;
 
   // set up weight
   aFontFace->GetDesc(eCSSFontDesc_Weight, val);
@@ -1320,26 +1312,16 @@ FontFaceSet::RemoveUnavailableFontFace(F
   // might be shutting down the document and had DestroyUserFontSet called
   // on us, which clears out mUnavailableFaces.
   mUnavailableFaces.RemoveElement(aFontFace);
 
   MOZ_ASSERT(!mUnavailableFaces.Contains(aFontFace));
 }
 
 void
-FontFaceSet::OnFontFaceInitialized(FontFace* aFontFace)
-{
-  MOZ_ASSERT(HasAvailableFontFace(aFontFace));
-  MOZ_ASSERT(!aFontFace->HasRule());
-  MOZ_ASSERT(aFontFace->IsInitialized());
-
-  mPresContext->RebuildUserFontSet();
-}
-
-void
 FontFaceSet::OnFontFaceStatusChanged(FontFace* aFontFace)
 {
   MOZ_ASSERT(HasAvailableFontFace(aFontFace));
 
   mHasLoadingFontFacesIsDirty = true;
 
   if (aFontFace->Status() == FontFaceLoadStatus::Loading) {
     CheckLoadingStarted();
--- a/layout/style/FontFaceSet.h
+++ b/layout/style/FontFaceSet.h
@@ -131,25 +131,16 @@ public:
   /**
    * Finds an existing entry in the user font cache or creates a new user
    * font entry for the given FontFace object.
    */
   already_AddRefed<gfxUserFontEntry>
     FindOrCreateUserFontEntryFromFontFace(FontFace* aFontFace);
 
   /**
-   * Notification method called by a FontFace once it has been initialized.
-   *
-   * This is needed for the FontFaceSet to handle a FontFace that was created
-   * and inserted into the set immediately, before the event loop has spun and
-   * the FontFace's initialization tasks have run.
-   */
-  void OnFontFaceInitialized(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.
--- a/layout/style/test/test_font_loading_api.html
+++ b/layout/style/test/test_font_loading_api.html
@@ -187,44 +187,45 @@ function runTest() {
     // (TEST 5) Test that document.fonts.status starts off as loaded.
     is(document.fonts.status, "loaded", "initial value of document.fonts.status (TEST 5)");
 
     // (TEST 6) Test initial value of FontFace.status when a url() source is
     // used.
     is(new FontFace("test", "url(x)").status, "unloaded", "initial value of FontFace.status when a url() source is used (TEST 6)");
 
     // (TEST 7) Test initial value of FontFace.status when an invalid
-    // ArrayBuffer source is used.
-    is(new FontFace("test", new ArrayBuffer(0)).status, "unloaded", "initial value of FontFace.status when an invalid ArrayBuffer source is used (TEST 7)");
+    // ArrayBuffer source is used.  Because it has an implicit initial
+    // load() call, it should either be "loading" if the browser is
+    // asynchronously parsing the font data, or "error" if it parsed
+    // it immediately.
+    var status = new FontFace("test", new ArrayBuffer(0)).status;
+    ok(status == "loading" || status == "error", "initial value of FontFace.status when an invalid ArrayBuffer source is used (TEST 7)");
 
     // (TEST 8) Test initial value of FontFace.status when a valid ArrayBuffer
-    // source is used.
-    is(new FontFace("test", fontData).status, "unloaded", "initial value of FontFace.status when a valid ArrayBuffer source is used (TEST 8)");
+    // source is used.  Because it has an implicit initial load() call, it
+    // should either be "loading" if the browser is asynchronously parsing the
+    // font data, or "loaded" if it parsed it immediately.
+    status = new FontFace("test", fontData).status;
+    ok(status == "loading" || status == "loaded", "initial value of FontFace.status when a valid ArrayBuffer source is used (TEST 8)");
 
-    // (TEST 9) Test initial value of FontFace.loaded when an invalid url()
-    // source is used.
-    return is_pending(new FontFace("test", "").loaded, "initial value of FontFace.loaded when an invalid url() source is used", "(TEST 9)");
+    // (TEST 9) (old test became redundant with TEST 19)
 
   }).then(function() {
 
     // (TEST 10) Test initial value of FontFace.loaded when a valid url()
     // source is used.
     return is_pending(new FontFace("test", "url(x)").loaded, "initial value of FontFace.loaded when a valid url() source is used", "(TEST 10)");
 
   }).then(function() {
 
-    // (TEST 11) Test initial value of FontFace.loaded when an invalid
-    // ArrayBuffer source is used.
-    return is_pending(new FontFace("test", new ArrayBuffer(0)).loaded, "initial value of FontFace.loaded when an invalid ArrayBuffer source is used", "(TEST 11)");
+    // (TEST 11) (old test became redundant with TEST 21)
 
   }).then(function() {
 
-    // (TEST 12) Test initial value of FontFace.loaded when a valid ArrayBuffer
-    // source is used.
-    return is_pending(new FontFace("test", fontData).loaded, "initial value of FontFace.loaded when a valid ArrayBuffer source is used", "(TEST 12)");
+    // (TEST 12) (old test became redundant with TEST 20)
 
   }).then(function() {
 
     // (TEST 13) Test initial values of the descriptor attributes on FontFace
     // objects.
     var face = new FontFace("test", fontData);
     // XXX Spec issue: what values do the descriptor attributes have before the
     // constructor's dictionary argument is parsed?
@@ -324,32 +325,34 @@ function runTest() {
   }).then(function() {
 
     // (TEST 19) Test passing invalid url() source strings to the FontFace
     // constructor.
     var srcTests = Promise.resolve();
     gCSSFontFaceDescriptors.src.invalid_values.forEach(function(aSrc) {
       srcTests = srcTests.then(function() {
         var face = new FontFace("test", aSrc);
-        return face.load().then(function() {
+        is(face.status, "error", "FontFace.status should be \"error\" when constructed with an invalid url() src " + aSrc + " (TEST 19");
+        return face.loaded.then(function() {
           ok(false, "FontFace should not load with invalid url() src " + aSrc + " (TEST 19)");
         }, function(aError) {
-          is(aError.name, "SyntaxError", "FontFace.ready should have been rejected with a SyntaxError when loaded with an invalid url() src " + aSrc + " (TEST 19)");
+          is(aError.name, "SyntaxError", "FontFace.ready should have been rejected with a SyntaxError when constructed with an invalid url() src " + aSrc + " (TEST 19)");
         });
       });
     });
     return srcTests;
 
   }).then(function() {
 
     // (TEST 20) Test that the status of a FontFace constructed with a valid
     // ArrayBuffer source eventually becomes "loaded".
     var face = new FontFace("test", fontData);
-    return face.loaded.then(function() {
+    return face.loaded.then(function(aFace) {
       is(face.status, "loaded", "status of FontFace constructed with a valid ArrayBuffer source should eventually be \"loaded\" (TEST 20)");
+      is(face, aFace, "FontFace.loaded was resolved with the FontFace object once loaded (TEST 20)");
     }, function(aError) {
       ok(false, "FontFace constructed with a valid ArrayBuffer should eventually load (TEST 20)");
     });
 
   }).then(function() {
 
     // (TEST 21) Test that the status of a FontFace constructed with an invalid
     // ArrayBuffer source eventually becomes "error".
@@ -697,17 +700,17 @@ function runTest() {
       document.fonts.addEventListener("loadingdone", doneListener);
       document.fonts.onloadingdone = function(aEvent) {
         is(aEvent.fontfaces[0], face, "the CSSFontFaceLoadEvent should have a fontfaces array with the FontFace in it (TEST 30)");
         onloadingdoneTriggered = true;
         check();
       };
     });
 
-    face = new FontFace("test", "url(BitPattern.woff)");
+    face = new FontFace("test", "url(BitPattern.woff?test30)");
     face.load();
     is(face.status, "loading", "FontFace should have status \"loading\" (TEST 30)");
     document.fonts.add(face);
 
     return face.loaded
       .then(function() {
         is(face.status, "loaded", "FontFace should have status \"loaded\" (TEST 30)");
         return awaitEvents;
@@ -741,17 +744,17 @@ function runTest() {
       document.fonts.addEventListener("loadingdone", doneListener);
       document.fonts.onloadingdone = function(aEvent) {
         is(aEvent.fontfaces[0], face, "the CSSFontFaceLoadEvent should have a fontfaces array with the FontFace in it (TEST 31)");
         onloadingdoneTriggered = true;
         check();
       };
     });
 
-    face = new FontFace("test", "url(BitPattern.woff)");
+    face = new FontFace("test", "url(BitPattern.woff?test31)");
     is(face.status, "unloaded", "FontFace should have status \"unloaded\" (TEST 31)");
     document.fonts.add(face);
 
     return face.load()
         .then(function() {
           return awaitEvents;
         })
         .then(function() {
@@ -935,27 +938,27 @@ function runTest() {
       events.push(e);
     };
     document.fonts.onloadingerror = function(e) {
       events.push(e);
     };
 
     is(document.fonts.status, "loaded", "document.fonts.status should have status \"loaded\" (TEST 37)");
 
-    face = new FontFace("test", "url(BitPattern.woff)");
+    face = new FontFace("test", "url(BitPattern.woff?test37a)");
     face.load();
     document.fonts.add(face);
     is(document.fonts.status, "loading", "document.fonts.status should have status \"loading\" after first font added (TEST 37)");
 
     return document.fonts.ready
       .then(function() {
         is(document.fonts.status, "loaded", "document.fonts.status should have status \"loaded\" after first font loaded (TEST 37)");
         is(face.status, "loaded", "first FontFace should have status \"loaded\" (TEST 37)");
 
-        face2 = new FontFace("test2", "url(BitPattern.woff)");
+        face2 = new FontFace("test2", "url(BitPattern.woff?test37b)");
         face2.load();
         document.fonts.add(face2);
         is(document.fonts.status, "loading", "document.fonts.status should have status \"loading\" after second font added (TEST 37)");
 
         return document.fonts.ready;
       }).then(function() {
         is(document.fonts.status, "loaded", "document.fonts.status should have status \"loaded\" after second font loaded (TEST 37)");
         is(face2.status, "loaded", "second FontFace should have status \"loaded\" (TEST 37)");
@@ -1035,18 +1038,18 @@ function runTest() {
       });
 
   }).then(function() {
 
     // (TEST 39) Test that a FontFace for an @font-face rule only has one
     // loadingdone event dispatched at the FontFaceSet containing it.
 
     var style = document.querySelector("style");
-    var ruleText = "@font-face { font-family: test; src: url(BitPattern.woff); } " +
-                   "@font-face { font-family: test2; src: url(BitPattern.woff?2); }";
+    var ruleText = "@font-face { font-family: test; src: url(BitPattern.woff?test39a); } " +
+                   "@font-face { font-family: test2; src: url(BitPattern.woff?test39b); }";
 
     style.textContent = ruleText;
 
     var all = Array.from(document.fonts);
     var events = [];
 
     document.fonts.onloadingdone = function(e) {
       events.push(e);
@@ -1091,17 +1094,17 @@ function runTest() {
       });
 
   }).then(function() {
 
     // (TEST LAST) Test that a pending style sheet load prevents
     // document.fonts.status from being set to "loaded".
 
     // First, add a FontFace to document.fonts that will load soon.
-    var face = new FontFace("test", "url(BitPattern.woff)");
+    var face = new FontFace("test", "url(BitPattern.woff?testlast)");
     face.load();
     document.fonts.add(face);
 
     // Next, add a style sheet reference.
     var link = document.createElement("link");
     link.rel = "stylesheet";
     link.href = "neverending_stylesheet_load.sjs";
     link.type = "text/css";