Bug 1274046 - Add FailureId to gfxConfig (FeatureState). r=dvander draft
authorBenoit Girard <b56girard@gmail.com>
Fri, 03 Jun 2016 14:54:56 -0400
changeset 375264 908c5469bcf1211bcced8542835570836b8a7e8a
parent 372909 3435dd7ad71fe9003bdeee18fd38d815e033beef
child 375302 372d35e8633efa706d7c80f3f7e3fbc36250217d
child 375358 721f04a4199ad1a90707754822de42032e24bbba
child 375362 e9db586204c9cc3fa3b87a1a256113112d85057b
child 375364 a88aba41cfa57ead2b25f89523cbc79862eff477
child 375365 4cb44fb1496ae1b36741b8e014b51ae73dbfebc7
child 375399 a24e8889e3c85119284a6f2c4ab44997c0fb41c1
child 375496 c73d03ff7a2f9324eabf32532ea05da8eb2e145f
child 375522 f1a46561477ce81cdcc53e26cbee6e8342471621
push id20202
push userb56girard@gmail.com
push dateFri, 03 Jun 2016 18:55:09 +0000
reviewersdvander
bugs1274046
milestone49.0a1
Bug 1274046 - Add FailureId to gfxConfig (FeatureState). r=dvander MozReview-Commit-ID: Cig23OR7tWi
gfx/config/gfxConfig.cpp
gfx/config/gfxConfig.h
gfx/config/gfxFeature.cpp
gfx/config/gfxFeature.h
gfx/gl/GLLibraryEGL.cpp
gfx/thebes/gfxPlatform.cpp
gfx/thebes/gfxWindowsPlatform.cpp
gfx/thebes/gfxWindowsPlatform.h
--- a/gfx/config/gfxConfig.cpp
+++ b/gfx/config/gfxConfig.cpp
@@ -1,158 +1,162 @@
 /* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* vim: set sts=2 ts=8 sw=2 tw=99 et: */
 /* 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/. */
 #include "gfxConfig.h"
+#include "mozilla/UniquePtr.h"
 #include "plstr.h"
 
 namespace mozilla {
 namespace gfx {
 
-static gfxConfig sConfig;
+static UniquePtr<gfxConfig> sConfig;
 
 /* static */ FeatureState&
 gfxConfig::GetFeature(Feature aFeature)
 {
-  return sConfig.GetState(aFeature);
+  return sConfig->GetState(aFeature);
 }
 
 /* static */ bool
 gfxConfig::IsEnabled(Feature aFeature)
 {
-  const FeatureState& state = sConfig.GetState(aFeature);
+  const FeatureState& state = sConfig->GetState(aFeature);
   return state.IsEnabled();
 }
 
 /* static */ bool
 gfxConfig::IsDisabledByDefault(Feature aFeature)
 {
-  const FeatureState& state = sConfig.GetState(aFeature);
+  const FeatureState& state = sConfig->GetState(aFeature);
   return state.DisabledByDefault();
 }
 
 /* static */ bool
 gfxConfig::IsForcedOnByUser(Feature aFeature)
 {
-  const FeatureState& state = sConfig.GetState(aFeature);
+  const FeatureState& state = sConfig->GetState(aFeature);
   return state.IsForcedOnByUser();
 }
 
 /* static */ FeatureStatus
 gfxConfig::GetValue(Feature aFeature)
 {
-  const FeatureState& state = sConfig.GetState(aFeature);
+  const FeatureState& state = sConfig->GetState(aFeature);
   return state.GetValue();
 }
 
 /* static */ bool
 gfxConfig::SetDefault(Feature aFeature,
                       bool aEnable,
                       FeatureStatus aDisableStatus,
                       const char* aDisableMessage)
 {
-  FeatureState& state = sConfig.GetState(aFeature);
+  FeatureState& state = sConfig->GetState(aFeature);
   return state.SetDefault(aEnable, aDisableStatus, aDisableMessage);
 }
 
 /* static */ void
 gfxConfig::DisableByDefault(Feature aFeature,
                             FeatureStatus aDisableStatus,
-                            const char* aDisableMessage)
+                            const char* aDisableMessage,
+                            const nsACString& aFailureId)
 {
-  FeatureState& state = sConfig.GetState(aFeature);
-  state.DisableByDefault(aDisableStatus, aDisableMessage);
+  FeatureState& state = sConfig->GetState(aFeature);
+  state.DisableByDefault(aDisableStatus, aDisableMessage, aFailureId);
 }
 
 /* static */ void
 gfxConfig::EnableByDefault(Feature aFeature)
 {
-  FeatureState& state = sConfig.GetState(aFeature);
+  FeatureState& state = sConfig->GetState(aFeature);
   state.EnableByDefault();
 }
 
 /* static */ void
 gfxConfig::SetDefaultFromPref(Feature aFeature,
                               const char* aPrefName,
                               bool aIsEnablePref,
                               bool aDefaultValue)
 {
-  FeatureState& state = sConfig.GetState(aFeature);
+  FeatureState& state = sConfig->GetState(aFeature);
   return state.SetDefaultFromPref(aPrefName, aIsEnablePref, aDefaultValue);
 }
 
 /* static */ bool
 gfxConfig::InitOrUpdate(Feature aFeature,
                         bool aEnable,
                         FeatureStatus aDisableStatus,
                         const char* aDisableMessage)
 {
-  FeatureState& state = sConfig.GetState(aFeature);
+  FeatureState& state = sConfig->GetState(aFeature);
   return state.InitOrUpdate(aEnable, aDisableStatus, aDisableMessage);
 }
 
 /* static */ void
-gfxConfig::SetFailed(Feature aFeature, FeatureStatus aStatus, const char* aMessage)
+gfxConfig::SetFailed(Feature aFeature, FeatureStatus aStatus, const char* aMessage,
+                     const nsACString& aFailureId)
 {
-  FeatureState& state = sConfig.GetState(aFeature);
-  state.SetFailed(aStatus, aMessage);
+  FeatureState& state = sConfig->GetState(aFeature);
+  state.SetFailed(aStatus, aMessage, aFailureId);
 }
 
 /* static */ void
-gfxConfig::Disable(Feature aFeature, FeatureStatus aStatus, const char* aMessage)
+gfxConfig::Disable(Feature aFeature, FeatureStatus aStatus, const char* aMessage,
+                   const nsACString& aFailureId)
 {
-  FeatureState& state = sConfig.GetState(aFeature);
-  state.Disable(aStatus, aMessage);
+  FeatureState& state = sConfig->GetState(aFeature);
+  state.Disable(aStatus, aMessage, aFailureId);
 }
 
 /* static */ void
 gfxConfig::UserEnable(Feature aFeature, const char* aMessage)
 {
-  FeatureState& state = sConfig.GetState(aFeature);
+  FeatureState& state = sConfig->GetState(aFeature);
   state.UserEnable(aMessage);
 }
 
 /* static */ void
 gfxConfig::UserForceEnable(Feature aFeature, const char* aMessage)
 {
-  FeatureState& state = sConfig.GetState(aFeature);
+  FeatureState& state = sConfig->GetState(aFeature);
   state.UserForceEnable(aMessage);
 }
 
 /* static */ void
-gfxConfig::UserDisable(Feature aFeature, const char* aMessage)
+gfxConfig::UserDisable(Feature aFeature, const char* aMessage, const nsACString& aFailureId)
 {
-  FeatureState& state = sConfig.GetState(aFeature);
-  state.UserDisable(aMessage);
+  FeatureState& state = sConfig->GetState(aFeature);
+  state.UserDisable(aMessage, aFailureId);
 }
 
 /* static */ void
 gfxConfig::Reenable(Feature aFeature, Fallback aFallback)
 {
-  FeatureState& state = sConfig.GetState(aFeature);
+  FeatureState& state = sConfig->GetState(aFeature);
   MOZ_ASSERT(IsFeatureStatusFailure(state.GetValue()));
 
   const char* message = state.GetRuntimeMessage();
   EnableFallback(aFallback, message);
   state.SetRuntime(FeatureStatus::Available, nullptr);
 }
 
 /* static */ bool
 gfxConfig::UseFallback(Fallback aFallback)
 {
-  return sConfig.UseFallbackImpl(aFallback);
+  return sConfig->UseFallbackImpl(aFallback);
 }
 
 /* static */ void
 gfxConfig::EnableFallback(Fallback aFallback, const char* aMessage)
 {
   // Ignore aMessage for now.
-  sConfig.EnableFallbackImpl(aFallback, aMessage);
+  sConfig->EnableFallbackImpl(aFallback, aMessage);
 }
 
 bool
 gfxConfig::UseFallbackImpl(Fallback aFallback) const
 {
   return !!(mFallbackBits & (uint64_t(1) << uint64_t(aFallback)));
 }
 
@@ -202,22 +206,41 @@ static const char* sFallbackNames[] = {
   GFX_FALLBACK_MAP(FOR_EACH_FALLBACK)
 #undef FOR_EACH_FALLBACK
   nullptr
 };
 
 /* static  */ void
 gfxConfig::ForEachFallback(const FallbackIterCallback& aCallback)
 {
-  sConfig.ForEachFallbackImpl(aCallback);
+  sConfig->ForEachFallbackImpl(aCallback);
 }
 
 void
 gfxConfig::ForEachFallbackImpl(const FallbackIterCallback& aCallback)
 {
   for (size_t i = 0; i < mNumFallbackLogEntries; i++) {
     const FallbackLogEntry& entry = mFallbackLog[i];
     aCallback(sFallbackNames[size_t(entry.mFallback)], entry.mMessage);
   }
 }
 
+/* static */ const nsACString&
+gfxConfig::GetFailureId(Feature aFeature)
+{
+  const FeatureState& state = sConfig->GetState(aFeature);
+  return state.GetFailureId();
+}
+
+/* static */ void
+gfxConfig::Init()
+{
+  sConfig = mozilla::MakeUnique<gfxConfig>();
+}
+
+/* static */ void
+gfxConfig::Shutdown()
+{
+  sConfig = nullptr;
+}
+
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/config/gfxConfig.h
+++ b/gfx/config/gfxConfig.h
@@ -58,76 +58,82 @@ public:
 
   // Initialize the base value of a parameter. The return value is aEnable.
   static bool SetDefault(Feature aFeature,
                          bool aEnable,
                          FeatureStatus aDisableStatus,
                          const char* aDisableMessage);
   static void DisableByDefault(Feature aFeature,
                                FeatureStatus aDisableStatus,
-                               const char* aDisableMessage);
+                               const char* aDisableMessage,
+                               const nsACString& aFailureId = EmptyCString());
   static void EnableByDefault(Feature aFeature);
 
   // Set a environment status that overrides both the default and user
   // statuses; this should be used to disable features based on system
   // or hardware problems that can be determined up-front. The only
   // status that can override this decision is the user force-enabling
   // the feature.
   static void Disable(Feature aFeature,
                       FeatureStatus aStatus,
-                      const char* aMessage);
+                      const char* aMessage,
+                      const nsACString& aFailureId = EmptyCString());
 
   // Given a preference name, infer the default value and whether or not the
   // user has changed it. |aIsEnablePref| specifies whether or not the pref
   // is intended to enable a feature (true), or disable it (false).
   static void SetDefaultFromPref(Feature aFeature,
                                  const char* aPrefName,
                                  bool aIsEnablePref,
                                  bool aDefaultValue);
 
   // Disable a parameter based on a runtime decision. This permanently
   // disables the feature, since runtime decisions override all other
   // decisions.
   static void SetFailed(Feature aFeature,
                         FeatureStatus aStatus,
-                        const char* aMessage);
+                        const char* aMessage,
+                        const nsACString& aFailureId = EmptyCString());
 
   // Force a feature to be disabled permanently. This is the same as
   // SetFailed(), but the name may be clearer depending on the context.
   static void ForceDisable(Feature aFeature,
                            FeatureStatus aStatus,
-                           const char* aMessage)
+                           const char* aMessage,
+                           const nsACString& aFailureId = EmptyCString())
   {
-    SetFailed(aFeature, aStatus, aMessage);
+    SetFailed(aFeature, aStatus, aMessage, aFailureId);
   }
 
   // Convenience helpers for SetFailed().
   static bool MaybeSetFailed(Feature aFeature,
                              bool aEnable,
                              FeatureStatus aDisableStatus,
-                             const char* aDisableMessage)
+                             const char* aDisableMessage,
+                             const nsACString& aFailureId = EmptyCString())
   {
     if (!aEnable) {
-      SetFailed(aFeature, aDisableStatus, aDisableMessage);
+      SetFailed(aFeature, aDisableStatus, aDisableMessage, aFailureId);
       return false;
     }
     return true;
   }
 
   // Convenience helper for SetFailed().
   static bool MaybeSetFailed(Feature aFeature,
                              FeatureStatus aStatus,
-                             const char* aDisableMessage)
+                             const char* aDisableMessage,
+                             const nsACString& aFailureId = EmptyCString())
   {
     return MaybeSetFailed(
       aFeature,
       (aStatus != FeatureStatus::Available &&
        aStatus != FeatureStatus::ForceEnabled),
       aStatus,
-      aDisableMessage);
+      aDisableMessage, aFailureId);
   }
 
   // Re-enables a feature that was previously disabled, by attaching it to a
   // fallback. The fallback inherits the message that was used for disabling
   // the feature. This can be used, for example, when D3D11 fails at runtime
   // but we acquire a second, successful device with WARP.
   static void Reenable(Feature aFeature, Fallback aFallback);
 
@@ -139,17 +145,17 @@ public:
                            bool aEnable,
                            FeatureStatus aDisableStatus,
                            const char* aDisableMessage);
 
   // Set a user status that overrides the base value (but not runtime value)
   // of a parameter.
   static void UserEnable(Feature aFeature, const char* aMessage);
   static void UserForceEnable(Feature aFeature, const char* aMessage);
-  static void UserDisable(Feature aFeature, const char* aMessage);
+  static void UserDisable(Feature aFeature, const char* aMessage, const nsACString& aFailureId = EmptyCString());
 
   // Query whether a fallback has been toggled.
   static bool UseFallback(Fallback aFallback);
 
   // Enable a fallback.
   static void EnableFallback(Fallback aFallback, const char* aMessage);
 
   // Run a callback for each initialized FeatureState.
@@ -158,16 +164,22 @@ public:
                                  FeatureState& aFeature)> FeatureIterCallback;
   static void ForEachFeature(const FeatureIterCallback& aCallback);
 
   // Run a callback for each enabled fallback.
   typedef mozilla::function<void(const char* aName, const char* aMsg)> 
     FallbackIterCallback;
   static void ForEachFallback(const FallbackIterCallback& aCallback);
 
+  // Get the most descriptive failure id message for this feature.
+  static const nsACString& GetFailureId(Feature aFeature);
+
+  static void Init();
+  static void Shutdown();
+
 private:
   void ForEachFallbackImpl(const FallbackIterCallback& aCallback);
 
 private:
   FeatureState& GetState(Feature aFeature) {
     MOZ_ASSERT(size_t(aFeature) < kNumFeatures);
     return mFeatures[size_t(aFeature)];
   }
--- a/gfx/config/gfxFeature.cpp
+++ b/gfx/config/gfxFeature.cpp
@@ -38,17 +38,18 @@ FeatureState::GetValue() const
 }
 
 bool
 FeatureState::SetDefault(bool aEnable,
                          FeatureStatus aDisableStatus,
                          const char* aDisableMessage)
 {
   if (!aEnable) {
-    DisableByDefault(aDisableStatus, aDisableMessage);
+    DisableByDefault(aDisableStatus, aDisableMessage,
+                     NS_LITERAL_CSTRING("FEATURE_FAILURE_DISABLED"));
     return false;
   }
   EnableByDefault();
   return true;
 }
 
 void
 FeatureState::SetDefaultFromPref(const char* aPrefName,
@@ -62,30 +63,30 @@ FeatureState::SetDefaultFromPref(const c
     bool userValue = Preferences::GetBool(aPrefName, aDefaultValue);
     if (userValue == aIsEnablePref) {
       nsCString message("Enabled via ");
       message.AppendASCII(aPrefName);
       UserEnable(message.get());
     } else {
       nsCString message("Disabled via ");
       message.AppendASCII(aPrefName);
-      UserDisable(message.get());
+      UserDisable(message.get(), NS_LITERAL_CSTRING("FEATURE_FAILURE_PREF_OFF"));
     }
   }
 }
 
 bool
 FeatureState::InitOrUpdate(bool aEnable,
                            FeatureStatus aDisableStatus,
                            const char* aDisableMessage)
 {
   if (!IsInitialized()) {
     return SetDefault(aEnable, aDisableStatus, aDisableMessage);
   }
-  return MaybeSetFailed(aEnable, aDisableStatus, aDisableMessage);
+  return MaybeSetFailed(aEnable, aDisableStatus, aDisableMessage, nsCString());
 }
 
 void
 FeatureState::UserEnable(const char* aMessage)
 {
   AssertInitialized();
   SetUser(FeatureStatus::Available, aMessage);
 }
@@ -93,60 +94,68 @@ FeatureState::UserEnable(const char* aMe
 void
 FeatureState::UserForceEnable(const char* aMessage)
 {
   AssertInitialized();
   SetUser(FeatureStatus::ForceEnabled, aMessage);
 }
 
 void
-FeatureState::UserDisable(const char* aMessage)
+FeatureState::UserDisable(const char* aMessage, const nsACString& aFailureId)
 {
   AssertInitialized();
   SetUser(FeatureStatus::Disabled, aMessage);
+  SetFailureId(aFailureId);
 }
 
 void
-FeatureState::Disable(FeatureStatus aStatus, const char* aMessage)
+FeatureState::Disable(FeatureStatus aStatus, const char* aMessage,
+                      const nsACString& aFailureId)
 {
   AssertInitialized();
 
   // We should never bother setting an environment status to "enabled," since
   // it could override an explicit user decision to disable it.
   MOZ_ASSERT(IsFeatureStatusFailure(aStatus));
 
   SetEnvironment(aStatus, aMessage);
+  SetFailureId(aFailureId);
 }
 
 void
-FeatureState::SetFailed(FeatureStatus aStatus, const char* aMessage)
+FeatureState::SetFailed(FeatureStatus aStatus, const char* aMessage,
+                        const nsACString& aFailureId)
 {
   AssertInitialized();
 
   // We should never bother setting a runtime status to "enabled," since it could
   // override an explicit user decision to disable it.
   MOZ_ASSERT(IsFeatureStatusFailure(aStatus));
 
   SetRuntime(aStatus, aMessage);
+  SetFailureId(aFailureId);
 }
 
 bool
-FeatureState::MaybeSetFailed(bool aEnable, FeatureStatus aStatus, const char* aMessage)
+FeatureState::MaybeSetFailed(bool aEnable, FeatureStatus aStatus, const char* aMessage,
+                             const nsACString& aFailureId)
 {
   if (!aEnable) {
-    SetFailed(aStatus, aMessage);
+    SetFailed(aStatus, aMessage, aFailureId);
     return false;
   }
   return true;
 }
 
 bool
-FeatureState::MaybeSetFailed(FeatureStatus aStatus, const char* aMessage)
+FeatureState::MaybeSetFailed(FeatureStatus aStatus, const char* aMessage,
+                             const nsACString& aFailureId)
 {
-  return MaybeSetFailed(IsFeatureStatusSuccess(aStatus), aStatus, aMessage);
+  return MaybeSetFailed(IsFeatureStatusSuccess(aStatus), aStatus, aMessage,
+                        aFailureId);
 }
 
 bool
 FeatureState::DisabledByDefault() const
 {
   AssertInitialized();
   return mDefault.mStatus != FeatureStatus::Available;
 }
@@ -165,24 +174,26 @@ FeatureState::EnableByDefault()
   MOZ_ASSERT(!mUser.IsInitialized());
   MOZ_ASSERT(!mEnvironment.IsInitialized());
   MOZ_ASSERT(!mRuntime.IsInitialized());
 
   mDefault.Set(FeatureStatus::Available);
 }
 
 void
-FeatureState::DisableByDefault(FeatureStatus aStatus, const char* aMessage)
+FeatureState::DisableByDefault(FeatureStatus aStatus, const char* aMessage,
+                               const nsACString& aFailureId)
 {
   // User/runtime decisions should not have been made yet.
   MOZ_ASSERT(!mUser.IsInitialized());
   MOZ_ASSERT(!mEnvironment.IsInitialized());
   MOZ_ASSERT(!mRuntime.IsInitialized());
 
   mDefault.Set(aStatus, aMessage);
+  SetFailureId(aFailureId);
 }
 
 void
 FeatureState::SetUser(FeatureStatus aStatus, const char* aMessage)
 {
   // Default decision must have been made, but not runtime or environment.
   MOZ_ASSERT(mDefault.IsInitialized());
   MOZ_ASSERT(!mEnvironment.IsInitialized());
@@ -229,16 +240,31 @@ FeatureState::ForEachStatusChange(const 
     aCallback("env", mEnvironment.mStatus, mEnvironment.Message());
   }
   if (mRuntime.IsInitialized()) {
     aCallback("runtime", mRuntime.mStatus, mRuntime.Message());
   }
 }
 
 void
+FeatureState::SetFailureId(const nsACString& aFailureId)
+{
+  if (mFailureId.IsEmpty()) {
+    mFailureId = aFailureId;
+  }
+}
+
+const nsACString&
+FeatureState::GetFailureId() const
+{
+  MOZ_ASSERT(!IsEnabled());
+  return mFailureId;
+}
+
+void
 FeatureState::Instance::Set(FeatureStatus aStatus, const char* aMessage /* = nullptr */)
 {
   mStatus = aStatus;
   if (aMessage) {
     PR_snprintf(mMessage, sizeof(mMessage), "%s", aMessage);
   }
 }
 
--- a/gfx/config/gfxFeature.h
+++ b/gfx/config/gfxFeature.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #ifndef mozilla_gfx_config_gfxFeature_h
 #define mozilla_gfx_config_gfxFeature_h
 
 #include <stdint.h>
 #include "gfxTelemetry.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Function.h"
+#include "nsString.h"
 
 namespace mozilla {
 namespace gfx {
 
 #define GFX_FEATURE_MAP(_)                                                        \
   /* Name,                        Type,         Description */                    \
   _(HW_COMPOSITING,               Feature,      "Compositing")                    \
   _(D3D11_COMPOSITING,            Feature,      "Direct3D11 Compositing")         \
@@ -34,58 +35,62 @@ class FeatureState
 {
   friend class gfxConfig;
 
  public:
   bool IsEnabled() const;
   FeatureStatus GetValue() const;
 
   void EnableByDefault();
-  void DisableByDefault(FeatureStatus aStatus, const char* aMessage);
+  void DisableByDefault(FeatureStatus aStatus, const char* aMessage, const nsACString& aFailureId);
   bool SetDefault(bool aEnable, FeatureStatus aDisableStatus, const char* aDisableMessage);
   bool InitOrUpdate(bool aEnable,
                     FeatureStatus aDisableStatus,
                     const char* aMessage);
   void SetDefaultFromPref(const char* aPrefName,
                           bool aIsEnablePref,
                           bool aDefaultValue);
   void UserEnable(const char* aMessage);
   void UserForceEnable(const char* aMessage);
-  void UserDisable(const char* aMessage);
-  void Disable(FeatureStatus aStatus, const char* aMessage);
-  void ForceDisable(FeatureStatus aStatus, const char* aMessage) {
-    SetFailed(aStatus, aMessage);
+  void UserDisable(const char* aMessage, const nsACString& aFailureId);
+  void Disable(FeatureStatus aStatus, const char* aMessage, const nsACString& aFailureId);
+  void ForceDisable(FeatureStatus aStatus, const char* aMessage, const nsACString& aFailureId) {
+    SetFailed(aStatus, aMessage, aFailureId);
   }
-  void SetFailed(FeatureStatus aStatus, const char* aMessage);
-  bool MaybeSetFailed(bool aEnable, FeatureStatus aStatus, const char* aMessage);
-  bool MaybeSetFailed(FeatureStatus aStatus, const char* aMessage);
+  void SetFailed(FeatureStatus aStatus, const char* aMessage, const nsACString& aFailureId);
+  bool MaybeSetFailed(bool aEnable, FeatureStatus aStatus, const char* aMessage, const nsACString& aFailureId);
+  bool MaybeSetFailed(FeatureStatus aStatus, const char* aMessage, const nsACString& aFailureId);
 
   // aType is "base", "user", "env", or "runtime".
   // aMessage may be null.
   typedef mozilla::function<void(const char* aType,
                                  FeatureStatus aStatus,
                                  const char* aMessage)> StatusIterCallback;
   void ForEachStatusChange(const StatusIterCallback& aCallback) const;
 
+  const nsACString& GetFailureId() const;
+
  private:
   void SetUser(FeatureStatus aStatus, const char* aMessage);
   void SetEnvironment(FeatureStatus aStatus, const char* aMessage);
   void SetRuntime(FeatureStatus aStatus, const char* aMessage);
   bool IsForcedOnByUser() const;
   bool DisabledByDefault() const;
   const char* GetRuntimeMessage() const;
   bool IsInitialized() const {
     return mDefault.IsInitialized();
   }
 
   void AssertInitialized() const {
     MOZ_ASSERT(IsInitialized());
   }
 
  private:
+  void SetFailureId(const nsACString& aFailureId);
+
   struct Instance {
     char mMessage[64];
     FeatureStatus mStatus;
 
     void Set(FeatureStatus aStatus, const char* aMessage = nullptr);
     bool IsInitialized() const {
       return mStatus != FeatureStatus::Unused;
     }
@@ -106,14 +111,18 @@ class FeatureState
   // The environment state factors in any additional decisions made, such as
   // availability or blacklisting.
   //
   // The runtime state factors in any problems discovered at runtime.
   Instance mDefault;
   Instance mUser;
   Instance mEnvironment;
   Instance mRuntime;
+
+  // Store the first reported failureId for now but we might want to track this
+  // by instance later if we need a specific breakdown.
+  nsCString mFailureId;
 };
 
 } // namespace gfx
 } // namespace mozilla
 
 #endif // mozilla_gfx_config_gfxFeature_h
--- a/gfx/gl/GLLibraryEGL.cpp
+++ b/gfx/gl/GLLibraryEGL.cpp
@@ -168,17 +168,18 @@ GetAndInitDisplay(GLLibraryEGL& egl, voi
 static EGLDisplay
 GetAndInitDisplayForAccelANGLE(GLLibraryEGL& egl)
 {
     EGLDisplay ret = 0;
 
     FeatureState& d3d11ANGLE = gfxConfig::GetFeature(Feature::D3D11_HW_ANGLE);
 
     if (!gfxPrefs::WebGLANGLETryD3D11())
-        d3d11ANGLE.UserDisable("User disabled D3D11 ANGLE by pref");
+        d3d11ANGLE.UserDisable("User disabled D3D11 ANGLE by pref",
+                               NS_LITERAL_CSTRING("FAILURE_ID_ANGLE_PREF"));
 
     if (gfxPrefs::WebGLANGLEForceD3D11())
         d3d11ANGLE.UserForceEnable("User force-enabled D3D11 ANGLE on disabled hardware");
 
     if (gfxConfig::IsForcedOnByUser(Feature::D3D11_HW_ANGLE))
         return GetAndInitDisplay(egl, LOCAL_EGL_D3D11_ONLY_DISPLAY_ANGLE);
 
     if (d3d11ANGLE.IsEnabled()) {
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -587,16 +587,18 @@ gfxPlatform::Init()
         NS_RUNTIMEABORT("Already started???");
     }
     gEverInitialized = true;
 
     // Initialize the preferences by creating the singleton.
     gfxPrefs::GetSingleton();
     MediaPrefs::GetSingleton();
 
+    gfxConfig::Init();
+
     GPUProcessManager::Initialize();
 
     auto fwd = new CrashStatsLogForwarder("GraphicsCriticalError");
     fwd->SetCircularBufferSize(gfxPrefs::GfxLoggingCrashLength());
 
     // Drop a note in the crash report if we end up forcing an option that could
     // destabilize things.  New items should be appended at the end (of an existing
     // or in a new section), so that we don't have to know the version to interpret
@@ -852,16 +854,18 @@ gfxPlatform::Shutdown()
 
     gfx::Factory::ShutDown();
 
     delete gGfxPlatformPrefsLock;
 
     gfxPrefs::DestroySingleton();
     gfxFont::DestroySingletons();
 
+    gfxConfig::Shutdown();
+
     delete gPlatform;
     gPlatform = nullptr;
 }
 
 /* static */ void
 gfxPlatform::InitLayersIPC()
 {
     if (sLayersIPCIsUp) {
@@ -2122,34 +2126,37 @@ gfxPlatform::InitCompositorAccelerationP
   FeatureState& feature = gfxConfig::GetFeature(Feature::HW_COMPOSITING);
 
   // Base value - does the platform allow acceleration?
   if (feature.SetDefault(AccelerateLayersByDefault(),
                          FeatureStatus::Blocked,
                          "Acceleration blocked by platform"))
   {
     if (gfxPrefs::LayersAccelerationDisabledDoNotUseDirectly()) {
-      feature.UserDisable("Disabled by pref");
+      feature.UserDisable("Disabled by pref",
+                          NS_LITERAL_CSTRING("FEATURE_FAILURE_COMP_PREF"));
     } else if (acceleratedEnv && *acceleratedEnv == '0') {
-      feature.UserDisable("Disabled by envvar");
+      feature.UserDisable("Disabled by envvar",
+                          NS_LITERAL_CSTRING("FEATURE_FAILURE_COMP_ENV"));
     }
   } else {
     if (acceleratedEnv && *acceleratedEnv == '1') {
       feature.UserEnable("Enabled by envvar");
     }
   }
 
   // This has specific meaning elsewhere, so we always record it.
   if (gfxPrefs::LayersAccelerationForceEnabledDoNotUseDirectly()) {
     feature.UserForceEnable("Force-enabled by pref");
   }
 
   // Safe mode trumps everything.
   if (InSafeMode()) {
-    feature.ForceDisable(FeatureStatus::Blocked, "Acceleration blocked by safe-mode");
+    feature.ForceDisable(FeatureStatus::Blocked, "Acceleration blocked by safe-mode",
+                         NS_LITERAL_CSTRING("FEATURE_FAILURE_COMP_SAFEMODE"));
   }
 }
 
 bool
 gfxPlatform::CanUseHardwareVideoDecoding()
 {
   // this function is called from the compositor thread, so it is not
   // safe to init the prefs etc. from here.
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -555,17 +555,18 @@ gfxWindowsPlatform::CreatePlatformFontLi
         pfl = new gfxDWriteFontList();
         if (NS_SUCCEEDED(pfl->InitFontList())) {
             return pfl;
         }
         // DWrite font initialization failed! Don't know why this would happen,
         // but apparently it can - see bug 594865.
         // So we're going to fall back to GDI fonts & rendering.
         gfxPlatformFontList::Shutdown();
-        DisableD2D(FeatureStatus::Failed, "Failed to initialize fonts");
+        DisableD2D(FeatureStatus::Failed, "Failed to initialize fonts",
+                   NS_LITERAL_CSTRING("FEATURE_FAILURE_FONT_FAIL"));
     }
 
     pfl = new gfxGDIFontList();
 
     if (NS_SUCCEEDED(pfl->InitFontList())) {
         return pfl;
     }
 
@@ -575,19 +576,20 @@ gfxWindowsPlatform::CreatePlatformFontLi
 
 // This function will permanently disable D2D for the session. It's intended to
 // be used when, after initially chosing to use Direct2D, we encounter a
 // scenario we can't support.
 //
 // This is called during gfxPlatform::Init() so at this point there should be no
 // DrawTargetD2D/1 instances.
 void
-gfxWindowsPlatform::DisableD2D(FeatureStatus aStatus, const char* aMessage)
+gfxWindowsPlatform::DisableD2D(FeatureStatus aStatus, const char* aMessage,
+                               const nsACString& aFailureId)
 {
-  gfxConfig::SetFailed(Feature::DIRECT2D, aStatus, aMessage);
+  gfxConfig::SetFailed(Feature::DIRECT2D, aStatus, aMessage, aFailureId);
   Factory::SetDirect3D11Device(nullptr);
   UpdateBackendPrefs();
 }
 
 already_AddRefed<gfxASurface>
 gfxWindowsPlatform::CreateOffscreenSurface(const IntSize& aSize,
                                            gfxImageFormat aFormat)
 {
@@ -1897,30 +1899,29 @@ bool DoesD3D11TextureSharingWork(ID3D11D
 }
 
 bool DoesD3D11AlphaTextureSharingWork(ID3D11Device *device)
 {
   return DoesD3D11TextureSharingWorkInternal(device, DXGI_FORMAT_R8_UNORM, D3D11_BIND_SHADER_RESOURCE);
 }
 
 static inline bool
-IsGfxInfoStatusOkay(int32_t aFeature, nsCString* aOutMessage)
+IsGfxInfoStatusOkay(int32_t aFeature, nsCString* aOutMessage, nsCString& aFailureId)
 {
   nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
   if (!gfxInfo) {
     return true;
   }
 
   int32_t status;
-  nsCString failureId;
-  if (SUCCEEDED(gfxInfo->GetFeatureStatus(aFeature, failureId, &status)) &&
+  if (SUCCEEDED(gfxInfo->GetFeatureStatus(aFeature, aFailureId, &status)) &&
       status != nsIGfxInfo::FEATURE_STATUS_OK)
   {
     aOutMessage->AssignLiteral("#BLOCKLIST_");
-    aOutMessage->AppendASCII(failureId.get());
+    aOutMessage->AppendASCII(aFailureId.get());
     return false;
   }
 
   return true;
 }
 
 static inline bool
 IsWARPStable()
@@ -1933,25 +1934,28 @@ IsWARPStable()
 }
 
 static void
 InitializeANGLEConfig()
 {
   FeatureState& d3d11ANGLE = gfxConfig::GetFeature(Feature::D3D11_HW_ANGLE);
 
   if (!gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
-    d3d11ANGLE.DisableByDefault(FeatureStatus::Unavailable, "D3D11 compositing is disabled");
+    d3d11ANGLE.DisableByDefault(FeatureStatus::Unavailable, "D3D11 compositing is disabled",
+                                NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_DISABLED"));
     return;
   }
 
   d3d11ANGLE.EnableByDefault();
 
   nsCString message;
-  if (!IsGfxInfoStatusOkay(nsIGfxInfo::FEATURE_DIRECT3D_11_ANGLE, &message)) {
-    d3d11ANGLE.Disable(FeatureStatus::Blacklisted, message.get());
+  nsCString failureId;
+  if (!IsGfxInfoStatusOkay(nsIGfxInfo::FEATURE_DIRECT3D_11_ANGLE, &message,
+                           failureId)) {
+    d3d11ANGLE.Disable(FeatureStatus::Blacklisted, message.get(), failureId);
   }
 
 }
 
 void
 gfxWindowsPlatform::InitializeConfig()
 {
   if (!XRE_IsParentProcess()) {
@@ -1968,17 +1972,18 @@ gfxWindowsPlatform::InitializeConfig()
 void
 gfxWindowsPlatform::InitializeD3D9Config()
 {
   MOZ_ASSERT(XRE_IsParentProcess());
 
   FeatureState& d3d9 = gfxConfig::GetFeature(Feature::D3D9_COMPOSITING);
 
   if (!gfxConfig::IsEnabled(Feature::HW_COMPOSITING)) {
-    d3d9.DisableByDefault(FeatureStatus::Unavailable, "Hardware compositing is disabled");
+    d3d9.DisableByDefault(FeatureStatus::Unavailable, "Hardware compositing is disabled",
+                          NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D9_NEED_HWCOMP"));
     return;
   }
 
   if (!IsVistaOrLater()) {
     d3d9.EnableByDefault();
   } else {
     d3d9.SetDefaultFromPref(
       gfxPrefs::GetLayersAllowD3D9FallbackPrefName(),
@@ -1986,53 +1991,59 @@ gfxWindowsPlatform::InitializeD3D9Config
       gfxPrefs::GetLayersAllowD3D9FallbackPrefDefault());
 
     if (!d3d9.IsEnabled() && gfxPrefs::LayersPreferD3D9()) {
       d3d9.UserEnable("Direct3D9 enabled via layers.prefer-d3d9");
     }
   }
 
   nsCString message;
-  if (!IsGfxInfoStatusOkay(nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS, &message)) {
-    d3d9.Disable(FeatureStatus::Blacklisted, message.get());
+  nsCString failureId;
+  if (!IsGfxInfoStatusOkay(nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS, &message,
+                           failureId)) {
+    d3d9.Disable(FeatureStatus::Blacklisted, message.get(), failureId);
   }
 
   if (gfxConfig::IsForcedOnByUser(Feature::HW_COMPOSITING)) {
     d3d9.UserForceEnable("Hardware compositing is force-enabled");
   }
 }
 
 void
 gfxWindowsPlatform::InitializeD3D11Config()
 {
   MOZ_ASSERT(XRE_IsParentProcess());
 
   FeatureState& d3d11 = gfxConfig::GetFeature(Feature::D3D11_COMPOSITING);
 
   if (!gfxConfig::IsEnabled(Feature::HW_COMPOSITING)) {
-    d3d11.DisableByDefault(FeatureStatus::Unavailable, "Hardware compositing is disabled");
+    d3d11.DisableByDefault(FeatureStatus::Unavailable, "Hardware compositing is disabled",
+                           NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_NEED_HWCOMP"));
     return;
   }
 
   d3d11.EnableByDefault();
 
   // If the user prefers D3D9, act as though they disabled D3D11.
   if (gfxPrefs::LayersPreferD3D9()) {
-    d3d11.UserDisable("Disabled due to user preference for Direct3D 9");
+    d3d11.UserDisable("Disabled due to user preference for Direct3D 9",
+                      NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_PREF"));
     return;
   }
 
   nsCString message;
-  if (!IsGfxInfoStatusOkay(nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS, &message)) {
+  nsCString failureId;
+  if (!IsGfxInfoStatusOkay(nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS, &message, failureId)) {
     if (IsWARPStable() && !gfxPrefs::LayersD3D11DisableWARP()) {
       // We do not expect hardware D3D11 to work, so we'll try WARP.
       gfxConfig::EnableFallback(Fallback::USE_D3D11_WARP_COMPOSITOR, message.get());
     } else {
       // There is little to no chance of D3D11 working, so just disable it.
-      d3d11.Disable(FeatureStatus::Blacklisted, message.get());
+      d3d11.Disable(FeatureStatus::Blacklisted, message.get(),
+                    failureId);
     }
   }
 
   // Check if the user really, really wants WARP.
   if (gfxPrefs::LayersD3D11ForceWARP()) {
     // Force D3D11 on even if we disabled it.
     d3d11.UserForceEnable("User force-enabled WARP on disabled hardware");
 
@@ -2095,35 +2106,39 @@ gfxWindowsPlatform::AttemptD3D11DeviceCr
   return true;
 }
 
 void
 gfxWindowsPlatform::AttemptD3D11DeviceCreation(FeatureState& d3d11)
 {
   RefPtr<IDXGIAdapter1> adapter = GetDXGIAdapter();
   if (!adapter) {
-    d3d11.SetFailed(FeatureStatus::Unavailable, "Failed to acquire a DXGI adapter");
+    d3d11.SetFailed(FeatureStatus::Unavailable, "Failed to acquire a DXGI adapter",
+                    NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_DXGI"));
     return;
   }
 
   HRESULT hr;
   RefPtr<ID3D11Device> device;
   if (!AttemptD3D11DeviceCreationHelper(adapter, device, hr)) {
     gfxCriticalError() << "Crash during D3D11 device creation";
-    d3d11.SetFailed(FeatureStatus::CrashedInHandler, "Crashed trying to acquire a D3D11 device");
+    d3d11.SetFailed(FeatureStatus::CrashedInHandler, "Crashed trying to acquire a D3D11 device",
+                    NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_DEVICE1"));
     return;
   }
 
   if (FAILED(hr) || !device) {
     gfxCriticalError() << "D3D11 device creation failed: " << hexa(hr);
-    d3d11.SetFailed(FeatureStatus::Failed, "Failed to acquire a D3D11 device");
+    d3d11.SetFailed(FeatureStatus::Failed, "Failed to acquire a D3D11 device",
+                    NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_DEVICE2"));
     return;
   }
   if (!DoesD3D11DeviceWork()) {
-    d3d11.SetFailed(FeatureStatus::Broken, "Direct3D11 device was determined to be broken");
+    d3d11.SetFailed(FeatureStatus::Broken, "Direct3D11 device was determined to be broken",
+                    NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_BROKEN"));
     return;
   }
 
   {
     MutexAutoLock lock(mDeviceLock);
     mD3D11Device = device;
   }
 
@@ -2175,24 +2190,26 @@ gfxWindowsPlatform::AttemptWARPDeviceCre
 {
   ScopedGfxFeatureReporter reporterWARP("D3D11-WARP", gfxPrefs::LayersD3D11ForceWARP());
   FeatureState& d3d11 = gfxConfig::GetFeature(Feature::D3D11_COMPOSITING);
 
   HRESULT hr;
   RefPtr<ID3D11Device> device;
   if (!AttemptWARPDeviceCreationHelper(reporterWARP, device, hr)) {
     gfxCriticalError() << "Exception occurred initializing WARP D3D11 device!";
-    d3d11.SetFailed(FeatureStatus::CrashedInHandler, "Crashed creating a D3D11 WARP device");
+    d3d11.SetFailed(FeatureStatus::CrashedInHandler, "Crashed creating a D3D11 WARP device",
+                     NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_WARP_DEVICE"));
     return;
   }
 
   if (FAILED(hr) || !device) {
     // This should always succeed... in theory.
     gfxCriticalError() << "Failed to initialize WARP D3D11 device! " << hexa(hr);
-    d3d11.SetFailed(FeatureStatus::Failed, "Failed to create a D3D11 WARP device");
+    d3d11.SetFailed(FeatureStatus::Failed, "Failed to create a D3D11 WARP device",
+                    NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_WARP_DEVICE2"));
     return;
   }
 
   {
     MutexAutoLock lock(mDeviceLock);
     mD3D11Device = device;
   }
 
@@ -2429,23 +2446,25 @@ gfxWindowsPlatform::InitializeD3D11()
   }
 
   // Check if D3D11 is available on this system.
   nsModuleHandle d3d11Module(LoadLibrarySystem32(L"d3d11.dll"));
   sD3D11CreateDeviceFn =
     (decltype(D3D11CreateDevice)*)GetProcAddress(d3d11Module, "D3D11CreateDevice");
   if (!sD3D11CreateDeviceFn) {
     // We should just be on Windows Vista or XP in this case.
-    d3d11.SetFailed(FeatureStatus::Unavailable, "Direct3D11 not available on this computer");
+    d3d11.SetFailed(FeatureStatus::Unavailable, "Direct3D11 not available on this computer",
+                    NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_LIB"));
     return;
   }
 
   // Check if a failure was injected for testing.
   if (gfxPrefs::DeviceFailForTesting()) {
-    d3d11.SetFailed(FeatureStatus::Failed, "Direct3D11 device failure simulated by preference");
+    d3d11.SetFailed(FeatureStatus::Failed, "Direct3D11 device failure simulated by preference",
+                    NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_SIM"));
     return;
   }
 
   if (XRE_IsParentProcess()) {
     if (!gfxConfig::UseFallback(Fallback::USE_D3D11_WARP_COMPOSITOR)) {
       AttemptD3D11DeviceCreation(d3d11);
       if (d3d11.GetValue() == FeatureStatus::CrashedInHandler) {
         return;
@@ -2504,17 +2523,18 @@ gfxWindowsPlatform::InitializeD3D11()
   d3d11Module.disown();
 }
 
 void
 gfxWindowsPlatform::DisableD3D11AfterCrash()
 {
   gfxConfig::Disable(Feature::D3D11_COMPOSITING,
     FeatureStatus::CrashedInHandler,
-    "Crashed while acquiring a Direct3D11 device");
+    "Crashed while acquiring a Direct3D11 device",
+    NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_CRASH"));
   ResetD3D11Devices();
 }
 
 void
 gfxWindowsPlatform::ResetD3D11Devices()
 {
   MutexAutoLock lock(mDeviceLock);
 
@@ -2525,32 +2545,35 @@ gfxWindowsPlatform::ResetD3D11Devices()
 }
 
 void
 gfxWindowsPlatform::InitializeD2DConfig()
 {
   FeatureState& d2d1 = gfxConfig::GetFeature(Feature::DIRECT2D);
 
   if (!gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
-    d2d1.DisableByDefault(FeatureStatus::Unavailable, "Direct2D requires Direct3D 11 compositing");
+    d2d1.DisableByDefault(FeatureStatus::Unavailable, "Direct2D requires Direct3D 11 compositing",
+                          NS_LITERAL_CSTRING("FEATURE_FAILURE_D2D_D3D11_COMP"));
     return;
   }
   if (!IsVistaOrLater()) {
-    d2d1.DisableByDefault(FeatureStatus::Unavailable, "Direct2D is not available on Windows XP");
+    d2d1.DisableByDefault(FeatureStatus::Unavailable, "Direct2D is not available on Windows XP",
+                          NS_LITERAL_CSTRING("FEATURE_FAILURE_D2D_XP"));
     return;
   }
 
   d2d1.SetDefaultFromPref(
     gfxPrefs::GetDirect2DDisabledPrefName(),
     false,
     gfxPrefs::GetDirect2DDisabledPrefDefault());
 
   nsCString message;
-  if (!IsGfxInfoStatusOkay(nsIGfxInfo::FEATURE_DIRECT2D, &message)) {
-    d2d1.Disable(FeatureStatus::Blacklisted, message.get());
+  nsCString failureId;
+  if (!IsGfxInfoStatusOkay(nsIGfxInfo::FEATURE_DIRECT2D, &message, failureId)) {
+    d2d1.Disable(FeatureStatus::Blacklisted, message.get(), failureId);
   }
 
   if (!d2d1.IsEnabled() && gfxPrefs::Direct2DForceEnabled()) {
     d2d1.UserForceEnable("Force-enabled via user-preference");
   }
 }
 
 void
@@ -2558,48 +2581,54 @@ gfxWindowsPlatform::InitializeD2D()
 {
   ScopedGfxFeatureReporter d2d1_1("D2D1.1");
 
   FeatureState& d2d1 = gfxConfig::GetFeature(Feature::DIRECT2D);
 
   // We don't know this value ahead of time, but the user can force-override
   // it, so we use Disable instead of SetFailed.
   if (mIsWARP) {
-    d2d1.Disable(FeatureStatus::Blocked, "Direct2D is not compatible with Direct3D11 WARP");
+    d2d1.Disable(FeatureStatus::Blocked, "Direct2D is not compatible with Direct3D11 WARP",
+                 NS_LITERAL_CSTRING("FEATURE_FAILURE_D2D_WARP_BLOCK"));
   }
 
   // If we pass all the initial checks, we can proceed to runtime decisions.
   if (!d2d1.IsEnabled()) {
     return;
   }
 
   if (!Factory::SupportsD2D1()) {
-    d2d1.SetFailed(FeatureStatus::Unavailable, "Failed to acquire a Direct2D 1.1 factory");
+    d2d1.SetFailed(FeatureStatus::Unavailable, "Failed to acquire a Direct2D 1.1 factory",
+                   NS_LITERAL_CSTRING("FEATURE_FAILURE_D2D_FACTORY"));
     return;
   }
 
   if (!mD3D11ContentDevice) {
-    d2d1.SetFailed(FeatureStatus::Failed, "Failed to acquire a Direct3D 11 content device");
+    d2d1.SetFailed(FeatureStatus::Failed, "Failed to acquire a Direct3D 11 content device",
+                   NS_LITERAL_CSTRING("FEATURE_FAILURE_D2D_DEVICE"));
     return;
   }
 
   if (!mCompositorD3D11TextureSharingWorks) {
-    d2d1.SetFailed(FeatureStatus::Failed, "Direct3D11 device does not support texture sharing");
+    d2d1.SetFailed(FeatureStatus::Failed, "Direct3D11 device does not support texture sharing",
+                   NS_LITERAL_CSTRING("FEATURE_FAILURE_D2D_TXT_SHARING"));
     return;
   }
 
   // Using Direct2D depends on DWrite support.
   if (!mDWriteFactory && !InitDWriteSupport()) {
-    d2d1.SetFailed(FeatureStatus::Failed, "Failed to initialize DirectWrite support");
+    d2d1.SetFailed(FeatureStatus::Failed, "Failed to initialize DirectWrite support",
+                   NS_LITERAL_CSTRING("FEATURE_FAILURE_D2D_DWRITE"));
     return;
   }
 
   // Verify that Direct2D device creation succeeded.
   if (!Factory::SetDirect3D11Device(mD3D11ContentDevice)) {
-    d2d1.SetFailed(FeatureStatus::Failed, "Failed to create a Direct2D device");
+    d2d1.SetFailed(FeatureStatus::Failed, "Failed to create a Direct2D device",
+                   NS_LITERAL_CSTRING("FEATURE_FAILURE_D2D_CREATE_FAILED"));
     return;
   }
 
   MOZ_ASSERT(d2d1.IsEnabled());
   d2d1_1.SetSuccessful();
 }
 
 bool
--- a/gfx/thebes/gfxWindowsPlatform.h
+++ b/gfx/thebes/gfxWindowsPlatform.h
@@ -279,17 +279,18 @@ private:
     void Init();
     void InitAcceleration() override;
 
     void InitializeDevices();
     void InitializeD3D11();
     void InitializeD2D();
     bool InitDWriteSupport();
 
-    void DisableD2D(mozilla::gfx::FeatureStatus aStatus, const char* aMessage);
+    void DisableD2D(mozilla::gfx::FeatureStatus aStatus, const char* aMessage,
+                    const nsACString& aFailureId);
 
     void InitializeConfig();
     void InitializeD3D9Config();
     void InitializeD3D11Config();
     void InitializeD2DConfig();
 
     void AttemptD3D11DeviceCreation(mozilla::gfx::FeatureState& d3d11);
     bool AttemptD3D11DeviceCreationHelper(