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