Add gfxConfig to nsIGfxInfo, for about:support access. (bug 1254899 part 11, r=jrmuizel)
authorDavid Anderson <danderson@mozilla.com>
Thu, 28 Apr 2016 21:52:56 -0700
changeset 334412 c1ca5d9a644ce527f4868a7dce2dfb458a871c6f
parent 334411 f4b8910028b3e5a33ead7e204c981616e67f132c
child 334413 32295f6fbc6aacc5100f6dcfe7527614facd8cf8
push id6249
push userjlund@mozilla.com
push dateMon, 01 Aug 2016 13:59:36 +0000
treeherdermozilla-beta@bad9d4f5bf7e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel
bugs1254899
milestone49.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
Add gfxConfig to nsIGfxInfo, for about:support access. (bug 1254899 part 11, r=jrmuizel)
gfx/config/gfxConfig.cpp
gfx/config/gfxConfig.h
gfx/config/gfxFeature.cpp
gfx/config/gfxFeature.h
widget/GfxInfoBase.cpp
widget/GfxInfoBase.h
widget/nsIGfxInfo.idl
--- a/gfx/config/gfxConfig.cpp
+++ b/gfx/config/gfxConfig.cpp
@@ -1,14 +1,15 @@
 /* -*- 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 "plstr.h"
 
 namespace mozilla {
 namespace gfx {
 
 static gfxConfig sConfig;
 
 /* static */ FeatureState&
 gfxConfig::GetFeature(Feature aFeature)
@@ -141,25 +142,82 @@ gfxConfig::UseFallback(Fallback aFallbac
 {
   return sConfig.UseFallbackImpl(aFallback);
 }
 
 /* static */ void
 gfxConfig::EnableFallback(Fallback aFallback, const char* aMessage)
 {
   // Ignore aMessage for now.
-  sConfig.EnableFallbackImpl(aFallback);
+  sConfig.EnableFallbackImpl(aFallback, aMessage);
 }
 
 bool
 gfxConfig::UseFallbackImpl(Fallback aFallback) const
 {
   return !!(mFallbackBits & (uint64_t(1) << uint64_t(aFallback)));
 }
 
 void
-gfxConfig::EnableFallbackImpl(Fallback aFallback)
+gfxConfig::EnableFallbackImpl(Fallback aFallback, const char* aMessage)
+{
+  if (!UseFallbackImpl(aFallback)) {
+    MOZ_ASSERT(mNumFallbackLogEntries < kNumFallbacks);
+
+    FallbackLogEntry& entry = mFallbackLog[mNumFallbackLogEntries];
+    mNumFallbackLogEntries++;
+
+    entry.mFallback = aFallback;
+    PL_strncpyz(entry.mMessage, aMessage, sizeof(entry.mMessage));
+  }
+  mFallbackBits |= (uint64_t(1) << uint64_t(aFallback));
+}
+
+struct FeatureInfo {
+  const char* name;
+  const char* description;
+};
+static const FeatureInfo sFeatureInfo[] = {
+#define FOR_EACH_FEATURE(name, type, desc) {#name, desc},
+  GFX_FEATURE_MAP(FOR_EACH_FEATURE)
+#undef FOR_EACH_FEATURE
+  {nullptr, nullptr}
+};
+
+/* static */ void
+gfxConfig::ForEachFeature(const FeatureIterCallback& aCallback)
 {
-  mFallbackBits |= (uint64_t(1) << uint64_t(aFallback));
+  for (size_t i = 0; i < kNumFeatures; i++) {
+    FeatureState& state = GetFeature(static_cast<Feature>(i));
+    if (!state.IsInitialized()) {
+      continue;
+    }
+
+    aCallback(sFeatureInfo[i].name,
+              sFeatureInfo[i].description,
+              state);
+  }
+}
+
+static const char* sFallbackNames[] = {
+#define FOR_EACH_FALLBACK(name) #name,
+  GFX_FALLBACK_MAP(FOR_EACH_FALLBACK)
+#undef FOR_EACH_FALLBACK
+  nullptr
+};
+
+/* static  */ void
+gfxConfig::ForEachFallback(const FallbackIterCallback& 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);
+  }
 }
 
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/config/gfxConfig.h
+++ b/gfx/config/gfxConfig.h
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #ifndef mozilla_gfx_config_gfxConfig_h
 #define mozilla_gfx_config_gfxConfig_h
 
 #include "gfxFeature.h"
 #include "gfxFallback.h"
 #include "mozilla/Assertions.h"
+#include "mozilla/Function.h"
 
 namespace mozilla {
 namespace gfx {
 
 // Manages the history and state of a graphics feature. The flow of a feature
 // is:
 //   - A default value, set by all.js, gfxPrefs, or gfxPlatform.
 //   - A user value, set by an external value or user pref.
@@ -146,33 +147,57 @@ public:
   static void UserDisable(Feature aFeature, const char* aMessage);
 
   // 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.
+  typedef mozilla::function<void(const char* aName,
+                                 const char* aDescription,
+                                 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);
+
+private:
+  void ForEachFallbackImpl(const FallbackIterCallback& aCallback);
+
 private:
   FeatureState& GetState(Feature aFeature) {
     MOZ_ASSERT(size_t(aFeature) < kNumFeatures);
     return mFeatures[size_t(aFeature)];
   }
   const FeatureState& GetState(Feature aFeature) const {
     MOZ_ASSERT(size_t(aFeature) < kNumFeatures);
     return mFeatures[size_t(aFeature)];
   }
 
   bool UseFallbackImpl(Fallback aFallback) const;
-  void EnableFallbackImpl(Fallback aFallback);
+  void EnableFallbackImpl(Fallback aFallback, const char* aMessage);
 
 private:
   static const size_t kNumFeatures = size_t(Feature::NumValues);
+  static const size_t kNumFallbacks = size_t(Fallback::NumValues);
 
 private:
   FeatureState mFeatures[kNumFeatures];
   uint64_t mFallbackBits;
+
+private:
+  struct FallbackLogEntry {
+    Fallback mFallback;
+    char mMessage[80];
+  };
+
+  FallbackLogEntry mFallbackLog[kNumFallbacks];
+  size_t mNumFallbackLogEntries;
 };
 
 } // namespace gfx
 } // namespace mozilla
 
 #endif // mozilla_gfx_config_gfxConfig_h
--- a/gfx/config/gfxFeature.cpp
+++ b/gfx/config/gfxFeature.cpp
@@ -152,70 +152,88 @@ FeatureState::IsForcedOnByUser() const
   AssertInitialized();
   return mUser.mStatus == FeatureStatus::ForceEnabled;
 }
 
 void
 FeatureState::EnableByDefault()
 {
   // User/runtime decisions should not have been made yet.
-  MOZ_ASSERT(mUser.mStatus == FeatureStatus::Unused);
-  MOZ_ASSERT(mRuntime.mStatus == FeatureStatus::Unused);
+  MOZ_ASSERT(!mUser.IsInitialized());
+  MOZ_ASSERT(!mEnvironment.IsInitialized());
+  MOZ_ASSERT(!mRuntime.IsInitialized());
 
   mDefault.Set(FeatureStatus::Available);
 }
 
 void
 FeatureState::DisableByDefault(FeatureStatus aStatus, const char* aMessage)
 {
   // User/runtime decisions should not have been made yet.
-  MOZ_ASSERT(mUser.mStatus == FeatureStatus::Unused);
-  MOZ_ASSERT(mRuntime.mStatus == FeatureStatus::Unused);
+  MOZ_ASSERT(!mUser.IsInitialized());
+  MOZ_ASSERT(!mEnvironment.IsInitialized());
+  MOZ_ASSERT(!mRuntime.IsInitialized());
 
   mDefault.Set(aStatus, aMessage);
 }
 
 void
 FeatureState::SetUser(FeatureStatus aStatus, const char* aMessage)
 {
   // Default decision must have been made, but not runtime or environment.
-  MOZ_ASSERT(mDefault.mStatus != FeatureStatus::Unused);
-  MOZ_ASSERT(mEnvironment.mStatus == FeatureStatus::Unused);
-  MOZ_ASSERT(mRuntime.mStatus == FeatureStatus::Unused);
+  MOZ_ASSERT(mDefault.IsInitialized());
+  MOZ_ASSERT(!mEnvironment.IsInitialized());
+  MOZ_ASSERT(!mRuntime.IsInitialized());
 
   mUser.Set(aStatus, aMessage);
 }
 
 void
 FeatureState::SetEnvironment(FeatureStatus aStatus, const char* aMessage)
 {
   // Default decision must have been made, but not runtime.
-  MOZ_ASSERT(mDefault.mStatus != FeatureStatus::Unused);
-  MOZ_ASSERT(mRuntime.mStatus == FeatureStatus::Unused);
+  MOZ_ASSERT(mDefault.IsInitialized());
+  MOZ_ASSERT(!mRuntime.IsInitialized());
 
   mEnvironment.Set(aStatus, aMessage);
 }
 
 void
 FeatureState::SetRuntime(FeatureStatus aStatus, const char* aMessage)
 {
-  // Default decision must have been made.
-  MOZ_ASSERT(mDefault.mStatus != FeatureStatus::Unused);
+  AssertInitialized();
 
   mRuntime.Set(aStatus, aMessage);
 }
 
 const char*
 FeatureState::GetRuntimeMessage() const
 {
   MOZ_ASSERT(IsFeatureStatusFailure(mRuntime.mStatus));
   return mRuntime.mMessage;
 }
 
 void
+FeatureState::ForEachStatusChange(const StatusIterCallback& aCallback) const
+{
+  AssertInitialized();
+
+  aCallback("default", mDefault.mStatus, mDefault.MessageOrNull());
+  if (mUser.IsInitialized()) {
+    aCallback("user", mUser.mStatus, mUser.Message());
+  }
+  if (mEnvironment.IsInitialized()) {
+    aCallback("env", mEnvironment.mStatus, mEnvironment.Message());
+  }
+  if (mRuntime.IsInitialized()) {
+    aCallback("runtime", mRuntime.mStatus, mRuntime.Message());
+  }
+}
+
+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
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #ifndef mozilla_gfx_config_gfxFeature_h
 #define mozilla_gfx_config_gfxFeature_h
 
 #include <stdint.h>
 #include "gfxTelemetry.h"
 #include "mozilla/Assertions.h"
+#include "mozilla/Function.h"
 
 namespace mozilla {
 namespace gfx {
 
 #define GFX_FEATURE_MAP(_)                                                        \
   /* Name,                        Type,         Description */                    \
   _(HW_COMPOSITING,               Feature,      "Compositing")                    \
   _(D3D11_COMPOSITING,            Feature,      "Direct3D11 Compositing")         \
@@ -50,37 +51,54 @@ class FeatureState
   void Disable(FeatureStatus aStatus, const char* aMessage);
   void ForceDisable(FeatureStatus aStatus, const char* aMessage) {
     SetFailed(aStatus, aMessage);
   }
   void SetFailed(FeatureStatus aStatus, const char* aMessage);
   bool MaybeSetFailed(bool aEnable, FeatureStatus aStatus, const char* aMessage);
   bool MaybeSetFailed(FeatureStatus aStatus, const char* aMessage);
 
+  // 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;
+
  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.mStatus != FeatureStatus::Unused;
+    return mDefault.IsInitialized();
   }
 
   void AssertInitialized() const {
     MOZ_ASSERT(IsInitialized());
   }
 
  private:
   struct Instance {
     char mMessage[64];
     FeatureStatus mStatus;
 
     void Set(FeatureStatus aStatus, const char* aMessage = nullptr);
+    bool IsInitialized() const {
+      return mStatus != FeatureStatus::Unused;
+    }
+    const char* MessageOrNull() const {
+      return mMessage[0] != '\0' ? mMessage : nullptr;
+    }
+    const char* Message() const {
+      MOZ_ASSERT(MessageOrNull());
+      return mMessage;
+    }
   };
 
   // The default state is the state we decide on startup, based on the operating
   // system or a base preference.
   //
   // The user state factors in any changes to preferences that the user made.
   //
   // The environment state factors in any additional decisions made, such as
--- a/widget/GfxInfoBase.cpp
+++ b/widget/GfxInfoBase.cpp
@@ -28,16 +28,17 @@
 #include "nsXULAppAPI.h"
 #include "nsIXULAppInfo.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/Logging.h"
 #include "gfxPrefs.h"
 #include "gfxPlatform.h"
+#include "gfxConfig.h"
 
 #if defined(MOZ_CRASHREPORTER)
 #include "nsExceptionHandler.h"
 #endif
 
 using namespace mozilla::widget;
 using namespace mozilla;
 using mozilla::MutexAutoLock;
@@ -1271,44 +1272,172 @@ GetLayersBackendName(layers::LayersBacke
     case layers::LayersBackend::LAYERS_BASIC:
       return "basic";
     default:
       MOZ_ASSERT_UNREACHABLE("unknown layers backend");
       return "unknown";
   }
 }
 
+static inline bool
+SetJSPropertyString(JSContext* aCx, JS::Handle<JSObject*> aObj,
+                    const char* aProp, const char* aString)
+{
+  JS::Rooted<JSString*> str(aCx, JS_NewStringCopyZ(aCx, aString));
+  if (!str) {
+    return false;
+  }
+
+  JS::Rooted<JS::Value> val(aCx, JS::StringValue(str));
+  return JS_SetProperty(aCx, aObj, aProp, val);
+}
+
+template <typename T>
+static inline bool
+AppendJSElement(JSContext* aCx, JS::Handle<JSObject*> aObj, const T& aValue)
+{
+  uint32_t index;
+  if (!JS_GetArrayLength(aCx, aObj, &index)) {
+    return false;
+  }
+  return JS_SetElement(aCx, aObj, index, aValue);
+}
+
 nsresult
 GfxInfoBase::GetFeatures(JSContext* aCx, JS::MutableHandle<JS::Value> aOut)
 {
   JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
   if (!obj) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
   aOut.setObject(*obj);
 
   layers::LayersBackend backend = gfxPlatform::Initialized()
                                   ? gfxPlatform::GetPlatform()->GetCompositorBackend()
                                   : layers::LayersBackend::LAYERS_NONE;
   const char* backendName = GetLayersBackendName(backend);
-  {
-    JS::Rooted<JSString*> str(aCx, JS_NewStringCopyZ(aCx, backendName));
-    JS::Rooted<JS::Value> val(aCx, StringValue(str));
-    JS_SetProperty(aCx, obj, "compositor", val);
-  }
+  SetJSPropertyString(aCx, obj, "compositor", backendName);
 
   // If graphics isn't initialized yet, just stop now.
   if (!gfxPlatform::Initialized()) {
     return NS_OK;
   }
 
   DescribeFeatures(aCx, obj);
   return NS_OK;
 }
 
+nsresult GfxInfoBase::GetFeatureLog(JSContext* aCx, JS::MutableHandle<JS::Value> aOut)
+{
+  JS::Rooted<JSObject*> containerObj(aCx, JS_NewPlainObject(aCx));
+  if (!containerObj) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+  aOut.setObject(*containerObj);
+
+  JS::Rooted<JSObject*> featureArray(aCx, JS_NewArrayObject(aCx, 0));
+  if (!featureArray) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  // Collect features.
+  gfxConfig::ForEachFeature([&](const char* aName,
+                                const char* aDescription,
+                                FeatureState& aFeature) -> void {
+    JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
+    if (!obj) {
+      return;
+    }
+    if (!SetJSPropertyString(aCx, obj, "name", aName) ||
+        !SetJSPropertyString(aCx, obj, "description", aDescription) ||
+        !SetJSPropertyString(aCx, obj, "status", FeatureStatusToString(aFeature.GetValue())))
+    {
+      return;
+    }
+
+    JS::Rooted<JS::Value> log(aCx);
+    if (!BuildFeatureStateLog(aCx, aFeature, &log)) {
+      return;
+    }
+    if (!JS_SetProperty(aCx, obj, "log", log)) {
+      return;
+    }
+
+    if (!AppendJSElement(aCx, featureArray, obj)) {
+      return;
+    }
+  });
+
+  JS::Rooted<JSObject*> fallbackArray(aCx, JS_NewArrayObject(aCx, 0));
+  if (!fallbackArray) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  // Collect fallbacks.
+  gfxConfig::ForEachFallback([&](const char* aName, const char* aMessage) -> void {
+    JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
+    if (!obj) {
+      return;
+    }
+
+    if (!SetJSPropertyString(aCx, obj, "name", aName) ||
+        !SetJSPropertyString(aCx, obj, "message", aMessage))
+    {
+      return;
+    }
+
+    if (!AppendJSElement(aCx, fallbackArray, obj)) {
+      return;
+    }
+  });
+
+  JS::Rooted<JS::Value> val(aCx);
+
+  val = JS::ObjectValue(*featureArray);
+  JS_SetProperty(aCx, containerObj, "features", val);
+
+  val = JS::ObjectValue(*fallbackArray);
+  JS_SetProperty(aCx, containerObj, "fallbacks", val);
+
+  return NS_OK;
+}
+
+bool
+GfxInfoBase::BuildFeatureStateLog(JSContext* aCx, const FeatureState& aFeature,
+                                  JS::MutableHandle<JS::Value> aOut)
+{
+  JS::Rooted<JSObject*> log(aCx, JS_NewArrayObject(aCx, 0));
+  if (!log) {
+    return false;
+  }
+  aOut.setObject(*log);
+
+  aFeature.ForEachStatusChange([&](const char* aType,
+                                   FeatureStatus aStatus,
+                                   const char* aMessage) -> void {
+    JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
+    if (!obj) {
+      return;
+    }
+
+    if (!SetJSPropertyString(aCx, obj, "type", aType) ||
+        !SetJSPropertyString(aCx, obj, "status", FeatureStatusToString(aStatus)) ||
+        (aMessage && !SetJSPropertyString(aCx, obj, "message", aMessage)))
+    {
+      return;
+    }
+
+    if (!AppendJSElement(aCx, log, obj)) {
+      return;
+    }
+  });
+
+  return true;
+}
+
 void
 GfxInfoBase::DescribeFeatures(JSContext* cx, JS::Handle<JSObject*> aOut)
 {
 }
 
 bool
 GfxInfoBase::InitFeatureObject(JSContext* aCx,
                                JS::Handle<JSObject*> aContainer,
--- a/widget/GfxInfoBase.h
+++ b/widget/GfxInfoBase.h
@@ -12,16 +12,17 @@
 #include "nsCOMPtr.h"
 #include "nsIObserver.h"
 #include "nsWeakReference.h"
 #include "GfxDriverInfo.h"
 #include "nsTArray.h"
 #include "nsString.h"
 #include "GfxInfoCollector.h"
 #include "gfxTelemetry.h"
+#include "gfxFeature.h"
 #include "nsIGfxInfoDebug.h"
 #include "mozilla/Mutex.h"
 #include "js/Value.h"
 #include "mozilla/Attributes.h"
 
 namespace mozilla {
 namespace widget {  
 
@@ -49,16 +50,17 @@ public:
   NS_IMETHOD GetFeatureSuggestedDriverVersion(int32_t aFeature, nsAString & _retval) override;
   NS_IMETHOD GetWebGLParameter(const nsAString & aParam, nsAString & _retval) override;
 
   NS_IMETHOD GetMonitors(JSContext* cx, JS::MutableHandleValue _retval) override;
   NS_IMETHOD GetFailures(uint32_t *failureCount, int32_t** indices, char ***failures) override;
   NS_IMETHOD_(void) LogFailure(const nsACString &failure) override;
   NS_IMETHOD GetInfo(JSContext*, JS::MutableHandle<JS::Value>) override;
   NS_IMETHOD GetFeatures(JSContext*, JS::MutableHandle<JS::Value>) override;
+  NS_IMETHOD GetFeatureLog(JSContext*, JS::MutableHandle<JS::Value>) override;
 
   // Initialization function. If you override this, you must call this class's
   // version of Init first.
   // We need Init to be called separately from the constructor so we can
   // register as an observer after all derived classes have been constructed
   // and we know we have a non-zero refcount.
   // Ideally, Init() would be void-return, but the rules of
   // NS_GENERIC_FACTORY_CONSTRUCTOR_INIT require it be nsresult return.
@@ -112,16 +114,19 @@ private:
   virtual int32_t FindBlocklistedDeviceInList(const nsTArray<GfxDriverInfo>& aDriverInfo,
                                               nsAString& aSuggestedVersion,
                                               int32_t aFeature,
                                               nsACString &aFailureId,
                                               OperatingSystem os);
 
   void EvaluateDownloadedBlacklist(nsTArray<GfxDriverInfo>& aDriverInfo);
 
+  bool BuildFeatureStateLog(JSContext* aCx, const gfx::FeatureState& aFeature,
+                            JS::MutableHandle<JS::Value> aOut);
+
   Mutex mMutex;
 
 };
 
 } // namespace widget
 } // namespace mozilla
 
 #endif /* __mozilla_widget_GfxInfoBase_h__ */
--- a/widget/nsIGfxInfo.idl
+++ b/widget/nsIGfxInfo.idl
@@ -154,16 +154,45 @@ interface nsIGfxInfo : nsISupports
   DOMString getWebGLParameter(in DOMString aParam);
 
   // only useful on X11
   [noscript, notxpcom] void GetData();
 
   [implicit_jscontext]
   jsval getInfo();
 
+  // Return an object describing all features that have been configured:
+  //
+  //   "features": [
+  //     // For each feature:
+  //     {
+  //       "name": <string>,
+  //       "description": <string>,
+  //       "status": <string>,
+  //       "log": [
+  //          // One or more log entries, the first denotes the default value.
+  //          {
+  //            "type": <string>,    // "base", "user", "env", or "runtime"
+  //            "status": <string>,
+  //            "message": <string>  // Set unless type is "base" and status is "available".
+  //          }
+  //       ]
+  //     }
+  //   ]
+  //   "fallbacks": [
+  //     // For each workaround:
+  //     {
+  //       "name:" <string>,
+  //       "description": <string>,
+  //       "message": <string>
+  //     ]
+  //   }
+  [implicit_jscontext]
+  jsval getFeatureLog();
+
   // Returns an object containing information about graphics features. It is
   // intended to be directly included into the Telemetry environment.
   //
   //   "layers":
   //   {
   //     "compositor": "d3d9", "d3d11", "opengl", "basic", or "none"
   //                   // ("none" indicates no compositors have been created)
   //     // Feature is one of "d3d9", "d3d11", "opengl", "basic", or "d2d".