BatteryManager.cpp (6113B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "BatteryManager.h" 8 9 #include <cmath> 10 #include <limits> 11 12 #include "Constants.h" 13 #include "mozilla/DOMEventTargetHelper.h" 14 #include "mozilla/Hal.h" 15 #include "mozilla/Preferences.h" 16 #include "mozilla/dom/BatteryManagerBinding.h" 17 #include "mozilla/dom/Document.h" 18 #include "nsContentUtils.h" 19 #include "nsGlobalWindowInner.h" 20 21 /** 22 * We have to use macros here because our leak analysis tool things we are 23 * leaking strings when we have |static const nsString|. Sad :( 24 */ 25 #define LEVELCHANGE_EVENT_NAME u"levelchange"_ns 26 #define CHARGINGCHANGE_EVENT_NAME u"chargingchange"_ns 27 #define DISCHARGINGTIMECHANGE_EVENT_NAME u"dischargingtimechange"_ns 28 #define CHARGINGTIMECHANGE_EVENT_NAME u"chargingtimechange"_ns 29 30 namespace mozilla::dom::battery { 31 32 BatteryManager::BatteryManager(nsPIDOMWindowInner* aWindow) 33 : DOMEventTargetHelper(aWindow), 34 mLevel(kDefaultLevel), 35 mCharging(kDefaultCharging), 36 mRemainingTime(kDefaultRemainingTime) {} 37 38 void BatteryManager::Init() { 39 hal::RegisterBatteryObserver(this); 40 41 hal::BatteryInformation batteryInfo; 42 hal::GetCurrentBatteryInformation(&batteryInfo); 43 44 UpdateFromBatteryInfo(batteryInfo); 45 } 46 47 void BatteryManager::Shutdown() { hal::UnregisterBatteryObserver(this); } 48 49 JSObject* BatteryManager::WrapObject(JSContext* aCx, 50 JS::Handle<JSObject*> aGivenProto) { 51 return BatteryManager_Binding::Wrap(aCx, this, aGivenProto); 52 } 53 54 bool BatteryManager::Charging() const { 55 MOZ_ASSERT(NS_IsMainThread()); 56 // For testing, unable to report the battery status information 57 if (Preferences::GetBool("dom.battery.test.default", false)) { 58 return true; 59 } 60 if (Preferences::GetBool("dom.battery.test.charging", false)) { 61 return true; 62 } 63 if (Preferences::GetBool("dom.battery.test.discharging", false)) { 64 return false; 65 } 66 67 return mCharging; 68 } 69 70 double BatteryManager::DischargingTime() const { 71 MOZ_ASSERT(NS_IsMainThread()); 72 // For testing, unable to report the battery status information 73 if (Preferences::GetBool("dom.battery.test.default", false)) { 74 return std::numeric_limits<double>::infinity(); 75 } 76 if (Preferences::GetBool("dom.battery.test.discharging", false)) { 77 return 42.0; 78 } 79 80 if (Charging() || mRemainingTime == kUnknownRemainingTime) { 81 return std::numeric_limits<double>::infinity(); 82 } 83 84 return mRemainingTime; 85 } 86 87 double BatteryManager::ChargingTime() const { 88 MOZ_ASSERT(NS_IsMainThread()); 89 // For testing, unable to report the battery status information 90 if (Preferences::GetBool("dom.battery.test.default", false)) { 91 return 0.0; 92 } 93 if (Preferences::GetBool("dom.battery.test.charging", false)) { 94 return 42.0; 95 } 96 97 if (!Charging() || mRemainingTime == kUnknownRemainingTime) { 98 return std::numeric_limits<double>::infinity(); 99 } 100 101 return mRemainingTime; 102 } 103 104 double BatteryManager::Level() const { 105 MOZ_ASSERT(NS_IsMainThread()); 106 // For testing, unable to report the battery status information 107 if (Preferences::GetBool("dom.battery.test.default")) { 108 return 1.0; 109 } 110 111 return mLevel; 112 } 113 114 void BatteryManager::UpdateFromBatteryInfo( 115 const hal::BatteryInformation& aBatteryInfo) { 116 mLevel = aBatteryInfo.level(); 117 118 // Round to the nearest ten percent for non-chrome. 119 Document* doc = GetOwnerWindow() ? GetOwnerWindow()->GetDoc() : nullptr; 120 121 mCharging = aBatteryInfo.charging(); 122 mRemainingTime = aBatteryInfo.remainingTime(); 123 124 if (!nsContentUtils::IsChromeDoc(doc)) { 125 mLevel = lround(mLevel * 10.0) / 10.0; 126 if (mLevel == 1.0) { 127 mRemainingTime = 128 mCharging ? kDefaultRemainingTime : kUnknownRemainingTime; 129 } else if (mRemainingTime != kUnknownRemainingTime) { 130 // Round the remaining time to a multiple of 15 minutes and never zero 131 const double MINUTES_15 = 15.0 * 60.0; 132 mRemainingTime = 133 fmax(lround(mRemainingTime / MINUTES_15) * MINUTES_15, MINUTES_15); 134 } 135 } 136 137 // Add some guards to make sure the values are coherent. 138 if (mLevel == 1.0 && mCharging == true && 139 mRemainingTime != kDefaultRemainingTime) { 140 mRemainingTime = kDefaultRemainingTime; 141 NS_ERROR( 142 "Battery API: When charging and level at 1.0, remaining time " 143 "should be 0. Please fix your backend!"); 144 } 145 } 146 147 void BatteryManager::Notify(const hal::BatteryInformation& aBatteryInfo) { 148 double previousLevel = mLevel; 149 bool previousCharging = mCharging; 150 double previousRemainingTime = mRemainingTime; 151 152 UpdateFromBatteryInfo(aBatteryInfo); 153 154 if (previousCharging != mCharging) { 155 DispatchTrustedEvent(CHARGINGCHANGE_EVENT_NAME); 156 } 157 158 if (previousLevel != mLevel) { 159 DispatchTrustedEvent(LEVELCHANGE_EVENT_NAME); 160 } 161 162 /* 163 * There are a few situations that could happen here: 164 * 1. Charging state changed: 165 * a. Previous remaining time wasn't unkwonw, we have to fire an event for 166 * the change. 167 * b. New remaining time isn't unkwonw, we have to fire an event for it. 168 * 2. Charging state didn't change but remainingTime did, we have to fire 169 * the event that correspond to the current charging state. 170 */ 171 if (mCharging != previousCharging) { 172 if (previousRemainingTime != kUnknownRemainingTime) { 173 DispatchTrustedEvent(previousCharging ? CHARGINGTIMECHANGE_EVENT_NAME 174 : DISCHARGINGTIMECHANGE_EVENT_NAME); 175 } 176 if (mRemainingTime != kUnknownRemainingTime) { 177 DispatchTrustedEvent(mCharging ? CHARGINGTIMECHANGE_EVENT_NAME 178 : DISCHARGINGTIMECHANGE_EVENT_NAME); 179 } 180 } else if (previousRemainingTime != mRemainingTime) { 181 DispatchTrustedEvent(mCharging ? CHARGINGTIMECHANGE_EVENT_NAME 182 : DISCHARGINGTIMECHANGE_EVENT_NAME); 183 } 184 } 185 186 } // namespace mozilla::dom::battery