Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim set: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : */
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/. */
7 #import <CoreFoundation/CoreFoundation.h>
8 #import <IOKit/ps/IOPowerSources.h>
9 #import <IOKit/ps/IOPSKeys.h>
11 #include <mozilla/Hal.h>
12 #include <mozilla/dom/battery/Constants.h>
13 #include <mozilla/Services.h>
15 #include <nsIObserverService.h>
16 #include <nsIObserver.h>
18 #include <dlfcn.h>
20 #define IOKIT_FRAMEWORK_PATH "/System/Library/Frameworks/IOKit.framework/IOKit"
22 #ifndef kIOPSTimeRemainingUnknown
23 #define kIOPSTimeRemainingUnknown ((CFTimeInterval)-1.0)
24 #endif
25 #ifndef kIOPSTimeRemainingUnlimited
26 #define kIOPSTimeRemainingUnlimited ((CFTimeInterval)-2.0)
27 #endif
29 using namespace mozilla::dom::battery;
31 namespace mozilla {
32 namespace hal_impl {
34 typedef CFTimeInterval (*IOPSGetTimeRemainingEstimateFunc)(void);
36 class MacPowerInformationService
37 {
38 public:
39 static MacPowerInformationService* GetInstance();
40 static void Shutdown();
42 void BeginListening();
43 void StopListening();
45 static void HandleChange(void *aContext);
47 ~MacPowerInformationService();
49 private:
50 MacPowerInformationService();
52 // The reference to the runloop that is notified of power changes.
53 CFRunLoopSourceRef mRunLoopSource;
55 double mLevel;
56 bool mCharging;
57 double mRemainingTime;
58 bool mShouldNotify;
60 friend void GetCurrentBatteryInformation(hal::BatteryInformation* aBatteryInfo);
62 static MacPowerInformationService* sInstance;
64 static void* sIOKitFramework;
65 static IOPSGetTimeRemainingEstimateFunc sIOPSGetTimeRemainingEstimate;
66 };
68 void* MacPowerInformationService::sIOKitFramework;
69 IOPSGetTimeRemainingEstimateFunc MacPowerInformationService::sIOPSGetTimeRemainingEstimate;
71 /*
72 * Implementation of mozilla::hal_impl::EnableBatteryNotifications,
73 * mozilla::hal_impl::DisableBatteryNotifications,
74 * and mozilla::hal_impl::GetCurrentBatteryInformation.
75 */
77 void
78 EnableBatteryNotifications()
79 {
80 MacPowerInformationService::GetInstance()->BeginListening();
81 }
83 void
84 DisableBatteryNotifications()
85 {
86 MacPowerInformationService::GetInstance()->StopListening();
87 }
89 void
90 GetCurrentBatteryInformation(hal::BatteryInformation* aBatteryInfo)
91 {
92 MacPowerInformationService* powerService = MacPowerInformationService::GetInstance();
94 aBatteryInfo->level() = powerService->mLevel;
95 aBatteryInfo->charging() = powerService->mCharging;
96 aBatteryInfo->remainingTime() = powerService->mRemainingTime;
97 }
99 /*
100 * Following is the implementation of MacPowerInformationService.
101 */
103 MacPowerInformationService* MacPowerInformationService::sInstance = nullptr;
105 namespace {
106 struct SingletonDestroyer MOZ_FINAL : public nsIObserver
107 {
108 NS_DECL_ISUPPORTS
109 NS_DECL_NSIOBSERVER
110 };
112 NS_IMPL_ISUPPORTS(SingletonDestroyer, nsIObserver)
114 NS_IMETHODIMP
115 SingletonDestroyer::Observe(nsISupports*, const char* aTopic, const char16_t*)
116 {
117 MOZ_ASSERT(!strcmp(aTopic, "xpcom-shutdown"));
118 MacPowerInformationService::Shutdown();
119 return NS_OK;
120 }
121 } // anonymous namespace
123 /* static */ MacPowerInformationService*
124 MacPowerInformationService::GetInstance()
125 {
126 if (sInstance) {
127 return sInstance;
128 }
130 sInstance = new MacPowerInformationService();
132 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
133 if (obs) {
134 obs->AddObserver(new SingletonDestroyer(), "xpcom-shutdown", false);
135 }
137 return sInstance;
138 }
140 void
141 MacPowerInformationService::Shutdown()
142 {
143 delete sInstance;
144 sInstance = nullptr;
145 }
147 MacPowerInformationService::MacPowerInformationService()
148 : mRunLoopSource(nullptr)
149 , mLevel(kDefaultLevel)
150 , mCharging(kDefaultCharging)
151 , mRemainingTime(kDefaultRemainingTime)
152 , mShouldNotify(false)
153 {
154 // IOPSGetTimeRemainingEstimate (and the related constants) are only available
155 // on 10.7, so we test for their presence at runtime.
156 sIOKitFramework = dlopen(IOKIT_FRAMEWORK_PATH, RTLD_LAZY | RTLD_LOCAL);
157 if (sIOKitFramework) {
158 sIOPSGetTimeRemainingEstimate =
159 (IOPSGetTimeRemainingEstimateFunc)dlsym(sIOKitFramework, "IOPSGetTimeRemainingEstimate");
160 } else {
161 sIOPSGetTimeRemainingEstimate = nullptr;
162 }
163 }
165 MacPowerInformationService::~MacPowerInformationService()
166 {
167 MOZ_ASSERT(!mRunLoopSource,
168 "The observers have not been correctly removed! "
169 "(StopListening should have been called)");
171 if (sIOKitFramework) {
172 dlclose(sIOKitFramework);
173 }
174 }
176 void
177 MacPowerInformationService::BeginListening()
178 {
179 // Set ourselves up to be notified about changes.
180 MOZ_ASSERT(!mRunLoopSource, "IOPS Notification Loop Source already set up. "
181 "(StopListening should have been called)");
183 mRunLoopSource = ::IOPSNotificationCreateRunLoopSource(HandleChange, this);
184 if (mRunLoopSource) {
185 ::CFRunLoopAddSource(::CFRunLoopGetCurrent(), mRunLoopSource,
186 kCFRunLoopDefaultMode);
188 // Invoke our callback now so we have data if GetCurrentBatteryInformation is
189 // called before a change happens.
190 HandleChange(this);
191 mShouldNotify = true;
192 }
193 }
195 void
196 MacPowerInformationService::StopListening()
197 {
198 MOZ_ASSERT(mRunLoopSource, "IOPS Notification Loop Source not set up. "
199 "(StopListening without BeginListening)");
201 ::CFRunLoopRemoveSource(::CFRunLoopGetCurrent(), mRunLoopSource,
202 kCFRunLoopDefaultMode);
203 mRunLoopSource = nullptr;
204 }
206 void
207 MacPowerInformationService::HandleChange(void* aContext) {
208 MacPowerInformationService* power =
209 static_cast<MacPowerInformationService*>(aContext);
211 CFTypeRef data = ::IOPSCopyPowerSourcesInfo();
212 if (!data) {
213 ::CFRelease(data);
214 return;
215 }
217 // Get the list of power sources.
218 CFArrayRef list = ::IOPSCopyPowerSourcesList(data);
219 if (!list) {
220 ::CFRelease(list);
221 return;
222 }
224 // Default values. These will be used if there are 0 sources or we can't find
225 // better information.
226 double level = kDefaultLevel;
227 double charging = kDefaultCharging;
228 double remainingTime = kDefaultRemainingTime;
230 // Look for the first battery power source to give us the information we need.
231 // Usually there's only 1 available, depending on current power source.
232 for (CFIndex i = 0; i < ::CFArrayGetCount(list); ++i) {
233 CFTypeRef source = ::CFArrayGetValueAtIndex(list, i);
234 CFDictionaryRef currPowerSourceDesc = ::IOPSGetPowerSourceDescription(data, source);
235 if (!currPowerSourceDesc) {
236 continue;
237 }
239 // Get a battery level estimate. This key is required.
240 int currentCapacity = 0;
241 const void* cfRef = ::CFDictionaryGetValue(currPowerSourceDesc, CFSTR(kIOPSCurrentCapacityKey));
242 ::CFNumberGetValue((CFNumberRef)cfRef, kCFNumberSInt32Type, ¤tCapacity);
244 // This key is also required.
245 int maxCapacity = 0;
246 cfRef = ::CFDictionaryGetValue(currPowerSourceDesc, CFSTR(kIOPSMaxCapacityKey));
247 ::CFNumberGetValue((CFNumberRef)cfRef, kCFNumberSInt32Type, &maxCapacity);
249 if (maxCapacity > 0) {
250 level = static_cast<double>(currentCapacity)/static_cast<double>(maxCapacity);
251 }
253 // Find out if we're charging.
254 // This key is optional, we fallback to kDefaultCharging if the current power
255 // source doesn't have that info.
256 if(::CFDictionaryGetValueIfPresent(currPowerSourceDesc, CFSTR(kIOPSIsChargingKey), &cfRef)) {
257 charging = ::CFBooleanGetValue((CFBooleanRef)cfRef);
259 // Get an estimate of how long it's going to take until we're fully charged.
260 // This key is optional.
261 if (charging) {
262 // Default value that will be changed if we happen to find the actual
263 // remaining time.
264 remainingTime = level == 1.0 ? kDefaultRemainingTime : kUnknownRemainingTime;
266 if (::CFDictionaryGetValueIfPresent(currPowerSourceDesc,
267 CFSTR(kIOPSTimeToFullChargeKey), &cfRef)) {
268 int timeToCharge;
269 ::CFNumberGetValue((CFNumberRef)cfRef, kCFNumberIntType, &timeToCharge);
270 if (timeToCharge != kIOPSTimeRemainingUnknown) {
271 remainingTime = timeToCharge*60;
272 }
273 }
274 } else if (sIOPSGetTimeRemainingEstimate) { // not charging
275 // See if we can get a time estimate.
276 CFTimeInterval estimate = sIOPSGetTimeRemainingEstimate();
277 if (estimate == kIOPSTimeRemainingUnlimited || estimate == kIOPSTimeRemainingUnknown) {
278 remainingTime = kUnknownRemainingTime;
279 } else {
280 remainingTime = estimate;
281 }
282 }
283 }
285 break;
286 }
288 bool isNewData = level != power->mLevel || charging != power->mCharging ||
289 remainingTime != power->mRemainingTime;
291 power->mRemainingTime = remainingTime;
292 power->mCharging = charging;
293 power->mLevel = level;
295 // Notify the observers if stuff changed.
296 if (power->mShouldNotify && isNewData) {
297 hal::NotifyBatteryChange(hal::BatteryInformation(power->mLevel,
298 power->mCharging,
299 power->mRemainingTime));
300 }
302 ::CFRelease(data);
303 ::CFRelease(list);
304 }
306 } // namespace hal_impl
307 } // namespace mozilla