author | Kan-Ru Chen <kanru@kanru.info> |
Wed, 07 Mar 2012 12:03:25 +0100 | |
changeset 88439 | 72bc6f12d1cc5237b7347f2ed737e929def0ad07 |
parent 88438 | 031fd6560e841f2ec22714f2319883c55a1597bb |
child 88440 | d68420c97e0e8edb8bca539ce706b300451f119a |
push id | 22199 |
push user | bmo@edmorley.co.uk |
push date | Thu, 08 Mar 2012 13:15:53 +0000 |
treeherder | mozilla-central@09a8a48476e5 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | jlebar |
bugs | 697132 |
milestone | 13.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/dom/base/Navigator.cpp +++ b/dom/base/Navigator.cpp @@ -65,16 +65,18 @@ #include "nsIJSContextStack.h" #include "nsCharSeparatedTokenizer.h" #include "nsContentUtils.h" #include "nsUnicharUtils.h" #include "mozilla/Preferences.h" #include "mozilla/Telemetry.h" #include "BatteryManager.h" #include "PowerManager.h" +#include "nsIDOMWakeLock.h" +#include "nsIPowerManagerService.h" #include "SmsManager.h" #include "nsISmsService.h" #include "mozilla/Hal.h" #include "nsIWebNavigation.h" #include "mozilla/ClearOnShutdown.h" #include "Connection.h" #ifdef MOZ_B2G_RIL @@ -167,17 +169,20 @@ Navigator::Invalidate() mNotification = nsnull; } if (mBatteryManager) { mBatteryManager->Shutdown(); mBatteryManager = nsnull; } - mPowerManager = nsnull; + if (mPowerManager) { + mPowerManager->Shutdown(); + mPowerManager = nsnull; + } if (mSmsManager) { mSmsManager->Shutdown(); mSmsManager = nsnull; } #ifdef MOZ_B2G_RIL if (mTelephony) { @@ -953,25 +958,48 @@ Navigator::GetMozBattery(nsIDOMMozBatter NS_ADDREF(*aBattery = mBatteryManager); return NS_OK; } NS_IMETHODIMP Navigator::GetMozPower(nsIDOMMozPowerManager** aPower) { + *aPower = nsnull; + if (!mPowerManager) { + nsCOMPtr<nsPIDOMWindow> win = do_QueryReferent(mWindow); + NS_ENSURE_TRUE(win, NS_OK); + mPowerManager = new power::PowerManager(); + mPowerManager->Init(win); } - NS_ADDREF(*aPower = mPowerManager); + nsCOMPtr<nsIDOMMozPowerManager> power = + do_QueryInterface(NS_ISUPPORTS_CAST(nsIDOMMozPowerManager*, mPowerManager)); + power.forget(aPower); return NS_OK; } +NS_IMETHODIMP +Navigator::RequestWakeLock(const nsAString &aTopic, nsIDOMMozWakeLock **aWakeLock) +{ + *aWakeLock = nsnull; + + nsCOMPtr<nsPIDOMWindow> win = do_QueryReferent(mWindow); + NS_ENSURE_TRUE(win, NS_OK); + + nsCOMPtr<nsIPowerManagerService> pmService = + do_GetService(POWERMANAGERSERVICE_CONTRACTID); + NS_ENSURE_TRUE(pmService, NS_OK); + + return pmService->NewWakeLock(aTopic, win, aWakeLock); +} + //***************************************************************************** // Navigator::nsIDOMNavigatorSms //***************************************************************************** bool Navigator::IsSmsAllowed() const { static const bool defaultSmsPermission = false;
--- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -502,16 +502,17 @@ using mozilla::dom::indexedDB::IDBWrappe #include "nsIDOMCustomEvent.h" #include "nsWrapperCacheInlines.h" #include "dombindings.h" #include "nsIDOMBatteryManager.h" #include "BatteryManager.h" #include "nsIDOMPowerManager.h" +#include "nsIDOMWakeLock.h" #include "nsIDOMSmsManager.h" #include "nsIDOMSmsMessage.h" #include "nsIDOMSmsEvent.h" #include "nsIDOMSmsRequest.h" #include "nsIDOMSmsFilter.h" #include "nsIDOMSmsCursor.h" #include "nsIPrivateDOMEvent.h" #include "nsIDOMConnection.h" @@ -1431,16 +1432,19 @@ static nsDOMClassInfoData sClassInfoData DOM_DEFAULT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(MozBatteryManager, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(MozPowerManager, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) + NS_DEFINE_CLASSINFO_DATA(MozWakeLock, nsDOMGenericSH, + DOM_DEFAULT_SCRIPTABLE_FLAGS) + NS_DEFINE_CLASSINFO_DATA(MozSmsManager, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(MozSmsMessage, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(MozSmsEvent, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) @@ -4026,16 +4030,20 @@ nsDOMClassInfo::Init() DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozBatteryManager) DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget) DOM_CLASSINFO_MAP_END DOM_CLASSINFO_MAP_BEGIN(MozPowerManager, nsIDOMMozPowerManager) DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozPowerManager) DOM_CLASSINFO_MAP_END + DOM_CLASSINFO_MAP_BEGIN(MozWakeLock, nsIDOMMozWakeLock) + DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozWakeLock) + DOM_CLASSINFO_MAP_END + DOM_CLASSINFO_MAP_BEGIN(MozSmsManager, nsIDOMMozSmsManager) DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozSmsManager) DOM_CLASSINFO_MAP_END DOM_CLASSINFO_MAP_BEGIN(MozSmsMessage, nsIDOMMozSmsMessage) DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozSmsMessage) DOM_CLASSINFO_MAP_END
--- a/dom/base/nsDOMClassInfoClasses.h +++ b/dom/base/nsDOMClassInfoClasses.h @@ -424,16 +424,17 @@ DOMCI_CLASS(GeoGeolocation) DOMCI_CLASS(GeoPosition) DOMCI_CLASS(GeoPositionCoords) DOMCI_CLASS(GeoPositionAddress) DOMCI_CLASS(GeoPositionError) DOMCI_CLASS(MozBatteryManager) DOMCI_CLASS(MozPowerManager) +DOMCI_CLASS(MozWakeLock) DOMCI_CLASS(MozSmsManager) DOMCI_CLASS(MozSmsMessage) DOMCI_CLASS(MozSmsEvent) DOMCI_CLASS(MozSmsRequest) DOMCI_CLASS(MozSmsFilter) DOMCI_CLASS(MozSmsCursor)
--- a/dom/interfaces/base/domstubs.idl +++ b/dom/interfaces/base/domstubs.idl @@ -126,8 +126,9 @@ interface nsIDOMCrypto; interface nsIDOMPkcs11; // Used font face (for inspector) interface nsIDOMFontFace; interface nsIDOMFontFaceList; // Power interface nsIDOMMozPowerManager; +interface nsIDOMMozWakeLock;
--- a/dom/interfaces/base/nsIDOMNavigator.idl +++ b/dom/interfaces/base/nsIDOMNavigator.idl @@ -34,17 +34,17 @@ * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "domstubs.idl" -[scriptable, uuid(b1f4b1fa-49c2-4375-9ce8-bf97ecf6b428)] +[scriptable, uuid(e610c037-db58-4cd7-8ed3-0d7f1422b4d3)] interface nsIDOMNavigator : nsISupports { readonly attribute DOMString appCodeName; readonly attribute DOMString appName; readonly attribute DOMString appVersion; readonly attribute DOMString language; readonly attribute nsIDOMMimeTypeArray mimeTypes; readonly attribute DOMString platform; @@ -105,9 +105,39 @@ interface nsIDOMNavigator : nsISupports * pass an even number of elements (that is, if your list ends with b_n * instead of a_n), the final element doesn't specify anything meaningful. * * We may throw NS_ERROR_DOM_NOT_SUPPORTED_ERR if the vibration pattern is * too long, or if any of its elements is too large. */ [implicit_jscontext] void mozVibrate(in jsval aPattern); + + /** + * Request a wake lock for a resource. + * + * A page holds a wake lock to request that a resource not be turned + * off (or otherwise made unavailable). + * + * The topic is the name of a resource that might be made unavailable for + * various reasons. For example, on a mobile device the power manager might + * decide to turn off the screen after a period of idle time to save power. + * + * The resource manager checks the lock state of a topic before turning off + * the associated resource. For example, a page could hold a lock on the + * "screen" topic to prevent the screensaver from appearing or the screen + * from turning off. + * + * The resource manager defines what each topic means and sets policy. For + * example, the resource manager might decide to ignore 'screen' wake locks + * held by pages which are not visible. + * + * One topic can be locked multiple times; it is considered released only when + * all locks on the topic have been released. + * + * The returned nsIDOMMozWakeLock object is a token of the lock. You can + * unlock the lock via the object's |unlock| method. The lock is released + * automatically when its associated window is unloaded. + * + * @param aTopic resource name + */ + nsIDOMMozWakeLock requestWakeLock(in DOMString aTopic); };
--- a/dom/power/Makefile.in +++ b/dom/power/Makefile.in @@ -47,25 +47,29 @@ LIBXUL_LIBRARY = 1 FORCE_STATIC_LIB = 1 include $(topsrcdir)/dom/dom-config.mk EXPORTS_NAMESPACES = mozilla/dom/power EXPORTS_mozilla/dom/power = \ PowerManagerService.h \ + Types.h \ $(NULL) CPPSRCS = \ PowerManager.cpp \ PowerManagerService.cpp \ + WakeLock.cpp \ $(NULL) XPIDLSRCS = \ nsIDOMPowerManager.idl \ + nsIDOMWakeLock.idl \ + nsIDOMWakeLockListener.idl \ nsIPowerManagerService.idl \ $(NULL) ifdef ENABLE_TESTS DIRS += test endif include $(topsrcdir)/config/config.mk
--- a/dom/power/PowerManager.cpp +++ b/dom/power/PowerManager.cpp @@ -31,59 +31,166 @@ * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "PowerManager.h" +#include "WakeLock.h" #include "nsContentUtils.h" #include "nsDOMClassInfoID.h" +#include "nsIDOMWakeLockListener.h" #include "nsIPowerManagerService.h" +#include "nsIPrincipal.h" +#include "nsPIDOMWindow.h" #include "nsServiceManagerUtils.h" DOMCI_DATA(MozPowerManager, mozilla::dom::power::PowerManager) namespace mozilla { namespace dom { namespace power { NS_INTERFACE_MAP_BEGIN(PowerManager) NS_INTERFACE_MAP_ENTRY(nsIDOMMozPowerManager) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMMozPowerManager) + NS_INTERFACE_MAP_ENTRY(nsIDOMMozWakeLockListener) NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(MozPowerManager) NS_INTERFACE_MAP_END NS_IMPL_ADDREF(PowerManager) NS_IMPL_RELEASE(PowerManager) +nsresult +PowerManager::Init(nsIDOMWindow *aWindow) +{ + mWindow = do_GetWeakReference(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; +} + +nsresult +PowerManager::CheckPermission() +{ + nsCOMPtr<nsPIDOMWindow> win = do_QueryReferent(mWindow); + NS_ENSURE_STATE(win); + nsCOMPtr<nsIDocument> doc = do_QueryInterface(win->GetExtantDocument()); + NS_ENSURE_STATE(doc); + + nsCOMPtr<nsIURI> uri; + doc->NodePrincipal()->GetURI(getter_AddRefs(uri)); + + if (!nsContentUtils::URIIsChromeOrInPref(uri, "dom.power.whitelist")) { + return NS_ERROR_DOM_SECURITY_ERR; + } + + return NS_OK; +} + NS_IMETHODIMP PowerManager::Reboot() { - NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_DOM_SECURITY_ERR); + nsresult rv = CheckPermission(); + NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr<nsIPowerManagerService> pmService = do_GetService(POWERMANAGERSERVICE_CONTRACTID); - NS_ENSURE_TRUE(pmService, NS_OK); + NS_ENSURE_STATE(pmService); pmService->Reboot(); return NS_OK; } NS_IMETHODIMP PowerManager::PowerOff() { - NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_DOM_SECURITY_ERR); + nsresult rv = CheckPermission(); + NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr<nsIPowerManagerService> pmService = do_GetService(POWERMANAGERSERVICE_CONTRACTID); - NS_ENSURE_TRUE(pmService, NS_OK); + NS_ENSURE_STATE(pmService); pmService->PowerOff(); return NS_OK; } +NS_IMETHODIMP +PowerManager::AddWakeLockListener(nsIDOMMozWakeLockListener *aListener) +{ + nsresult rv = CheckPermission(); + NS_ENSURE_SUCCESS(rv, rv); + + // already added? bail out. + if (mListeners.Contains(aListener)) + return NS_OK; + + mListeners.AppendElement(aListener); + return NS_OK; +} + +NS_IMETHODIMP +PowerManager::RemoveWakeLockListener(nsIDOMMozWakeLockListener *aListener) +{ + nsresult rv = CheckPermission(); + NS_ENSURE_SUCCESS(rv, rv); + + mListeners.RemoveElement(aListener); + return NS_OK; +} + +NS_IMETHODIMP +PowerManager::GetWakeLockState(const nsAString &aTopic, nsAString &aState) +{ + nsresult rv = CheckPermission(); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIPowerManagerService> pmService = + do_GetService(POWERMANAGERSERVICE_CONTRACTID); + NS_ENSURE_STATE(pmService); + + return pmService->GetWakeLockState(aTopic, aState); +} + +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. + */ + nsAutoTArray<nsCOMPtr<nsIDOMMozWakeLockListener>, 2> listeners(mListeners); + for (PRUint32 i = 0; i < listeners.Length(); ++i) { + listeners[i]->Callback(aTopic, aState); + } + + return NS_OK; +} + } // power } // dom } // mozilla
--- a/dom/power/PowerManager.h +++ b/dom/power/PowerManager.h @@ -32,30 +32,46 @@ * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #ifndef mozilla_dom_power_PowerManager_h #define mozilla_dom_power_PowerManager_h +#include "nsCOMPtr.h" +#include "nsTArray.h" #include "nsIDOMPowerManager.h" +#include "nsIDOMWakeLockListener.h" +#include "nsIDOMWindow.h" +#include "nsWeakReference.h" namespace mozilla { namespace dom { namespace power { class PowerManager : public nsIDOMMozPowerManager + , public nsIDOMMozWakeLockListener { public: NS_DECL_ISUPPORTS NS_DECL_NSIDOMMOZPOWERMANAGER + NS_DECL_NSIDOMMOZWAKELOCKLISTENER PowerManager() {}; virtual ~PowerManager() {}; + + nsresult Init(nsIDOMWindow *aWindow); + nsresult Shutdown(); + +private: + nsresult CheckPermission(); + + nsWeakPtr mWindow; + nsTArray<nsCOMPtr<nsIDOMMozWakeLockListener> > mListeners; }; } // namespace power } // namespace dom } // namespace mozilla #endif // mozilla_dom_power_PowerManager_h
--- a/dom/power/PowerManagerService.cpp +++ b/dom/power/PowerManagerService.cpp @@ -31,43 +31,145 @@ * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "mozilla/Hal.h" +#include "mozilla/HalWakeLock.h" +#include "mozilla/ClearOnShutdown.h" +#include "nsIDOMWakeLockListener.h" +#include "nsIDOMWindow.h" #include "PowerManagerService.h" +#include "WakeLock.h" namespace mozilla { namespace dom { namespace power { NS_IMPL_ISUPPORTS1(PowerManagerService, nsIPowerManagerService) +/* static */ nsRefPtr<PowerManagerService> PowerManagerService::sSingleton; + /* static */ already_AddRefed<nsIPowerManagerService> PowerManagerService::GetInstance() { - nsCOMPtr<nsIPowerManagerService> pmService; + if (!sSingleton) { + sSingleton = new PowerManagerService(); + sSingleton->Init(); + ClearOnShutdown(&sSingleton); + } + + nsCOMPtr<nsIPowerManagerService> service(do_QueryInterface(sSingleton)); + return service.forget(); +} + +void +PowerManagerService::Init() +{ + hal::RegisterWakeLockObserver(this); +} + +PowerManagerService::~PowerManagerService() +{ + hal::UnregisterWakeLockObserver(this); +} - pmService = new PowerManagerService(); +void +PowerManagerService::ComputeWakeLockState(const hal::WakeLockInformation& aWakeLockInfo, + nsAString &aState) +{ + hal::WakeLockState state = hal::ComputeWakeLockState(aWakeLockInfo.numLocks(), + aWakeLockInfo.numHidden()); + switch (state) { + case hal::WAKE_LOCK_STATE_UNLOCKED: + aState.AssignLiteral("unlocked"); + break; + case hal::WAKE_LOCK_STATE_HIDDEN: + aState.AssignLiteral("locked-background"); + break; + case hal::WAKE_LOCK_STATE_VISIBLE: + aState.AssignLiteral("locked-foreground"); + break; + } +} - return pmService.forget(); +void +PowerManagerService::Notify(const hal::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. + */ + nsAutoTArray<nsCOMPtr<nsIDOMMozWakeLockListener>, 2> listeners(mWakeLockListeners); + + for (PRUint32 i = 0; i < listeners.Length(); ++i) { + listeners[i]->Callback(aWakeLockInfo.topic(), state); + } } NS_IMETHODIMP PowerManagerService::Reboot() { hal::Reboot(); return NS_OK; } NS_IMETHODIMP PowerManagerService::PowerOff() { hal::PowerOff(); return NS_OK; } +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) +{ + hal::WakeLockInformation info; + hal::GetWakeLockInfo(aTopic, &info); + + ComputeWakeLockState(info, aState); + + return NS_OK; +} + +NS_IMETHODIMP +PowerManagerService::NewWakeLock(const nsAString &aTopic, + nsIDOMWindow *aWindow, + nsIDOMMozWakeLock **aWakeLock) +{ + nsRefPtr<WakeLock> wakelock = new WakeLock(); + nsresult rv = wakelock->Init(aTopic, aWindow); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIDOMMozWakeLock> wl = + do_QueryInterface(NS_ISUPPORTS_CAST(nsIDOMMozWakeLock*, wakelock)); + wl.forget(aWakeLock); + + return NS_OK; +} + } // power } // dom } // mozilla
--- a/dom/power/PowerManagerService.h +++ b/dom/power/PowerManagerService.h @@ -32,30 +32,52 @@ * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #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 "nsIPowerManagerService.h" -#include "nsCOMPtr.h" // for already_AddRefed +#include "mozilla/Observer.h" +#include "Types.h" namespace mozilla { namespace dom { namespace power { class PowerManagerService : public nsIPowerManagerService + , public WakeLockObserver { public: NS_DECL_ISUPPORTS NS_DECL_NSIPOWERMANAGERSERVICE static already_AddRefed<nsIPowerManagerService> GetInstance(); + + void Init(); + + // Implement WakeLockObserver + void Notify(const hal::WakeLockInformation& aWakeLockInfo); + +private: + + ~PowerManagerService(); + + void ComputeWakeLockState(const hal::WakeLockInformation& aWakeLockInfo, + nsAString &aState); + + static nsRefPtr<PowerManagerService> sSingleton; + + nsTArray<nsCOMPtr<nsIDOMMozWakeLockListener> > mWakeLockListeners; }; } // namespace power } // namespace dom } // namespace mozilla #endif // mozilla_dom_power_PowerManagerService_h
new file mode 100644 --- /dev/null +++ b/dom/power/Types.h @@ -0,0 +1,21 @@ +/* -*- 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/. */ +#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 +
new file mode 100644 --- /dev/null +++ b/dom/power/WakeLock.cpp @@ -0,0 +1,204 @@ +/* -*- 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 "mozilla/Hal.h" +#include "mozilla/HalWakeLock.h" +#include "nsDOMClassInfoID.h" +#include "nsDOMError.h" +#include "nsIDOMWindow.h" +#include "nsIDOMEvent.h" +#include "nsIDOMDocument.h" +#include "nsIDOMEventTarget.h" +#include "nsPIDOMWindow.h" +#include "PowerManager.h" +#include "WakeLock.h" + +DOMCI_DATA(MozWakeLock, mozilla::dom::power::WakeLock) + +namespace mozilla { +namespace dom { +namespace power { + +NS_INTERFACE_MAP_BEGIN(WakeLock) + NS_INTERFACE_MAP_ENTRY(nsIDOMMozWakeLock) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMMozWakeLock) + NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener) + NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(MozWakeLock) +NS_INTERFACE_MAP_END + +NS_IMPL_ADDREF(WakeLock) +NS_IMPL_RELEASE(WakeLock) + +WakeLock::WakeLock() + : mLocked(false) + , mHidden(true) +{ +} + +WakeLock::~WakeLock() +{ + DoUnlock(); + DetachEventListener(); +} + +nsresult +WakeLock::Init(const nsAString &aTopic, nsIDOMWindow *aWindow) +{ + mTopic.Assign(aTopic); + + mWindow = do_GetWeakReference(aWindow); + nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow); + + /** + * Null windows are allowed. A wake lock without associated window + * is always considered invisible. + */ + if (window) { + nsCOMPtr<nsIDOMDocument> domDoc = window->GetExtantDocument(); + NS_ENSURE_STATE(domDoc); + domDoc->GetMozHidden(&mHidden); + } + + AttachEventListener(); + DoLock(); + + 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); + } +} + +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); + } +} + +void +WakeLock::AttachEventListener() +{ + nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow); + + if (window) { + nsCOMPtr<nsIDOMDocument> domDoc = window->GetExtantDocument(); + if (domDoc) { + nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(domDoc); + target->AddSystemEventListener(NS_LITERAL_STRING("mozvisibilitychange"), + this, + /* useCapture = */ true, + /* wantsUntrusted = */ false); + + 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() +{ + nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow); + + if (window) { + nsCOMPtr<nsIDOMDocument> domDoc = window->GetExtantDocument(); + if (domDoc) { + nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(domDoc); + target->RemoveSystemEventListener(NS_LITERAL_STRING("mozvisibilitychange"), + this, + /* useCapture = */ true); + target = do_QueryInterface(window); + target->RemoveSystemEventListener(NS_LITERAL_STRING("pagehide"), + this, + /* useCapture = */ true); + target->RemoveSystemEventListener(NS_LITERAL_STRING("pageshow"), + this, + /* useCapture = */ true); + } + } +} + +NS_IMETHODIMP +WakeLock::Unlock() +{ + /* + * We throw NS_ERROR_DOM_INVALID_STATE_ERR on double unlock. + */ + if (!mLocked) { + return NS_ERROR_DOM_INVALID_STATE_ERR; + } + + DoUnlock(); + DetachEventListener(); + + return NS_OK; +} + +NS_IMETHODIMP +WakeLock::GetTopic(nsAString &aTopic) +{ + aTopic.Assign(mTopic); + return NS_OK; +} + +NS_IMETHODIMP +WakeLock::HandleEvent(nsIDOMEvent *aEvent) +{ + nsAutoString type; + aEvent->GetType(type); + + if (type.EqualsLiteral("mozvisibilitychange")) { + nsCOMPtr<nsIDOMEventTarget> target; + aEvent->GetTarget(getter_AddRefs(target)); + nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(target); + NS_ENSURE_STATE(domDoc); + domDoc->GetMozHidden(&mHidden); + + if (mLocked) { + hal::ModifyWakeLock(mTopic, + hal::WAKE_LOCK_NO_CHANGE, + mHidden ? hal::WAKE_LOCK_ADD_ONE : hal::WAKE_LOCK_REMOVE_ONE); + } + + return NS_OK; + } + + if (type.EqualsLiteral("pagehide")) { + DoUnlock(); + return NS_OK; + } + + if (type.EqualsLiteral("pageshow")) { + DoLock(); + return NS_OK; + } + + return NS_OK; +} + +} // power +} // dom +} // mozilla
new file mode 100644 --- /dev/null +++ b/dom/power/WakeLock.h @@ -0,0 +1,53 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_power_WakeLock_h +#define mozilla_dom_power_WakeLock_h + +#include "nsCOMPtr.h" +#include "nsIDOMWakeLock.h" +#include "nsIDOMEventListener.h" +#include "nsString.h" +#include "nsWeakReference.h" + +class nsIDOMWindow; + +namespace mozilla { +namespace dom { +namespace power { + +class WakeLock + : public nsIDOMMozWakeLock + , public nsIDOMEventListener +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIDOMMOZWAKELOCK + NS_DECL_NSIDOMEVENTLISTENER + + WakeLock(); + virtual ~WakeLock(); + + nsresult Init(const nsAString &aTopic, nsIDOMWindow *aWindow); + +private: + void DoUnlock(); + void DoLock(); + void AttachEventListener(); + void DetachEventListener(); + + bool mLocked; + bool mHidden; + nsString mTopic; + + // window that this was created for. Weak reference. + nsWeakPtr mWindow; +}; + +} // namespace power +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_power_WakeLock_h
--- a/dom/power/nsIDOMPowerManager.idl +++ b/dom/power/nsIDOMPowerManager.idl @@ -32,14 +32,45 @@ * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "nsISupports.idl" -[scriptable, uuid(6ec16abc-2fe8-4ab3-99b0-0f08405be81b)] +interface nsIDOMMozWakeLockListener; + +/** + * This interface implements navigator.mozPower + */ +[scriptable, uuid(abf4b2b1-139d-4eff-998d-8f24616910ae)] interface nsIDOMMozPowerManager : nsISupports { - void powerOff(); - void reboot(); + void powerOff(); + void reboot(); + + /** + * The listeners are notified when a resource changes its lock state to: + * - unlocked + * - locked but not visible + * - locked and visible + */ + void addWakeLockListener(in nsIDOMMozWakeLockListener aListener); + void removeWakeLockListener(in nsIDOMMozWakeLockListener aListener); + + /** + * Query the wake lock state of the topic. + * + * 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. + */ + DOMString getWakeLockState(in DOMString aTopic); };
new file mode 100644 --- /dev/null +++ b/dom/power/nsIDOMWakeLock.idl @@ -0,0 +1,19 @@ +/* -*- 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, uuid(2e61eed1-5983-4562-8f26-fd361ab4a00d)] +interface nsIDOMMozWakeLock : nsISupports +{ + readonly attribute DOMString topic; + + /** + * Release the wake lock. + * + * @throw NS_ERROR_DOM_INVALID_STATE_ERR if already unlocked. + */ + void unlock(); +};
new file mode 100644 --- /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); +};
--- a/dom/power/nsIPowerManagerService.idl +++ b/dom/power/nsIPowerManagerService.idl @@ -37,14 +37,31 @@ #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" %} -[scriptable, builtinclass, uuid(38919539-4641-4f0b-9f11-6b6294a9386f)] +interface nsIDOMMozWakeLock; +interface nsIDOMMozWakeLockListener; +interface nsIDOMWindow; + +/** + * For use with non-content code. + */ +[scriptable, builtinclass, uuid(235ca1a1-d0c8-41f3-9b4a-dbaa4437d69c)] interface nsIPowerManagerService : nsISupports { - void powerOff(); - void reboot(); + void powerOff(); + void reboot(); + void addWakeLockListener(in nsIDOMMozWakeLockListener aListener); + void removeWakeLockListener(in nsIDOMMozWakeLockListener aListener); + DOMString getWakeLockState(in DOMString aTopic); + + /** + * Return a wake lock object of aTopic associated with aWindow. + * A wake lock without associated window, e.g. used in chrome, is + * always considered invisible. + */ + nsIDOMMozWakeLock newWakeLock(in DOMString aTopic, [optional] in nsIDOMWindow aWindow); };
--- a/dom/power/test/Makefile.in +++ b/dom/power/test/Makefile.in @@ -47,13 +47,20 @@ DIRS = \ $(NULL) include $(topsrcdir)/config/rules.mk _TEST_FILES = \ test_power_basics.html \ $(NULL) +_BROWSER_TEST_FILES = \ + browser_bug697132.js \ + $(NULL) + libs:: $(_TEST_FILES) $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir) +libs:: $(_BROWSER_TEST_FILES) + $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir) + #libs:: $(_CHROME_TEST_FILES) # $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
new file mode 100644 --- /dev/null +++ b/dom/power/test/browser_bug697132.js @@ -0,0 +1,235 @@ +"use strict"; + +waitForExplicitFinish(); + +let kPrefNode = "dom.power.whitelist"; +let kPageSource1 = "data:text/html,1"; +let kPageSource2 = "data:text/html,2"; + +let gOldPref; +let gWin, gWin1, gWin2; +let gTab, gTab1, gTab2; +let gLock, gLock1, gLock2; +let gCurStepIndex = -1; +let gSteps = [ + function basicWakeLock() { + gTab = gBrowser.addTab(kPageSource1); + 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.code, DOMException.INVALID_STATE_ERR, "double unlock should throw InvalidStateError"); + } + + gBrowser.removeTab(gTab); + + executeSoon(runNextStep); + }, true); + }, + function multiWakeLock() { + gTab = gBrowser.addTab(kPageSource1); + 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(kPageSource1); + gWin1 = gBrowser.getBrowserForTab(gTab1).contentWindow; + gTab2 = gBrowser.addTab(kPageSource1); + 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.mozHidden, true, + "window is background") + is(gWin2.navigator.mozPower.getWakeLockState("test"), "locked-background", + "wake lock is background"); + let doc2 = gWin2.document; + doc2.addEventListener("mozvisibilitychange", function onVisibilityChange(e) { + if (!doc2.mozHidden) { + doc2.removeEventListener("mozvisibilitychange", onVisibilityChange); + executeSoon(runNextStep); + } + }); + gBrowser.selectedTab = gTab2; + }, true); + }, + function crossTabWakeLock2() { + is(gWin2.document.mozHidden, 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 = kPageSource2; + }, + 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.mozHidden, 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 = kPageSource2; + }, + 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.mozHidden, true, + "gWin1 is background"); + is(gWin2.document.mozHidden, 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 { + Services.prefs.setCharPref(kPrefNode, gOldPref); + finish(); + } +} + +function test() { + try { + gOldPref = Services.prefs.getCharPref(kPrefNode); + } catch (e) { + gOldPref = ""; + } + // data url inherits its parent's principal, which is |about:| here. + Services.prefs.setCharPref(kPrefNode, "about:"); + runNextStep(); +}
--- a/hal/Hal.cpp +++ b/hal/Hal.cpp @@ -179,55 +179,73 @@ public: void RemoveObserver(Observer<InfoType>* aObserver) { MOZ_ASSERT(mObservers); mObservers->RemoveObserver(aObserver); if (mObservers->Length() == 0) { DisableNotifications(); + OnNotificationsDisabled(); + delete mObservers; mObservers = 0; - - mHasValidCache = false; } } + void BroadcastInformation(const InfoType& aInfo) { + MOZ_ASSERT(mObservers); + mObservers->Broadcast(aInfo); + } + +protected: + virtual void EnableNotifications() = 0; + virtual void DisableNotifications() = 0; + virtual void OnNotificationsDisabled() {} + +private: + mozilla::ObserverList<InfoType>* mObservers; +}; + +template <class InfoType> +class CachingObserversManager : public ObserversManager<InfoType> +{ +public: InfoType GetCurrentInformation() { if (mHasValidCache) { return mInfo; } mHasValidCache = true; GetCurrentInformationInternal(&mInfo); return mInfo; } void CacheInformation(const InfoType& aInfo) { mHasValidCache = true; mInfo = aInfo; } void BroadcastCachedInformation() { - MOZ_ASSERT(mObservers); - mObservers->Broadcast(mInfo); + BroadcastInformation(mInfo); } protected: - virtual void EnableNotifications() = 0; - virtual void DisableNotifications() = 0; virtual void GetCurrentInformationInternal(InfoType*) = 0; + virtual void OnNotificationsDisabled() { + mHasValidCache = false; + } + private: - mozilla::ObserverList<InfoType>* mObservers; InfoType mInfo; bool mHasValidCache; }; -class BatteryObserversManager : public ObserversManager<BatteryInformation> +class BatteryObserversManager : public CachingObserversManager<BatteryInformation> { protected: void EnableNotifications() { PROXY_IF_SANDBOXED(EnableBatteryNotifications()); } void DisableNotifications() { PROXY_IF_SANDBOXED(DisableBatteryNotifications()); @@ -235,17 +253,17 @@ protected: void GetCurrentInformationInternal(BatteryInformation* aInfo) { PROXY_IF_SANDBOXED(GetCurrentBatteryInformation(aInfo)); } }; static BatteryObserversManager sBatteryObservers; -class NetworkObserversManager : public ObserversManager<NetworkInformation> +class NetworkObserversManager : public CachingObserversManager<NetworkInformation> { protected: void EnableNotifications() { PROXY_IF_SANDBOXED(EnableNetworkNotifications()); } void DisableNotifications() { PROXY_IF_SANDBOXED(DisableNetworkNotifications()); @@ -253,16 +271,30 @@ protected: void GetCurrentInformationInternal(NetworkInformation* aInfo) { PROXY_IF_SANDBOXED(GetCurrentNetworkInformation(aInfo)); } }; static NetworkObserversManager sNetworkObservers; +class WakeLockObserversManager : public ObserversManager<WakeLockInformation> +{ +protected: + void EnableNotifications() { + PROXY_IF_SANDBOXED(EnableWakeLockNotifications()); + } + + void DisableNotifications() { + PROXY_IF_SANDBOXED(DisableWakeLockNotifications()); + } +}; + +static WakeLockObserversManager sWakeLockObservers; + void RegisterBatteryObserver(BatteryObserver* aObserver) { AssertMainThread(); sBatteryObservers.AddObserver(aObserver); } void @@ -430,10 +462,47 @@ void Reboot() } void PowerOff() { AssertMainThread(); PROXY_IF_SANDBOXED(PowerOff()); } +void +RegisterWakeLockObserver(WakeLockObserver* aObserver) +{ + AssertMainThread(); + sWakeLockObservers.AddObserver(aObserver); +} + +void +UnregisterWakeLockObserver(WakeLockObserver* aObserver) +{ + AssertMainThread(); + sWakeLockObservers.RemoveObserver(aObserver); +} + +void +ModifyWakeLock(const nsAString &aTopic, + hal::WakeLockControl aLockAdjust, + hal::WakeLockControl aHiddenAdjust) +{ + AssertMainThread(); + PROXY_IF_SANDBOXED(ModifyWakeLock(aTopic, aLockAdjust, aHiddenAdjust)); +} + +void +GetWakeLockInfo(const nsAString &aTopic, WakeLockInformation *aWakeLockInfo) +{ + AssertMainThread(); + PROXY_IF_SANDBOXED(GetWakeLockInfo(aTopic, aWakeLockInfo)); +} + +void +NotifyWakeLockChange(const WakeLockInformation& aInfo) +{ + AssertMainThread(); + sWakeLockObservers.BroadcastInformation(aInfo); +} + } // namespace hal } // namespace mozilla
--- a/hal/Hal.h +++ b/hal/Hal.h @@ -9,16 +9,17 @@ #include "mozilla/hal_sandbox/PHal.h" #include "base/basictypes.h" #include "mozilla/Types.h" #include "nsTArray.h" #include "prlog.h" #include "mozilla/dom/battery/Types.h" #include "mozilla/dom/network/Types.h" +#include "mozilla/dom/power/Types.h" #include "mozilla/hal_sandbox/PHal.h" /* * Hal.h contains the public Hal API. * * By default, this file defines its functions in the hal namespace, but if * MOZ_HAL_NAMESPACE is defined, we'll define our functions in that namespace. * @@ -235,16 +236,65 @@ void SetTimezone(const nsCString& aTimez */ void Reboot(); /** * Power off the device. */ void PowerOff(); +/** + * Enable wake lock notifications from the backend. + * + * This method is only used by WakeLockObserversManager. + */ +void EnableWakeLockNotifications(); + +/** + * Disable wake lock notifications from the backend. + * + * This method is only used by WakeLockObserversManager. + */ +void DisableWakeLockNotifications(); + +/** + * Inform the wake lock backend there is a new wake lock observer. + * @param aWakeLockObserver The observer that should be added. + */ +void RegisterWakeLockObserver(WakeLockObserver* aObserver); + +/** + * Inform the wake lock backend a wake lock observer unregistered. + * @param aWakeLockObserver The observer that should be removed. + */ +void UnregisterWakeLockObserver(WakeLockObserver* aObserver); + +/** + * Adjust the internal wake lock counts. + * @param aTopic lock topic + * @param aLockAdjust to increase or decrease active locks + * @param aHiddenAdjust to increase or decrease hidden locks + */ +void ModifyWakeLock(const nsAString &aTopic, + hal::WakeLockControl aLockAdjust, + hal::WakeLockControl aHiddenAdjust); + +/** + * Query the wake lock numbers of aTopic. + * @param aTopic lock topic + * @param aWakeLockInfo wake lock numbers + */ +void GetWakeLockInfo(const nsAString &aTopic, hal::WakeLockInformation *aWakeLockInfo); + +/** + * Notify of a change in the wake lock state. + * @param aWakeLockInfo The new wake lock information. + */ +void NotifyWakeLockChange(const hal::WakeLockInformation& aWakeLockInfo); + } // namespace MOZ_HAL_NAMESPACE } // namespace mozilla #ifdef MOZ_DEFINED_HAL_NAMESPACE # undef MOZ_DEFINED_HAL_NAMESPACE # undef MOZ_HAL_NAMESPACE #endif
--- a/hal/HalTypes.h +++ b/hal/HalTypes.h @@ -31,19 +31,35 @@ enum LightMode { eHalLightMode_User = 0, // brightness is managed by user setting eHalLightMode_Sensor = 1 // brightness is managed by a light sensor }; enum FlashMode { eHalLightFlash_None = 0, eHalLightFlash_Timed = 1, // timed flashing. Use flashOnMS and flashOffMS for timing eHalLightFlash_Hardware = 2 // hardware assisted flashing }; + } // namespace hal } // namespace mozilla +namespace mozilla { +namespace hal { + +/** + * Used by ModifyWakeLock + */ +enum WakeLockControl { + WAKE_LOCK_REMOVE_ONE = -1, + WAKE_LOCK_NO_CHANGE = 0, + WAKE_LOCK_ADD_ONE = 1, +}; + +} +} + namespace IPC { /** * Light type serializer. */ template <> struct ParamTraits<mozilla::hal::LightType> : public EnumSerializer<mozilla::hal::LightType, @@ -66,11 +82,21 @@ struct ParamTraits<mozilla::hal::LightMo */ template <> struct ParamTraits<mozilla::hal::FlashMode> : public EnumSerializer<mozilla::hal::FlashMode, mozilla::hal::eHalLightFlash_None, mozilla::hal::eHalLightFlash_Hardware> {}; +/** + * WakeLockControl serializer. + */ +template <> +struct ParamTraits<mozilla::hal::WakeLockControl> + : public EnumSerializer<mozilla::hal::WakeLockControl, + mozilla::hal::WAKE_LOCK_REMOVE_ONE, + mozilla::hal::WAKE_LOCK_ADD_ONE> +{}; + } // namespace IPC #endif // mozilla_hal_Types_h
new file mode 100644 --- /dev/null +++ b/hal/HalWakeLock.cpp @@ -0,0 +1,125 @@ +/* -*- 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 "mozilla/Hal.h" +#include "mozilla/HalWakeLock.h" +#include "mozilla/ClearOnShutdown.h" +#include "nsDataHashtable.h" +#include "nsHashKeys.h" + +using namespace mozilla::hal; + +namespace mozilla { +namespace hal { + +WakeLockState +ComputeWakeLockState(int aNumLocks, int aNumHidden) +{ + if (aNumLocks == 0) { + return WAKE_LOCK_STATE_UNLOCKED; + } else if (aNumLocks == aNumHidden) { + return WAKE_LOCK_STATE_HIDDEN; + } else { + return WAKE_LOCK_STATE_VISIBLE; + } +} + +} // hal +} // mozilla + +namespace mozilla { +namespace hal_impl { + +namespace { +struct LockCount { + PRUint32 numLocks; + PRUint32 numHidden; +}; +} + +static int sActiveChildren = 0; +static nsAutoPtr<nsDataHashtable<nsStringHashKey, LockCount> > sLockTable; +static bool sInitialized = false; + +static void +Init() +{ + sLockTable = new nsDataHashtable<nsStringHashKey, LockCount>(); + sLockTable->Init(); + ClearOnShutdown(&sLockTable); + sInitialized = true; +} + +void +EnableWakeLockNotifications() +{ + sActiveChildren++; +} + +void +DisableWakeLockNotifications() +{ + sActiveChildren--; +} + +void +ModifyWakeLock(const nsAString &aTopic, + hal::WakeLockControl aLockAdjust, + hal::WakeLockControl aHiddenAdjust) +{ + if (!sInitialized) { + Init(); + } + + LockCount count; + count.numLocks = 0; + count.numHidden = 0; + sLockTable->Get(aTopic, &count); + MOZ_ASSERT(count.numLocks >= count.numHidden); + MOZ_ASSERT(aLockAdjust >= 0 || count.numLocks > 0); + MOZ_ASSERT(aHiddenAdjust >= 0 || count.numHidden > 0); + + WakeLockState oldState = ComputeWakeLockState(count.numLocks, count.numHidden); + + count.numLocks += aLockAdjust; + count.numHidden += aHiddenAdjust; + MOZ_ASSERT(count.numLocks >= count.numHidden); + + if (count.numLocks) { + sLockTable->Put(aTopic, count); + } else { + sLockTable->Remove(aTopic); + } + + WakeLockState newState = ComputeWakeLockState(count.numLocks, count.numHidden); + + if (sActiveChildren && oldState != newState) { + WakeLockInformation info; + info.numLocks() = count.numLocks; + info.numHidden() = count.numHidden; + info.topic() = aTopic; + NotifyWakeLockChange(info); + } +} + +void +GetWakeLockInfo(const nsAString &aTopic, WakeLockInformation *aWakeLockInfo) +{ + if (!sInitialized) { + Init(); + } + + LockCount count; + count.numLocks = 0; + count.numHidden = 0; + sLockTable->Get(aTopic, &count); + + aWakeLockInfo->numLocks() = count.numLocks; + aWakeLockInfo->numHidden() = count.numHidden; + aWakeLockInfo->topic() = aTopic; +} + +} // hal_impl +} // mozilla
new file mode 100644 --- /dev/null +++ b/hal/HalWakeLock.h @@ -0,0 +1,26 @@ +/* -*- 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/. */ + +#ifndef __HAL_WAKELOCK_H_ +#define __HAL_WAKELOCK_H_ + +namespace mozilla { +namespace hal { + +enum WakeLockState { + WAKE_LOCK_STATE_UNLOCKED, + WAKE_LOCK_STATE_HIDDEN, + WAKE_LOCK_STATE_VISIBLE +}; + +/** + * Return the wake lock state according to the numbers. + */ +WakeLockState ComputeWakeLockState(int aNumLocks, int aNumHidden); + +} // hal +} // mozilla + +#endif /* __HAL_WAKELOCK_H_ */
--- a/hal/Makefile.in +++ b/hal/Makefile.in @@ -58,22 +58,24 @@ EXPORT_LIBRARY = 1 EXPORTS_NAMESPACES = mozilla EXPORTS_mozilla = \ Hal.h \ HalImpl.h \ HalSandbox.h \ HalSensor.h \ HalTypes.h \ + HalWakeLock.h \ $(NULL) CPPSRCS = \ Hal.cpp \ SandboxHal.cpp \ WindowIdentifier.cpp \ + HalWakeLock.cpp \ $(NULL) ifeq (android,$(MOZ_WIDGET_TOOLKIT)) CPPSRCS += \ AndroidHal.cpp \ AndroidSensor.cpp \ $(NULL) else ifeq (gonk,$(MOZ_WIDGET_TOOLKIT))
--- a/hal/sandbox/PHal.ipdl +++ b/hal/sandbox/PHal.ipdl @@ -43,16 +43,17 @@ include "nspr/prtime.h"; include "mozilla/HalSensor.h"; include "mozilla/HalTypes.h"; using PRTime; using mozilla::hal::FlashMode; using mozilla::hal::LightType; using mozilla::hal::LightMode; using mozilla::hal::SensorType; +using mozilla::hal::WakeLockControl; namespace mozilla { namespace hal { struct BatteryInformation { double level; bool charging; double remainingTime; @@ -76,24 +77,33 @@ namespace hal { namespace hal { struct NetworkInformation { double bandwidth; bool canBeMetered; }; } +namespace hal { + struct WakeLockInformation { + uint32_t numLocks; + uint32_t numHidden; + nsString topic; + }; +} + namespace hal_sandbox { sync protocol PHal { manager PContent; child: NotifyBatteryChange(BatteryInformation aBatteryInfo); NotifyNetworkChange(NetworkInformation aNetworkInfo); + NotifyWakeLockChange(WakeLockInformation aWakeLockInfo); parent: Vibrate(uint32[] pattern, uint64[] id, PBrowser browser); CancelVibrate(uint64[] id, PBrowser browser); EnableBatteryNotifications(); DisableBatteryNotifications(); sync GetCurrentBatteryInformation() @@ -116,16 +126,22 @@ parent: sync SetLight(LightType light, LightConfiguration aConfig) returns (bool status); sync GetLight(LightType light) returns (LightConfiguration aConfig, bool status); Reboot(); PowerOff(); + ModifyWakeLock(nsString aTopic, WakeLockControl aLockAdjust, WakeLockControl aHiddenAdjust); + EnableWakeLockNotifications(); + DisableWakeLockNotifications(); + sync GetWakeLockInfo(nsString aTopic) + returns (WakeLockInformation aWakeLockInfo); + child: NotifySensorChange(SensorData aSensorData); parent: EnableSensorNotifications(SensorType aSensor); DisableSensorNotifications(SensorType aSensor); __delete__();
--- a/hal/sandbox/SandboxHal.cpp +++ b/hal/sandbox/SandboxHal.cpp @@ -164,20 +164,45 @@ EnableSensorNotifications(SensorType aSe Hal()->SendEnableSensorNotifications(aSensor); } void DisableSensorNotifications(SensorType aSensor) { Hal()->SendDisableSensorNotifications(aSensor); } +void +EnableWakeLockNotifications() +{ + Hal()->SendEnableWakeLockNotifications(); +} + +void +DisableWakeLockNotifications() +{ + Hal()->SendDisableWakeLockNotifications(); +} + +void +ModifyWakeLock(const nsAString &aTopic, WakeLockControl aLockAdjust, WakeLockControl aHiddenAdjust) +{ + Hal()->SendModifyWakeLock(nsString(aTopic), aLockAdjust, aHiddenAdjust); +} + +void +GetWakeLockInfo(const nsAString &aTopic, WakeLockInformation *aWakeLockInfo) +{ + Hal()->SendGetWakeLockInfo(nsString(aTopic), aWakeLockInfo); +} + class HalParent : public PHalParent , public BatteryObserver , public NetworkObserver , public ISensorObserver + , public WakeLockObserver { public: NS_OVERRIDE virtual bool RecvVibrate(const InfallibleTArray<unsigned int>& pattern, const InfallibleTArray<uint64> &id, PBrowserParent *browserParent) { // Check whether browserParent is active. We should have already @@ -338,16 +363,51 @@ public: RecvDisableSensorNotifications(const SensorType &aSensor) { hal::UnregisterSensorObserver(aSensor, this); return true; } void Notify(const SensorData& aSensorData) { unused << SendNotifySensorChange(aSensorData); } + + NS_OVERRIDE virtual bool + RecvModifyWakeLock(const nsString &aTopic, + const WakeLockControl &aLockAdjust, + const WakeLockControl &aHiddenAdjust) + { + hal::ModifyWakeLock(aTopic, aLockAdjust, aHiddenAdjust); + return true; + } + + NS_OVERRIDE virtual bool + RecvEnableWakeLockNotifications() + { + hal::RegisterWakeLockObserver(this); + return true; + } + + NS_OVERRIDE virtual bool + RecvDisableWakeLockNotifications() + { + hal::UnregisterWakeLockObserver(this); + return true; + } + + NS_OVERRIDE virtual bool + RecvGetWakeLockInfo(const nsString &aTopic, WakeLockInformation *aWakeLockInfo) + { + hal::GetWakeLockInfo(aTopic, aWakeLockInfo); + return true; + } + + void Notify(const WakeLockInformation& aWakeLockInfo) + { + unused << SendNotifyWakeLockChange(aWakeLockInfo); + } }; class HalChild : public PHalChild { public: NS_OVERRIDE virtual bool RecvNotifyBatteryChange(const BatteryInformation& aBatteryInfo) { hal::NotifyBatteryChange(aBatteryInfo); return true; @@ -356,16 +416,22 @@ public: NS_OVERRIDE virtual bool RecvNotifySensorChange(const hal::SensorData &aSensorData); NS_OVERRIDE virtual bool RecvNotifyNetworkChange(const NetworkInformation& aNetworkInfo) { hal::NotifyNetworkChange(aNetworkInfo); return true; } + + NS_OVERRIDE virtual bool + RecvNotifyWakeLockChange(const WakeLockInformation& aWakeLockInfo) { + hal::NotifyWakeLockChange(aWakeLockInfo); + return true; + } }; bool HalChild::RecvNotifySensorChange(const hal::SensorData &aSensorData) { hal::NotifySensorChange(aSensorData); return true; }