From 5f8de423f190bbb79a62f804151bc24824fa32d8 Mon Sep 17 00:00:00 2001 From: "Matt A. Tobin" Date: Fri, 2 Feb 2018 04:16:08 -0500 Subject: Add m-esr52 at 52.6.0 --- dom/power/PowerManager.cpp | 210 +++++++++++++++++++ dom/power/PowerManager.h | 70 +++++++ dom/power/PowerManagerService.cpp | 255 +++++++++++++++++++++++ dom/power/PowerManagerService.h | 86 ++++++++ dom/power/Types.h | 22 ++ dom/power/WakeLock.cpp | 281 ++++++++++++++++++++++++++ dom/power/WakeLock.h | 90 +++++++++ dom/power/moz.build | 36 ++++ dom/power/nsIDOMWakeLockListener.idl | 29 +++ dom/power/nsIPowerManagerService.idl | 47 +++++ dom/power/test/browser.ini | 4 + dom/power/test/browser_wakelocks.js | 232 +++++++++++++++++++++ dom/power/test/chrome.ini | 1 + dom/power/test/mochitest.ini | 3 + dom/power/test/test_bug957893.html | 24 +++ dom/power/test/test_bug957899.html | 20 ++ dom/power/test/test_power_basics.html | 40 ++++ dom/power/test/test_wakelock_not_exposed.html | 17 ++ 18 files changed, 1467 insertions(+) create mode 100644 dom/power/PowerManager.cpp create mode 100644 dom/power/PowerManager.h create mode 100644 dom/power/PowerManagerService.cpp create mode 100644 dom/power/PowerManagerService.h create mode 100644 dom/power/Types.h create mode 100644 dom/power/WakeLock.cpp create mode 100644 dom/power/WakeLock.h create mode 100644 dom/power/moz.build create mode 100644 dom/power/nsIDOMWakeLockListener.idl create mode 100644 dom/power/nsIPowerManagerService.idl create mode 100644 dom/power/test/browser.ini create mode 100644 dom/power/test/browser_wakelocks.js create mode 100644 dom/power/test/chrome.ini create mode 100644 dom/power/test/mochitest.ini create mode 100644 dom/power/test/test_bug957893.html create mode 100644 dom/power/test/test_bug957899.html create mode 100644 dom/power/test/test_power_basics.html create mode 100644 dom/power/test/test_wakelock_not_exposed.html (limited to 'dom/power') 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 aGivenProto) +{ + return MozPowerManagerBinding::Wrap(aCx, this, aGivenProto); +} + +nsresult +PowerManager::Init(nsPIDOMWindowInner* aWindow) +{ + mWindow = aWindow; + + nsCOMPtr 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 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 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 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 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, 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::CreateInstance(nsPIDOMWindowInner* aWindow) +{ + RefPtr 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 CreateInstance(nsPIDOMWindowInner*); + + // WebIDL + nsPIDOMWindowInner* GetParentObject() const + { + return mWindow; + } + virtual JSObject* WrapObject(JSContext* aCx, JS::Handle 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 mWindow; + nsTArray > 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 +#else +#include +#endif + +#ifdef ANDROID +#include +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 : ""); + 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::sSingleton; + +/* static */ already_AddRefed +PowerManagerService::GetInstance() +{ + if (!sSingleton) { + sSingleton = new PowerManagerService(); + sSingleton->Init(); + ClearOnShutdown(&sSingleton); + } + + RefPtr 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, 2> listeners(mWakeLockListeners); + + for (uint32_t i = 0; i < listeners.Length(); ++i) { + listeners[i]->Callback(aWakeLockInfo.topic(), state); + } +} + +void +PowerManagerService::SyncProfile() +{ + nsCOMPtr 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 +PowerManagerService::NewWakeLock(const nsAString& aTopic, + nsPIDOMWindowInner* aWindow, + mozilla::ErrorResult& aRv) +{ + RefPtr 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 = + NewWakeLock(aTopic, nsPIDOMWindowInner::From(aWindow), rv); + if (rv.Failed()) { + return rv.StealNSResult(); + } + + nsCOMPtr eventListener = wakelock.get(); + eventListener.forget(aWakeLock); + return NS_OK; +} + +already_AddRefed +PowerManagerService::NewWakeLockOnBehalfOfProcess(const nsAString& aTopic, + ContentParent* aContentParent) +{ + RefPtr 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 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 + NewWakeLockOnBehalfOfProcess(const nsAString& aTopic, + ContentParent* aContentParent); + + already_AddRefed + NewWakeLock(const nsAString& aTopic, nsPIDOMWindowInner* aWindow, + mozilla::ErrorResult& aRv); + +private: + + ~PowerManagerService(); + + void ComputeWakeLockState(const hal::WakeLockInformation& aWakeLockInfo, + nsAString &aState); + + void SyncProfile(); + + static StaticRefPtr sSingleton; + + nsTArray> 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 Observer; + +typedef Observer 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 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 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 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 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 window = do_QueryReferent(mWindow)) { + nsCOMPtr doc = window->GetExtantDoc(); + if (doc) { + doc->AddSystemEventListener(NS_LITERAL_STRING("visibilitychange"), + this, + /* useCapture = */ true, + /* wantsUntrusted = */ false); + + nsCOMPtr 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 window = do_QueryReferent(mWindow)) { + nsCOMPtr doc = window->GetExtantDoc(); + if (doc) { + doc->RemoveSystemEventListener(NS_LITERAL_STRING("visibilitychange"), + this, + /* useCapture = */ true); + nsCOMPtr 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 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 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 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 @@ + + + + Test bug 957893 - Crash in WakeLock + + + + + + + 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 @@ + + + + Test bug 957899 - Crash in WakeLock + + + + + + + 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 @@ + + + + Test for Power API + + + + + +

+ + +
+
+ + 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 @@ + + + + + Test navigator.requestWakeLock is not exposed to non-B2G platform + + + + + + + -- cgit v1.2.3