summaryrefslogtreecommitdiffstats
path: root/dom/power
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /dom/power
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'dom/power')
-rw-r--r--dom/power/PowerManager.cpp210
-rw-r--r--dom/power/PowerManager.h70
-rw-r--r--dom/power/PowerManagerService.cpp255
-rw-r--r--dom/power/PowerManagerService.h86
-rw-r--r--dom/power/Types.h22
-rw-r--r--dom/power/WakeLock.cpp281
-rw-r--r--dom/power/WakeLock.h90
-rw-r--r--dom/power/moz.build36
-rw-r--r--dom/power/nsIDOMWakeLockListener.idl29
-rw-r--r--dom/power/nsIPowerManagerService.idl47
-rw-r--r--dom/power/test/browser.ini4
-rw-r--r--dom/power/test/browser_wakelocks.js232
-rw-r--r--dom/power/test/chrome.ini1
-rw-r--r--dom/power/test/mochitest.ini3
-rw-r--r--dom/power/test/test_bug957893.html24
-rw-r--r--dom/power/test/test_bug957899.html20
-rw-r--r--dom/power/test/test_power_basics.html40
-rw-r--r--dom/power/test/test_wakelock_not_exposed.html17
18 files changed, 1467 insertions, 0 deletions
diff --git a/dom/power/PowerManager.cpp b/dom/power/PowerManager.cpp
new file mode 100644
index 000000000..64534976d
--- /dev/null
+++ b/dom/power/PowerManager.cpp
@@ -0,0 +1,210 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 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/. */
+
+#include "mozilla/dom/PowerManager.h"
+
+#include "mozilla/Hal.h"
+#include "WakeLock.h"
+#include "nsDOMClassInfoID.h"
+#include "nsIDOMWakeLockListener.h"
+#include "nsIDocument.h"
+#include "nsIPermissionManager.h"
+#include "nsIPowerManagerService.h"
+#include "nsIPrincipal.h"
+#include "nsPIDOMWindow.h"
+#include "nsServiceManagerUtils.h"
+#include "nsError.h"
+#include "mozilla/dom/MozPowerManagerBinding.h"
+#include "mozilla/Services.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PowerManager)
+ NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+ NS_INTERFACE_MAP_ENTRY(nsIDOMMozWakeLockListener)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PowerManager, mListeners, mWindow)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(PowerManager)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(PowerManager)
+
+/* virtual */ JSObject*
+PowerManager::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+ return MozPowerManagerBinding::Wrap(aCx, this, aGivenProto);
+}
+
+nsresult
+PowerManager::Init(nsPIDOMWindowInner* aWindow)
+{
+ mWindow = aWindow;
+
+ nsCOMPtr<nsIPowerManagerService> pmService =
+ do_GetService(POWERMANAGERSERVICE_CONTRACTID);
+ NS_ENSURE_STATE(pmService);
+
+ // Add ourself to the global notification list.
+ pmService->AddWakeLockListener(this);
+ return NS_OK;
+}
+
+nsresult
+PowerManager::Shutdown()
+{
+ nsCOMPtr<nsIPowerManagerService> pmService =
+ do_GetService(POWERMANAGERSERVICE_CONTRACTID);
+ NS_ENSURE_STATE(pmService);
+
+ // Remove ourself from the global notification list.
+ pmService->RemoveWakeLockListener(this);
+ return NS_OK;
+}
+
+void
+PowerManager::Reboot(ErrorResult& aRv)
+{
+ nsCOMPtr<nsIPowerManagerService> pmService =
+ do_GetService(POWERMANAGERSERVICE_CONTRACTID);
+ if (pmService) {
+ pmService->Reboot();
+ } else {
+ aRv.Throw(NS_ERROR_UNEXPECTED);
+ }
+}
+
+void
+PowerManager::FactoryReset(mozilla::dom::FactoryResetReason& aReason)
+{
+ hal::FactoryReset(aReason);
+}
+
+void
+PowerManager::PowerOff(ErrorResult& aRv)
+{
+ nsCOMPtr<nsIPowerManagerService> pmService =
+ do_GetService(POWERMANAGERSERVICE_CONTRACTID);
+ if (pmService) {
+ pmService->PowerOff();
+ } else {
+ aRv.Throw(NS_ERROR_UNEXPECTED);
+ }
+}
+
+void
+PowerManager::AddWakeLockListener(nsIDOMMozWakeLockListener *aListener)
+{
+ if (!mListeners.Contains(aListener)) {
+ mListeners.AppendElement(aListener);
+ }
+}
+
+void
+PowerManager::RemoveWakeLockListener(nsIDOMMozWakeLockListener *aListener)
+{
+ mListeners.RemoveElement(aListener);
+}
+
+void
+PowerManager::GetWakeLockState(const nsAString& aTopic,
+ nsAString& aState,
+ ErrorResult& aRv)
+{
+ nsCOMPtr<nsIPowerManagerService> pmService =
+ do_GetService(POWERMANAGERSERVICE_CONTRACTID);
+ if (pmService) {
+ aRv = pmService->GetWakeLockState(aTopic, aState);
+ } else {
+ aRv.Throw(NS_ERROR_UNEXPECTED);
+ }
+}
+
+NS_IMETHODIMP
+PowerManager::Callback(const nsAString &aTopic, const nsAString &aState)
+{
+ /**
+ * We maintain a local listener list instead of using the global
+ * list so that when the window is destroyed we don't have to
+ * cleanup the mess.
+ * Copy the listeners list before we walk through the callbacks
+ * because the callbacks may install new listeners. We expect no
+ * more than one listener per window, so it shouldn't be too long.
+ */
+ AutoTArray<nsCOMPtr<nsIDOMMozWakeLockListener>, 2> listeners(mListeners);
+ for (uint32_t i = 0; i < listeners.Length(); ++i) {
+ listeners[i]->Callback(aTopic, aState);
+ }
+
+ return NS_OK;
+}
+
+bool
+PowerManager::ScreenEnabled()
+{
+ return hal::GetScreenEnabled();
+}
+
+void
+PowerManager::SetScreenEnabled(bool aEnabled)
+{
+ hal::SetScreenEnabled(aEnabled);
+}
+
+bool
+PowerManager::KeyLightEnabled()
+{
+ return hal::GetKeyLightEnabled();
+}
+
+void
+PowerManager::SetKeyLightEnabled(bool aEnabled)
+{
+ hal::SetKeyLightEnabled(aEnabled);
+}
+
+double
+PowerManager::ScreenBrightness()
+{
+ return hal::GetScreenBrightness();
+}
+
+void
+PowerManager::SetScreenBrightness(double aBrightness, ErrorResult& aRv)
+{
+ if (0 <= aBrightness && aBrightness <= 1) {
+ hal::SetScreenBrightness(aBrightness);
+ } else {
+ aRv.Throw(NS_ERROR_INVALID_ARG);
+ }
+}
+
+bool
+PowerManager::CpuSleepAllowed()
+{
+ return hal::GetCpuSleepAllowed();
+}
+
+void
+PowerManager::SetCpuSleepAllowed(bool aAllowed)
+{
+ hal::SetCpuSleepAllowed(aAllowed);
+}
+
+already_AddRefed<PowerManager>
+PowerManager::CreateInstance(nsPIDOMWindowInner* aWindow)
+{
+ RefPtr<PowerManager> powerManager = new PowerManager();
+ if (NS_FAILED(powerManager->Init(aWindow))) {
+ powerManager = nullptr;
+ }
+
+ return powerManager.forget();
+}
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/power/PowerManager.h b/dom/power/PowerManager.h
new file mode 100644
index 000000000..d8f9b5d79
--- /dev/null
+++ b/dom/power/PowerManager.h
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 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/. */
+#ifndef mozilla_dom_power_PowerManager_h
+#define mozilla_dom_power_PowerManager_h
+
+#include "nsCOMPtr.h"
+#include "nsTArray.h"
+#include "nsIDOMWakeLockListener.h"
+#include "nsIDOMWindow.h"
+#include "nsWeakReference.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsWrapperCache.h"
+#include "mozilla/dom/MozPowerManagerBinding.h"
+
+class nsPIDOMWindowInner;
+
+namespace mozilla {
+class ErrorResult;
+
+namespace dom {
+
+class PowerManager final : public nsIDOMMozWakeLockListener
+ , public nsWrapperCache
+{
+public:
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(PowerManager)
+ NS_DECL_NSIDOMMOZWAKELOCKLISTENER
+
+ nsresult Init(nsPIDOMWindowInner* aWindow);
+ nsresult Shutdown();
+
+ static already_AddRefed<PowerManager> CreateInstance(nsPIDOMWindowInner*);
+
+ // WebIDL
+ nsPIDOMWindowInner* GetParentObject() const
+ {
+ return mWindow;
+ }
+ virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+ void Reboot(ErrorResult& aRv);
+ void FactoryReset(mozilla::dom::FactoryResetReason& aReason);
+ void PowerOff(ErrorResult& aRv);
+ void AddWakeLockListener(nsIDOMMozWakeLockListener* aListener);
+ void RemoveWakeLockListener(nsIDOMMozWakeLockListener* aListener);
+ void GetWakeLockState(const nsAString& aTopic, nsAString& aState,
+ ErrorResult& aRv);
+ bool ScreenEnabled();
+ void SetScreenEnabled(bool aEnabled);
+ bool KeyLightEnabled();
+ void SetKeyLightEnabled(bool aEnabled);
+ double ScreenBrightness();
+ void SetScreenBrightness(double aBrightness, ErrorResult& aRv);
+ bool CpuSleepAllowed();
+ void SetCpuSleepAllowed(bool aAllowed);
+
+private:
+ ~PowerManager() {}
+
+ nsCOMPtr<nsPIDOMWindowInner> mWindow;
+ nsTArray<nsCOMPtr<nsIDOMMozWakeLockListener> > mListeners;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_power_PowerManager_h
diff --git a/dom/power/PowerManagerService.cpp b/dom/power/PowerManagerService.cpp
new file mode 100644
index 000000000..40110aa67
--- /dev/null
+++ b/dom/power/PowerManagerService.cpp
@@ -0,0 +1,255 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 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/. */
+
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/Hal.h"
+#include "mozilla/HalWakeLock.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Services.h"
+#include "jsprf.h"
+#include "nsIDOMWakeLockListener.h"
+#include "nsIDOMWindow.h"
+#include "nsIObserverService.h"
+#include "PowerManagerService.h"
+#include "WakeLock.h"
+
+// For _exit().
+#ifdef XP_WIN
+#include <process.h>
+#else
+#include <unistd.h>
+#endif
+
+#ifdef ANDROID
+#include <android/log.h>
+extern "C" char* PrintJSStack();
+static void LogFunctionAndJSStack(const char* funcname) {
+ char *jsstack = PrintJSStack();
+ __android_log_print(ANDROID_LOG_INFO, "PowerManagerService", \
+ "Call to %s. The JS stack is:\n%s\n",
+ funcname,
+ jsstack ? jsstack : "<no JS stack>");
+ JS_smprintf_free(jsstack);
+}
+// bug 839452
+#define LOG_FUNCTION_AND_JS_STACK() \
+ LogFunctionAndJSStack(__PRETTY_FUNCTION__);
+#else
+#define LOG_FUNCTION_AND_JS_STACK()
+#endif
+
+namespace mozilla {
+namespace dom {
+namespace power {
+
+using namespace hal;
+
+NS_IMPL_ISUPPORTS(PowerManagerService, nsIPowerManagerService)
+
+/* static */ StaticRefPtr<PowerManagerService> PowerManagerService::sSingleton;
+
+/* static */ already_AddRefed<PowerManagerService>
+PowerManagerService::GetInstance()
+{
+ if (!sSingleton) {
+ sSingleton = new PowerManagerService();
+ sSingleton->Init();
+ ClearOnShutdown(&sSingleton);
+ }
+
+ RefPtr<PowerManagerService> service = sSingleton.get();
+ return service.forget();
+}
+
+void
+PowerManagerService::Init()
+{
+ RegisterWakeLockObserver(this);
+
+ // NB: default to *enabling* the watchdog even when the pref is
+ // absent, in case the profile might be damaged and we need to
+ // restart to repair it.
+ mWatchdogTimeoutSecs =
+ Preferences::GetInt("shutdown.watchdog.timeoutSecs", 10);
+}
+
+PowerManagerService::~PowerManagerService()
+{
+ UnregisterWakeLockObserver(this);
+}
+
+void
+PowerManagerService::ComputeWakeLockState(const WakeLockInformation& aWakeLockInfo,
+ nsAString &aState)
+{
+ WakeLockState state = hal::ComputeWakeLockState(aWakeLockInfo.numLocks(),
+ aWakeLockInfo.numHidden());
+ switch (state) {
+ case WAKE_LOCK_STATE_UNLOCKED:
+ aState.AssignLiteral("unlocked");
+ break;
+ case WAKE_LOCK_STATE_HIDDEN:
+ aState.AssignLiteral("locked-background");
+ break;
+ case WAKE_LOCK_STATE_VISIBLE:
+ aState.AssignLiteral("locked-foreground");
+ break;
+ }
+}
+
+void
+PowerManagerService::Notify(const WakeLockInformation& aWakeLockInfo)
+{
+ nsAutoString state;
+ ComputeWakeLockState(aWakeLockInfo, state);
+
+ /**
+ * Copy the listeners list before we walk through the callbacks
+ * because the callbacks may install new listeners. We expect no
+ * more than one listener per window, so it shouldn't be too long.
+ */
+ AutoTArray<nsCOMPtr<nsIDOMMozWakeLockListener>, 2> listeners(mWakeLockListeners);
+
+ for (uint32_t i = 0; i < listeners.Length(); ++i) {
+ listeners[i]->Callback(aWakeLockInfo.topic(), state);
+ }
+}
+
+void
+PowerManagerService::SyncProfile()
+{
+ nsCOMPtr<nsIObserverService> obsServ = services::GetObserverService();
+ if (obsServ) {
+ NS_NAMED_LITERAL_STRING(context, "shutdown-persist");
+ obsServ->NotifyObservers(nullptr, "profile-change-net-teardown", context.get());
+ obsServ->NotifyObservers(nullptr, "profile-change-teardown", context.get());
+ obsServ->NotifyObservers(nullptr, "profile-before-change", context.get());
+ obsServ->NotifyObservers(nullptr, "profile-before-change-qm", context.get());
+ obsServ->NotifyObservers(nullptr, "profile-before-change-telemetry", context.get());
+ }
+}
+
+NS_IMETHODIMP
+PowerManagerService::Reboot()
+{
+ LOG_FUNCTION_AND_JS_STACK() // bug 839452
+
+ StartForceQuitWatchdog(eHalShutdownMode_Reboot, mWatchdogTimeoutSecs);
+ // To synchronize any unsaved user data before rebooting.
+ SyncProfile();
+ hal::Reboot();
+ MOZ_CRASH("hal::Reboot() shouldn't return");
+}
+
+NS_IMETHODIMP
+PowerManagerService::PowerOff()
+{
+ LOG_FUNCTION_AND_JS_STACK() // bug 839452
+
+ StartForceQuitWatchdog(eHalShutdownMode_PowerOff, mWatchdogTimeoutSecs);
+ // To synchronize any unsaved user data before powering off.
+ SyncProfile();
+ hal::PowerOff();
+ MOZ_CRASH("hal::PowerOff() shouldn't return");
+}
+
+NS_IMETHODIMP
+PowerManagerService::Restart()
+{
+ LOG_FUNCTION_AND_JS_STACK() // bug 839452
+
+ // FIXME/bug 796826 this implementation is currently gonk-specific,
+ // because it relies on the Gonk to initialize the Gecko processes to
+ // restart B2G. It's better to do it here to have a real "restart".
+ StartForceQuitWatchdog(eHalShutdownMode_Restart, mWatchdogTimeoutSecs);
+ // Ensure all content processes are dead before we continue
+ // restarting. This code is used to restart to apply updates, and
+ // if we don't join all the subprocesses, race conditions can cause
+ // them to see an inconsistent view of the application directory.
+ ContentParent::JoinAllSubprocesses();
+
+ // To synchronize any unsaved user data before restarting.
+ SyncProfile();
+#ifdef XP_UNIX
+ sync();
+#endif
+ _exit(0);
+ MOZ_CRASH("_exit() shouldn't return");
+}
+
+NS_IMETHODIMP
+PowerManagerService::AddWakeLockListener(nsIDOMMozWakeLockListener *aListener)
+{
+ if (mWakeLockListeners.Contains(aListener))
+ return NS_OK;
+
+ mWakeLockListeners.AppendElement(aListener);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+PowerManagerService::RemoveWakeLockListener(nsIDOMMozWakeLockListener *aListener)
+{
+ mWakeLockListeners.RemoveElement(aListener);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+PowerManagerService::GetWakeLockState(const nsAString &aTopic, nsAString &aState)
+{
+ WakeLockInformation info;
+ GetWakeLockInfo(aTopic, &info);
+
+ ComputeWakeLockState(info, aState);
+
+ return NS_OK;
+}
+
+already_AddRefed<WakeLock>
+PowerManagerService::NewWakeLock(const nsAString& aTopic,
+ nsPIDOMWindowInner* aWindow,
+ mozilla::ErrorResult& aRv)
+{
+ RefPtr<WakeLock> wakelock = new WakeLock();
+ aRv = wakelock->Init(aTopic, aWindow);
+ if (aRv.Failed()) {
+ return nullptr;
+ }
+
+ return wakelock.forget();
+}
+
+NS_IMETHODIMP
+PowerManagerService::NewWakeLock(const nsAString &aTopic,
+ mozIDOMWindow *aWindow,
+ nsISupports **aWakeLock)
+{
+ mozilla::ErrorResult rv;
+ RefPtr<WakeLock> wakelock =
+ NewWakeLock(aTopic, nsPIDOMWindowInner::From(aWindow), rv);
+ if (rv.Failed()) {
+ return rv.StealNSResult();
+ }
+
+ nsCOMPtr<nsIDOMEventListener> eventListener = wakelock.get();
+ eventListener.forget(aWakeLock);
+ return NS_OK;
+}
+
+already_AddRefed<WakeLock>
+PowerManagerService::NewWakeLockOnBehalfOfProcess(const nsAString& aTopic,
+ ContentParent* aContentParent)
+{
+ RefPtr<WakeLock> wakelock = new WakeLock();
+ nsresult rv = wakelock->Init(aTopic, aContentParent);
+ NS_ENSURE_SUCCESS(rv, nullptr);
+ return wakelock.forget();
+}
+
+} // namespace power
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/power/PowerManagerService.h b/dom/power/PowerManagerService.h
new file mode 100644
index 000000000..9d176f1dc
--- /dev/null
+++ b/dom/power/PowerManagerService.h
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 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/. */
+#ifndef mozilla_dom_power_PowerManagerService_h
+#define mozilla_dom_power_PowerManagerService_h
+
+#include "nsCOMPtr.h"
+#include "nsDataHashtable.h"
+#include "nsHashKeys.h"
+#include "nsTArray.h"
+#include "nsIDOMWakeLockListener.h"
+#include "nsIPowerManagerService.h"
+#include "mozilla/Observer.h"
+#include "Types.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/dom/WakeLock.h"
+
+namespace mozilla {
+namespace dom {
+
+class ContentParent;
+
+namespace power {
+
+class PowerManagerService
+ : public nsIPowerManagerService
+ , public WakeLockObserver
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIPOWERMANAGERSERVICE
+
+ PowerManagerService()
+ : mWatchdogTimeoutSecs(0)
+ {}
+
+ static already_AddRefed<PowerManagerService> GetInstance();
+
+ void Init();
+
+ // Implement WakeLockObserver
+ void Notify(const hal::WakeLockInformation& aWakeLockInfo) override;
+
+ /**
+ * Acquire a wake lock on behalf of a given process (aContentParent).
+ *
+ * This method stands in contrast to nsIPowerManagerService::NewWakeLock,
+ * which acquires a wake lock on behalf of the /current/ process.
+ *
+ * NewWakeLockOnBehalfOfProcess is different from NewWakeLock in that
+ *
+ * - The wake lock unlocks itself if the /given/ process dies, and
+ * - The /given/ process shows up in WakeLockInfo::lockingProcesses.
+ *
+ */
+ already_AddRefed<WakeLock>
+ NewWakeLockOnBehalfOfProcess(const nsAString& aTopic,
+ ContentParent* aContentParent);
+
+ already_AddRefed<WakeLock>
+ NewWakeLock(const nsAString& aTopic, nsPIDOMWindowInner* aWindow,
+ mozilla::ErrorResult& aRv);
+
+private:
+
+ ~PowerManagerService();
+
+ void ComputeWakeLockState(const hal::WakeLockInformation& aWakeLockInfo,
+ nsAString &aState);
+
+ void SyncProfile();
+
+ static StaticRefPtr<PowerManagerService> sSingleton;
+
+ nsTArray<nsCOMPtr<nsIDOMMozWakeLockListener>> mWakeLockListeners;
+
+ int32_t mWatchdogTimeoutSecs;
+};
+
+} // namespace power
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_power_PowerManagerService_h
diff --git a/dom/power/Types.h b/dom/power/Types.h
new file mode 100644
index 000000000..f1ea022f4
--- /dev/null
+++ b/dom/power/Types.h
@@ -0,0 +1,22 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 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/. */
+#ifndef mozilla_dom_power_Types_h
+#define mozilla_dom_power_Types_h
+
+namespace mozilla {
+namespace hal {
+class WakeLockInformation;
+} // namespace hal
+
+template <class T>
+class Observer;
+
+typedef Observer<hal::WakeLockInformation> WakeLockObserver;
+
+} // namespace mozilla
+
+#endif // mozilla_dom_power_Types_h
+
diff --git a/dom/power/WakeLock.cpp b/dom/power/WakeLock.cpp
new file mode 100644
index 000000000..9d93bad0f
--- /dev/null
+++ b/dom/power/WakeLock.cpp
@@ -0,0 +1,281 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 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/. */
+
+#include "WakeLock.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/Event.h" // for nsIDOMEvent::InternalDOMEvent()
+#include "mozilla/dom/MozWakeLockBinding.h"
+#include "mozilla/Hal.h"
+#include "mozilla/HalWakeLock.h"
+#include "nsError.h"
+#include "nsIDocument.h"
+#include "nsIDOMWindow.h"
+#include "nsIDOMEvent.h"
+#include "nsPIDOMWindow.h"
+#include "nsIPropertyBag2.h"
+
+using namespace mozilla::hal;
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WakeLock)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WakeLock)
+ NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMEventListener)
+ NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
+ NS_INTERFACE_MAP_ENTRY(nsIObserver)
+ NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(WakeLock)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(WakeLock)
+
+WakeLock::WakeLock()
+ : mLocked(false)
+ , mHidden(true)
+ , mContentParentID(CONTENT_PROCESS_ID_UNKNOWN)
+{
+}
+
+WakeLock::~WakeLock()
+{
+ DoUnlock();
+ DetachEventListener();
+}
+
+JSObject*
+WakeLock::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+ return MozWakeLockBinding::Wrap(aCx, this, aGivenProto);
+}
+
+nsresult
+WakeLock::Init(const nsAString &aTopic, nsPIDOMWindowInner* aWindow)
+{
+ // Don't Init() a WakeLock twice.
+ MOZ_ASSERT(mTopic.IsEmpty());
+
+ if (aTopic.IsEmpty()) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ mTopic.Assign(aTopic);
+
+ mWindow = do_GetWeakReference(aWindow);
+
+ /**
+ * Null windows are allowed. A wake lock without associated window
+ * is always considered invisible.
+ */
+ if (aWindow) {
+ nsCOMPtr<nsIDocument> doc = aWindow->GetExtantDoc();
+ NS_ENSURE_STATE(doc);
+ mHidden = doc->Hidden();
+ }
+
+ AttachEventListener();
+ DoLock();
+
+ return NS_OK;
+}
+
+nsresult
+WakeLock::Init(const nsAString& aTopic, ContentParent* aContentParent)
+{
+ // Don't Init() a WakeLock twice.
+ MOZ_ASSERT(mTopic.IsEmpty());
+ MOZ_ASSERT(aContentParent);
+
+ if (aTopic.IsEmpty()) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ mTopic.Assign(aTopic);
+ mContentParentID = aContentParent->ChildID();
+ mHidden = false;
+
+ nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+ if (obs) {
+ obs->AddObserver(this, "ipc:content-shutdown", /* ownsWeak */ true);
+ }
+
+ DoLock();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+WakeLock::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* data)
+{
+ // If this wake lock was acquired on behalf of another process, unlock it
+ // when that process dies.
+ //
+ // Note that we do /not/ call DoUnlock() here! The wake lock back-end is
+ // already listening for ipc:content-shutdown messages and will clear out its
+ // tally for the process when it dies. All we need to do here is ensure that
+ // unlock() becomes a nop.
+
+ MOZ_ASSERT(!strcmp(aTopic, "ipc:content-shutdown"));
+
+ nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject);
+ if (!props) {
+ NS_WARNING("ipc:content-shutdown message without property bag as subject");
+ return NS_OK;
+ }
+
+ uint64_t childID = 0;
+ nsresult rv = props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"),
+ &childID);
+ if (NS_SUCCEEDED(rv)) {
+ if (childID == mContentParentID) {
+ mLocked = false;
+ }
+ } else {
+ NS_WARNING("ipc:content-shutdown message without childID property");
+ }
+ return NS_OK;
+}
+
+void
+WakeLock::DoLock()
+{
+ if (!mLocked) {
+ // Change the flag immediately to prevent recursive reentering
+ mLocked = true;
+
+ hal::ModifyWakeLock(mTopic,
+ hal::WAKE_LOCK_ADD_ONE,
+ mHidden ? hal::WAKE_LOCK_ADD_ONE : hal::WAKE_LOCK_NO_CHANGE,
+ mContentParentID);
+ }
+}
+
+void
+WakeLock::DoUnlock()
+{
+ if (mLocked) {
+ // Change the flag immediately to prevent recursive reentering
+ mLocked = false;
+
+ hal::ModifyWakeLock(mTopic,
+ hal::WAKE_LOCK_REMOVE_ONE,
+ mHidden ? hal::WAKE_LOCK_REMOVE_ONE : hal::WAKE_LOCK_NO_CHANGE,
+ mContentParentID);
+ }
+}
+
+void
+WakeLock::AttachEventListener()
+{
+ if (nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(mWindow)) {
+ nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
+ if (doc) {
+ doc->AddSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
+ this,
+ /* useCapture = */ true,
+ /* wantsUntrusted = */ false);
+
+ nsCOMPtr<EventTarget> target = do_QueryInterface(window);
+ target->AddSystemEventListener(NS_LITERAL_STRING("pagehide"),
+ this,
+ /* useCapture = */ true,
+ /* wantsUntrusted = */ false);
+ target->AddSystemEventListener(NS_LITERAL_STRING("pageshow"),
+ this,
+ /* useCapture = */ true,
+ /* wantsUntrusted = */ false);
+ }
+ }
+}
+
+void
+WakeLock::DetachEventListener()
+{
+ if (nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(mWindow)) {
+ nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
+ if (doc) {
+ doc->RemoveSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
+ this,
+ /* useCapture = */ true);
+ nsCOMPtr<EventTarget> target = do_QueryInterface(window);
+ target->RemoveSystemEventListener(NS_LITERAL_STRING("pagehide"),
+ this,
+ /* useCapture = */ true);
+ target->RemoveSystemEventListener(NS_LITERAL_STRING("pageshow"),
+ this,
+ /* useCapture = */ true);
+ }
+ }
+}
+
+void
+WakeLock::Unlock(ErrorResult& aRv)
+{
+ /*
+ * We throw NS_ERROR_DOM_INVALID_STATE_ERR on double unlock.
+ */
+ if (!mLocked) {
+ aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+ return;
+ }
+
+ DoUnlock();
+ DetachEventListener();
+}
+
+void
+WakeLock::GetTopic(nsAString &aTopic)
+{
+ aTopic.Assign(mTopic);
+}
+
+NS_IMETHODIMP
+WakeLock::HandleEvent(nsIDOMEvent *aEvent)
+{
+ nsAutoString type;
+ aEvent->GetType(type);
+
+ if (type.EqualsLiteral("visibilitychange")) {
+ nsCOMPtr<nsIDocument> doc =
+ do_QueryInterface(aEvent->InternalDOMEvent()->GetTarget());
+ NS_ENSURE_STATE(doc);
+
+ bool oldHidden = mHidden;
+ mHidden = doc->Hidden();
+
+ if (mLocked && oldHidden != mHidden) {
+ hal::ModifyWakeLock(mTopic,
+ hal::WAKE_LOCK_NO_CHANGE,
+ mHidden ? hal::WAKE_LOCK_ADD_ONE : hal::WAKE_LOCK_REMOVE_ONE,
+ mContentParentID);
+ }
+
+ return NS_OK;
+ }
+
+ if (type.EqualsLiteral("pagehide")) {
+ DoUnlock();
+ return NS_OK;
+ }
+
+ if (type.EqualsLiteral("pageshow")) {
+ DoLock();
+ return NS_OK;
+ }
+
+ return NS_OK;
+}
+
+nsPIDOMWindowInner*
+WakeLock::GetParentObject() const
+{
+ nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mWindow);
+ return window;
+}
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/power/WakeLock.h b/dom/power/WakeLock.h
new file mode 100644
index 000000000..fc8919946
--- /dev/null
+++ b/dom/power/WakeLock.h
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 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/. */
+
+#ifndef mozilla_dom_power_WakeLock_h
+#define mozilla_dom_power_WakeLock_h
+
+#include "nsCOMPtr.h"
+#include "nsIDOMEventListener.h"
+#include "nsIObserver.h"
+#include "nsString.h"
+#include "nsWeakReference.h"
+#include "nsWrapperCache.h"
+#include "mozilla/ErrorResult.h"
+
+class nsPIDOMWindowInner;
+
+namespace mozilla {
+namespace dom {
+
+class ContentParent;
+
+class WakeLock final
+ : public nsIDOMEventListener
+ , public nsWrapperCache
+ , public nsIObserver
+ , public nsSupportsWeakReference
+{
+public:
+ NS_DECL_NSIDOMEVENTLISTENER
+ NS_DECL_NSIOBSERVER
+
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(WakeLock, nsIDOMEventListener)
+
+ // Note: WakeLock lives for the lifetime of the document in order to avoid
+ // exposing GC behavior to pages. This means that
+ // |var foo = navigator.requestWakeLock('cpu'); foo = null;|
+ // doesn't unlock the 'cpu' resource.
+
+ WakeLock();
+
+ // Initialize this wake lock on behalf of the given window. Null windows are
+ // allowed; a lock without an associated window is always considered
+ // invisible.
+ nsresult Init(const nsAString &aTopic, nsPIDOMWindowInner* aWindow);
+
+ // Initialize this wake lock on behalf of the given process. If the process
+ // dies, the lock is released. A wake lock initialized via this method is
+ // always considered visible.
+ nsresult Init(const nsAString &aTopic, ContentParent* aContentParent);
+
+ // WebIDL methods
+
+ nsPIDOMWindowInner* GetParentObject() const;
+
+ virtual JSObject*
+ WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+
+ void GetTopic(nsAString& aTopic);
+
+ void Unlock(ErrorResult& aRv);
+
+private:
+ virtual ~WakeLock();
+
+ void DoUnlock();
+ void DoLock();
+ void AttachEventListener();
+ void DetachEventListener();
+
+ bool mLocked;
+ bool mHidden;
+
+ // The ID of the ContentParent on behalf of whom we acquired this lock, or
+ // CONTENT_PROCESS_UNKNOWN_ID if this lock was acquired on behalf of the
+ // current process.
+ uint64_t mContentParentID;
+ nsString mTopic;
+
+ // window that this was created for. Weak reference.
+ nsWeakPtr mWindow;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_power_WakeLock_h
diff --git a/dom/power/moz.build b/dom/power/moz.build
new file mode 100644
index 000000000..cdd99264d
--- /dev/null
+++ b/dom/power/moz.build
@@ -0,0 +1,36 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+XPIDL_SOURCES += [
+ 'nsIDOMWakeLockListener.idl',
+ 'nsIPowerManagerService.idl',
+]
+
+XPIDL_MODULE = 'dom_power'
+
+EXPORTS.mozilla.dom += [
+ 'PowerManager.h',
+ 'WakeLock.h',
+]
+
+EXPORTS.mozilla.dom.power += [
+ 'PowerManagerService.h',
+ 'Types.h',
+]
+
+UNIFIED_SOURCES += [
+ 'PowerManager.cpp',
+ 'PowerManagerService.cpp',
+ 'WakeLock.cpp',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul'
+
+MOCHITEST_MANIFESTS += ['test/mochitest.ini']
+MOCHITEST_CHROME_MANIFESTS += ['test/chrome.ini']
+BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
diff --git a/dom/power/nsIDOMWakeLockListener.idl b/dom/power/nsIDOMWakeLockListener.idl
new file mode 100644
index 000000000..b837c3b05
--- /dev/null
+++ b/dom/power/nsIDOMWakeLockListener.idl
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#include "nsISupports.idl"
+
+[scriptable, function, uuid(4e258af8-cffb-47bc-b16d-e8241243426e)]
+interface nsIDOMMozWakeLockListener : nsISupports
+{
+ /**
+ * The callback will be called when a lock topic changes its lock
+ * state.
+ *
+ * Possible states are:
+ *
+ * - "unlocked" - nobody holds the wake lock.
+ *
+ * - "locked-foreground" - at least one window holds the wake lock,
+ * and it is visible.
+ *
+ * - "locked-background" - at least one window holds the wake lock,
+ * but all of them are hidden.
+ *
+ * @param aTopic The resource name related to the wake lock.
+ * @param aState The wake lock state
+ */
+ void callback(in DOMString aTopic, in DOMString aState);
+};
diff --git a/dom/power/nsIPowerManagerService.idl b/dom/power/nsIPowerManagerService.idl
new file mode 100644
index 000000000..30d609c30
--- /dev/null
+++ b/dom/power/nsIPowerManagerService.idl
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+#include "nsISupports.idl"
+
+%{C++
+#define NS_POWERMANAGERSERVICE_CID { 0x18c2e238, 0x3a0a, 0x4153, {0x89, 0xfc, 0x16, 0x6b, 0x3b, 0x14, 0x65, 0xa1 } }
+#define POWERMANAGERSERVICE_CONTRACTID "@mozilla.org/power/powermanagerservice;1"
+%}
+
+interface nsIDOMMozWakeLockListener;
+interface mozIDOMWindow;
+
+/**
+ * For use with non-content code.
+ */
+[scriptable, builtinclass, uuid(ba7ca4c1-9d92-4425-a83b-85dd7fa953f7)]
+interface nsIPowerManagerService : nsISupports
+{
+ /**
+ * This API will power off the machine.
+ */
+ void powerOff();
+
+ /**
+ * This API will completely shut down and boot the machine.
+ */
+ void reboot();
+
+ /**
+ * This API will restart the Gecko processes without powering off the machine.
+ */
+ void restart();
+
+ void addWakeLockListener(in nsIDOMMozWakeLockListener aListener);
+ void removeWakeLockListener(in nsIDOMMozWakeLockListener aListener);
+ DOMString getWakeLockState(in DOMString aTopic);
+
+ /**
+ * Return a wake lock (MozWakeLock) object of aTopic associated with aWindow.
+ * A wake lock without associated window, e.g. used in chrome, is
+ * always considered invisible.
+ */
+ nsISupports newWakeLock(in DOMString aTopic, [optional] in mozIDOMWindow aWindow);
+};
diff --git a/dom/power/test/browser.ini b/dom/power/test/browser.ini
new file mode 100644
index 000000000..e8dd8c4b1
--- /dev/null
+++ b/dom/power/test/browser.ini
@@ -0,0 +1,4 @@
+[DEFAULT]
+
+[browser_wakelocks.js]
+skip-if = e10s # Bug ?????? - SpecialPowers issues ({file: "chrome://mochikit/content/tests/SimpleTest/specialpowersAPI.js" line: 759}]'[JavaScript Error: "content.window is undefined" {file: "chrome://mochikit/content/tests/SimpleTest/specialpowersAPI.js" line: 759}]' when calling method: [nsIObserver::observe]")
diff --git a/dom/power/test/browser_wakelocks.js b/dom/power/test/browser_wakelocks.js
new file mode 100644
index 000000000..5bd4de67c
--- /dev/null
+++ b/dom/power/test/browser_wakelocks.js
@@ -0,0 +1,232 @@
+/* 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/. */
+
+"use strict";
+
+waitForExplicitFinish();
+
+var kUrlSource = "http://mochi.test:8888/";
+var kDataSource = "data:text/html,";
+
+var gOldPref;
+var gWin, gWin1, gWin2;
+var gTab, gTab1, gTab2;
+var gLock, gLock1, gLock2;
+var gCurStepIndex = -1;
+var gSteps = [
+ function basicWakeLock() {
+ gTab = gBrowser.addTab(kUrlSource);
+ gWin = gBrowser.getBrowserForTab(gTab).contentWindow;
+ let browser = gBrowser.getBrowserForTab(gTab);
+
+ browser.addEventListener("load", function onLoad(e) {
+ browser.removeEventListener("load", onLoad, true);
+ let nav = gWin.navigator;
+ let power = nav.mozPower;
+ gLock = nav.requestWakeLock("test");
+
+ ok(gLock != null,
+ "navigator.requestWakeLock should return a wake lock");
+ is(gLock.topic, "test",
+ "wake lock should remember the locked topic");
+ isnot(power.getWakeLockState("test"), "unlocked",
+ "topic is locked");
+
+ gLock.unlock();
+
+ is(gLock.topic, "test",
+ "wake lock should remember the locked topic even after unlock");
+ is(power.getWakeLockState("test"), "unlocked",
+ "topic is unlocked");
+
+ try {
+ gLock.unlock();
+ ok(false, "Should have thrown an error.");
+ } catch (e) {
+ is(e.name, "InvalidStateError", "double unlock should throw InvalidStateError");
+ is(e.code, DOMException.INVALID_STATE_ERR, "double unlock should throw InvalidStateError");
+ }
+
+ gBrowser.removeTab(gTab);
+
+ executeSoon(runNextStep);
+ }, true);
+ },
+ function multiWakeLock() {
+ gTab = gBrowser.addTab(kUrlSource);
+ gWin = gBrowser.getBrowserForTab(gTab).contentWindow;
+ let browser = gBrowser.getBrowserForTab(gTab);
+
+ browser.addEventListener("load", function onLoad(e) {
+ browser.removeEventListener("load", onLoad, true);
+ let nav = gWin.navigator;
+ let power = nav.mozPower;
+ let count = 0;
+ power.addWakeLockListener(function onWakeLockEvent(topic, state) {
+ is(topic, "test", "gLock topic is test");
+ ok(state == "unlocked" ||
+ state == "locked-foreground" ||
+ state == "locked-background",
+ "wake lock should be either locked or unlocked");
+ count++;
+ if (state == "locked-foreground" ||
+ state == "locked-background") {
+ is(count, 1,
+ "wake lock should be locked and the listener should only fire once");
+ }
+ if (state == "unlocked") {
+ is(count, 2,
+ "wake lock should be unlocked and the listener should only fire once");
+
+ ok(power.getWakeLockState("test") == "unlocked",
+ "topic is unlocked");
+ power.removeWakeLockListener(onWakeLockEvent);
+ gBrowser.removeTab(gTab);
+ executeSoon(runNextStep);
+ }
+ });
+
+ gLock1 = nav.requestWakeLock("test");
+ isnot(power.getWakeLockState("test"), "unlocked",
+ "topic is locked");
+
+ gLock2 = nav.requestWakeLock("test");
+ isnot(power.getWakeLockState("test"), "unlocked",
+ "topic is locked");
+
+ gLock1.unlock();
+ isnot(power.getWakeLockState("test"), "unlocked",
+ "topic is locked");
+
+ gLock2.unlock();
+ }, true);
+ },
+ function crossTabWakeLock1() {
+ gTab1 = gBrowser.addTab(kUrlSource);
+ gWin1 = gBrowser.getBrowserForTab(gTab1).contentWindow;
+ gTab2 = gBrowser.addTab(kUrlSource);
+ gWin2 = gBrowser.getBrowserForTab(gTab2).contentWindow;
+
+ gBrowser.selectedTab = gTab1;
+ let browser = gBrowser.getBrowserForTab(gTab2);
+
+ browser.addEventListener("load", function onLoad(e) {
+ browser.removeEventListener("load", onLoad, true);
+ gLock2 = gWin2.navigator.requestWakeLock("test");
+ is(gWin2.document.hidden, true,
+ "window is background")
+ is(gWin2.navigator.mozPower.getWakeLockState("test"), "locked-background",
+ "wake lock is background");
+ let doc2 = gWin2.document;
+ doc2.addEventListener("visibilitychange", function onVisibilityChange(e) {
+ if (!doc2.hidden) {
+ doc2.removeEventListener("visibilitychange", onVisibilityChange);
+ executeSoon(runNextStep);
+ }
+ });
+ gBrowser.selectedTab = gTab2;
+ }, true);
+ },
+ function crossTabWakeLock2() {
+ is(gWin2.document.hidden, false,
+ "window is foreground")
+ is(gWin2.navigator.mozPower.getWakeLockState("test"), "locked-foreground",
+ "wake lock is foreground");
+ gWin2.addEventListener("pagehide", function onPageHide(e) {
+ gWin2.removeEventListener("pagehide", onPageHide, true);
+ executeSoon(runNextStep);
+ }, true);
+ gWin2.addEventListener("pageshow", function onPageShow(e) {
+ gWin2.removeEventListener("pageshow", onPageShow, true);
+ executeSoon(runNextStep);
+ }, true);
+ gWin2.location = kDataSource;
+ },
+ function crossTabWakeLock3() {
+ is(gWin1.navigator.mozPower.getWakeLockState("test"), "unlocked",
+ "wake lock should auto-unlock when page is unloaded");
+ gWin2.back();
+ // runNextStep called in onPageShow
+ },
+ function crossTabWakeLock4() {
+ is(gWin1.navigator.mozPower.getWakeLockState("test"), "locked-foreground",
+ "wake lock should auto-reacquire when page is available again");
+ gBrowser.selectedTab = gTab1;
+ executeSoon(runNextStep);
+ },
+ function crossTabWakeLock5() {
+ // Test again in background tab
+ is(gWin2.document.hidden, true,
+ "window is background")
+ is(gWin2.navigator.mozPower.getWakeLockState("test"), "locked-background",
+ "wake lock is background");
+ gWin2.addEventListener("pagehide", function onPageHide(e) {
+ gWin2.removeEventListener("pagehide", onPageHide, true);
+ executeSoon(runNextStep);
+ }, true);
+ gWin2.addEventListener("pageshow", function onPageShow(e) {
+ gWin2.removeEventListener("pageshow", onPageShow, true);
+ executeSoon(runNextStep);
+ }, true);
+ gWin2.location = kDataSource;
+ },
+ function crossTabWakeLock6() {
+ is(gWin1.navigator.mozPower.getWakeLockState("test"), "unlocked",
+ "wake lock should auto-unlock when page is unloaded");
+ gWin2.back();
+ // runNextStep called in onPageShow
+ },
+ function crossTabWakeLock7() {
+ is(gWin1.navigator.mozPower.getWakeLockState("test"), "locked-background",
+ "wake lock should auto-reacquire when page is available again");
+ gLock2.unlock();
+ gBrowser.selectedTab = gTab2;
+ executeSoon(runNextStep);
+ },
+ function crossTabWakeLock8() {
+ is(gWin1.document.hidden, true,
+ "gWin1 is background");
+ is(gWin2.document.hidden, false,
+ "gWin2 is foreground");
+
+ gLock1 = gWin1.navigator.requestWakeLock("test");
+ gLock2 = gWin2.navigator.requestWakeLock("test");
+
+ is(gWin1.navigator.mozPower.getWakeLockState("test"), "locked-foreground",
+ "topic is locked-foreground when one page is foreground and one is background");
+
+ gLock2.unlock();
+
+ is(gWin1.navigator.mozPower.getWakeLockState("test"), "locked-background",
+ "topic is locked-background when all locks are background");
+
+ gLock2 = gWin2.navigator.requestWakeLock("test");
+
+ is(gWin1.navigator.mozPower.getWakeLockState("test"), "locked-foreground",
+ "topic is locked-foreground when one page is foreground and one is background");
+
+ gLock1.unlock();
+
+ is(gWin1.navigator.mozPower.getWakeLockState("test"), "locked-foreground",
+ "topic is locked-foreground");
+
+ gBrowser.removeTab(gTab1);
+ gBrowser.removeTab(gTab2);
+ executeSoon(runNextStep);
+ },
+];
+
+function runNextStep() {
+ gCurStepIndex++;
+ if (gCurStepIndex < gSteps.length) {
+ gSteps[gCurStepIndex]();
+ } else {
+ finish();
+ }
+}
+
+function test() {
+ SpecialPowers.pushPrefEnv({"set": [["dom.wakelock.enabled", true]]},
+ runNextStep);
+}
diff --git a/dom/power/test/chrome.ini b/dom/power/test/chrome.ini
new file mode 100644
index 000000000..2a88cc576
--- /dev/null
+++ b/dom/power/test/chrome.ini
@@ -0,0 +1 @@
+[test_power_basics.html]
diff --git a/dom/power/test/mochitest.ini b/dom/power/test/mochitest.ini
new file mode 100644
index 000000000..5a82b286e
--- /dev/null
+++ b/dom/power/test/mochitest.ini
@@ -0,0 +1,3 @@
+[test_bug957893.html]
+[test_bug957899.html]
+[test_wakelock_not_exposed.html]
diff --git a/dom/power/test/test_bug957893.html b/dom/power/test/test_bug957893.html
new file mode 100644
index 000000000..c8c86a36d
--- /dev/null
+++ b/dom/power/test/test_bug957893.html
@@ -0,0 +1,24 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test bug 957893 - Crash in WakeLock</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+ <script type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+ function test() {
+ try {
+ var wl = navigator.requestWakeLock('');
+ ok(false, "RequestWakeLock throws an exception!");
+ } catch(e) {
+ ok(true, "RequestWakeLock throws an exception!");
+ }
+ info("Still alive!");
+ SimpleTest.finish();
+ }
+ SpecialPowers.pushPrefEnv({"set": [["dom.wakelock.enabled", true]]}, test);
+ </script>
+</body>
+</html>
diff --git a/dom/power/test/test_bug957899.html b/dom/power/test/test_bug957899.html
new file mode 100644
index 000000000..8d5b9a872
--- /dev/null
+++ b/dom/power/test/test_bug957899.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test bug 957899 - Crash in WakeLock</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+ <script type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+ function test() {
+ var wl = navigator.requestWakeLock('a');
+ ok(wl, "WakeLock created!");
+ ok(!(wl instanceof XPathEvaluator), "Crashing?");
+ SimpleTest.finish();
+ }
+ SpecialPowers.pushPrefEnv({"set": [["dom.wakelock.enabled", true]]}, test);
+ </script>
+</body>
+</html>
diff --git a/dom/power/test/test_power_basics.html b/dom/power/test/test_power_basics.html
new file mode 100644
index 000000000..b78c99f48
--- /dev/null
+++ b/dom/power/test/test_power_basics.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for Power API</title>
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+<script type="application/javascript">
+
+/** Test for Power API **/
+
+SimpleTest.waitForExplicitFinish();
+
+function startTest() {
+ doTest1();
+}
+
+function doTest1() {
+ window.frames[0].frameElement.setAttribute('onload', 'doTest3()');
+ power = window.frames[0].navigator.mozPower;
+ ok(power, "Should be able to access power manager with permission.");
+
+ window.frames[0].location.reload();
+}
+
+function doTest3() {
+ power = window.frames[0].navigator.mozPower;
+ ok(power, "Should be able to access power manager with permission.");
+ SimpleTest.finish();
+}
+</script>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<iframe onload="startTest()"></iframe>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/power/test/test_wakelock_not_exposed.html b/dom/power/test/test_wakelock_not_exposed.html
new file mode 100644
index 000000000..1f88993b4
--- /dev/null
+++ b/dom/power/test/test_wakelock_not_exposed.html
@@ -0,0 +1,17 @@
+<!--
+ https://bugzilla.mozilla.org/show_bug.cgi?id=963366
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test navigator.requestWakeLock is not exposed to non-B2G platform</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+ <script type="application/javascript">
+ ok(navigator.requestWakeLock === undefined,
+ "navigator.requestWakeLock is not exposed to non-B2G platform");
+ </script>
+</body>
+</html>