Bug 1490942 - Ignore UPS batteries for WebRender qualified purposes. r=jrmuizel
authorAndrew Osmond <aosmond@mozilla.com>
Wed, 10 Oct 2018 08:37:03 -0400
changeset 490904 27cb49221d42fd3d31187bcd40360669f5110e1a
parent 490903 89cef77c46f583a1f20c050453fdb7db43f128f1
child 490905 d7bb4168f88486e1058b5546ce0512c600f9893e
push id247
push userfmarier@mozilla.com
push dateSat, 27 Oct 2018 01:06:44 +0000
reviewersjrmuizel
bugs1490942
milestone65.0a1
Bug 1490942 - Ignore UPS batteries for WebRender qualified purposes. r=jrmuizel Differential Revision: https://phabricator.services.mozilla.com/D8255
gfx/thebes/gfxPlatform.cpp
gfx/thebes/gfxPlatform.h
gfx/thebes/gfxWindowsPlatform.cpp
gfx/thebes/gfxWindowsPlatform.h
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -57,17 +57,16 @@
 #elif defined(ANDROID)
 #include "gfxAndroidPlatform.h"
 #endif
 #if defined(MOZ_WIDGET_ANDROID)
 #include "mozilla/jni/Utils.h"  // for IsFennec
 #endif
 
 #ifdef XP_WIN
-#include <windows.h>
 #include "mozilla/WindowsVersion.h"
 #include "mozilla/gfx/DeviceManagerDx.h"
 #endif
 
 #include "nsGkAtoms.h"
 #include "gfxPlatformFontList.h"
 #include "gfxContext.h"
 #include "gfxImageSurface.h"
@@ -2664,32 +2663,16 @@ gfxPlatform::WebRenderPrefEnabled()
 
 /*static*/ bool
 gfxPlatform::WebRenderEnvvarEnabled()
 {
   const char* env = PR_GetEnv("MOZ_WEBRENDER");
   return (env && *env == '1');
 }
 
-/* This is a pretty conservative check for having a battery.
- * For now we'd rather err on the side of thinking we do. */
-static bool HasBattery()
-{
-#ifdef XP_WIN
-  SYSTEM_POWER_STATUS status;
-  const BYTE NO_SYSTEM_BATTERY = 128;
-  if (GetSystemPowerStatus(&status)) {
-    if (status.BatteryFlag == NO_SYSTEM_BATTERY) {
-      return false;
-    }
-  }
-#endif
-  return true;
-}
-
 void
 gfxPlatform::InitWebRenderConfig()
 {
   bool prefEnabled = WebRenderPrefEnabled();
   bool envvarEnabled = WebRenderEnvvarEnabled();
 
   // On Nightly:
   //   WR? WR+   => means WR was enabled via gfx.webrender.all.qualified
--- a/gfx/thebes/gfxPlatform.h
+++ b/gfx/thebes/gfxPlatform.h
@@ -749,16 +749,20 @@ public:
     static bool WebRenderEnvvarEnabled();
 
     virtual void
     OnMemoryPressure(mozilla::layers::MemoryPressureReason aWhy) override;
 protected:
     gfxPlatform();
     virtual ~gfxPlatform();
 
+    virtual bool HasBattery() {
+      return true;
+    }
+
     virtual void InitAcceleration();
     virtual void InitWebRenderConfig();
 
     /**
      * Called immediately before deleting the gfxPlatform object.
      */
     virtual void WillShutdown();
 
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -1,14 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* vim: set ts=8 sts=4 et sw=4 tw=80: */
 /* 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/. */
 
+#define INITGUID // set before devguid.h
+
 #include "gfxWindowsPlatform.h"
 
 #include "cairo.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/layers/CompositorBridgeChild.h"
 
 #include "gfxImageSurface.h"
 #include "gfxWindowsSurface.h"
@@ -76,16 +78,21 @@
 #include "gfxConfig.h"
 #include "VsyncSource.h"
 #include "DriverCrashGuard.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/gfx/DeviceManagerDx.h"
 #include "mozilla/layers/DeviceAttachmentsD3D11.h"
 #include "D3D11Checks.h"
 
+#include <devguid.h>  // for GUID_DEVCLASS_BATTERY
+#include <setupapi.h> // for SetupDi*
+#include <winioctl.h> // for IOCTL_*
+#include <batclass.h> // for BATTERY_*
+
 using namespace mozilla;
 using namespace mozilla::gfx;
 using namespace mozilla::layers;
 using namespace mozilla::widget;
 using namespace mozilla::image;
 using namespace mozilla::unicode;
 
 DCFromDrawTarget::DCFromDrawTarget(DrawTarget& aDrawTarget)
@@ -335,16 +342,133 @@ gfxWindowsPlatform::~gfxWindowsPlatform(
 static void
 UpdateANGLEConfig()
 {
   if (!gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
     gfxConfig::Disable(Feature::D3D11_HW_ANGLE, FeatureStatus::Disabled, "D3D11 compositing is disabled");
   }
 }
 
+bool
+gfxWindowsPlatform::HasBattery()
+{
+  // Helper classes to manage lifetimes of Windows structs.
+  class MOZ_STACK_CLASS HDevInfoHolder final
+  {
+  public:
+    explicit HDevInfoHolder(HDEVINFO aHandle)
+      : mHandle(aHandle)
+    { }
+
+    ~HDevInfoHolder()
+    {
+      ::SetupDiDestroyDeviceInfoList(mHandle);
+    }
+
+  private:
+    HDEVINFO mHandle;
+  };
+
+  class MOZ_STACK_CLASS HandleHolder final
+  {
+  public:
+    explicit HandleHolder(HANDLE aHandle)
+      : mHandle(aHandle)
+    { }
+
+    ~HandleHolder()
+    {
+      ::CloseHandle(mHandle);
+    }
+
+  private:
+    HANDLE mHandle;
+  };
+
+  HDEVINFO hdev =
+    ::SetupDiGetClassDevs(&GUID_DEVCLASS_BATTERY, nullptr, nullptr,
+                          DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
+  if (hdev == INVALID_HANDLE_VALUE) {
+    return true;
+  }
+
+  HDevInfoHolder hdevHolder(hdev);
+
+  DWORD i = 0;
+  SP_DEVICE_INTERFACE_DATA did = { 0 };
+  did.cbSize = sizeof(did);
+
+  while(::SetupDiEnumDeviceInterfaces(hdev, nullptr, &GUID_DEVCLASS_BATTERY,
+                                      i, &did)) {
+    DWORD bufferSize = 0;
+    ::SetupDiGetDeviceInterfaceDetail(hdev, &did, nullptr, 0,
+                                      &bufferSize, nullptr);
+    if (::GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+      return true;
+    }
+
+    UniquePtr<uint8_t[]> buffer(new (std::nothrow) uint8_t[bufferSize]);
+    if (!buffer) {
+      return true;
+    }
+
+    PSP_DEVICE_INTERFACE_DETAIL_DATA pdidd =
+      reinterpret_cast<PSP_DEVICE_INTERFACE_DETAIL_DATA>(buffer.get());
+    pdidd->cbSize = sizeof(*pdidd);
+    if (!::SetupDiGetDeviceInterfaceDetail(hdev, &did, pdidd, bufferSize,
+                                           &bufferSize, nullptr)) {
+      return true;
+    }
+
+    HANDLE hbat = ::CreateFile(pdidd->DevicePath, GENERIC_READ | GENERIC_WRITE,
+                               FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
+                               OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
+    if (hbat == INVALID_HANDLE_VALUE) {
+      return true;
+    }
+
+    HandleHolder hbatHolder(hbat);
+
+    BATTERY_QUERY_INFORMATION bqi = {0};
+    DWORD dwWait = 0;
+    DWORD dwOut;
+
+    // We need the tag to query the information below.
+    if (!::DeviceIoControl(hbat, IOCTL_BATTERY_QUERY_TAG,
+                           &dwWait, sizeof(dwWait),
+                           &bqi.BatteryTag, sizeof(bqi.BatteryTag),
+                           &dwOut, nullptr) || !bqi.BatteryTag) {
+      return true;
+    }
+
+    BATTERY_INFORMATION bi = {0};
+    bqi.InformationLevel = BatteryInformation;
+
+    if (!::DeviceIoControl(hbat, IOCTL_BATTERY_QUERY_INFORMATION,
+                           &bqi, sizeof(bqi), &bi, sizeof(bi),
+                           &dwOut, nullptr)) {
+      return true;
+    }
+
+    // If a battery intended for general use (i.e. system use) is not a UPS
+    // (i.e. short term), then we know for certain we have a battery.
+    if ((bi.Capabilities & BATTERY_SYSTEM_BATTERY) &&
+        !(bi.Capabilities & BATTERY_IS_SHORT_TERM)) {
+      return true;
+    }
+
+    // Otherwise we check the next battery.
+    ++i;
+  }
+
+  // If we fail to enumerate because there are no more batteries to check, then
+  // we can safely say there are indeed no system batteries.
+  return ::GetLastError() != ERROR_NO_MORE_ITEMS;
+}
+
 void
 gfxWindowsPlatform::InitAcceleration()
 {
   gfxPlatform::InitAcceleration();
 
   DeviceManagerDx::Init();
 
   InitializeConfig();
--- a/gfx/thebes/gfxWindowsPlatform.h
+++ b/gfx/thebes/gfxWindowsPlatform.h
@@ -236,16 +236,18 @@ protected:
     BackendPrefsData GetBackendPrefs() const override;
 
     bool CheckVariationFontSupport() override;
 
 protected:
     RenderMode mRenderMode;
 
 private:
+    bool HasBattery() override;
+
     void Init();
     void InitAcceleration() override;
     void InitWebRenderConfig() override;
 
     void InitializeDevices();
     void InitializeD3D11();
     void InitializeD2D();
     bool InitDWriteSupport();