Bug 1050749 - Expose BatteryManager via getBattery() returning a Promise r=bz,baku

This commit is contained in:
Wei-Cheng Pan 2015-08-20 15:39:47 +08:00
parent ca5609da1f
commit 23e360c8c1
25 changed files with 852 additions and 78 deletions

View File

@ -185,6 +185,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Navigator)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGeolocation) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGeolocation)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNotification) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNotification)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBatteryManager) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBatteryManager)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBatteryPromise)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPowerManager) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPowerManager)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCellBroadcast) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCellBroadcast)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIccManager) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIccManager)
@ -252,6 +253,8 @@ Navigator::Invalidate()
mBatteryManager = nullptr; mBatteryManager = nullptr;
} }
mBatteryPromise = nullptr;
#ifdef MOZ_B2G_FM #ifdef MOZ_B2G_FM
if (mFMRadio) { if (mFMRadio) {
mFMRadio->Shutdown(); mFMRadio->Shutdown();
@ -1455,8 +1458,37 @@ Navigator::GetMozFMRadio(ErrorResult& aRv)
// Navigator::nsINavigatorBattery // Navigator::nsINavigatorBattery
//***************************************************************************** //*****************************************************************************
battery::BatteryManager* Promise*
Navigator::GetBattery(ErrorResult& aRv) Navigator::GetBattery(ErrorResult& aRv)
{
if (mBatteryPromise) {
return mBatteryPromise;
}
if (!mWindow || !mWindow->GetDocShell()) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(mWindow);
nsRefPtr<Promise> batteryPromise = Promise::Create(go, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
mBatteryPromise = batteryPromise;
if (!mBatteryManager) {
mBatteryManager = new battery::BatteryManager(mWindow);
mBatteryManager->Init();
}
mBatteryPromise->MaybeResolve(mBatteryManager);
return mBatteryPromise;
}
battery::BatteryManager*
Navigator::GetDeprecatedBattery(ErrorResult& aRv)
{ {
if (!mBatteryManager) { if (!mBatteryManager) {
if (!mWindow) { if (!mWindow) {

View File

@ -160,7 +160,8 @@ public:
Permissions* GetPermissions(ErrorResult& aRv); Permissions* GetPermissions(ErrorResult& aRv);
// The XPCOM GetDoNotTrack is ok // The XPCOM GetDoNotTrack is ok
Geolocation* GetGeolocation(ErrorResult& aRv); Geolocation* GetGeolocation(ErrorResult& aRv);
battery::BatteryManager* GetBattery(ErrorResult& aRv); Promise* GetBattery(ErrorResult& aRv);
battery::BatteryManager* GetDeprecatedBattery(ErrorResult& aRv);
static already_AddRefed<Promise> GetDataStores(nsPIDOMWindow* aWindow, static already_AddRefed<Promise> GetDataStores(nsPIDOMWindow* aWindow,
const nsAString& aName, const nsAString& aName,
@ -371,6 +372,7 @@ private:
nsRefPtr<Geolocation> mGeolocation; nsRefPtr<Geolocation> mGeolocation;
nsRefPtr<DesktopNotificationCenter> mNotification; nsRefPtr<DesktopNotificationCenter> mNotification;
nsRefPtr<battery::BatteryManager> mBatteryManager; nsRefPtr<battery::BatteryManager> mBatteryManager;
nsRefPtr<Promise> mBatteryPromise;
#ifdef MOZ_B2G_FM #ifdef MOZ_B2G_FM
nsRefPtr<FMRadio> mFMRadio; nsRefPtr<FMRadio> mFMRadio;
#endif #endif

View File

@ -10,6 +10,7 @@
#include "mozilla/DOMEventTargetHelper.h" #include "mozilla/DOMEventTargetHelper.h"
#include "mozilla/Hal.h" #include "mozilla/Hal.h"
#include "mozilla/dom/BatteryManagerBinding.h" #include "mozilla/dom/BatteryManagerBinding.h"
#include "mozilla/Preferences.h"
#include "nsIDOMClassInfo.h" #include "nsIDOMClassInfo.h"
/** /**
@ -56,10 +57,37 @@ BatteryManager::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
return BatteryManagerBinding::Wrap(aCx, this, aGivenProto); return BatteryManagerBinding::Wrap(aCx, this, aGivenProto);
} }
bool
BatteryManager::Charging() const
{
MOZ_ASSERT(NS_IsMainThread());
// For testing, unable to report the battery status information
if (Preferences::GetBool("dom.battery.test.default", false)) {
return true;
}
if (Preferences::GetBool("dom.battery.test.charging", false)) {
return true;
}
if (Preferences::GetBool("dom.battery.test.discharging", false)) {
return false;
}
return mCharging;
}
double double
BatteryManager::DischargingTime() const BatteryManager::DischargingTime() const
{ {
if (mCharging || mRemainingTime == kUnknownRemainingTime) { MOZ_ASSERT(NS_IsMainThread());
// For testing, unable to report the battery status information
if (Preferences::GetBool("dom.battery.test.default", false)) {
return std::numeric_limits<double>::infinity();
}
if (Preferences::GetBool("dom.battery.test.discharging", false)) {
return 42.0;
}
if (Charging() || mRemainingTime == kUnknownRemainingTime) {
return std::numeric_limits<double>::infinity(); return std::numeric_limits<double>::infinity();
} }
@ -69,13 +97,34 @@ BatteryManager::DischargingTime() const
double double
BatteryManager::ChargingTime() const BatteryManager::ChargingTime() const
{ {
if (!mCharging || mRemainingTime == kUnknownRemainingTime) { MOZ_ASSERT(NS_IsMainThread());
// For testing, unable to report the battery status information
if (Preferences::GetBool("dom.battery.test.default", false)) {
return 0.0;
}
if (Preferences::GetBool("dom.battery.test.charging", false)) {
return 42.0;
}
if (!Charging() || mRemainingTime == kUnknownRemainingTime) {
return std::numeric_limits<double>::infinity(); return std::numeric_limits<double>::infinity();
} }
return mRemainingTime; return mRemainingTime;
} }
double
BatteryManager::Level() const
{
MOZ_ASSERT(NS_IsMainThread());
// For testing, unable to report the battery status information
if (Preferences::GetBool("dom.battery.test.default")) {
return 1.0;
}
return mLevel;
}
void void
BatteryManager::UpdateFromBatteryInfo(const hal::BatteryInformation& aBatteryInfo) BatteryManager::UpdateFromBatteryInfo(const hal::BatteryInformation& aBatteryInfo)
{ {

View File

@ -46,19 +46,13 @@ public:
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
bool Charging() const bool Charging() const;
{
return mCharging;
}
double ChargingTime() const; double ChargingTime() const;
double DischargingTime() const; double DischargingTime() const;
double Level() const double Level() const;
{
return mLevel;
}
IMPL_EVENT_HANDLER(chargingchange) IMPL_EVENT_HANDLER(chargingchange)
IMPL_EVENT_HANDLER(chargingtimechange) IMPL_EVENT_HANDLER(chargingtimechange)

View File

@ -9,3 +9,9 @@ qemu = true
[test_battery_status_full.js] [test_battery_status_full.js]
[test_battery_status_not_charging.js] [test_battery_status_not_charging.js]
[test_battery_status_unknown.js] [test_battery_status_unknown.js]
[test_deprecated_battery_level.js]
[test_deprecated_battery_status_charging.js]
[test_deprecated_battery_status_discharging.js]
[test_deprecated_battery_status_full.js]
[test_deprecated_battery_status_not_charging.js]
[test_deprecated_battery_status_unknown.js]

View File

@ -3,15 +3,18 @@
MARIONETTE_TIMEOUT = 10000; MARIONETTE_TIMEOUT = 10000;
let battery = window.navigator.battery; let battery = null;
function verifyInitialState() { function verifyInitialState() {
ok(battery, "battery"); window.navigator.getBattery().then(function (b) {
is(battery.level, 0.5, "battery.level"); battery = b;
runEmulatorCmd("power display", function (result) { ok(battery, "battery");
is(result.pop(), "OK", "power display successful"); is(battery.level, 0.5, "battery.level");
ok(result.indexOf("capacity: 50") !== -1, "power capacity"); runEmulatorCmd("power display", function (result) {
setUp(); is(result.pop(), "OK", "power display successful");
ok(result.indexOf("capacity: 50") !== -1, "power capacity");
setUp();
});
}); });
} }
@ -29,10 +32,10 @@ function changeCapacity(capacity, changeExpected, nextFunction) {
log("Changing power capacity to " + capacity); log("Changing power capacity to " + capacity);
if (changeExpected) { if (changeExpected) {
battery.onlevelchange = function (event) { battery.onlevelchange = function (event) {
battery.onlevelchange = unexpectedEvent; battery.onlevelchange = unexpectedEvent;
is(event.type, "levelchange", "event.type"); is(event.type, "levelchange", "event.type");
is(battery.level, capacity / 100, "battery.level"); is(battery.level, capacity / 100, "battery.level");
nextFunction(); nextFunction();
}; };
runEmulatorCmd("power capacity " + capacity); runEmulatorCmd("power capacity " + capacity);
} }

View File

@ -3,17 +3,20 @@
MARIONETTE_TIMEOUT = 10000; MARIONETTE_TIMEOUT = 10000;
let battery = window.navigator.battery; let battery = null;
let fromStatus = "charging"; let fromStatus = "charging";
let fromCharging = true; let fromCharging = true;
function verifyInitialState() { function verifyInitialState() {
ok(battery, "battery"); window.navigator.getBattery().then(function (b) {
ok(battery.charging, "battery.charging"); battery = b;
runEmulatorCmd("power display", function (result) { ok(battery, "battery");
is(result.pop(), "OK", "power display successful"); ok(battery.charging, "battery.charging");
ok(result.indexOf("status: Charging") !== -1, "power status charging"); runEmulatorCmd("power display", function (result) {
setUp(); is(result.pop(), "OK", "power display successful");
ok(result.indexOf("status: Charging") !== -1, "power status charging");
setUp();
});
}); });
} }

View File

@ -3,17 +3,20 @@
MARIONETTE_TIMEOUT = 10000; MARIONETTE_TIMEOUT = 10000;
let battery = window.navigator.battery; let battery = null;
let fromStatus = "discharging"; let fromStatus = "discharging";
let fromCharging = false; let fromCharging = false;
function verifyInitialState() { function verifyInitialState() {
ok(battery, "battery"); window.navigator.getBattery().then(function (b) {
ok(battery.charging, "battery.charging"); battery = b;
runEmulatorCmd("power display", function (result) { ok(battery, "battery");
is(result.pop(), "OK", "power display successful"); ok(battery.charging, "battery.charging");
ok(result.indexOf("status: Charging") !== -1, "power status charging"); runEmulatorCmd("power display", function (result) {
setUp(); is(result.pop(), "OK", "power display successful");
ok(result.indexOf("status: Charging") !== -1, "power status charging");
setUp();
});
}); });
} }

View File

@ -8,12 +8,15 @@ let fromStatus = "full";
let fromCharging = true; let fromCharging = true;
function verifyInitialState() { function verifyInitialState() {
ok(battery, "battery"); window.navigator.getBattery().then(function (b) {
ok(battery.charging, "battery.charging"); battery = b;
runEmulatorCmd("power display", function (result) { ok(battery, "battery");
is(result.pop(), "OK", "power display successful"); ok(battery.charging, "battery.charging");
ok(result.indexOf("status: Charging") !== -1, "power status charging"); runEmulatorCmd("power display", function (result) {
setUp(); is(result.pop(), "OK", "power display successful");
ok(result.indexOf("status: Charging") !== -1, "power status charging");
setUp();
});
}); });
} }

View File

@ -8,12 +8,15 @@ let fromStatus = "not-charging";
let fromCharging = false; let fromCharging = false;
function verifyInitialState() { function verifyInitialState() {
ok(battery, "battery"); window.navigator.getBattery().then(function (b) {
ok(battery.charging, "battery.charging"); battery = b;
runEmulatorCmd("power display", function (result) { ok(battery, "battery");
is(result.pop(), "OK", "power display successful"); ok(battery.charging, "battery.charging");
ok(result.indexOf("status: Charging") !== -1, "power status charging"); runEmulatorCmd("power display", function (result) {
setUp(); is(result.pop(), "OK", "power display successful");
ok(result.indexOf("status: Charging") !== -1, "power status charging");
setUp();
});
}); });
} }

View File

@ -8,12 +8,15 @@ let fromStatus = "unknown";
let fromCharging = false; let fromCharging = false;
function verifyInitialState() { function verifyInitialState() {
ok(battery, "battery"); window.navigator.getBattery().then(function (b) {
ok(battery.charging, "battery.charging"); battery = b;
runEmulatorCmd("power display", function (result) { ok(battery, "battery");
is(result.pop(), "OK", "power display successful"); ok(battery.charging, "battery.charging");
ok(result.indexOf("status: Charging") !== -1, "power status charging"); runEmulatorCmd("power display", function (result) {
setUp(); is(result.pop(), "OK", "power display successful");
ok(result.indexOf("status: Charging") !== -1, "power status charging");
setUp();
});
}); });
} }

View File

@ -0,0 +1,68 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
MARIONETTE_TIMEOUT = 10000;
let battery = window.navigator.battery;
function verifyInitialState() {
ok(battery, "battery");
is(battery.level, 0.5, "battery.level");
runEmulatorCmd("power display", function (result) {
is(result.pop(), "OK", "power display successful");
ok(result.indexOf("capacity: 50") !== -1, "power capacity");
setUp();
});
}
function unexpectedEvent(event) {
ok(false, "Unexpected " + event.type + " event");
}
function setUp() {
battery.onchargingchange = unexpectedEvent;
battery.onlevelchange = unexpectedEvent;
levelUp();
}
function changeCapacity(capacity, changeExpected, nextFunction) {
log("Changing power capacity to " + capacity);
if (changeExpected) {
battery.onlevelchange = function (event) {
battery.onlevelchange = unexpectedEvent;
is(event.type, "levelchange", "event.type");
is(battery.level, capacity / 100, "battery.level");
nextFunction();
};
runEmulatorCmd("power capacity " + capacity);
}
else {
runEmulatorCmd("power capacity " + capacity, function () {
is(battery.level, capacity / 100, "battery.level");
nextFunction();
});
}
}
function levelUp() {
changeCapacity("90", true, levelDown);
}
function levelDown() {
changeCapacity("10", true, levelSame);
}
function levelSame() {
changeCapacity("10", false, cleanUp);
}
function cleanUp() {
battery.onchargingchange = null;
battery.onlevelchange = function () {
battery.onlevelchange = null;
finish();
};
runEmulatorCmd("power capacity 50");
}
verifyInitialState();

View File

@ -0,0 +1,85 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
MARIONETTE_TIMEOUT = 10000;
let battery = window.navigator.battery;
let fromStatus = "charging";
let fromCharging = true;
function verifyInitialState() {
ok(battery, "battery");
ok(battery.charging, "battery.charging");
runEmulatorCmd("power display", function (result) {
is(result.pop(), "OK", "power display successful");
ok(result.indexOf("status: Charging") !== -1, "power status charging");
setUp();
});
}
function unexpectedEvent(event) {
ok(false, "Unexpected " + event.type + " event");
}
function setUp() {
battery.onchargingchange = unexpectedEvent;
battery.onlevelchange = unexpectedEvent;
toDischarging();
}
function resetStatus(charging, nextFunction) {
log("Resetting power status to " + fromStatus);
if (charging !== fromCharging) {
battery.onchargingchange = function () {
battery.onchargingchange = unexpectedEvent;
nextFunction();
};
runEmulatorCmd("power status " + fromStatus);
}
else {
runEmulatorCmd("power status " + fromStatus, nextFunction);
}
}
function changeStatus(toStatus, toCharging, nextFunction) {
log("Changing power status to " + toStatus);
if (fromCharging !== toCharging) {
battery.onchargingchange = function (event) {
battery.onchargingchange = unexpectedEvent;
is(event.type, "chargingchange", "event type");
is(battery.charging, toCharging, "battery.charging");
resetStatus(toCharging, nextFunction);
};
runEmulatorCmd("power status " + toStatus);
}
else {
runEmulatorCmd("power status " + toStatus, function () {
is(battery.charging, toCharging, "battery.charging");
resetStatus(toCharging, nextFunction);
});
}
}
function toDischarging() {
changeStatus("discharging", false, toFull);
}
function toFull() {
changeStatus("full", true, toNotCharging);
}
function toNotCharging() {
changeStatus("not-charging", false, toUnknown);
}
function toUnknown() {
changeStatus("unknown", false, cleanUp);
}
function cleanUp() {
battery.onchargingchange = null;
battery.onlevelchange = null;
finish();
}
verifyInitialState();

View File

@ -0,0 +1,93 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
MARIONETTE_TIMEOUT = 10000;
let battery = window.navigator.battery;
let fromStatus = "discharging";
let fromCharging = false;
function verifyInitialState() {
ok(battery, "battery");
ok(battery.charging, "battery.charging");
runEmulatorCmd("power display", function (result) {
is(result.pop(), "OK", "power display successful");
ok(result.indexOf("status: Charging") !== -1, "power status charging");
setUp();
});
}
function unexpectedEvent(event) {
ok(false, "Unexpected " + event.type + " event");
}
function setUp() {
battery.onchargingchange = function () {
battery.onchargingchange = unexpectedEvent;
toCharging();
};
battery.onlevelchange = unexpectedEvent;
log("Changing power status to " + fromStatus);
runEmulatorCmd("power status " + fromStatus);
}
function resetStatus(charging, nextFunction) {
log("Resetting power status to " + fromStatus);
if (charging !== fromCharging) {
battery.onchargingchange = function () {
battery.onchargingchange = unexpectedEvent;
nextFunction();
};
runEmulatorCmd("power status " + fromStatus);
}
else {
runEmulatorCmd("power status " + fromStatus, nextFunction);
}
}
function changeStatus(toStatus, toCharging, nextFunction) {
log("Changing power status to " + toStatus);
if (fromCharging !== toCharging) {
battery.onchargingchange = function (event) {
battery.onchargingchange = unexpectedEvent;
is(event.type, "chargingchange", "event type");
is(battery.charging, toCharging, "battery.charging");
resetStatus(toCharging, nextFunction);
};
runEmulatorCmd("power status " + toStatus);
}
else {
runEmulatorCmd("power status " + toStatus, function () {
is(battery.charging, toCharging, "battery.charging");
resetStatus(toCharging, nextFunction);
});
}
}
function toCharging() {
changeStatus("charging", true, toFull);
}
function toFull() {
changeStatus("full", true, toNotCharging);
}
function toNotCharging() {
changeStatus("not-charging", false, toUnknown);
}
function toUnknown() {
changeStatus("unknown", false, cleanUp);
}
function cleanUp() {
battery.onchargingchange = function () {
battery.onchargingchange = null;
finish();
};
battery.onlevelchange = null;
log("Resetting power status to charging");
runEmulatorCmd("power status charging");
}
verifyInitialState();

View File

@ -0,0 +1,87 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
MARIONETTE_TIMEOUT = 10000;
let battery = window.navigator.battery;
let fromStatus = "full";
let fromCharging = true;
function verifyInitialState() {
ok(battery, "battery");
ok(battery.charging, "battery.charging");
runEmulatorCmd("power display", function (result) {
is(result.pop(), "OK", "power display successful");
ok(result.indexOf("status: Charging") !== -1, "power status charging");
setUp();
});
}
function unexpectedEvent(event) {
ok(false, "Unexpected " + event.type + " event");
}
function setUp() {
battery.onchargingchange = unexpectedEvent;
battery.onlevelchange = unexpectedEvent;
log("Changing power status to " + fromStatus);
runEmulatorCmd("power status " + fromStatus, toCharging);
}
function resetStatus(charging, nextFunction) {
log("Resetting power status to " + fromStatus);
if (charging !== fromCharging) {
battery.onchargingchange = function () {
battery.onchargingchange = unexpectedEvent;
nextFunction();
};
runEmulatorCmd("power status " + fromStatus);
}
else {
runEmulatorCmd("power status " + fromStatus, nextFunction);
}
}
function changeStatus(toStatus, toCharging, nextFunction) {
log("Changing power status to " + toStatus);
if (fromCharging !== toCharging) {
battery.onchargingchange = function (event) {
battery.onchargingchange = unexpectedEvent;
is(event.type, "chargingchange", "event type");
is(battery.charging, toCharging, "battery.charging");
resetStatus(toCharging, nextFunction);
};
runEmulatorCmd("power status " + toStatus);
}
else {
runEmulatorCmd("power status " + toStatus, function () {
is(battery.charging, toCharging, "battery.charging");
resetStatus(toCharging, nextFunction);
});
}
}
function toCharging() {
changeStatus("charging", true, toDischarging);
}
function toDischarging() {
changeStatus("discharging", false, toNotCharging);
}
function toNotCharging() {
changeStatus("not-charging", false, toUnknown);
}
function toUnknown() {
changeStatus("unknown", false, cleanUp);
}
function cleanUp() {
battery.onchargingchange = null;
battery.onlevelchange = null;
log("Resetting power status to charging");
runEmulatorCmd("power status charging", finish);
}
verifyInitialState();

View File

@ -0,0 +1,93 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
MARIONETTE_TIMEOUT = 10000;
let battery = window.navigator.battery;
let fromStatus = "not-charging";
let fromCharging = false;
function verifyInitialState() {
ok(battery, "battery");
ok(battery.charging, "battery.charging");
runEmulatorCmd("power display", function (result) {
is(result.pop(), "OK", "power display successful");
ok(result.indexOf("status: Charging") !== -1, "power status charging");
setUp();
});
}
function unexpectedEvent(event) {
ok(false, "Unexpected " + event.type + " event");
}
function setUp() {
battery.onchargingchange = function () {
battery.onchargingchange = unexpectedEvent;
toCharging();
};
battery.onlevelchange = unexpectedEvent;
log("Changing power status to " + fromStatus);
runEmulatorCmd("power status " + fromStatus);
}
function resetStatus(charging, nextFunction) {
log("Resetting power status to " + fromStatus);
if (charging !== fromCharging) {
battery.onchargingchange = function () {
battery.onchargingchange = unexpectedEvent;
nextFunction();
};
runEmulatorCmd("power status " + fromStatus);
}
else {
runEmulatorCmd("power status " + fromStatus, nextFunction);
}
}
function changeStatus(toStatus, toCharging, nextFunction) {
log("Changing power status to " + toStatus);
if (fromCharging !== toCharging) {
battery.onchargingchange = function (event) {
battery.onchargingchange = unexpectedEvent;
is(event.type, "chargingchange", "event type");
is(battery.charging, toCharging, "battery.charging");
resetStatus(toCharging, nextFunction);
};
runEmulatorCmd("power status " + toStatus);
}
else {
runEmulatorCmd("power status " + toStatus, function () {
is(battery.charging, toCharging, "battery.charging");
resetStatus(toCharging, nextFunction);
});
}
}
function toCharging() {
changeStatus("charging", true, toDischarging);
}
function toDischarging() {
changeStatus("discharging", false, toFull);
}
function toFull() {
changeStatus("full", true, toUnknown);
}
function toUnknown() {
changeStatus("unknown", false, cleanUp);
}
function cleanUp() {
battery.onchargingchange = function () {
battery.onchargingchange = null;
finish();
};
battery.onlevelchange = null;
log("Resetting power status to charging");
runEmulatorCmd("power status charging");
}
verifyInitialState();

View File

@ -0,0 +1,93 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
MARIONETTE_TIMEOUT = 10000;
let battery = window.navigator.battery;
let fromStatus = "unknown";
let fromCharging = false;
function verifyInitialState() {
ok(battery, "battery");
ok(battery.charging, "battery.charging");
runEmulatorCmd("power display", function (result) {
is(result.pop(), "OK", "power display successful");
ok(result.indexOf("status: Charging") !== -1, "power status charging");
setUp();
});
}
function unexpectedEvent(event) {
ok(false, "Unexpected " + event.type + " event");
}
function setUp() {
battery.onchargingchange = function () {
battery.onchargingchange = unexpectedEvent;
toCharging();
};
battery.onlevelchange = unexpectedEvent;
log("Changing power status to " + fromStatus);
runEmulatorCmd("power status " + fromStatus);
}
function resetStatus(charging, nextFunction) {
log("Resetting power status to " + fromStatus);
if (charging !== fromCharging) {
battery.onchargingchange = function () {
battery.onchargingchange = unexpectedEvent;
nextFunction();
};
runEmulatorCmd("power status " + fromStatus);
}
else {
runEmulatorCmd("power status " + fromStatus, nextFunction);
}
}
function changeStatus(toStatus, toCharging, nextFunction) {
log("Changing power status to " + toStatus);
if (fromCharging !== toCharging) {
battery.onchargingchange = function (event) {
battery.onchargingchange = unexpectedEvent;
is(event.type, "chargingchange", "event type");
is(battery.charging, toCharging, "battery.charging");
resetStatus(toCharging, nextFunction);
};
runEmulatorCmd("power status " + toStatus);
}
else {
runEmulatorCmd("power status " + toStatus, function () {
is(battery.charging, toCharging, "battery.charging");
resetStatus(toCharging, nextFunction);
});
}
}
function toCharging() {
changeStatus("charging", true, toDischarging);
}
function toDischarging() {
changeStatus("discharging", false, toFull);
}
function toFull() {
changeStatus("full", true, toNotCharging);
}
function toNotCharging() {
changeStatus("not-charging", false, cleanUp);
}
function cleanUp() {
battery.onchargingchange = function () {
battery.onchargingchange = null;
finish();
};
battery.onlevelchange = null;
log("Resetting power status to charging");
runEmulatorCmd("power status charging");
}
verifyInitialState();

View File

@ -1,2 +1,8 @@
[test_battery_basics.html] [test_battery_basics.html]
skip-if = (buildapp == 'b2g' && (toolkit != 'gonk' || debug)) skip-if = (buildapp == 'b2g' && (toolkit != 'gonk' || debug))
[test_battery_charging.html]
skip-if = (buildapp == 'b2g' && (toolkit != 'gonk' || debug))
[test_battery_discharging.html]
skip-if = (buildapp == 'b2g' && (toolkit != 'gonk' || debug))
[test_deprecated_battery_basics.html]
skip-if = (buildapp == 'b2g' && (toolkit != 'gonk' || debug))

View File

@ -12,20 +12,23 @@
<pre id="test"> <pre id="test">
<script type="application/javascript"> <script type="application/javascript">
SimpleTest.waitForExplicitFinish();
/** Test for Battery API **/ /** Test for Battery API **/
ok("getBattery" in navigator, "navigator.getBattery should exist");
ok('battery' in navigator, "navigator.battery should exist"); navigator.getBattery().then(function (battery) {
ok(battery.level >= 0.0 && battery.level <= 1.0, "Battery level " + battery.level + " should be in the range [0.0, 1.0]");
var battery = navigator.battery; SpecialPowers.pushPrefEnv({"set": [["dom.battery.test.default", true]]}, function () {
ok(battery.level >= 0.0 && battery.level <= 1.0, "Battery level " + battery.level + " should be in the range [0.0, 1.0]"); ok(battery.charging, "Battery should be charging by default");
is(battery.chargingTime, 0, "Battery chargingTime " + battery.chargingTime + " should be zero by default");
is(battery.dischargingTime, Infinity, "Battery dischargingTime should be Infinity by default");
is(battery.level, 1.0, "Battery level " + battery.level + " should be 1.0 by default");
if (battery.charging) { SimpleTest.finish();
ok(battery.chargingTime >= 0, "Battery chargingTime " + battery.chargingTime + " should be nonnegative when charging"); });
is(battery.dischargingTime, Infinity, "Battery dischargingTime should be Infinity when charging"); });
} else {
is(battery.chargingTime, Infinity, "Battery chargingTime should be Infinity when discharging");
ok(battery.dischargingTime > 0, "Battery dischargingTime " + battery.dischargingTime + " should be positive when discharging");
}
</script> </script>
</pre> </pre>

View File

@ -0,0 +1,33 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for Battery API</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script type="application/javascript">
SimpleTest.waitForExplicitFinish();
/** Test for Battery API **/
navigator.getBattery().then(function (battery) {
ok(battery.level >= 0.0 && battery.level <= 1.0, "Battery level " + battery.level + " should be in the range [0.0, 1.0]");
SpecialPowers.pushPrefEnv({"set": [["dom.battery.test.charging", true]]}, function () {
is(battery.charging, true, "Battery should be charging");
ok(battery.chargingTime >= 0, "Battery chargingTime " + battery.chargingTime + " should be nonnegative when charging");
is(battery.dischargingTime, Infinity, "Battery dischargingTime should be Infinity when charging");
SimpleTest.finish();
});
});
</script>
</pre>
</body>
</html>

View File

@ -0,0 +1,33 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for Battery API</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script type="application/javascript">
SimpleTest.waitForExplicitFinish();
/** Test for Battery API **/
navigator.getBattery().then(function (battery) {
ok(battery.level >= 0.0 && battery.level <= 1.0, "Battery level " + battery.level + " should be in the range [0.0, 1.0]");
SpecialPowers.pushPrefEnv({"set": [["dom.battery.test.discharging", true]]}, function () {
is(battery.charging, false, "Battery should be discharging");
is(battery.chargingTime, Infinity, "Battery chargingTime should be Infinity when discharging");
ok(battery.dischargingTime > 0, "Battery dischargingTime " + battery.dischargingTime + " should be positive when discharging");
SimpleTest.finish();
});
});
</script>
</pre>
</body>
</html>

View File

@ -0,0 +1,32 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for Battery API</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script type="application/javascript">
/** Test for Battery API **/
ok("battery" in navigator, "navigator.battery should exist");
var battery = navigator.battery;
ok(battery.level >= 0.0 && battery.level <= 1.0, "Battery level " + battery.level + " should be in the range [0.0, 1.0]");
if (battery.charging) {
ok(battery.chargingTime >= 0, "Battery chargingTime " + battery.chargingTime + " should be nonnegative when charging");
is(battery.dischargingTime, Infinity, "Battery dischargingTime should be Infinity when charging");
} else {
is(battery.chargingTime, Infinity, "Battery chargingTime should be Infinity when discharging");
ok(battery.dischargingTime > 0, "Battery dischargingTime " + battery.dischargingTime + " should be positive when discharging");
}
</script>
</pre>
</body>
</html>

View File

@ -125,14 +125,15 @@ interface NavigatorGeolocation {
Navigator implements NavigatorGeolocation; Navigator implements NavigatorGeolocation;
// http://www.w3.org/TR/battery-status/#navigatorbattery-interface // http://www.w3.org/TR/battery-status/#navigatorbattery-interface
[NoInterfaceObject] partial interface Navigator {
interface NavigatorBattery { [Throws, Pref="dom.battery.enabled"]
// XXXbz Per spec this should be non-nullable, but we return null in Promise<BatteryManager> getBattery();
// torn-down windows. See bug 884925. // Deprecated. Use getBattery() instead.
[Throws, Pref="dom.battery.enabled"] // XXXbz Per spec this should be non-nullable, but we return null in
readonly attribute BatteryManager? battery; // torn-down windows. See bug 884925.
[Throws, Pref="dom.battery.enabled", BinaryName="deprecatedBattery"]
readonly attribute BatteryManager? battery;
}; };
Navigator implements NavigatorBattery;
// https://wiki.mozilla.org/WebAPI/DataStore // https://wiki.mozilla.org/WebAPI/DataStore
[NoInterfaceObject, [NoInterfaceObject,

View File

@ -5,12 +5,12 @@
"use strict"; "use strict";
/** This module wraps around navigator.battery (https://developer.mozilla.org/en-US/docs/Web/API/Navigator.battery). /** This module wraps around navigator.getBattery (https://developer.mozilla.org/en-US/docs/Web/API/Navigator.getBattery).
* and provides a framework for spoofing battery values in test code. * and provides a framework for spoofing battery values in test code.
* To spoof the battery values, set `Debugging.fake = true` after exporting this with a BackstagePass, * To spoof the battery values, set `Debugging.fake = true` after exporting this with a BackstagePass,
* after which you can spoof a property yb setting the relevant property of the Battery object. * after which you can spoof a property yb setting the relevant property of the BatteryManager object.
*/ */
this.EXPORTED_SYMBOLS = ["Battery"]; this.EXPORTED_SYMBOLS = ["GetBattery", "Battery"];
const Ci = Components.interfaces; const Ci = Components.interfaces;
const Cc = Components.classes; const Cc = Components.classes;
@ -40,6 +40,17 @@ this.Debugging = {
fake: false fake: false
} }
this.GetBattery = function () {
return new Services.appShell.hiddenDOMWindow.Promise(function (resolve, reject) {
// Return fake values if spoofing is enabled, otherwise fetch the real values from the BatteryManager API
if (Debugging.fake) {
resolve(gFakeBattery);
return;
}
Services.appShell.hiddenDOMWindow.navigator.getBattery().then(resolve, reject);
});
};
this.Battery = {}; this.Battery = {};
for (let k of ["charging", "chargingTime", "dischargingTime", "level"]) { for (let k of ["charging", "chargingTime", "dischargingTime", "level"]) {
@ -54,9 +65,9 @@ for (let k of ["charging", "chargingTime", "dischargingTime", "level"]) {
}, },
set: function(fakeSetting) { set: function(fakeSetting) {
if (!Debugging.fake) { if (!Debugging.fake) {
throw new Error("Tried to set fake battery value when battery spoofing was disabled"); throw new Error("Tried to set fake battery value when battery spoofing was disabled");
} }
gFakeBattery[prop] = fakeSetting; gFakeBattery[prop] = fakeSetting;
} }
}) })
} }

View File

@ -6,8 +6,11 @@ let imported = Components.utils.import("resource://gre/modules/Battery.jsm", thi
Cu.import("resource://gre/modules/Services.jsm", this); Cu.import("resource://gre/modules/Services.jsm", this);
function test() { function test() {
waitForExplicitFinish();
is(imported.Debugging.fake, false, "Battery spoofing is initially false") is(imported.Debugging.fake, false, "Battery spoofing is initially false")
// begin deprecated battery API testing
for (let k of ["charging", "chargingTime", "dischargingTime", "level"]) { for (let k of ["charging", "chargingTime", "dischargingTime", "level"]) {
Assert.throws(() => Battery[k] = 10, "Setting battery " + k + "preference without spoofing enabled should throw"); Assert.throws(() => Battery[k] = 10, "Setting battery " + k + "preference without spoofing enabled should throw");
ok(Battery[k] == Services.appShell.hiddenDOMWindow.navigator.battery[k], "Battery "+ k + " is correctly set"); ok(Battery[k] == Services.appShell.hiddenDOMWindow.navigator.battery[k], "Battery "+ k + " is correctly set");
@ -27,4 +30,36 @@ function test() {
ok(!Battery.charging, "Test for charging setter"); ok(!Battery.charging, "Test for charging setter");
is(Battery.dischargingTime, 50, "Test for dischargingTime setter"); is(Battery.dischargingTime, 50, "Test for dischargingTime setter");
is(Battery.level, 0.7, "Test for level setter"); is(Battery.level, 0.7, "Test for level setter");
}
imported.Debugging.fake = false;
// end deprecated battery API testing
GetBattery().then(function (battery) {
for (let k of ["charging", "chargingTime", "dischargingTime", "level"]) {
let backup = battery[k];
battery[k] = "__magic__";
is(battery[k], backup, "Setting battery " + k + "preference without spoofing enabled should fail");
}
imported.Debugging.fake = true;
// reload again to get the fake one
GetBattery().then(function (battery) {
battery.charging = true;
battery.chargingTime = 100;
battery.level = 0.5;
ok(battery.charging, "Test for charging setter");
is(battery.chargingTime, 100, "Test for chargingTime setter");
is(battery.level, 0.5, "Test for level setter");
battery.charging = false;
battery.dischargingTime = 50;
battery.level = 0.7;
ok(!battery.charging, "Test for charging setter");
is(battery.dischargingTime, 50, "Test for dischargingTime setter");
is(battery.level, 0.7, "Test for level setter");
finish();
});
});
}